From 75273ad58b4c7ada711cf4eb5acf9be9010060a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lenon?= Date: Sat, 14 Jan 2023 17:49:31 -0300 Subject: [PATCH] chore(helpers): little improvements --- package-lock.json | 4 +- package.json | 5 +- src/Helpers/Exception.js | 56 +++++-- src/Helpers/FakeApi.js | 18 +++ src/Helpers/HttpClient.js | 8 +- src/Helpers/Path.js | 22 +++ src/index.d.ts | 309 +++++++++++++++++++++++++++++++++++- src/index.js | 6 + tests/Unit/ExceptionTest.js | 14 ++ tests/Unit/FakeApiTest.js | 4 + tests/Unit/PathTest.js | 13 +- 11 files changed, 437 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index a999c1f..b8d25d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@athenna/common", - "version": "3.0.0", + "version": "3.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@athenna/common", - "version": "3.0.0", + "version": "3.0.1", "license": "MIT", "dependencies": { "@fastify/formbody": "7.4.0", diff --git a/package.json b/package.json index 75b5ed3..90bb873 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@athenna/common", - "version": "3.0.0", + "version": "3.0.1", "description": "The Athenna common helpers to use in any Node.js ESM project.", "license": "MIT", "author": "João Lenon ", @@ -141,6 +141,9 @@ "es2021": true, "node": true }, + "globals": { + "Path": true + }, "plugins": [ "prettier" ], diff --git a/src/Helpers/Exception.js b/src/Helpers/Exception.js index 07b12d4..267c5fb 100644 --- a/src/Helpers/Exception.js +++ b/src/Helpers/Exception.js @@ -1,3 +1,4 @@ +/* eslint-disable no-extend-native */ /** * @athenna/common * @@ -114,19 +115,56 @@ export class Exception extends Error { displayMainFrameOnly: false, }) - const message = `${chalk.yellow.bold('MESSAGE')}\n ${this.message}` - const help = ` ${chalk.green.bold('HELP')}\n ${this.help}` - this.name = this.code + const helpKey = chalk.green.bold('HELP') + const messageKey = chalk.yellow.bold('MESSAGE') + + if (this.message && this.message !== '') { + this.message = `${messageKey}\n ${this.message}` + } - if (this.help) { - this.message = `${message}\n\n${help}` - } else { - this.message = `${message}` + if (this.help && this.help !== '') { + this.message = `${this.message}\n\n ${helpKey}\n ${this.help}` } - const jsonResponse = await new Youch(this, {}).toJSON() + const pretty = await new Youch(this, {}).toJSON() - return YouchTerminal(jsonResponse, options).concat('\n') + if (!pretty.error.frames.find(frame => frame.isApp)) { + pretty.error.frames = pretty.error.frames.map(frame => { + frame.isApp = true + return frame + }) + } + + return YouchTerminal(pretty, options).concat('\n') } } + +/** + * Transform your error to an instance of + * the Athenna exception. + * + * @param options {{ + * code?: string, + * status?: number, + * content?: string, + * help?: string, + * stack?: any + * }} + * @return {Exception} + */ +Error.prototype.toAthennaException = function (options) { + const { content, status, code, help, stack } = Options.create(options, { + content: this.message, + status: 0, + code: this.name, + help: undefined, + stack: this.stack, + }) + + const exception = new Exception(content, status, code, help) + + exception.stack = stack + + return exception +} diff --git a/src/Helpers/FakeApi.js b/src/Helpers/FakeApi.js index 794a684..dab73ba 100644 --- a/src/Helpers/FakeApi.js +++ b/src/Helpers/FakeApi.js @@ -16,6 +16,13 @@ import { Debug } from '#src/Helpers/Debug' import { Folder } from '#src/Helpers/Folder' export class FakeApi { + /** + * Set if the FakeApi server is running. + * + * @type {boolean} + */ + static #isRunning = false + /** * Create the fastify server with plugins. * @@ -49,6 +56,15 @@ export class FakeApi { return app.printRoutes() } + /** + * List the routes registered in the fake server. + * + * @return {boolean} + */ + static isRunning() { + return this.#isRunning + } + /** * Register all routes inside folder path * and start the fake api server at port 8989. @@ -63,6 +79,7 @@ export class FakeApi { } await app.listen({ port }) + this.#isRunning = true } /** @@ -74,6 +91,7 @@ export class FakeApi { await app.close() app = FakeApi.recreate() + this.#isRunning = false } /** diff --git a/src/Helpers/HttpClient.js b/src/Helpers/HttpClient.js index 26bee32..4b3f5c7 100644 --- a/src/Helpers/HttpClient.js +++ b/src/Helpers/HttpClient.js @@ -634,15 +634,13 @@ export class HttpClientBuilder { } /** - * Alias for the searchParameters method. + * Alias for the searchParams method. * * @param value { string | import('got').SearchParameters | URLSearchParams } * @return {HttpClientBuilder} */ - searchParameters(value) { - this.#options.searchParameters = value - - return this + queryParams(value) { + return this.searchParams(value) } /** diff --git a/src/Helpers/Path.js b/src/Helpers/Path.js index 1fec71b..1fe0793 100644 --- a/src/Helpers/Path.js +++ b/src/Helpers/Path.js @@ -21,6 +21,28 @@ export class Path { */ static defaultBeforePath = '' + /** + * Resolve the environment where the application + * is running by verifying the import.meta.url. + * + * This method will auto set the IS_TS env and the + * defaultBeforePath if IS_TS is true. + * + * The beforePath is always set as '/build' by default. + * + * @param metaUrl {string} + * @param beforePath {string} + * @return {typeof Path} + */ + static resolveEnvironment(metaUrl, beforePath = '/build') { + const isTs = metaUrl.endsWith('.ts') ? 'true' : 'false' + + process.env.IS_TS = isTs + this.defaultBeforePath = isTs === 'true' ? beforePath : '' + + return this + } + /** * Return js or ts extension depending on IS_TS. * diff --git a/src/index.d.ts b/src/index.d.ts index 0a5c148..fd007ef 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -371,6 +371,13 @@ export class FakeApi { */ static listRoutes(): string + /** + * List the routes registered in the fake server. + * + * @return {boolean} + */ + static isRunning(): boolean + /** * Register all routes inside folder path * and start the fake api server at port 8989. @@ -1411,14 +1418,12 @@ export declare class HttpClientBuilder { ): HttpClientBuilder /** - * Alias for the searchParameters method. + * Alias for the searchParams method. * * @param value { string | import('got').SearchParameters | URLSearchParams } * @return {HttpClientBuilder} */ - searchParameters( - value: string | import('got').SearchParameters | URLSearchParams, - ): HttpClientBuilder + queryParams(value: string | import('got').SearchParameters | URLSearchParams): HttpClientBuilder /** * Set the dnsLookup parameter. @@ -2708,6 +2713,21 @@ export declare class Path { */ static defaultBeforePath: string + /** + * Resolve the environment where the application + * is running by verifying the import.meta.url. + * + * This method will auto set the IS_TS env and the + * defaultBeforePath if IS_TS is true. + * + * The beforePath is always set as '/build' by default. + * + * @param metaUrl {string} + * @param beforePath {string} + * @return {typeof Path} + */ + static resolveEnvironment(metaUrl: string, beforePath?: string): typeof Path + /** * Return js or ts extension depending on IS_TS. * @@ -3182,9 +3202,290 @@ export declare class Uuid { } declare global { + export class Path { + /** + * Set a default beforePath for all Path methods that + * use Path.pwd. + * + * @type {string} + */ + static defaultBeforePath: string + + /** + * Resolve the environment where the application + * is running by verifying the import.meta.url. + * + * This method will auto set the IS_TS env and the + * defaultBeforePath if IS_TS is true. + * + * The beforePath is always set as '/build' by default. + * + * @param metaUrl {string} + * @param beforePath {string} + * @return {typeof Path} + */ + static resolveEnvironment(metaUrl: string, beforePath?: string): typeof Path + + /** + * Return js or ts extension depending on IS_TS. + * + * @return {string} + */ + static ext(): string + + /** + * Return the pwd path of your project. + * + * @param {string} [subPath] + * @return {string} + */ + static pwd(subPath?: string): string + + /** + * Return the app path of your project. + * + * @param {string} subPath + * @return {string} + */ + static app(subPath?: string): string + + /** + * Return the bootstrap path of your project. + * + * @param {string} subPath + * @return {string} + */ + static bootstrap(subPath?: string): string + + /** + * Return the config path of your project. + * + * @param {string} subPath + * @return {string} + */ + static config(subPath?: string): string + + /** + * Return the database path of your project. + * + * @param {string} subPath + * @return {string} + */ + static database(subPath?: string): string + + /** + * Return the lang path of your project. + * + * @param {string} subPath + * @return {string} + */ + static lang(subPath?: string): string + + /** + * Return the node_modules path of your project. + * + * @param {string} subPath + * @return {string} + */ + static nodeModules(subPath?: string): string + + /** + * Return the providers' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static providers(subPath?: string): string + + /** + * Return the public path of your project. + * + * @param {string} subPath + * @return {string} + */ + static public(subPath?: string): string + + /** + * Return the resources' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static resources(subPath?: string): string + + /** + * Return the routes' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static routes(subPath?: string): string + + /** + * Return the storage path of your project. + * + * @param {string} subPath + * @return {string} + */ + static storage(subPath?: string): string + + /** + * Return the tests' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static tests(subPath?: string): string + + /** + * Return the logs' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static logs(subPath?: string): string + + /** + * Return the views' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static views(subPath?: string): string + + /** + * Return the assets' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static assets(subPath?: string): string + + /** + * Return the locales' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static locales(subPath?: string): string + + /** + * Return the facades' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static facades(subPath?: string): string + + /** + * Return the stubs' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static stubs(subPath?: string): string + + /** + * Return the http path of your project. + * + * @param {string} subPath + * @return {string} + */ + static http(subPath?: string): string + + /** + * Return the console path of your project. + * + * @param {string} subPath + * @return {string} + */ + static console(subPath?: string): string + + /** + * Return the services' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static services(subPath?: string): string + + /** + * Return the migrations' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static migrations(subPath?: string): string + + /** + * Return the seeders' path of your project. + * + * @param {string} subPath + * @return {string} + */ + static seeders(subPath?: string): string + + /** + * Return the .bin path of your node_modules. + * + * @param {string} subPath + * @return {string} + */ + static bin(subPath?: string): string + + /** + * Return the tmp path of your vm. + * + * @param {string} subPath + * @return {string} + */ + static vmTmp(subPath?: string): string + + /** + * Return the home path of your vm. + * + * @param {string} subPath + * @return {string} + */ + static vmHome(subPath?: string): string + + /** + * Return the execution path of where this method + * is being called. + * + * @param {string} subPath + * @param {number} [stackIndex] + * @return {string} + */ + static this(subPath?: string, stackIndex?: number): string + } + interface Array { toResource(criterias?: any): T[] toCollection(): Collection } + + interface ErrorConstructor { + /** + * Transform your error to an instance of + * the Athenna exception. + * + * @param options {{ + * code?: string, + * status?: number, + * content?: string, + * help?: string, + * stack?: any + * }} + * @return {Exception} + */ + toAthennaException(options?: { + code?: string + status?: number + content?: string + help?: string + stack?: any + }): Exception + } } diff --git a/src/index.js b/src/index.js index b4b6b25..7682a70 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,8 @@ */ // Helpers +import { Path } from './Helpers/Path.js' + export * from './Helpers/Clean.js' export * from './Helpers/Collection.js' export * from './Helpers/Debug.js' @@ -27,3 +29,7 @@ export * from './Helpers/Path.js' export * from './Helpers/Route.js' export * from './Helpers/String.js' export * from './Helpers/Uuid.js' + +if (!global.Path) { + global.Path = Path +} diff --git a/tests/Unit/ExceptionTest.js b/tests/Unit/ExceptionTest.js index b21febd..1687f76 100644 --- a/tests/Unit/ExceptionTest.js +++ b/tests/Unit/ExceptionTest.js @@ -22,6 +22,20 @@ test.group('ExceptionTest', () => { assert.equal(errorJson.content, 'My custom instance error') }) + test('should be able to create a new exception from vanilla errors', async ({ assert }) => { + const exception = new Error('My custom instance error').toAthennaException({ + code: 'EXCEPTION', + name: 'Exception', + }) + + const errorJson = exception.toJSON() + + assert.equal(errorJson.status, 0) + assert.equal(errorJson.code, 'EXCEPTION') + assert.equal(errorJson.name, 'Exception') + assert.equal(errorJson.content, 'My custom instance error') + }) + test('should be able to extend exception class to create a new exception', async ({ assert }) => { class InternalServerException extends Exception { constructor(content = 'Internal Server Error', status = 500) { diff --git a/tests/Unit/FakeApiTest.js b/tests/Unit/FakeApiTest.js index 74949eb..09d4648 100644 --- a/tests/Unit/FakeApiTest.js +++ b/tests/Unit/FakeApiTest.js @@ -31,10 +31,12 @@ test.group('FakeApiTest', group => { .statusCode(201) .register() + assert.isFalse(FakeApi.isRunning()) await FakeApi.start(8989, null) const response = await HttpClient.get('http://localhost:8989/example', { responseType: 'json' }) + assert.isTrue(FakeApi.isRunning()) assert.equal(response.statusCode, 201) assert.deepEqual(response.body, { hello: 'world', example: 'example' }) }) @@ -49,10 +51,12 @@ test.group('FakeApiTest', group => { FakeApi.build().path('/example-redirect').redirectTo('http://localhost:8989/example').register() + assert.isFalse(FakeApi.isRunning()) await FakeApi.start(8989, null) const response = await HttpClient.get('http://localhost:8989/example-redirect', { responseType: 'json' }) + assert.isTrue(FakeApi.isRunning()) assert.equal(response.statusCode, 201) assert.deepEqual(response.body, { hello: 'world', example: 'example' }) }) diff --git a/tests/Unit/PathTest.js b/tests/Unit/PathTest.js index 5524f68..4374c76 100644 --- a/tests/Unit/PathTest.js +++ b/tests/Unit/PathTest.js @@ -8,10 +8,21 @@ */ import { sep } from 'node:path' -import { Path } from '#src/index' import { test } from '@japa/runner' test.group('PathTest', () => { + test('should be able to resolve the environment where the app will run', async ({ assert }) => { + Path.resolveEnvironment(import.meta.url.replace('.js', '.ts')) + + assert.equal(process.env.IS_TS, 'true') + assert.equal(Path.defaultBeforePath, '/build') + + Path.resolveEnvironment(import.meta.url) + + assert.equal(process.env.IS_TS, 'false') + assert.equal(Path.defaultBeforePath, '') + }) + test('should get the extension js and ts', async ({ assert }) => { assert.equal(Path.ext(), 'js')