Skip to content

Commit

Permalink
fix: improve secrets openapi spec (#2589)
Browse files Browse the repository at this point in the history
Co-authored-by: Alon Peretz <8467965+alonp99@users.noreply.github.com>
  • Loading branch information
MatanYadaev and alonp99 authored Jul 31, 2024
1 parent fb89dce commit ae89f57
Show file tree
Hide file tree
Showing 7 changed files with 21,603 additions and 16,390 deletions.
37,868 changes: 21,494 additions & 16,374 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion services/workflows-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"aws-cloudfront-sign": "3.0.2",
"axios": "^1.6.8",
"axios-retry": "^4.0.0",
"ballerine-nestjs-typebox": "3.0.2-next.9",
"ballerine-nestjs-typebox": "3.0.2-next.11",
"base64-stream": "^1.0.0",
"bcrypt": "5.1.0",
"class-transformer": "0.5.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ export class AwsSecretsManager implements SecretsManager {
);
}

async delete(key: string) {
const dataToSet = await this.getAll();
delete dataToSet[key];

await this.client.send(
new PutSecretValueCommand({
SecretId: this.getSecretName(),
SecretString: JSON.stringify(dataToSet),
}),
);
}

private async createSecret() {
await this.client.send(
new CreateSecretCommand({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ export class InMemorySecretsManager implements SecretsManager {
...data,
};
}

async delete(key: string) {
if (secretsStore[this.customerId]) {
delete secretsStore[this.customerId]![key];
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Type } from '@sinclair/typebox';

export const CreateSecretInputSchema = Type.Object({
secrets: Type.Record(
Type.String({
description: 'Secret name',
}),
Type.String({
description: 'Secret value',
}),
),
key: Type.String({
description: 'Secret key',
example: 'secret',
}),
value: Type.String({
description: 'Secret value',
example: 'secret-value',
}),
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as common from '@nestjs/common';

import { SecretsManagerFactory } from '@/secrets-manager/secrets-manager.factory';
import { ApiResponse, ApiTags } from '@nestjs/swagger';
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { type Static, Type } from '@sinclair/typebox';
import { Validate } from 'ballerine-nestjs-typebox';
import type { TProjectId } from '@/types';
Expand All @@ -10,42 +10,116 @@ import { env } from '@/env';
import { CustomerService } from '@/customer/customer.service';
import { CreateSecretInputSchema } from '@/secrets-manager/schemas/create-secret-input-schema';
import { UseCustomerAuthGuard } from '@/common/decorators/use-customer-auth-guard.decorator';
import { Res } from '@nestjs/common';
import express from 'express';

@ApiTags('Secrets')
@common.Controller('external/secrets')
export class SecretControllerExternal {
@common.Get('')
@UseCustomerAuthGuard()
@ApiBearerAuth()
@ApiResponse({
status: 403,
description: 'Forbidden',
schema: Type.Record(Type.String(), Type.Unknown()),
})
@ApiResponse({
status: 200,
description: 'All secrets returned successfully',
schema: Type.Record(Type.String(), Type.Unknown()),
})
@ApiOperation({
summary: 'List all secrets',
description: "An endpoint that returns all the current customer's secrets",
})
async list(@CurrentProject() projectId: TProjectId) {
const customer = await this.customerService.getByProjectId(projectId);

const secretsManager = this.secretsManagerFactory.create({
provider: env.SECRETS_MANAGER_PROVIDER,
customerId: customer.id,
});

return await secretsManager.getAll();
}

constructor(
private readonly secretsManagerFactory: SecretsManagerFactory,
private readonly customerService: CustomerService,
) {}

@UseCustomerAuthGuard()
@common.Post('')
@UseCustomerAuthGuard()
@ApiBearerAuth()
@ApiOperation({
summary: 'Set a secret',
description: 'An endpoint that sets a secret for the current customer',
})
@ApiResponse({
status: 403,
description: 'Forbidden',
schema: Type.Record(Type.String(), Type.Unknown()),
})
@ApiResponse({
status: 204,
description: 'Secret set successfully',
})
@Validate({
request: [
{
type: 'body',
schema: CreateSecretInputSchema,
},
],
response: Type.Any(),
})
async create(
@common.Body() { secrets }: Static<typeof CreateSecretInputSchema>,
@common.Body() { key, value }: Static<typeof CreateSecretInputSchema>,
@CurrentProject() currentProjectId: TProjectId,
) {
@Res() res: express.Response,
): Promise<any> {
const customer = await this.customerService.getByProjectId(currentProjectId);

const secretsManager = this.secretsManagerFactory.create({
provider: env.SECRETS_MANAGER_PROVIDER,
customerId: customer.id,
});

await secretsManager.set(secrets);
await secretsManager.set({ [key]: value });

res.status(204).send();
}

@common.Delete('/:key')
@UseCustomerAuthGuard()
@ApiBearerAuth()
@ApiOperation({
summary: 'Delete a secret',
description: 'An endpoint that deletes a secret for the current customer',
})
@ApiResponse({
status: 403,
description: 'Forbidden',
schema: Type.Record(Type.String(), Type.Unknown()),
})
@ApiResponse({
status: 204,
description: 'Secret deleted successfully',
})
async delete(
@common.Param('key') key: string,
@CurrentProject() projectId: TProjectId,
@Res() res: express.Response,
) {
const customer = await this.customerService.getByProjectId(projectId);

const secretsManager = this.secretsManagerFactory.create({
provider: env.SECRETS_MANAGER_PROVIDER,
customerId: customer.id,
});

await secretsManager.delete(key);

res.status(204).send();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface SecretsManager {
getAll(): Promise<Record<string, string>>;
set(data: Record<string, string>): Promise<void>;
set(data: Record<string, string | undefined>): Promise<void>;
delete(key: string): Promise<void>;
}

0 comments on commit ae89f57

Please sign in to comment.