From 2d09dc278fe528f43939d3f1c013dcb65ceaf671 Mon Sep 17 00:00:00 2001 From: shay Date: Thu, 2 Nov 2023 17:55:32 +0200 Subject: [PATCH] feature: added command to get storage items --- src/commands/code/storage.ts | 71 +++++++++++++++++++ src/consts/urls.ts | 4 ++ src/services/prompt-service.ts | 2 +- .../schemas/storage-service-schemas.ts | 17 +++++ src/services/storage-service.ts | 36 ++++++++++ src/types/services/storage-service.ts | 5 ++ 6 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/commands/code/storage.ts create mode 100644 src/services/schemas/storage-service-schemas.ts create mode 100644 src/services/storage-service.ts create mode 100644 src/types/services/storage-service.ts diff --git a/src/commands/code/storage.ts b/src/commands/code/storage.ts new file mode 100644 index 0000000..12167f9 --- /dev/null +++ b/src/commands/code/storage.ts @@ -0,0 +1,71 @@ +import { Flags } from '@oclif/core'; +import { StatusCodes } from 'http-status-codes'; + +import { AuthenticatedCommand } from 'commands-base/authenticated-command'; +import { DynamicChoicesService } from 'services/dynamic-choices-service'; +import { PromptService } from 'services/prompt-service'; +import { getStorageItemsSearch } from 'services/storage-service'; +import { HttpError } from 'types/errors'; +import logger from 'utils/logger'; + +const clientAccountNumberMessage = 'Client account number'; +const termMessage = 'Term to search for'; + +const fetchAndPrintStorageKeyValuesResults = async (appId: number, clientAccountId: number, term: string) => { + const itemsFound = await getStorageItemsSearch(appId, clientAccountId, term); + logger.table(itemsFound.records); + if (itemsFound.hasMoreRecords) { + console.log('There more records, please search for a more specific term.'); + } +}; + +export default class Storage extends AuthenticatedCommand { + static description = 'Get keys and values stored on monday for a specific customer.'; + + static examples = ['<%= config.bin %> <%= command.id %> -i APP_VERSION_ID']; + + static flags = Storage.serializeFlags({ + appId: Flags.integer({ + char: 'a', + aliases: ['v'], + description: 'Select the app that you wish to retrieve the key for', + }), + clientAccountId: Flags.integer({ + char: 'c', + description: `${clientAccountNumberMessage}.`, + }), + term: Flags.string({ + char: 't', + description: `${termMessage}.`, + }), + }); + + public async run(): Promise { + const { flags } = await this.parse(Storage); + let { appId, clientAccountId, term } = flags; + if (!appId) { + appId = await DynamicChoicesService.chooseApp(); + } + + if (!clientAccountId) { + clientAccountId = await PromptService.promptInputNumber(`${clientAccountNumberMessage}:`, true); + } + + if (!term) { + term = await PromptService.promptInput(`${termMessage}:`, true); + } + + await fetchAndPrintStorageKeyValuesResults(appId, clientAccountId, term); + try { + this.preparePrintCommand(this, { appId, clientAccountId, term }); + } catch (error: unknown) { + if (error instanceof HttpError && error.code === StatusCodes.NOT_FOUND) { + logger.error(`No deployment found for provided app version id - "${appId}"`); + } else { + logger.error(`An unknown error happened while fetching deployment status for app version id - "${appId}"`); + } + + process.exit(0); + } + } +} diff --git a/src/consts/urls.ts b/src/consts/urls.ts index 0fd53da..a0f8fbc 100644 --- a/src/consts/urls.ts +++ b/src/consts/urls.ts @@ -15,6 +15,10 @@ export const getDeploymentSignedUrl = (appVersionId: number): string => { return `${appVersionIdBaseUrl(appVersionId)}/deployments/signed-url`; }; +export const getStorageItemsSearchUrl = (appId: number, clientAccountId: number, term: string): string => { + return `/api/storage/app/${appId}/account/${clientAccountId}/records?term=${encodeURI(term)}`; +}; + export const getLogsStreamForAppVersionIdUrl = ( appVersionId: number, logsType: LogType, diff --git a/src/services/prompt-service.ts b/src/services/prompt-service.ts index 8f8aa10..486d622 100755 --- a/src/services/prompt-service.ts +++ b/src/services/prompt-service.ts @@ -142,7 +142,7 @@ export const PromptService = { }, ]); - return res.input; + return Number(res.input); }, async promptFile(message: string, extensions: string[]) { diff --git a/src/services/schemas/storage-service-schemas.ts b/src/services/schemas/storage-service-schemas.ts new file mode 100644 index 0000000..4c48003 --- /dev/null +++ b/src/services/schemas/storage-service-schemas.ts @@ -0,0 +1,17 @@ +import { z } from 'zod'; + +import { baseResponseHttpMetaDataSchema } from 'services/schemas/api-service-schemas'; + +export const appStorageApiRecordsResponseSchema = z.object({ + key: z.string(), + value: z.string(), + backendOnly: z.boolean(), +}); + +export const appStorageApiRecordsSearchResponseSchema = z + .object({ + term: z.string(), + records: z.array(appStorageApiRecordsResponseSchema), + hasMoreRecords: z.boolean(), + }) + .merge(baseResponseHttpMetaDataSchema); diff --git a/src/services/storage-service.ts b/src/services/storage-service.ts new file mode 100644 index 0000000..5abcbac --- /dev/null +++ b/src/services/storage-service.ts @@ -0,0 +1,36 @@ +import { getStorageItemsSearchUrl } from 'consts/urls'; +import { execute } from 'services/api-service'; +import { appStorageApiRecordsSearchResponseSchema } from 'services/schemas/storage-service-schemas'; +import { HttpError } from 'types/errors'; +import { HttpMethodTypes } from 'types/services/api-service'; +import { AppStorageApiRecordsSearchResponseSchema } from 'types/services/storage-service'; +import logger from 'utils/logger'; +import { appsUrlBuilder } from 'utils/urls-builder'; + +export const getStorageItemsSearch = async ( + appId: number, + clientAccountId: number, + term: string, +): Promise => { + const DEBUG_TAG = 'get_storage_items_search_url'; + try { + const baseSignUrl = getStorageItemsSearchUrl(appId, clientAccountId, term); + const url = appsUrlBuilder(baseSignUrl); + const response = await execute( + { + url, + headers: { Accept: 'application/json' }, + method: HttpMethodTypes.GET, + }, + appStorageApiRecordsSearchResponseSchema, + ); + return response; + } catch (error: any | HttpError) { + logger.debug(error, DEBUG_TAG); + if (error instanceof HttpError) { + throw error; + } + + throw new Error('Failed to build remote location for upload.'); + } +}; diff --git a/src/types/services/storage-service.ts b/src/types/services/storage-service.ts new file mode 100644 index 0000000..cd3516b --- /dev/null +++ b/src/types/services/storage-service.ts @@ -0,0 +1,5 @@ +import { z } from 'zod'; + +import { appStorageApiRecordsSearchResponseSchema } from 'services/schemas/storage-service-schemas'; + +export type AppStorageApiRecordsSearchResponseSchema = z.infer;