From ea4a3a6460a8073d4ef536521756e817bf13960a Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Thu, 31 Oct 2024 09:44:04 +0100 Subject: [PATCH 1/8] feat: pull-languages cmd --- .gitignore | 5 +- .vscode/launch.json | 11 +++++ src/commands/pull-languages/actions.ts | 64 ++++++++++++++++++++++++++ src/commands/pull-languages/index.ts | 45 ++++++++++++++++++ src/constants.ts | 4 +- src/index.ts | 3 +- 6 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 src/commands/pull-languages/actions.ts create mode 100644 src/commands/pull-languages/index.ts diff --git a/.gitignore b/.gitignore index 1d7923c..9452434 100644 --- a/.gitignore +++ b/.gitignore @@ -111,4 +111,7 @@ dist # CLI generated files components.*.json presets.*.json -storyblok-component-types.d.ts \ No newline at end of file +storyblok-component-types.d.ts + +# storyblok folder +.storyblok/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 17e0d8e..bd6ed14 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -45,6 +45,17 @@ "console": "integratedTerminal", "sourceMaps": true, "outFiles": ["${workspaceFolder}/dist/**/*.js"] + }, + { + "type": "node", + "request": "launch", + "name": "Debug Pull languages", + "program": "${workspaceFolder}/dist/index.mjs", + "args": ["pull-languages", "--space", "295018", "--path", ".storyblok"], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/dist/**/*.js"] } ] } diff --git a/src/commands/pull-languages/actions.ts b/src/commands/pull-languages/actions.ts new file mode 100644 index 0000000..5312292 --- /dev/null +++ b/src/commands/pull-languages/actions.ts @@ -0,0 +1,64 @@ +import { access, constants, mkdir, writeFile } from 'node:fs/promises' +import { join, resolve } from 'node:path' + +import { handleAPIError, handleFileSystemError } from '../../utils' +import { ofetch } from 'ofetch' +import { regionsDomain } from '../../constants' +import { session } from '../../session' +import { apiClient } from '../../api' + +export interface SpaceInternationalizationOptions { + languages: SpaceLanguage[] + default_lang_name: string +} +export interface SpaceLanguage { + code: string + name: string +} + +export const pullLanguages = async (space: string): Promise => { + try { + const { client } = apiClient() + if (!client) { + throw new Error('Client not initialized') + } + + const { state } = session() + const response = await ofetch(`https://${regionsDomain[state.region]}/v1/spaces/${space}`, { + headers: { + Authorization: state.password, + }, + }) + return { + default_lang_name: response.space.default_lang_name, + languages: response.space.languages, + } + } + catch (error) { + handleAPIError('pull_languages', error as Error) + } +} + +export const saveLanguagesToFile = async (space: string, internationalizationOptions: SpaceInternationalizationOptions, path?: string) => { + try { + const data = JSON.stringify(internationalizationOptions, null, 2) + const filename = `languages.${space}.json` + const resolvedPath = path ? resolve(process.cwd(), path) : process.cwd() + const filePath = join(resolvedPath, filename) + + // Check if the path exists, and create it if it doesn't + try { + await access(resolvedPath, constants.F_OK) + } + catch { + await mkdir(resolvedPath, { recursive: true }) + } + + return await writeFile(filePath, data, { + mode: 0o600, + }) + } + catch (error) { + handleFileSystemError('write', error as Error) + } +} diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts new file mode 100644 index 0000000..1376882 --- /dev/null +++ b/src/commands/pull-languages/index.ts @@ -0,0 +1,45 @@ +import { colorPalette, commands } from '../../constants' +import { CommandError, handleError, konsola } from '../../utils' +import { getProgram } from '../../program' +import { session } from '../../session' +import { pullLanguages, saveLanguagesToFile } from './actions' +import chalk from 'chalk' + +const program = getProgram() // Get the shared singleton instance + +export const pullLanguagesCommand = program + .command('pull-languages') + .description(`Download your space's languages schema as json`) + .option('-s, --space ', 'space ID') + .option('-p, --path ', 'path to save the file') + .action(async (options) => { + const { space, path } = options + konsola.title(` ${commands.PULL_LANGUAGES} `, colorPalette.PULL_LANGUAGES, 'Pulling languages...') + + const { state, initializeSession } = session() + await initializeSession() + + if (!state.isLoggedIn) { + handleError(new CommandError(`You are currently not logged in. Please login first to get your user info.`)) + return + } + + if (!space) { + handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`)) + return + } + + try { + const internationalization = await pullLanguages(space) + + if (!internationalization || !internationalization.languages) { + konsola.warn(`No languages found in the space ${space}`) + return + } + await saveLanguagesToFile(space, internationalization, path) + konsola.ok(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(path ? `${path}/languages.${space}.json` : `languages.${space}.json`)}`) + } + catch (error) { + handleError(error as Error, true) + } + }) diff --git a/src/constants.ts b/src/constants.ts index cc527b2..8380061 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,12 +2,14 @@ export const commands = { LOGIN: 'login', LOGOUT: 'logout', USER: 'user', + PULL_LANGUAGES: 'pull-languages', } as const export const colorPalette = { PRIMARY: '#45bfb9', LOGIN: '#8556D3', - USER: '#8BC34A', // Changed to a less saturated green color + USER: '#8BC34A', + PULL_LANGUAGES: '#FFC107', } as const export interface ReadonlyArray { diff --git a/src/index.ts b/src/index.ts index c378fe7..2e83379 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,8 @@ import { getProgram } from './program' import './commands/login' import './commands/logout' import './commands/user' +import './commands/pull-languages' + import { loginWithToken } from './commands/login/actions' dotenv.config() // This will load variables from .env into process.env @@ -17,7 +19,6 @@ const messageText = ` ` console.log(formatHeader(` ${introText} ${messageText}`)) -program.option('-s, --space [value]', 'space ID') program.option('-v, --verbose', 'Enable verbose output') program.on('command:*', () => { From a59db44c5e10b17df6086d92cc49301df3cab022 Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Thu, 31 Oct 2024 11:52:02 +0100 Subject: [PATCH 2/8] feat: add verbose error handling to pull languages --- .vscode/launch.json | 2 +- src/commands/login/index.ts | 3 +++ src/commands/pull-languages/index.ts | 7 +++++-- src/utils/error/api-error.ts | 6 +++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2a415e1..8f77209 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -60,7 +60,7 @@ "request": "launch", "name": "Debug Pull languages", "program": "${workspaceFolder}/dist/index.mjs", - "args": ["pull-languages", "--space", "295018", "--path", ".storyblok"], + "args": ["pull-languages", "--space", "2950182323", "--path", ".storyblok"], "cwd": "${workspaceFolder}", "console": "integratedTerminal", "sourceMaps": true, diff --git a/src/commands/login/index.ts b/src/commands/login/index.ts index fd08231..a329dd8 100644 --- a/src/commands/login/index.ts +++ b/src/commands/login/index.ts @@ -41,8 +41,11 @@ export const loginCommand = program region: RegionCode }) => { konsola.title(` ${commands.LOGIN} `, colorPalette.LOGIN) + // Global options const verbose = program.opts().verbose + // Command options const { token, region } = options + if (!isRegion(region)) { handleError(new CommandError(`The provided region: ${region} is not valid. Please use one of the following values: ${Object.values(regions).join(' | ')}`)) } diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts index 1376882..d50240d 100644 --- a/src/commands/pull-languages/index.ts +++ b/src/commands/pull-languages/index.ts @@ -13,8 +13,11 @@ export const pullLanguagesCommand = program .option('-s, --space ', 'space ID') .option('-p, --path ', 'path to save the file') .action(async (options) => { - const { space, path } = options konsola.title(` ${commands.PULL_LANGUAGES} `, colorPalette.PULL_LANGUAGES, 'Pulling languages...') + // Global options + const verbose = program.opts().verbose + // Command options + const { space, path } = options const { state, initializeSession } = session() await initializeSession() @@ -40,6 +43,6 @@ export const pullLanguagesCommand = program konsola.ok(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(path ? `${path}/languages.${space}.json` : `languages.${space}.json`)}`) } catch (error) { - handleError(error as Error, true) + handleError(error as Error, verbose) } }) diff --git a/src/utils/error/api-error.ts b/src/utils/error/api-error.ts index 0a859ee..122e11c 100644 --- a/src/utils/error/api-error.ts +++ b/src/utils/error/api-error.ts @@ -6,6 +6,7 @@ export const API_ACTIONS = { login_with_otp: 'Failed to log in with email, password and otp', login_email_password: 'Failed to log in with email and password', get_user: 'Failed to get user', + pull_languages: 'Failed to pull languages', } as const export const API_ERRORS = { @@ -13,7 +14,8 @@ export const API_ERRORS = { network_error: 'No response from server, please check if you are correctly connected to internet', invalid_credentials: 'The provided credentials are invalid', timeout: 'The API request timed out', - generic: 'Error logging in', + generic: 'Error fetching data from the API', + not_found: 'The requested resource was not found', } as const export function handleAPIError(action: keyof typeof API_ACTIONS, error: Error): void { @@ -23,6 +25,8 @@ export function handleAPIError(action: keyof typeof API_ACTIONS, error: Error): switch (status) { case 401: throw new APIError('unauthorized', action, error) + case 404: + throw new APIError('not_found', action, error) case 422: throw new APIError('invalid_credentials', action, error) default: From 8cdc543ceb44c064a69b651dfbaea8aff95a26c9 Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Thu, 31 Oct 2024 11:57:16 +0100 Subject: [PATCH 3/8] feat: verbose for command errors of pull languages --- src/commands/pull-languages/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts index d50240d..32e1bde 100644 --- a/src/commands/pull-languages/index.ts +++ b/src/commands/pull-languages/index.ts @@ -23,12 +23,12 @@ export const pullLanguagesCommand = program await initializeSession() if (!state.isLoggedIn) { - handleError(new CommandError(`You are currently not logged in. Please login first to get your user info.`)) + handleError(new CommandError(`You are currently not logged in. Please login first to get your user info.`), verbose) return } if (!space) { - handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`)) + handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`), verbose) return } From f7cbd354550390d102aa0c47dd648c2a1cba096a Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Thu, 31 Oct 2024 12:43:04 +0100 Subject: [PATCH 4/8] feat: remove sideeffects from pull languages actions and test coverage --- src/commands/pull-languages/actions.test.ts | 93 +++++++++++++++++++++ src/commands/pull-languages/actions.ts | 14 +--- src/commands/pull-languages/index.ts | 4 +- 3 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 src/commands/pull-languages/actions.test.ts diff --git a/src/commands/pull-languages/actions.test.ts b/src/commands/pull-languages/actions.test.ts new file mode 100644 index 0000000..34a97b6 --- /dev/null +++ b/src/commands/pull-languages/actions.test.ts @@ -0,0 +1,93 @@ +import { http, HttpResponse } from 'msw' +import { setupServer } from 'msw/node' +import chalk from 'chalk' +import { vol } from 'memfs' +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' +import { pullLanguages, saveLanguagesToFile } from './actions' + +const handlers = [ + http.get('https://api.storyblok.com/v1/spaces/12345', async ({ request }) => { + const token = request.headers.get('Authorization') + if (token === 'valid-token') { + return HttpResponse.json({ + space: { + default_lang_name: 'en', + languages: [ + { + code: 'ca', + name: 'Catalan', + }, + { + code: 'fr', + name: 'French', + }, + ], + }, + }) + } + return new HttpResponse('Unauthorized', { status: 401 }) + }), +] + +const server = setupServer(...handlers) + +beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) + +afterEach(() => server.resetHandlers()) +afterAll(() => server.close()) + +vi.mock('node:fs') +vi.mock('node:fs/promises') + +describe('pull languages actions', () => { + beforeEach(() => { + vi.clearAllMocks() + vol.reset() + }) + + describe('pullLanguages', () => { + it('should pull languages successfully with a valid token', async () => { + const mockResponse = { + default_lang_name: 'en', + languages: [ + { + code: 'ca', + name: 'Catalan', + }, + { + code: 'fr', + name: 'French', + }, + ], + } + const result = await pullLanguages('12345', 'valid-token', 'eu') + expect(result).toEqual(mockResponse) + }) + }) + it('should throw an masked error for invalid token', async () => { + await expect(pullLanguages('12345', 'invalid-token', 'eu')).rejects.toThrow( + new Error(`The user is not authorized to access the API`), + ) + }) + + describe('saveLanguagesToFile', () => { + it('should save languages to a json file with space number', async () => { + const mockResponse = { + default_lang_name: 'en', + languages: [ + { + code: 'ca', + name: 'Catalan', + }, + { + code: 'fr', + name: 'French', + }, + ], + } + await saveLanguagesToFile('12345', mockResponse, '/temp') + const content = vol.readFileSync('/temp/languages.12345.json', 'utf8') + expect(content).toBe(JSON.stringify(mockResponse, null, 2)) + }) + }) +}) diff --git a/src/commands/pull-languages/actions.ts b/src/commands/pull-languages/actions.ts index 5312292..7f4ebd1 100644 --- a/src/commands/pull-languages/actions.ts +++ b/src/commands/pull-languages/actions.ts @@ -4,8 +4,6 @@ import { join, resolve } from 'node:path' import { handleAPIError, handleFileSystemError } from '../../utils' import { ofetch } from 'ofetch' import { regionsDomain } from '../../constants' -import { session } from '../../session' -import { apiClient } from '../../api' export interface SpaceInternationalizationOptions { languages: SpaceLanguage[] @@ -16,17 +14,11 @@ export interface SpaceLanguage { name: string } -export const pullLanguages = async (space: string): Promise => { +export const pullLanguages = async (space: string, token: string, region: string): Promise => { try { - const { client } = apiClient() - if (!client) { - throw new Error('Client not initialized') - } - - const { state } = session() - const response = await ofetch(`https://${regionsDomain[state.region]}/v1/spaces/${space}`, { + const response = await ofetch(`https://${regionsDomain[region]}/v1/spaces/${space}`, { headers: { - Authorization: state.password, + Authorization: token, }, }) return { diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts index 32e1bde..2256070 100644 --- a/src/commands/pull-languages/index.ts +++ b/src/commands/pull-languages/index.ts @@ -22,7 +22,7 @@ export const pullLanguagesCommand = program const { state, initializeSession } = session() await initializeSession() - if (!state.isLoggedIn) { + if (!state.isLoggedIn || !state.password || !state.region) { handleError(new CommandError(`You are currently not logged in. Please login first to get your user info.`), verbose) return } @@ -33,7 +33,7 @@ export const pullLanguagesCommand = program } try { - const internationalization = await pullLanguages(space) + const internationalization = await pullLanguages(space, state.password, state.region) if (!internationalization || !internationalization.languages) { konsola.warn(`No languages found in the space ${space}`) From 2c561931221b03517bede0a4e8243e3af42557b9 Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Thu, 31 Oct 2024 12:45:16 +0100 Subject: [PATCH 5/8] chore: fix lint --- src/commands/pull-languages/actions.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/pull-languages/actions.test.ts b/src/commands/pull-languages/actions.test.ts index 34a97b6..dbec82b 100644 --- a/src/commands/pull-languages/actions.test.ts +++ b/src/commands/pull-languages/actions.test.ts @@ -1,6 +1,5 @@ import { http, HttpResponse } from 'msw' import { setupServer } from 'msw/node' -import chalk from 'chalk' import { vol } from 'memfs' import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { pullLanguages, saveLanguagesToFile } from './actions' From 03c825fe6d52082f62c6880f0d7c86b8604cdd5f Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Thu, 31 Oct 2024 15:39:44 +0100 Subject: [PATCH 6/8] feat: test coverage for command --- src/commands/pull-languages/index.test.ts | 164 ++++++++++++++++++++++ src/commands/pull-languages/index.ts | 3 +- src/commands/user/index.test.ts | 4 +- 3 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 src/commands/pull-languages/index.test.ts diff --git a/src/commands/pull-languages/index.test.ts b/src/commands/pull-languages/index.test.ts new file mode 100644 index 0000000..1f938fb --- /dev/null +++ b/src/commands/pull-languages/index.test.ts @@ -0,0 +1,164 @@ +import chalk from 'chalk' +import { pullLanguagesCommand } from '.' +import { session } from '../../session' +import { CommandError, konsola } from './../../utils/' +import { pullLanguages, saveLanguagesToFile } from './actions' +import { colorPalette } from '../../constants' + +vi.mock('./actions', () => ({ + pullLanguages: vi.fn(), + saveLanguagesToFile: vi.fn(), +})) + +vi.mock('../../creds', () => ({ + addNetrcEntry: vi.fn(), + isAuthorized: vi.fn(), + getNetrcCredentials: vi.fn(), + getCredentialsForMachine: vi.fn(), +})) + +// Mocking the session module +vi.mock('../../session', () => { + let _cache: Record | null = null + const session = () => { + if (!_cache) { + _cache = { + state: { + isLoggedIn: false, + }, + updateSession: vi.fn(), + persistCredentials: vi.fn(), + initializeSession: vi.fn(), + } + } + return _cache + } + + return { + session, + } +}) + +vi.mock('../../utils', async () => { + const actualUtils = await vi.importActual('../../utils') + return { + ...actualUtils, + konsola: { + ok: vi.fn(), + title: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }, + handleError: (error: Error, header = false) => { + konsola.error(error, header) + // Optionally, prevent process.exit during tests + }, + } +}) + +describe('pullLanguages', () => { + beforeEach(() => { + vi.resetAllMocks() + vi.clearAllMocks() + // Reset the option values + pullLanguagesCommand._optionValues = {} + }) + + describe('default mode', () => { + it('should prompt the user if operation was sucessfull', async () => { + const mockResponse = { + default_lang_name: 'en', + languages: [ + { + code: 'ca', + name: 'Catalan', + }, + { + code: 'fr', + name: 'French', + }, + ], + } + session().state = { + isLoggedIn: true, + password: 'valid-token', + region: 'eu', + } + + vi.mocked(pullLanguages).mockResolvedValue(mockResponse) + await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '12345']) + expect(pullLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu') + expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, undefined) + expect(konsola.ok).toHaveBeenCalledWith(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(`languages.12345.json`)}`) + }) + + it('should throw an error if the user is not logged in', async () => { + session().state = { + isLoggedIn: false, + } + const mockError = new CommandError(`You are currently not logged in. Please login first to get your user info.`) + await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '12345']) + expect(konsola.error).toHaveBeenCalledWith(mockError, false) + }) + + it('should throw an error if the space is not provided', async () => { + session().state = { + isLoggedIn: true, + password: 'valid-token', + region: 'eu', + } + + const mockError = new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`) + + console.log(pullLanguagesCommand) + await pullLanguagesCommand.parseAsync(['node', 'test']) + expect(konsola.error).toHaveBeenCalledWith(mockError, false) + }) + + it('should prompt a warning the user if no languages are found', async () => { + const mockResponse = { + default_lang_name: 'en', + languages: [], + } + session().state = { + isLoggedIn: true, + password: 'valid-token', + region: 'eu', + } + + vi.mocked(pullLanguages).mockResolvedValue(mockResponse) + + await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '24568']) + expect(konsola.warn).toHaveBeenCalledWith(`No languages found in the space 24568`) + }) + }) + + describe('--path option', () => { + it('should save the file at the provided path', async () => { + const mockResponse = { + default_lang_name: 'en', + languages: [ + { + code: 'ca', + name: 'Catalan', + }, + { + code: 'fr', + name: 'French', + }, + ], + } + session().state = { + isLoggedIn: true, + password: 'valid-token', + region: 'eu', + } + + vi.mocked(pullLanguages).mockResolvedValue(mockResponse) + await pullLanguagesCommand.parseAsync(['node', 'test', '--space', '12345', '--path', '/tmp']) + expect(pullLanguages).toHaveBeenCalledWith('12345', 'valid-token', 'eu') + expect(saveLanguagesToFile).toHaveBeenCalledWith('12345', mockResponse, '/tmp') + expect(konsola.ok).toHaveBeenCalledWith(`Languages schema downloaded successfully at ${chalk.hex(colorPalette.PRIMARY)(`/tmp/languages.12345.json`)}`) + }) + }) +}) diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts index 2256070..c617181 100644 --- a/src/commands/pull-languages/index.ts +++ b/src/commands/pull-languages/index.ts @@ -26,7 +26,6 @@ export const pullLanguagesCommand = program handleError(new CommandError(`You are currently not logged in. Please login first to get your user info.`), verbose) return } - if (!space) { handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`), verbose) return @@ -35,7 +34,7 @@ export const pullLanguagesCommand = program try { const internationalization = await pullLanguages(space, state.password, state.region) - if (!internationalization || !internationalization.languages) { + if (!internationalization || internationalization.languages.length === 0) { konsola.warn(`No languages found in the space ${space}`) return } diff --git a/src/commands/user/index.test.ts b/src/commands/user/index.test.ts index f869e5c..f350f1a 100644 --- a/src/commands/user/index.test.ts +++ b/src/commands/user/index.test.ts @@ -68,7 +68,7 @@ describe('userCommand', () => { password: 'valid-token', region: 'eu', } - getUser.mockResolvedValue(mockResponse) + vi.mocked(getUser).mockResolvedValue(mockResponse) await userCommand.parseAsync(['node', 'test']) expect(getUser).toHaveBeenCalledWith('valid-token', 'eu') @@ -95,7 +95,7 @@ describe('userCommand', () => { const mockError = new Error('Network error') - getUser.mockRejectedValue(mockError) + vi.mocked(getUser).mockRejectedValue(mockError) await userCommand.parseAsync(['node', 'test']) From b0deb4485a1dea1488efd7a5fe8d0080e93f4a1e Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Tue, 5 Nov 2024 09:27:08 +0100 Subject: [PATCH 7/8] feat: more granular error handling on save json file --- src/commands/pull-languages/actions.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/commands/pull-languages/actions.ts b/src/commands/pull-languages/actions.ts index 7f4ebd1..b1d963f 100644 --- a/src/commands/pull-languages/actions.ts +++ b/src/commands/pull-languages/actions.ts @@ -43,12 +43,21 @@ export const saveLanguagesToFile = async (space: string, internationalizationOpt await access(resolvedPath, constants.F_OK) } catch { - await mkdir(resolvedPath, { recursive: true }) + try { + await mkdir(resolvedPath, { recursive: true }) + } + catch (mkdirError) { + handleFileSystemError('mkdir', mkdirError as Error) + return // Exit early if the directory creation fails + } } - return await writeFile(filePath, data, { - mode: 0o600, - }) + try { + await writeFile(filePath, data, { mode: 0o600 }) + } + catch (writeError) { + handleFileSystemError('write', writeError as Error) + } } catch (error) { handleFileSystemError('write', error as Error) From 67c05b0702b4e58a7d61a6d8a75e601244b8a63e Mon Sep 17 00:00:00 2001 From: alvarosabu Date: Tue, 5 Nov 2024 09:28:40 +0100 Subject: [PATCH 8/8] feat: add conditional to languages --- src/commands/pull-languages/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/pull-languages/index.ts b/src/commands/pull-languages/index.ts index c617181..e567e5c 100644 --- a/src/commands/pull-languages/index.ts +++ b/src/commands/pull-languages/index.ts @@ -34,7 +34,7 @@ export const pullLanguagesCommand = program try { const internationalization = await pullLanguages(space, state.password, state.region) - if (!internationalization || internationalization.languages.length === 0) { + if (!internationalization || internationalization.languages?.length === 0) { konsola.warn(`No languages found in the space ${space}`) return }