Skip to content

Commit

Permalink
Add permissions params to create API key mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
floganz committed Nov 29, 2024
1 parent ea845ad commit 874162c
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 23 deletions.
3 changes: 2 additions & 1 deletion app/graphql/mutations/api_keys/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ class Create < BaseMutation
description 'Creates a new API key'

argument :name, String, required: false
argument :permissions, GraphQL::Types::JSON, required: false

type Types::ApiKeys::Object

def resolve(**args)
result = ::ApiKeys::CreateService.call(args.merge(organization_id: current_organization.id))
result = ::ApiKeys::CreateService.call(args.merge(organization: current_organization))

result.success? ? result.api_key : result_error(result)
end
Expand Down
4 changes: 2 additions & 2 deletions app/models/api_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ class ApiKey < ApplicationRecord

RESOURCES = %w[
add_on analytic billable_metric coupon applied_coupon credit_note customer_usage
customer fee invoice organization payment_request plan subscription lifetime_usage
tax wallet wallet_transaction webhook_endpoint
customer event fee invoice organization payment_request plan subscription lifetime_usage
tax wallet wallet_transaction webhook_endpoint webhook_jwt_public_key
].freeze

MODES = %w[read write].freeze
Expand Down
6 changes: 5 additions & 1 deletion app/services/api_keys/create_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ def initialize(params)
def call
return result.forbidden_failure! unless License.premium?

if params[:permissions].present? && !params[:organization].premium_integrations.include?('api_permissions')
return result.forbidden_failure!(code: 'premium_integration_missing')
end

api_key = ApiKey.create!(
params.slice(:organization_id, :name)
params.slice(:organization, :name, :permissions)
)

ApiKeyMailer.with(api_key:).created.deliver_later
Expand Down
13 changes: 13 additions & 0 deletions db/migrate/20241128132010_add_new_permissions_to_api_keys.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class AddNewPermissionsToApiKeys < ActiveRecord::Migration[7.1]
def up
ApiKey.update_all(permissions: ApiKey.default_permissions) # rubocop:disable Rails/SkipsModelValidations
end

def down
ApiKey.update_all( # rubocop:disable Rails/SkipsModelValidations
permissions: ApiKey.default_permissions.without("event", "webhook_jwt_public_key")
)
end
end
2 changes: 1 addition & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions schema.graphql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

175 changes: 157 additions & 18 deletions spec/services/api_keys/create_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,175 @@
describe '#call' do
subject(:service_result) { described_class.call(params) }

let!(:params) do
{
organization_id: create(:organization).id,
name: Faker::Lorem.words.join(' ')
}
end
let(:name) { Faker::Lorem.words.join(' ') }
let(:organization) { create(:organization) }

context 'with premium organization' do
around { |test| lago_premium!(&test) }

it 'creates a new API key' do
expect { service_result }.to change(ApiKey, :count).by(1)
context 'when permissions hash is provided' do
let(:params) { {permissions:, name:, organization:} }
let(:permissions) { ApiKey.default_permissions }

before { organization.update!(premium_integrations:) }

context 'when organization has api permissions addon' do
let(:premium_integrations) { ['api_permissions'] }

it 'creates a new API key' do
expect { service_result }.to change(ApiKey, :count).by(1)
end

it 'sends an API key created email' do
expect { service_result }
.to have_enqueued_mail(ApiKeyMailer, :created)
.with(hash_including(params: {api_key: instance_of(ApiKey)}))
end
end

context 'when organization has no api permissions addon' do
let(:premium_integrations) { [] }

it 'does not create an API key' do
expect { service_result }.not_to change(ApiKey, :count)
end

it 'does not send an API key created email' do
expect { service_result }.not_to have_enqueued_mail(ApiKeyMailer, :created)
end

it 'returns an error' do
aggregate_failures do
expect(service_result).not_to be_success
expect(service_result.error).to be_a(BaseService::ForbiddenFailure)
expect(service_result.error.code).to eq('premium_integration_missing')
end
end
end
end

it 'sends an API key created email' do
expect { service_result }
.to have_enqueued_mail(ApiKeyMailer, :created)
.with(hash_including(params: {api_key: instance_of(ApiKey)}))
context 'when permissions hash is missing' do
let(:params) { {name:, organization:} }

before { organization.update!(premium_integrations:) }

context 'when organization has api permissions addon' do
let(:premium_integrations) { ['api_permissions'] }

it 'creates a new API key' do
expect { service_result }.to change(ApiKey, :count).by(1)
end

it 'sends an API key created email' do
expect { service_result }
.to have_enqueued_mail(ApiKeyMailer, :created)
.with(hash_including(params: {api_key: instance_of(ApiKey)}))
end
end

context 'when organization has no api permissions addon' do
let(:premium_integrations) { [] }

it 'creates a new API key' do
expect { service_result }.to change(ApiKey, :count).by(1)
end

it 'sends an API key created email' do
expect { service_result }
.to have_enqueued_mail(ApiKeyMailer, :created)
.with(hash_including(params: {api_key: instance_of(ApiKey)}))
end
end
end
end

context 'with free organization' do
it 'does not create an API key' do
expect { service_result }.not_to change(ApiKey, :count)
context 'when permissions hash is provided' do
let(:params) { {permissions:, name:, organization:} }
let(:permissions) { ApiKey.default_permissions }

before { organization.update!(premium_integrations:) }

context 'when organization has api permissions addon' do
let(:premium_integrations) { ['api_permissions'] }

it 'does not create an API key' do
expect { service_result }.not_to change(ApiKey, :count)
end

it 'does not send an API key created email' do
expect { service_result }.not_to have_enqueued_mail(ApiKeyMailer, :created)
end

it 'returns an error' do
aggregate_failures do
expect(service_result).not_to be_success
expect(service_result.error).to be_a(BaseService::ForbiddenFailure)
end
end
end

context 'when organization has no api permissions addon' do
let(:premium_integrations) { [] }

it 'does not create an API key' do
expect { service_result }.not_to change(ApiKey, :count)
end

it 'does not send an API key created email' do
expect { service_result }.not_to have_enqueued_mail(ApiKeyMailer, :created)
end

it 'returns an error' do
aggregate_failures do
expect(service_result).not_to be_success
expect(service_result.error).to be_a(BaseService::ForbiddenFailure)
end
end
end
end

it 'returns an error' do
aggregate_failures do
expect(service_result).not_to be_success
expect(service_result.error).to be_a(BaseService::ForbiddenFailure)
context 'when permissions hash is missing' do
let(:params) { {name:, organization:} }

before { organization.update!(premium_integrations:) }

context 'when organization has api permissions addon' do
let(:premium_integrations) { ['api_permissions'] }

it 'does not create an API key' do
expect { service_result }.not_to change(ApiKey, :count)
end

it 'does not send an API key created email' do
expect { service_result }.not_to have_enqueued_mail(ApiKeyMailer, :created)
end

it 'returns an error' do
aggregate_failures do
expect(service_result).not_to be_success
expect(service_result.error).to be_a(BaseService::ForbiddenFailure)
end
end
end

context 'when organization has no api permissions addon' do
let(:premium_integrations) { [] }

it 'does not create an API key' do
expect { service_result }.not_to change(ApiKey, :count)
end

it 'does not send an API key created email' do
expect { service_result }.not_to have_enqueued_mail(ApiKeyMailer, :created)
end

it 'returns an error' do
aggregate_failures do
expect(service_result).not_to be_success
expect(service_result.error).to be_a(BaseService::ForbiddenFailure)
end
end
end
end
end
Expand Down

0 comments on commit 874162c

Please sign in to comment.