Skip to content

Commit

Permalink
Adding cliCommandWrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
CarlosGamero committed Jan 29, 2025
1 parent 1390dc9 commit fdda07b
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
67 changes: 67 additions & 0 deletions scripts/utils/cliCommandWrapper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { type MockInstance, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import z from 'zod'
import { cliCommandWrapper } from './cliCommandWrapper.js'

describe('cliCommandWrapper', () => {
let exitSpy: MockInstance

beforeEach(() => {
// Mock process.exit before each test
exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never)
})

afterEach(() => {
vi.restoreAllMocks()
})

it.each([
{ inputArgs: ['--key=value'], schema: undefined, expected: undefined },
{
inputArgs: ['--key=value'],
schema: z.object({ key: z.string() }),
expected: { key: 'value' },
},
{ inputArgs: ['--flag'], schema: z.object({ flag: z.boolean() }), expected: { flag: true } },
{
inputArgs: ['--key=value'],
schema: z.object({ flag: z.boolean().optional(), key: z.string() }),
expected: { key: 'value' },
},
{
inputArgs: ['--key=value', '-abc', '--flag'],
schema: z.object({ key: z.string(), flag: z.boolean(), a: z.boolean(), b: z.boolean(), c: z.boolean() }),
expected: { key: 'value', flag: true, a: true, b: true, c: true },
},
])('should parse arguments', async ({ inputArgs, schema, expected }) => {
process.argv = ['node', 'script.js', ...inputArgs]
await cliCommandWrapper(
'command',
(dependencies, requestContext, args) => {
expect(dependencies).toBeDefined()
expect(requestContext).toBeDefined()
expect(args).toEqual(expected)
},
schema,
)
expect(exitSpy).toHaveBeenCalledWith(0)
})

it('should fail if arguments are not valid', async () => {
process.argv = ['node', 'script.js', '--key=value']
await cliCommandWrapper(
'command',
() => undefined,
z.object({ key: z.number() }),
)
expect(exitSpy).toHaveBeenCalledWith(1)
})

it('should fail if cli command fail', async () => {
process.argv = ['node', 'script.js', '--key=value']
await cliCommandWrapper(
'command',
() => { throw new Error()},
)
expect(exitSpy).toHaveBeenCalledWith(1)
})
})
77 changes: 77 additions & 0 deletions scripts/utils/cliCommandWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { parseArgs } from 'node:util'
import type { RequestContext } from '@lokalise/fastify-extras'
import { generateMonotonicUuid } from '@lokalise/id-utils'
import { isError } from '@lokalise/universal-ts-utils/node'
import pino from 'pino'
import type z from 'zod'
import { getApp } from '../../src/app.js'
import type { Dependencies } from '../../src/infrastructure/parentDiConfig.js'

const getArgs = () => {
const { values } = parseArgs({
args: process.argv,
strict: false,
})

return values
}

export type CliCommand = <ArgsSchema extends z.Schema>(
dependencies: Dependencies,
requestContext: RequestContext,
args?: z.infer<ArgsSchema>,
) => Promise<void> | void

export const cliCommandWrapper = async <ArgsSchema extends z.Schema>(
cliCommandName: string,
command: CliCommand,
argsSchema?: ArgsSchema,
): Promise<void> => {
const app = await getApp({
queuesEnabled: false,
jobsEnabled: false,
healthchecksEnabled: false,
monitoringEnabled: false,
})

const requestId = generateMonotonicUuid()
const reqContext: RequestContext = {
reqId: requestId,
logger: app.diContainer.cradle.logger.child({
origin: cliCommandName,
'x-request-id': requestId,
}),
}

let args: z.infer<ArgsSchema> | undefined
if (argsSchema) {
const parseResult = argsSchema.safeParse(getArgs())
if (!parseResult.success) {
reqContext.logger.error(
{
errors: JSON.stringify(parseResult.error.errors),
},
'Invalid arguments',
)
await app.close()
process.exit(1)
}
args = parseResult.data
}

let isSuccess = true
try {
await command(app.diContainer.cradle, reqContext, args)
} catch (err) {
isSuccess = false
reqContext.logger.error(
{
error: JSON.stringify(isError(err) ? pino.stdSerializers.err(err) : err),
},
'Error running command',
)
}

await app.close()
process.exit(isSuccess ? 0 : 1)
}

0 comments on commit fdda07b

Please sign in to comment.