From 54b1798bd54dc03da31b57b9e5cc2428ac243f43 Mon Sep 17 00:00:00 2001 From: kwasniew Date: Fri, 3 Jan 2025 12:59:39 +0100 Subject: [PATCH 1/4] feat: ability to upsert single legal values --- src/lib/features/context/context-service.ts | 43 ++++++++++++++++- src/lib/features/context/context.test.ts | 53 +++++++++++++++++++++ src/lib/features/context/context.ts | 35 ++++++++++++++ src/lib/services/context-schema.ts | 2 +- 4 files changed, 130 insertions(+), 3 deletions(-) diff --git a/src/lib/features/context/context-service.ts b/src/lib/features/context/context-service.ts index da67f4d4632d..db4dd29444e1 100644 --- a/src/lib/features/context/context-service.ts +++ b/src/lib/features/context/context-service.ts @@ -20,9 +20,10 @@ import { } from '../../types'; import type { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType'; import type EventService from '../events/event-service'; -import { contextSchema } from '../../services/context-schema'; +import { contextSchema, legalValueSchema } from '../../services/context-schema'; import { NameExistsError } from '../../error'; import { nameSchema } from '../../schema/feature-schema'; +import type { LegalValueSchema } from '../../openapi'; class ContextService { private eventService: EventService; @@ -126,7 +127,6 @@ class ContextService { ); const value = await contextSchema.validateAsync(updatedContextField); - // update await this.contextFieldStore.update(value); const { createdAt, sortOrder, ...previousContextField } = contextField; @@ -140,6 +140,45 @@ class ContextService { }); } + async updateContextFieldLegalValue( + contextFieldLegalValue: { name: string; legalValue: LegalValueSchema }, + auditUser: IAuditUser, + ): Promise { + const contextField = await this.contextFieldStore.get( + contextFieldLegalValue.name, + ); + const validatedLegalValue = await legalValueSchema.validateAsync( + contextFieldLegalValue.legalValue, + ); + + const legalValues = contextField.legalValues + ? [...contextField.legalValues] + : []; + + const existingIndex = legalValues.findIndex( + (legalvalue) => legalvalue.value === validatedLegalValue.value, + ); + + if (existingIndex !== -1) { + legalValues[existingIndex] = validatedLegalValue; + } else { + legalValues.push(validatedLegalValue); + } + + const newContextField = { ...contextField, legalValues }; + + await this.contextFieldStore.update(newContextField); + + await this.eventService.storeEvent({ + type: CONTEXT_FIELD_UPDATED, + createdBy: auditUser.username, + createdByUserId: auditUser.id, + ip: auditUser.ip, + preData: contextField, + data: newContextField, + }); + } + async deleteContextField( name: string, auditUser: IAuditUser, diff --git a/src/lib/features/context/context.test.ts b/src/lib/features/context/context.test.ts index 0ed95aab4371..0b6c90f3f7ba 100644 --- a/src/lib/features/context/context.test.ts +++ b/src/lib/features/context/context.test.ts @@ -151,6 +151,59 @@ test('should update a context field with new legal values', () => { .expect(200); }); +test('should add and update a single context field with new legal values', async () => { + expect.assertions(1); + + // non existent context + await request + .put(`${base}/api/admin/context/doesntexist/legalValues`) + .send({ + value: 'local', + description: 'Local environment', + }) + .set('Content-Type', 'application/json') + .expect(404); + + // invalid schema + await request + .put(`${base}/api/admin/context/environment/legalValues`) + .send({ + valueInvalid: 'invalid schema', + description: 'Local environment', + }) + .set('Content-Type', 'application/json') + .expect(400); + + // add a new context field legal value + await request + .put(`${base}/api/admin/context/environment/legalValues`) + .send({ + value: 'newvalue', + description: 'new description', + }) + .set('Content-Type', 'application/json') + .expect(200); + + // update existing context field legal value description + await request + .put(`${base}/api/admin/context/environment/legalValues`) + .send({ + value: 'newvalue', + description: 'updated description', + }) + .set('Content-Type', 'application/json') + .expect(200); + + const { body } = await request.get(`${base}/api/admin/context/environment`); + + expect(body).toMatchObject({ + name: 'environment', + legalValues: [ + { value: 'newvalue', description: 'updated description' }, + ], + }); +}); + test('should not delete a unknown context field', () => { expect.assertions(0); diff --git a/src/lib/features/context/context.ts b/src/lib/features/context/context.ts index f6a9381a198e..949201d220f7 100644 --- a/src/lib/features/context/context.ts +++ b/src/lib/features/context/context.ts @@ -39,6 +39,7 @@ import { import type { UpdateContextFieldSchema } from '../../openapi/spec/update-context-field-schema'; import type { CreateContextFieldSchema } from '../../openapi/spec/create-context-field-schema'; import { extractUserIdFromUser } from '../../util'; +import type { LegalValueSchema } from '../../openapi'; interface ContextParam { contextField: string; @@ -168,6 +169,26 @@ export class ContextController extends Controller { ], }); + this.route({ + method: 'put', + path: '/:contextField/legalValues', + handler: this.updateContextFieldLegalValue, + permission: UPDATE_CONTEXT_FIELD, + middleware: [ + openApiService.validPath({ + tags: ['Context'], + summary: + 'Add or update a single legal value to the context field', + description: `Endpoint that allows adding or updating a custom context field legal value. If the legal value already exists, it will be updated with the new description`, + operationId: 'addContextFieldLegalValue', + requestBody: createRequestSchema('legalValueSchema'), + responses: { + 200: emptyResponse, + }, + }), + ], + }); + this.route({ method: 'delete', path: '/:contextField', @@ -271,6 +292,20 @@ export class ContextController extends Controller { res.status(200).end(); } + async updateContextFieldLegalValue( + req: IAuthRequest, + res: Response, + ): Promise { + const name = req.params.contextField; + const legalValue = req.body; + + await this.contextService.updateContextFieldLegalValue( + { name, legalValue }, + req.audit, + ); + res.status(200).end(); + } + async deleteContextField( req: IAuthRequest, res: Response, diff --git a/src/lib/services/context-schema.ts b/src/lib/services/context-schema.ts index 081962949884..9f20ad3e620f 100644 --- a/src/lib/services/context-schema.ts +++ b/src/lib/services/context-schema.ts @@ -3,7 +3,7 @@ import { nameType } from '../routes/util'; export const nameSchema = joi.object().keys({ name: nameType }); -const legalValueSchema = joi.object().keys({ +export const legalValueSchema = joi.object().keys({ value: joi.string().min(1).max(100).required(), description: joi.string().allow('').allow(null).optional(), }); From a0bc0112e735c674a4c2467b7fbcf3fcec653436 Mon Sep 17 00:00:00 2001 From: kwasniew Date: Fri, 3 Jan 2025 13:01:39 +0100 Subject: [PATCH 2/4] feat: ability to upsert single legal values --- src/lib/features/context/context.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/features/context/context.ts b/src/lib/features/context/context.ts index 949201d220f7..52527355ee6b 100644 --- a/src/lib/features/context/context.ts +++ b/src/lib/features/context/context.ts @@ -177,10 +177,9 @@ export class ContextController extends Controller { middleware: [ openApiService.validPath({ tags: ['Context'], - summary: - 'Add or update a single legal value to the context field', - description: `Endpoint that allows adding or updating a custom context field legal value. If the legal value already exists, it will be updated with the new description`, - operationId: 'addContextFieldLegalValue', + summary: 'Add or update legal value for the context field', + description: `Endpoint that allows adding or updating a single custom context field legal value. If the legal value already exists, it will be updated with the new description`, + operationId: 'updateContextFieldLegalValue', requestBody: createRequestSchema('legalValueSchema'), responses: { 200: emptyResponse, From 20dae1822df7248c76bf0691d742358908a36d7c Mon Sep 17 00:00:00 2001 From: kwasniew Date: Fri, 3 Jan 2025 14:10:14 +0100 Subject: [PATCH 3/4] feat: ability to upsert single legal values --- src/lib/features/context/context.test.ts | 8 ++++---- src/lib/features/context/context.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/features/context/context.test.ts b/src/lib/features/context/context.test.ts index 0b6c90f3f7ba..c6775085b387 100644 --- a/src/lib/features/context/context.test.ts +++ b/src/lib/features/context/context.test.ts @@ -156,7 +156,7 @@ test('should add and update a single context field with new legal values', async // non existent context await request - .put(`${base}/api/admin/context/doesntexist/legalValues`) + .post(`${base}/api/admin/context/doesntexist/legalValues`) .send({ value: 'local', description: 'Local environment', @@ -166,7 +166,7 @@ test('should add and update a single context field with new legal values', async // invalid schema await request - .put(`${base}/api/admin/context/environment/legalValues`) + .post(`${base}/api/admin/context/environment/legalValues`) .send({ valueInvalid: 'invalid schema', description: 'Local environment', @@ -176,7 +176,7 @@ test('should add and update a single context field with new legal values', async // add a new context field legal value await request - .put(`${base}/api/admin/context/environment/legalValues`) + .post(`${base}/api/admin/context/environment/legalValues`) .send({ value: 'newvalue', description: 'new description', @@ -186,7 +186,7 @@ test('should add and update a single context field with new legal values', async // update existing context field legal value description await request - .put(`${base}/api/admin/context/environment/legalValues`) + .post(`${base}/api/admin/context/environment/legalValues`) .send({ value: 'newvalue', description: 'updated description', diff --git a/src/lib/features/context/context.ts b/src/lib/features/context/context.ts index 52527355ee6b..05713c8f83cd 100644 --- a/src/lib/features/context/context.ts +++ b/src/lib/features/context/context.ts @@ -170,7 +170,7 @@ export class ContextController extends Controller { }); this.route({ - method: 'put', + method: 'post', path: '/:contextField/legalValues', handler: this.updateContextFieldLegalValue, permission: UPDATE_CONTEXT_FIELD, From 0e7aab63510b4b631f0479870b3ad40fc4dfa002 Mon Sep 17 00:00:00 2001 From: kwasniew Date: Fri, 3 Jan 2025 14:11:24 +0100 Subject: [PATCH 4/4] feat: ability to upsert single legal values --- src/lib/features/context/context.test.ts | 6 +++--- src/lib/features/context/context.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/features/context/context.test.ts b/src/lib/features/context/context.test.ts index c6775085b387..a1cb282acde4 100644 --- a/src/lib/features/context/context.test.ts +++ b/src/lib/features/context/context.test.ts @@ -166,7 +166,7 @@ test('should add and update a single context field with new legal values', async // invalid schema await request - .post(`${base}/api/admin/context/environment/legalValues`) + .post(`${base}/api/admin/context/environment/legal-values`) .send({ valueInvalid: 'invalid schema', description: 'Local environment', @@ -176,7 +176,7 @@ test('should add and update a single context field with new legal values', async // add a new context field legal value await request - .post(`${base}/api/admin/context/environment/legalValues`) + .post(`${base}/api/admin/context/environment/legal-values`) .send({ value: 'newvalue', description: 'new description', @@ -186,7 +186,7 @@ test('should add and update a single context field with new legal values', async // update existing context field legal value description await request - .post(`${base}/api/admin/context/environment/legalValues`) + .post(`${base}/api/admin/context/environment/legal-values`) .send({ value: 'newvalue', description: 'updated description', diff --git a/src/lib/features/context/context.ts b/src/lib/features/context/context.ts index 05713c8f83cd..20e28f062b0e 100644 --- a/src/lib/features/context/context.ts +++ b/src/lib/features/context/context.ts @@ -171,7 +171,7 @@ export class ContextController extends Controller { this.route({ method: 'post', - path: '/:contextField/legalValues', + path: '/:contextField/legal-values', handler: this.updateContextFieldLegalValue, permission: UPDATE_CONTEXT_FIELD, middleware: [