@zenginehq/handler-cli
v1.1.2
Published
A CLI used to generate and manage individual handlers within a NextZen handler workspace.
Downloads
750
Keywords
Readme
@zenginehq-handler-cli
A CLI used to generate and manage individual handlers within a NextZen handler workspace.
The CLI provides TypeScript based development tools for building and managing custom handler function code.
These tools allow for multiple handlers to be authored in one central workspace repository. They allow for code formatting, linting, testing, and building.
Builds are bundled into a single ESM module file and zipped for uploading and publishing. The uploaded module zip file is scanned for viruses before being allowed to be invoked.
First Install @zenginehq/handler-workspace-cli and create the handler workspace. Then install the handler-workspace CLI via npm:
npm install -g @zenginehq/handler-workspace-cli@latest
You can check the version installed with:
handler --version
You can display it's usage with:
handler --help
which would yield:
handler <command>
Commands:
handler generate <handler> Generate a new handler [aliases: g, gen]
handler list [handler] List handlers [aliases: l, ls]
handler format <handler> Format handler [aliases: f]
handler lint <handler> Lint handler
handler test <handler> Test handler
handler build <handler> Build handler [aliases: b]
handler upload <handler> Upload handler [aliases: u, up]
handler publish <handler> Publish handler [aliases: p, pub]
handler verify <handler> Verify handler
handler logs <handler> Logs for handler
Options:
--help Show help [boolean]
--version Show version [boolean]
So to initialize a new handler within the workspace, use:
cd ~/parent_folder/handlers
handler generate example1
which would fetch an access token, validate the organization, check that handler doesn't already exist:
➜ fetch token
✔ fetched token
➜ query organization
✔ queried organization
➜ query handler
✔ queried handler
✔ Handler name example1
and then prompt for handler type:
? Enter handler type …
❯ http
event
decision
score
then handler description:
? Enter handler description › Your Handler
then handler runtime:
? Enter handler runtime …
❯ nodejs20.x
nodejs18.x
then handler entry name:
? Enter handler entry name › handler.handler
which would generate the following:
~/parent_folder/handlers/http/example1
README.md
eslint.config.mjs
handler.json
jest.config.js
rollup.config.ts
src/
tsconfig.json
and:
~/parent_folder/handlers/http/example1/src
env.ts
index.test.ts
index.ts
and:
~/parent_folder/handlers/http/example1/src/index.ts
//@ts-expect-error: will be generated by rollup virtual plugin
import env from './env.ts'
export const handler = async (event) => {
const { method, url, headers, body } = event
const { SLUG, PORTAL_URL, CLIENT_ID, CLIENT_SECRET } = env
const mask = (s: string, mask = '*') => s.replace(/./g, mask)
console.log(
`handler: http: ${JSON.stringify(
{ SLUG, PORTAL_URL, CLIENT_ID: mask(CLIENT_ID), CLIENT_SECRET: mask(CLIENT_SECRET) },
null,
2
)}`
)
console.log(`handler: http: ${JSON.stringify({ method, url, headers, body }, null, 2)}`)
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: {
statusCode: 200,
message: 'Ok...'
}
}
}
You can edit your handlers main index file using your choice of editor or IDE. It is recommended that you add additional typescript files in your handlers src folder as the complexity of your handlers code increases. Simply import those files into you handlers main index file and the build tools will bundle them into the final ESM module file.
Formatting
You can run Prettier formatting on your handler's source files with:
handler format example1
Which does:
➜ format http/example1
prettier --config .prettierrc.json --ignore-path .prettierignore http/example1 --write
http/example1/handler.json 17ms
http/example1/jest.config.js 4ms (unchanged)
http/example1/rollup.config.ts 28ms (unchanged)
http/example1/src/env.ts 1ms (unchanged)
http/example1/src/index.test.ts 2ms (unchanged)
http/example1/src/index.ts 4ms (unchanged)
http/example1/tsconfig.json 1ms (unchanged)
✔ formated http/example1
You can change the formating rules for your workspace by editing ~/parent_folder/handlers/.prettierrc.json
Linting
You can run ESLint linting on your handler's source files with:
handler lint example1
Which does:
➜ lint http/example1
eslint --config http/example1/eslint.config.mjs http/example1
✔ linted http/example1
You can change the linting rules for your workspace by editing ~/parent_folder/handlers/eslint.config.mjs
Testing
You can run Jest testing of your handler with:
handler test example1
Which does:
➜ test http/example1
jest --config http/example1/jest.config.js --color
ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.
Test Suites: 1 skipped, 0 of 1 total
Tests: 1 skipped, 1 total
Snapshots: 0 total
Time: 0.639 s
Ran all test suites.
✔ tested http/example1
You can add and extend Jest tests to increase coverage of your handlers functionally.
Building
You can build and bundle your handlers with:
handler build example1
Which does:
➜ format http/example1
prettier --config .prettierrc.json --ignore-path .prettierignore http/example1 --write
http/example1/handler.json 17ms (unchanged)
http/example1/jest.config.js 4ms (unchanged)
http/example1/rollup.config.ts 28ms (unchanged)
http/example1/src/env.ts 1ms (unchanged)
http/example1/src/index.test.ts 1ms (unchanged)
http/example1/src/index.ts 5ms (unchanged)
http/example1/tsconfig.json 1ms (unchanged)
✔ formated http/example1
➜ lint http/example1
eslint --config http/example1/eslint.config.mjs http/example1
✔ linted http/example1
➜ test http/example1
jest --config http/example1/jest.config.js --color
ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.
Test Suites: 1 skipped, 0 of 1 total
Tests: 1 skipped, 1 total
Snapshots: 0 total
Time: 0.624 s
Ran all test suites.
✔ tested http/example1
➜ build http/example1
rimraf dist/http/example1
rollup --config http/example1/rollup.config.ts --configPlugin typescript={tsconfig:'http/example1/tsconfig.json'}
http/example1/src/index.ts → dist/http/example1/handler.mjs...
created dist/http/example1/handler.mjs in 575ms
zip -mq handler.zip handler.mjs
✔ built http/example1
build: output: 'dist/http/example1/handler.zip'
The build output would reside in: ~/parent_folder/handlers/http/example1/handler.zip
Note that execution rules will ensure chaining of dependent execution steps. This means executing a build will first format, then lint, then test, and finally build your handler. If any dependent step fails, the execution terminates. This helps improve your handlers module integrity.
Uploading
You can upload your handlers built module zip file with:
handler upload example1
Which does:
➜ format http/example1
prettier --config .prettierrc.json --ignore-path .prettierignore http/example1 --write
http/example1/handler.json 17ms (unchanged)
http/example1/jest.config.js 4ms (unchanged)
http/example1/rollup.config.ts 27ms (unchanged)
http/example1/src/env.ts 1ms (unchanged)
http/example1/src/index.test.ts 2ms (unchanged)
http/example1/src/index.ts 4ms (unchanged)
http/example1/tsconfig.json 1ms (unchanged)
✔ formated http/example1
➜ lint http/example1
eslint --config http/example1/eslint.config.mjs http/example1
✔ linted http/example1
➜ test http/example1
jest --config http/example1/jest.config.js --color
ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.
Test Suites: 1 skipped, 0 of 1 total
Tests: 1 skipped, 1 total
Snapshots: 0 total
Time: 0.578 s
Ran all test suites.
✔ tested http/example1
➜ build http/example1
rimraf dist/http/example1
rollup --config http/example1/rollup.config.ts --configPlugin typescript={tsconfig:'http/example1/tsconfig.json'}
http/example1/src/index.ts → dist/http/example1/handler.mjs...
created dist/http/example1/handler.mjs in 548ms
zip -mq handler.zip handler.mjs
✔ built http/example1
➜ fetch token
✔ fetched token
➜ query handler
✔ queried handler
➜ upload handler file
✔ uploaded handler file
upload: organizationId:
upload: handlerId:
upload: fileId: '1CGhApVaNfZJPsNq9ozojY'
Publishing
You can publish your handlers built module zip file with:
handler publish example1
Which does:
➜ format http/example1
prettier --config .prettierrc.json --ignore-path .prettierignore http/example1 --write
http/example1/handler.json 16ms
http/example1/jest.config.js 3ms (unchanged)
http/example1/rollup.config.ts 27ms (unchanged)
http/example1/src/env.ts 0ms (unchanged)
http/example1/src/index.test.ts 1ms (unchanged)
http/example1/src/index.ts 5ms (unchanged)
http/example1/tsconfig.json 1ms (unchanged)
✔ formated http/example1
➜ lint http/example1
eslint --config http/example1/eslint.config.mjs http/example1
✔ linted http/example1
➜ test http/example1
jest --config http/example1/jest.config.js --color
ts-jest[config] (WARN) message TS151001: If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.
Test Suites: 1 skipped, 0 of 1 total
Tests: 1 skipped, 1 total
Snapshots: 0 total
Time: 0.552 s
Ran all test suites.
✔ tested http/example1
➜ build http/example1
rimraf dist/http/example1
rollup --config http/example1/rollup.config.ts --configPlugin typescript={tsconfig:'http/example1/tsconfig.json'}
http/example1/src/index.ts → dist/http/example1/handler.mjs...
created dist/http/example1/handler.mjs in 554ms
zip -mq handler.zip handler.mjs
✔ built http/example1
➜ fetch token
✔ fetched token
➜ query handler
✔ queried handler
➜ upload handler file
✔ uploaded handler file
➜ query handler file
✔ queried handler file
➜ publish handler
✔ published handler
publish: enabled: true
publish: status: 'published'
Verifying
You can verify your published handler handler function with:
handler verify example1
Which does:
➜ fetch token
✔ fetched token
➜ query handler
✔ queried handler
➜ verify handler
✔ verified handler
verify: request: {
method: 'POST',
url: 'https://helping-hands.portals.zenginehq.dev:4201/handler/example1',
headers: { 'user-agent': 'agent', 'content-type': 'application/json' },
body: { foo: 1, bar: 2 }
}
verify: response: {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: { statusCode: 200, message: 'Ok...' }
}
Your published handler will be invoked with a sample request. Your handlers response will then be validated for type compliance. You may wish to include console.log() statements within your handlers code to help in debugging. These can be removed or commented out later.
Viewing Logs
You can view recent logs from invocation of your handler function with:
handler logs example1
This will display up to 10 log entries from within the last sixty seconds:
➜ fetch token
✔ fetched token
➜ query handler
✔ queried handler
➜ query handler logs
✔ queried handler logs
logs: [
'INIT_START Runtime Version: nodejs:20.v42\tRuntime Version ARN: arn:aws:lambda:us-east-1::runtime:af2737389420ddcdbcdc4db3585842c334ad2482127c15295905f54a62feefc6\n',
'START RequestId: 16f1ca8a-4ea5-4f10-83ae-19aa5bbe7487 Version: $LATEST\n',
'2024-10-29T23:20:16.126Z\t16f1ca8a-4ea5-4f10-83ae-19aa5bbe7487\tINFO\thandler: http: {\n' +
' "PORTAL_URL": "https://helping-hands.portals.zenginehq.dev:4201",\n' +
' "CLIENT_ID": "****************************************************************",\n' +
' "CLIENT_SECRET": "****************************************************************"\n' +
'}\n',
'2024-10-29T23:20:16.148Z\t16f1ca8a-4ea5-4f10-83ae-19aa5bbe7487\tINFO\thandler: http: {\n' +
' "method": "POST",\n' +
' "url": "https://helping-hands.portals.zenginehq.dev:4201/handler/example1",\n' +
' "headers": {\n' +
' "user-agent": "agent",\n' +
' "content-type": "application/json"\n' +
' },\n' +
' "body": {\n' +
' "foo": 1,\n' +
' "bar": 2\n' +
' }\n' +
'}\n',
'END RequestId: 16f1ca8a-4ea5-4f10-83ae-19aa5bbe7487\n',
'REPORT RequestId: 16f1ca8a-4ea5-4f10-83ae-19aa5bbe7487\tDuration: 40.86 ms\tBilled Duration: 41 ms\tMemory Size: 128 MB\tMax Memory Used: 64 MB\tInit Duration: 133.32 ms\t\n'
]
Log entries will automatically expire after a short duration.