Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

misc(api-key-management): Add permissions params to create API key mutation #2892

Merged
merged 1 commit into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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