From f20f501c3c54fae9facc3f2ebedef0d95e4573af Mon Sep 17 00:00:00 2001 From: Roy Weisselberg <75120221+royWeisselberg@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:57:12 +0200 Subject: [PATCH] feat(storage): Added search records functionality (#12) --- build/swagger.yaml | 48 ++++++++++++++++++++++++ src/domain/storage/base-storage.ts | 9 ++++- src/domain/storage/storage.controller.ts | 24 ++++++++++++ src/domain/storage/storage.service.ts | 21 +++++++++++ src/routes.ts | 34 +++++++++++++++++ src/shared/types/storage.type.ts | 20 ++++++++++ 6 files changed, 155 insertions(+), 1 deletion(-) diff --git a/build/swagger.yaml b/build/swagger.yaml index 2e4ae6e..9856792 100644 --- a/build/swagger.yaml +++ b/build/swagger.yaml @@ -295,6 +295,54 @@ paths: application/json: schema: $ref: "#/components/schemas/IncrementStorageForKeyRequestBody" + /storage/search/{term}: + get: + operationId: searchRecord + responses: + "200": + description: OK + content: + application/json: + schema: {} + "404": + description: "" + content: + application/json: + schema: + properties: + reason: + type: string + required: + - reason + type: object + "500": + description: "" + content: + application/json: + schema: + properties: + reason: + type: string + type: object + tags: + - Storage + security: [] + parameters: + - in: path + name: term + required: true + schema: + type: string + - in: header + name: x-monday-access-token + required: true + schema: + type: string + - in: query + name: cursor + required: false + schema: + type: string /secure-storage/{key}: get: operationId: getSecureStorage diff --git a/src/domain/storage/base-storage.ts b/src/domain/storage/base-storage.ts index edf9585..cca0398 100644 --- a/src/domain/storage/base-storage.ts +++ b/src/domain/storage/base-storage.ts @@ -3,7 +3,7 @@ import { isDefined } from 'types/type-guards'; import { fetchWrapper } from 'utils/fetch-wrapper'; import { Logger } from 'utils/logger'; -import type { Options, RequestOptions, Token } from 'types/storage.type'; +import type { Options, RequestOptions, SearchOptions, Token } from 'types/storage.type'; export abstract class BaseStorage { protected logger: Logger; @@ -81,4 +81,11 @@ export abstract class BaseStorage { const fullPath = `${storageUrl}/${key}?shareGlobally=${shareGlobally}`; return fullPath; } + + public searchUrl(key: string, options: SearchOptions) { + const storageUrl = this.getStorageUrlV2(); + const cursor = options.cursor ? `&cursor=${options.cursor}` : ''; + const fullPath = `${storageUrl}/items?term=${key}${cursor}`; + return fullPath; + } } diff --git a/src/domain/storage/storage.controller.ts b/src/domain/storage/storage.controller.ts index 7247795..33e2919 100644 --- a/src/domain/storage/storage.controller.ts +++ b/src/domain/storage/storage.controller.ts @@ -70,4 +70,28 @@ export class StorageController { const result = await storageService.incrementCounter(period, { incrementBy, kind, renewalDate }); return result; } + + @Get('search/{term}') + @OperationId('searchRecord') + @SuccessResponse(StatusCodes.OK, ReasonPhrases.OK) + public async searchRecords( + @Path() term: string, + @Header('x-monday-access-token') accessToken: string, + @Res() notFoundResponse: TsoaResponse, + @Res() serverError: TsoaResponse, + @Query() cursor?: string + ) { + const storageService = new StorageService(accessToken); + const result = await storageService.search(term, { cursor }); + const { success, records } = result; + if (!success && records === null) { + return notFoundResponse(StatusCodes.NOT_FOUND, { reason: 'Key not found' }); + } + + if (!success) { + return serverError(StatusCodes.INTERNAL_SERVER_ERROR, { reason: 'An error occurred while fetching the key' }); + } + + return result; + } } diff --git a/src/domain/storage/storage.service.ts b/src/domain/storage/storage.service.ts index 4bee4de..98be361 100644 --- a/src/domain/storage/storage.service.ts +++ b/src/domain/storage/storage.service.ts @@ -11,6 +11,9 @@ import type { IStorageInstance, Options, Period, + SearchOptions, + SearchResponse, + SearchServerResponse, SetResponse } from 'types/storage.type'; @@ -75,4 +78,22 @@ export class StorageService extends BaseStorage implements IStorageInstance { } } } + + async search( + key: string, + options: SearchOptions = {} + ): Promise> { + const url = this.searchUrl(key, options); + const params = { method: 'GET' }; + const result = await this.storageFetchV2>(url, params); + if (!isDefined(result)) { + return { success: false, records: null }; + } + + const response: SearchResponse = { success: true, records: result.records }; + if (result.cursor) { + response.cursor = result.cursor; + } + return response; + } } diff --git a/src/routes.ts b/src/routes.ts index ccf7413..23a28a0 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -235,6 +235,40 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.get('/storage/search/:term', + ...(fetchMiddlewares(StorageController)), + ...(fetchMiddlewares(StorageController.prototype.searchRecords)), + + async function StorageController_searchRecords(request: ExRequest, response: ExResponse, next: any) { + const args: Record = { + term: {"in":"path","name":"term","required":true,"dataType":"string"}, + accessToken: {"in":"header","name":"x-monday-access-token","required":true,"dataType":"string"}, + notFoundResponse: {"in":"res","name":"404","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"reason":{"dataType":"string","required":true}}}, + serverError: {"in":"res","name":"500","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"reason":{"dataType":"string"}}}, + cursor: {"in":"query","name":"cursor","dataType":"string"}, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = templateService.getValidatedArgs({ args, request, response }); + + const controller = new StorageController(); + + await templateService.apiHandler({ + methodName: 'searchRecords', + controller, + response, + next, + validatedArgs, + successStatus: 200, + }); + } catch (err) { + return next(err); + } + }); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.get('/secure-storage/:key', ...(fetchMiddlewares(SecureStorageController)), ...(fetchMiddlewares(SecureStorageController.prototype.getSecureValue)), diff --git a/src/shared/types/storage.type.ts b/src/shared/types/storage.type.ts index edf48e9..587bab0 100644 --- a/src/shared/types/storage.type.ts +++ b/src/shared/types/storage.type.ts @@ -67,3 +67,23 @@ export type ErrorResponse = } | undefined | null; + +export type SearchOptions = { + cursor?: string; +}; + +export type SearchEntity = { + key: string; + value: T; + backendOnly: boolean; +}; + +export type SearchServerResponse = { + records: Array> | null; + cursor?: string; +}; + +export type SearchResponse = { + success: boolean; + error?: string; +} & SearchServerResponse;