diff --git a/docs/blog/assets/version-5.0-is-here/banner.png b/docs/blog/assets/version-5.0-is-here/banner.png new file mode 100644 index 0000000000..2270aaebc4 Binary files /dev/null and b/docs/blog/assets/version-5.0-is-here/banner.png differ diff --git a/docs/blog/version-5.0-release-notes.md b/docs/blog/version-5.0-release-notes.md new file mode 100644 index 0000000000..b3aecf93e8 --- /dev/null +++ b/docs/blog/version-5.0-release-notes.md @@ -0,0 +1,19 @@ +--- +title: Version 5.0 release notes +author: Loïc Poullain +author_title: Creator of FoalTS. Software engineer. +author_url: https://loicpoullain.com +author_image_url: https://avatars1.githubusercontent.com/u/13604533?v=4 +image: blog/twitter-banners/version-5.0-release-notes.png +tags: [release] +--- + +![Banner](./assets/version-5.0-is-here/banner.png) + +Version 5.0 of [Foal](https://foalts.org/) is out! + + + +## Removal of depreacted components + +- The deprecated hook `@Log` has been removed. To replace it, you can use the `Logger` service in a custom `@Hook`. \ No newline at end of file diff --git a/docs/docs/architecture/architecture-overview.md b/docs/docs/architecture/architecture-overview.md index 11a2b5fb9b..6d395d3511 100644 --- a/docs/docs/architecture/architecture-overview.md +++ b/docs/docs/architecture/architecture-overview.md @@ -76,7 +76,7 @@ Controllers may have sub-controllers. Hooks can be attached to the controllers o Here's an example of what a FoalTS application may look like. ```typescript -import { Context, controller, Get, HttpResponseNotFound, HttpResponseOK, Log } from '@foal/core'; +import { Context, controller, Get, Hook, HttpResponseNotFound, HttpResponseOK } from '@foal/core'; import { JWTRequired } from '@foal/jwt'; @JWTRequired() @@ -105,7 +105,9 @@ class ApiController { } } -@Log('Receiving a request...') +@Hook(() => { + console.log('Receiving a request...') +}) class AppController { subControllers = [ controller('/api', ApiController) diff --git a/docs/docs/architecture/hooks.md b/docs/docs/architecture/hooks.md index ee868b7d9a..5bea7760a8 100644 --- a/docs/docs/architecture/hooks.md +++ b/docs/docs/architecture/hooks.md @@ -23,7 +23,6 @@ They improve code readability and make unit testing easier. Foal provides a number of hooks to handle the most common scenarios. - `ValidateBody`, `ValidateHeader`, `ValidatePathParam`, `ValidateCookie` and `ValidateQueryParam` validate the format of the incoming HTTP requests (see [Validation](../common/validation-and-sanitization.md)). -- `Log` displays information on the request (see [Logging](../common/logging.md)). - `JWTRequired`, `JWTOptional`, `UseSessions` authenticate the user by filling the `ctx.user` property. - `PermissionRequired` restricts the route access to certain users. @@ -73,7 +72,7 @@ If the user makes a POST request to `/products` whereas she/he is not authentica > If you need to apply a hook globally, you just have to make it decorate the root controller: `AppController`. > > ```typescript -> @Log('Request body:', { body: true }) +> @UseSessions() > export class AppController { > // ... > } diff --git a/docs/docs/common/logging.md b/docs/docs/common/logging.md index 8bbb66ebc2..bb2a61d6a1 100644 --- a/docs/docs/common/logging.md +++ b/docs/docs/common/logging.md @@ -274,36 +274,3 @@ logger.addTransport((level: 'debug'|'warn'|'info'|'error', log: string) => { // Do something }) ``` - -## Logging Hook (deprecated) - -> This hook is deprecated and will be removed in a next release. Use the `Logger` service in a custom hook instead. - -FoalTS provides a convenient hook for logging debug messages: `Log(message: string, options: LogOptions = {})`. - -```typescript -interface LogOptions { - body?: boolean; - params?: boolean; - headers?: string[]|boolean; - query?: boolean; -} -``` - -*Example:* -```typescript -import { Get, HttpResponseOK, Log } from '@foal/core'; - -@Log('AppController', { - body: true, - headers: [ 'X-CSRF-Token' ], - params: true, - query: true -}) -export class AppController { - @Get() - index() { - return new HttpResponseOK(); - } -} -``` diff --git a/docs/static/blog/twitter-banners/version-5.0-release-notes.png b/docs/static/blog/twitter-banners/version-5.0-release-notes.png new file mode 100644 index 0000000000..118c134a68 Binary files /dev/null and b/docs/static/blog/twitter-banners/version-5.0-release-notes.png differ diff --git a/packages/acceptance-tests/src/docs/architecture/architecture-overview/setting-up-a-simple-application.feature.ts b/packages/acceptance-tests/src/docs/architecture/architecture-overview/setting-up-a-simple-application.feature.ts index 58ab23456f..2bce691a82 100644 --- a/packages/acceptance-tests/src/docs/architecture/architecture-overview/setting-up-a-simple-application.feature.ts +++ b/packages/acceptance-tests/src/docs/architecture/architecture-overview/setting-up-a-simple-application.feature.ts @@ -9,9 +9,9 @@ import { controller, createApp, Get, + // Hook, HttpResponseNotFound, HttpResponseOK, - // Log, } from '@foal/core'; import { getSecretOrPrivateKey, JWTRequired } from '@foal/jwt'; @@ -58,7 +58,9 @@ describe('Feature: Setting up a simple application', () => { } // Commented in this test to avoid noise in the terminal. - // @Log('Receiving a request...') + // @Hook(() => { + // console.log('Receiving a request...') + // }) class AppController { subControllers = [ controller('/api', ApiController) diff --git a/packages/core/src/common/utils/index.ts b/packages/core/src/common/utils/index.ts index 57c5dcd9f9..8c5c1a7dc2 100644 --- a/packages/core/src/common/utils/index.ts +++ b/packages/core/src/common/utils/index.ts @@ -1,5 +1,4 @@ export { controller } from './controller.util'; export { displayServerURL } from './display-server-url.util'; export { isInFile } from './is-in-file.util'; -export { Log, LogOptions } from './log.hook'; export { streamToBuffer } from './stream-to-buffer'; diff --git a/packages/core/src/common/utils/log.hook.spec.ts b/packages/core/src/common/utils/log.hook.spec.ts deleted file mode 100644 index b428de27ab..0000000000 --- a/packages/core/src/common/utils/log.hook.spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -// std -import { strictEqual } from 'assert'; -import { mock } from 'node:test'; - -// FoalTS -import { Context, getHookFunction, ServiceManager, Logger } from '../../core'; -import { Log } from './log.hook'; - -describe('Log', () => { - - let logFn: (message?: any, ...optionalParams: any[]) => void; - let msgs: any[]; - - beforeEach(() => { - msgs = []; - logFn = (...args) => msgs.push(args); - }); - - afterEach(() => { - mock.reset(); - }) - - it('should log a deprecation message.', () => { - const hook = getHookFunction(Log('foo', { logFn })); - - const ctx = new Context({}); - const services = new ServiceManager(); - - const logger = services.get(Logger); - const loggerMock = mock.method(logger, 'warn').mock; - - hook(ctx, services); - - strictEqual(loggerMock.callCount(), 1); - strictEqual( - loggerMock.calls[0].arguments[0], - 'Using the @Log hook is deprecated. Use the Logger service in a custom hook instead.' - ); - }); - - it('should log the message.', () => { - const hook = getHookFunction(Log('foo', { logFn })); - - const ctx = new Context({}); - hook(ctx, new ServiceManager()); - - strictEqual(msgs.length, 1); - strictEqual(msgs[0][0], 'foo'); - }); - - it('should log the request body if options.body = true.', () => { - const hook = getHookFunction(Log('foo', { body: true, logFn })); - - const body = { foo: 'bar' }; - const ctx = new Context({ body }); - hook(ctx, new ServiceManager()); - - strictEqual(msgs.length, 2); - strictEqual(msgs[1][0], 'Body: '); - strictEqual(msgs[1][1], body); - }); - - it('should log the request params if options.params = true.', () => { - const hook = getHookFunction(Log('foo', { params: true, logFn })); - - const params = { foo: 'bar' }; - const ctx = new Context({ params }); - hook(ctx, new ServiceManager()); - - strictEqual(msgs.length, 2); - strictEqual(msgs[1][0], 'Params: '); - strictEqual(msgs[1][1], params); - }); - - it('should log the request query if options.query = true.', () => { - const hook = getHookFunction(Log('foo', { query: true, logFn })); - - const query = { foo: 'bar' }; - const ctx = new Context({ query }); - hook(ctx, new ServiceManager()); - - strictEqual(msgs.length, 2); - strictEqual(msgs[1][0], 'Query: '); - strictEqual(msgs[1][1], query); - }); - - it('should log the request headers if options.headers is a string array.', () => { - const hook = getHookFunction(Log('foo', { headers: [ 'my-header1', 'my-header2' ], logFn })); - - const headers = { - // According to RFC 7230, each header field consists of a case-INsensitive field name. - 'My-header2': 'header 2', - 'my-header1': 'header 1', - 'my-header3': 'header 3' - }; - const ctx = new Context({ headers }); - hook(ctx, new ServiceManager()); - - strictEqual(msgs.length, 3); - strictEqual(msgs[1][0], 'my-header1: '); - strictEqual(msgs[1][1], 'header 1'); - strictEqual(msgs[2][0], 'my-header2: '); - // Test the case - strictEqual(msgs[2][1], 'header 2'); - }); - - it('should log all the request headers if options.headers = true.', () => { - const hook = getHookFunction(Log('foo', { headers: true, logFn })); - - const headers = { - 'my-header1': 'header 1', - 'my-header2': 'header 2', - 'my-header3': 'header 3' - }; - const ctx = new Context({ headers }); - hook(ctx, new ServiceManager()); - - strictEqual(msgs.length, 2); - strictEqual(msgs[1][0], 'Headers: '); - strictEqual(msgs[1][1], headers); - }); - -}); diff --git a/packages/core/src/common/utils/log.hook.ts b/packages/core/src/common/utils/log.hook.ts deleted file mode 100644 index e414a7925d..0000000000 --- a/packages/core/src/common/utils/log.hook.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Context, Hook, HookDecorator, Logger } from '../../core'; - -/** - * Options of the `Log` hook. - * - * @export - * @interface LogOptions - */ -export interface LogOptions { - body?: boolean; - params?: boolean; - headers?: string[]|boolean; - query?: boolean; - logFn?: (message?: any, ...optionalParams: any[]) => void; -} - -/** - * Hook logging a message with optional information on the HTTP request. - * - * @param message The message to print. - * @param options Options to specify which information on the HTTP request should be printed. - */ - -/** - * Hook factory logging a message with optional information on the HTTP request. - * - * @export - * @deprecated Use the Logger service in a custom hook instead. - * @param {string} message - The message to print on each request. - * @param {LogOptions} [options={}] - Options to specify which information on the HTTP request should be printed. - * @returns {HookDecorator} The hook. - */ -export function Log(message: string, options: LogOptions = {}): HookDecorator { - const logFn = options.logFn || console.log; - return Hook((ctx: Context, services) => { - const logger = services.get(Logger); - logger.warn('Using the @Log hook is deprecated. Use the Logger service in a custom hook instead.'); - - logFn(message); - if (options.body) { - logFn('Body: ', ctx.request.body); - } - if (options.params) { - logFn('Params: ', ctx.request.params); - } - if (options.query) { - logFn('Query: ', ctx.request.query); - } - if (options.headers === true) { - logFn('Headers: ', ctx.request.headers); - } else if (Array.isArray(options.headers)) { - for (const header of options.headers) { - const headerName = Object.keys(ctx.request.headers).find( - head => header.toLowerCase() === head.toLowerCase() // Header names are case insensitive. - ); - logFn(`${header}: `, headerName === undefined ? undefined : ctx.request.headers[headerName]); - } - } - }); -} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 346fa2760e..99997606b2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -17,8 +17,6 @@ export { AsyncService, File, FileList, - Log, - LogOptions, UserRequired, ValidateBody, ValidateCookie,