diff --git a/src/application/template/action/createApiKeyAction.ts b/src/application/template/action/createApiKeyAction.ts new file mode 100644 index 0000000..02e60e3 --- /dev/null +++ b/src/application/template/action/createApiKeyAction.ts @@ -0,0 +1,64 @@ +import {Action, ActionError} from '@/application/template/action/action'; +import {ActionContext} from '@/application/template/action/context'; +import {ApplicationApi, GeneratedApiKey} from '@/application/api/application'; +import {ApiKeyPermission} from '@/application/model/application'; +import {ConfigurationManager} from '@/application/project/configuration/manager/configurationManager'; +import {ErrorReason} from '@/application/error'; + +export type CreateApiKeyOptions = { + keyName: string, + environment: 'development' | 'production', + permissions: ApiKeyPermission[], + result: string, +}; + +export type Configuration = { + applicationApi: ApplicationApi, + configurationManager: ConfigurationManager, +}; + +export class CreateApiKeyAction implements Action { + private readonly configurationManager: ConfigurationManager; + + private readonly api: ApplicationApi; + + public constructor({configurationManager, applicationApi}: Configuration) { + this.configurationManager = configurationManager; + this.api = applicationApi; + } + + public async execute(options: CreateApiKeyOptions, context: ActionContext): Promise { + const {output} = context; + const configuration = await this.configurationManager.load(); + + const applicationSlug = options.environment === 'production' + ? configuration.applications.production + : configuration.applications.development; + + if (applicationSlug === undefined) { + throw new ActionError('The project has no application configured for the selected environment.', { + reason: ErrorReason.PRECONDITION, + }); + } + + const notifier = output?.notify('Creating API key'); + + let apiKey: GeneratedApiKey; + + try { + apiKey = await this.api.createApiKey({ + organizationSlug: configuration.organization, + workspaceSlug: configuration.workspace, + applicationSlug: applicationSlug, + name: options.keyName, + permissions: options.permissions, + }); + } catch (error) { + throw ActionError.fromCause(error); + } finally { + notifier?.stop(); + } + + context.set(options.result, apiKey.secret); + } +} diff --git a/src/infrastructure/application/cli/cli.ts b/src/infrastructure/application/cli/cli.ts index da906e7..e091311 100644 --- a/src/infrastructure/application/cli/cli.ts +++ b/src/infrastructure/application/cli/cli.ts @@ -311,6 +311,10 @@ import {ResolveImportAction} from '@/application/template/action/resolveImportAc import { ResolveImportOptionsValidator, } from '@/infrastructure/application/validation/actions/resolveImportOptionsValidator'; +import {CreateApiKeyAction} from '@/application/template/action/createApiKeyAction'; +import { + CreateApiKeyOptionsValidator, +} from '@/infrastructure/application/validation/actions/createApiKeyOptionsValidator'; export type Configuration = { program: Program, @@ -1353,6 +1357,13 @@ export class Cli { }), validator: new AddComponentOptionsValidator(), }), + 'create-api-key': new ValidatedAction({ + action: new CreateApiKeyAction({ + applicationApi: this.getApplicationApi(), + configurationManager: this.getConfigurationManager(), + }), + validator: new CreateApiKeyOptionsValidator(), + }), 'create-resource': new ValidatedAction({ action: new CreateResourceAction({ configurationManager: this.getConfigurationManager(), diff --git a/src/infrastructure/application/validation/actions/createApiKeyOptionsValidator.ts b/src/infrastructure/application/validation/actions/createApiKeyOptionsValidator.ts new file mode 100644 index 0000000..896f895 --- /dev/null +++ b/src/infrastructure/application/validation/actions/createApiKeyOptionsValidator.ts @@ -0,0 +1,27 @@ +import {z, ZodType, ZodTypeDef} from 'zod'; +import {ActionOptionsValidator} from '@/infrastructure/application/validation/actions/actionOptionsValidator'; +import {CreateApiKeyOptions} from '@/application/template/action/createApiKeyAction'; +import {ApiKeyPermission} from '@/application/model/application'; + +const schema: ZodType = z.strictObject({ + keyName: z.string().min(1), + environment: z.enum(['development', 'production']), + permissions: z.array( + z.enum( + ApiKeyPermission.all() + .flatMap( + value => [ + value.toUpperCase(), + value.toLowerCase(), + ], + ) as [string, ...string[]], + ).transform(ApiKeyPermission.fromValue), + ).min(1), + result: z.string().min(1), +}); + +export class CreateApiKeyOptionsValidator extends ActionOptionsValidator { + public constructor() { + super(schema); + } +}