From 29ac2c6d258b0bfecb3fc1c21a9d76d77fe75f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Lenon?= Date: Sun, 8 Sep 2024 11:27:59 +0100 Subject: [PATCH] feat: add docs for schedulers page --- .../service-container.mdx | 2 +- docs/cli-application/commands.mdx | 8 +- docs/cli-application/error-handling.mdx | 6 +- docs/cron-application/annotations.mdx | 14 ++ docs/cron-application/cron-context.mdx | 15 ++ docs/cron-application/error-handling.mdx | 15 ++ docs/cron-application/schedulers.mdx | 189 +++++++++++++++++- docs/cron-application/tracing-executions.mdx | 15 ++ docs/rest-api-application/error-handling.mdx | 2 +- docs/rest-api-application/middlewares.mdx | 4 +- docs/the-basics/helpers.mdx | 22 +- src/components/path.module.css | 1 - src/components/path.tsx | 3 + 13 files changed, 272 insertions(+), 24 deletions(-) create mode 100644 docs/cron-application/annotations.mdx create mode 100644 docs/cron-application/cron-context.mdx create mode 100644 docs/cron-application/error-handling.mdx create mode 100644 docs/cron-application/tracing-executions.mdx diff --git a/docs/architecture-concepts/service-container.mdx b/docs/architecture-concepts/service-container.mdx index 245a420a..5fd9dc78 100644 --- a/docs/architecture-concepts/service-container.mdx +++ b/docs/architecture-concepts/service-container.mdx @@ -92,7 +92,7 @@ develop without it. The example above is just to show that we can place our services anywhere in our application, without depending on configuration files or any other kind of setup. We recommend you placing your services in specifics directory and not -inside your route file. A good place to put your services is `app/services` +inside your route file. A good place to put your services is `src/services` directory, since `make:service` command will save your services there. But remember that this is only a tip, at the end of the day you can do whatever you want with Athenna 😎. diff --git a/docs/cli-application/commands.mdx b/docs/cli-application/commands.mdx index 2d876bd0..bdf2dc8c 100644 --- a/docs/cli-application/commands.mdx +++ b/docs/cli-application/commands.mdx @@ -21,13 +21,13 @@ creating your own commands by extending the abstract class In addition to the commands provided with Artisan, you may build your own custom commands. Commands are typically stored -in the `app/console/commands` directory; however, you are +in the `src/console/commands` directory; however, you are free to choose your own storage location as long as your commands can be imported and registered. To create a new command, you may use the `make:command` Artisan command. This command will create a new command -class in the `app/console/commands` directory and register +class in the `src/console/commands` directory and register it inside `commands` object of `.athennarc.json` file. Don't worry if this directory does not exist in your application—it will be created the first time you run the @@ -732,7 +732,7 @@ export class Greet extends BaseCommand { this.logger.column(data, { columns: ['MODULE', 'COUNT'] }) - const path = 'app/services/Service.ts' + const path = 'src/services/Service.ts' const action = this.logger.action('create') action.succeeded(path) @@ -884,7 +884,7 @@ actions to explain to the user what happened to some determined operation: ```typescript -const path = 'app/services/Service.ts' +const path = 'src/services/Service.ts' const action = this.logger.action('create') action.succeeded(path) diff --git a/docs/cli-application/error-handling.mdx b/docs/cli-application/error-handling.mdx index 9bcc8967..d8c55002 100644 --- a/docs/cli-application/error-handling.mdx +++ b/docs/cli-application/error-handling.mdx @@ -26,7 +26,7 @@ This documentation will cover about error handling in the **CLI** application, which means that only errors that happens inside the `handle()` method of commands and bellow that will be handled: -```typescript title="app/console/commands/AppCommand.ts" +```typescript title="src/console/commands/AppCommand.ts" import { BaseCommand } from '@athenna/artisan' import { AppService } from '#src/services/AppService' @@ -127,7 +127,7 @@ Simple exceptions are recognized by `ConsoleExceptionHandler` by the `code`. If the code is `E_SIMPLE_CLI`, it will be considered as a simple exception: -```typescript title="app/console/exceptions/NotFoundDatabaseExceptio.ts" +```typescript title="src/console/exceptions/NotFoundDatabaseExceptio.ts" import { Exception } from '@athenna/common' export class NotFoundDatabaseException extends Exception { @@ -145,7 +145,7 @@ Let's suppose you want to write a custom logic for handling your exceptions. You can do so by creating your own exception handler: -```typescript title="app/console/exceptions/Handler.ts" +```typescript title="src/console/exceptions/Handler.ts" import { ConsoleExceptionHandler } from '@athenna/artisan' export class Handler extends ConsoleExceptionHandler { diff --git a/docs/cron-application/annotations.mdx b/docs/cron-application/annotations.mdx new file mode 100644 index 00000000..7993a752 --- /dev/null +++ b/docs/cron-application/annotations.mdx @@ -0,0 +1,14 @@ +--- +title: Annotations +sidebar_position: 3 +toc_max_heading_level: 2 +description: Check all available CRON annotations and it options. +--- + +import Path from '@site/src/components/path' + +# Annotations + +Check all available CRON annotations and it options. + +Coming Soon... diff --git a/docs/cron-application/cron-context.mdx b/docs/cron-application/cron-context.mdx new file mode 100644 index 00000000..7ea62f96 --- /dev/null +++ b/docs/cron-application/cron-context.mdx @@ -0,0 +1,15 @@ +--- +title: CRON Context +sidebar_position: 2 +description: Understand the purpose of the CRON context object. +--- + +import Path from '@site/src/components/path' + +# CRON Context + +Understand the purpose of the CRON context object. + +## Introduction + +Coming Soon... diff --git a/docs/cron-application/error-handling.mdx b/docs/cron-application/error-handling.mdx new file mode 100644 index 00000000..ee042d9f --- /dev/null +++ b/docs/cron-application/error-handling.mdx @@ -0,0 +1,15 @@ +--- +title: Error Handling +sidebar_position: 2 +description: Understand how you can handle the errors of the CRON Application. +--- + +import Path from '@site/src/components/path' + +# Error Handling + +Understand how you can handle the errors of the CRON Application. + +## Introduction + +Coming Soon... diff --git a/docs/cron-application/schedulers.mdx b/docs/cron-application/schedulers.mdx index 491c92e4..0a5773fb 100644 --- a/docs/cron-application/schedulers.mdx +++ b/docs/cron-application/schedulers.mdx @@ -4,10 +4,197 @@ sidebar_position: 1 description: See how to create and configure your CRON job schedulers. --- +import Path from '@site/src/components/path' + # Schedulers See how to create and configure your CRON job schedulers. ## Introduction -Coming soon... +Athenna's scheduler offers a fresh approach to managing +scheduled tasks on your server. The scheduler allows you +to fluently and expressively define your scheduler within +your Athenna application itself. When using the scheduler, +only a single cron entry is needed on your server. Your +task schedule can be defined in your application's + file or as a class +inside . + +## Defining Schedulers + +Schedulers are typically stored in the `src/cron/scheduelrs` +directory; however, you are free to choose your own storage +location as long as your schedulers can be imported and registered. + +To create a new scheduler, you may use the `make:scheduler` +Artisan command. This command will create a new command class +in the `src/cron/schedulers` directory and register it inside +`schedulers` array of `.athennarc.json` file. Don't worry if +this directory does not exist in your application—it will be +created the first time you run the `make:scheduler` Artisan command: + +```bash +node artisan make:scheduler DeleteRecentUsers +``` + +This will create the schedulers file and automatically register +it for you: + +```json title=".athennarc.json" +{ + "schedulers": [ + "#src/cron/schedulers/DeleteRecentUsers" 👈 + ] +} +``` + +### Defining schedulers logic + +In this example, we will schedule a `handler` method to be called +**every day at midnight**. Within the method we will execute a +database query to clear a table: + +```typescript +import { Database } from '@athenna/database' +import { Scheduler, type Context } from '@athenna/cron' + +@Scheduler({ pattern: '0 0 * * *' }) +export class DeleteRecentUsers { + public async handler(ctx: Context) { + await Database.table('recent_users').delete() + } +} +``` + +:::tip + +You can use [Crontab.guru](https://crontab.guru/) to help you +create your CRON pattern, or simply ask [ChatGPT](https://chatgpt.com/) 🤩. + +::: + +#### Defining schedulers in route file + +If you prefer, you can use the +file to register your schedulers: + +```typescript +import { Cron } from '@athenna/cron' +import { Database } from '@athenna/database' + +Cron.schedule().name('delete_recent_users') + .pattern('0 0 * * *') + .handler(async (ctx) => { + await Database.table('recent_users').delete() + }) +``` + +### Listing schedulers (Coming Soon) + +If you would like to view an overview of your scheduled tasks and the +next time they are scheduled to run, you may use the cron:list Artisan +command: + +```shell +node artisan cron:list +``` + +## Running scheduler locally (Coming Soon) + +When developing or even in production you might need to +force the scheduler to run. To do so you can use the +`node artisan cron:run` command: + +```typescript +node artisan cron:run DeleteRecentUsers +``` + +You can also use a CRON pattern, this will trigger all the +schedulers registered with the same pattern: + +```typescript +node artisan cron:run "0 0 * * *" +``` + +### Using `runOnInit` option + +Another way to run your schedulers locally is to define the +`runOnInit=true` option: + +```typescript +import { Database } from '@athenna/database' +import { Scheduler, type Context } from '@athenna/cron' + +@Scheduler({ + runOnInit: true, 👈 + pattern: '0 0 * * *' +}) +export class DeleteRecentUsers { + public async handler(ctx: Context) { + await Database.table('recent_users').delete() + } +} +``` + +If using routes you may call the `runOnInit()` method: + +```typescript +import { Cron } from '@athenna/cron' +import { Database } from '@athenna/database' + +Cron.schedule().name('delete_recent_users') + .runOnInit(true) 👈 + .pattern('0 0 * * *') + .handler(async (ctx) => { + await Database.table('recent_users').delete() + }) +``` + +If this option is set to `true`, it will automatically run your +scheduler when bootstrapping your Athenna application. + +## Dependency injection in schedulers + +When using schedulers classes you are able to use the `@Inject()` +annotation to inject dependencies from you application within your +scheduler class: + +```typescript +import { Inject } from '@athenna/ioc' +import { Scheduler, type Context } from '@athenna/cron' +import { RecentUserService } from '#src/services/RecentUserService' + +@Scheduler({ pattern: '0 0 * * *' }) +export class DeleteRecentUsers { + @Inject() + public recentUserService: RecentUserService + + public async handler(ctx: Context) { + await this.recentUserService.deleteAll() + } +} +``` + +### Automatic constructor injection + +You can also use the automatic constructor injection if +your don't want to use the `@Inject()` annotation: + +```typescript +import { Scheduler, type Context } from '@athenna/cron' +import type { RecentUserService } from '#src/services/RecentUserService' + +@Scheduler({ pattern: '0 0 * * *' }) +export class DeleteRecentUsers { + public recentUserService: RecentUserService + + public constructor(recentUserService: RecentUserService) { + this.recentUserService = recentUserService + } + + public async handler(ctx: Context) { + await this.recentUserService.deleteAll() + } +} +``` diff --git a/docs/cron-application/tracing-executions.mdx b/docs/cron-application/tracing-executions.mdx new file mode 100644 index 00000000..ec847c79 --- /dev/null +++ b/docs/cron-application/tracing-executions.mdx @@ -0,0 +1,15 @@ +--- +title: Tracing Executions +sidebar_position: 2 +description: Understand how to trace executions of your CRON application. +--- + +import Path from '@site/src/components/path' + +# Tracing Executions + +Understand how to trace executions of your CRON application. + +## Introduction + +Coming Soon... diff --git a/docs/rest-api-application/error-handling.mdx b/docs/rest-api-application/error-handling.mdx index f256d853..de9d4543 100644 --- a/docs/rest-api-application/error-handling.mdx +++ b/docs/rest-api-application/error-handling.mdx @@ -168,7 +168,7 @@ Let's suppose you want to write a custom logic for handling your exceptions. You can do so by creating your own exception handler: -```typescript title="app/http/exceptions/Handler.ts" +```typescript title="src/http/exceptions/Handler.ts" import { type ErrorContext, HttpExceptionHandler } from '@athenna/http' export class Handler extends HttpExceptionHandler { diff --git a/docs/rest-api-application/middlewares.mdx b/docs/rest-api-application/middlewares.mdx index 470be0b3..d90159f8 100644 --- a/docs/rest-api-application/middlewares.mdx +++ b/docs/rest-api-application/middlewares.mdx @@ -192,7 +192,7 @@ node artisan make:interceptor InterceptMiddleware Just like `make:middleware`, Athenna will auto register the middleware if using `make:interceptor` command. The above command will produce the following content at -`app/http/interceptors/InterceptMiddleware.ts` file: +`src/http/interceptors/InterceptMiddleware.ts` file: ```typescript import { Interceptor } from '@athenna/http' @@ -248,7 +248,7 @@ node artisan make:terminator TerminateMiddleware Just like `make:middleware`, Athenna will auto register the middleware if using `make:terminator` command. The above command will produce the following content at -`app/http/terminators/TerminateMiddleware.ts` file: +`src/http/terminators/TerminateMiddleware.ts` file: ```typescript import { Terminator } from '@athenna/http' diff --git a/docs/the-basics/helpers.mdx b/docs/the-basics/helpers.mdx index 8bb059da..193301eb 100644 --- a/docs/the-basics/helpers.mdx +++ b/docs/the-basics/helpers.mdx @@ -3920,9 +3920,9 @@ full paths: ```typescript import { Path } from '@athenna/common' -console.log(Path.dirs.services) // app/services -console.log(Path.dirs.controllers) // app/http/controllers -console.log(Path.dirs.bootstrap) // bootstrap +console.log(Path.dirs.services) // src/services +console.log(Path.dirs.controllers) // src/http/controllers +console.log(Path.dirs.bin) // bin ``` #### `Path::mergeDirs()` @@ -3933,8 +3933,8 @@ Merge the `Path.dirs` object with a new object: import { Path } from '@athenna/common' Path.mergeDirs({ - services: 'app/internal/services', - controllers: 'app/internal/http/controllers', + services: 'src/internal/services', + controllers: 'src/internal/http/controllers', bootstrap: 'bin', }) ``` @@ -3960,16 +3960,16 @@ console.log(Path.ext()) // js Parse a path extension relying on the `IS_TS` environment variable: ```typescript -const tsPath = 'app/services/MyService.ts' -const jsPath = 'app/services/MyService.js' +const tsPath = 'src/services/MyService.ts' +const jsPath = 'src/services/MyService.js' process.env.IS_TS = 'true' -console.log(Path.parseExt(tsPath)) // app/services/MyService.ts -console.log(Path.parseExt(jsPath)) // app/services/MyService.ts +console.log(Path.parseExt(tsPath)) // src/services/MyService.ts +console.log(Path.parseExt(jsPath)) // src/services/MyService.ts process.env.IS_TS = 'false' -console.log(Path.parseExt(tsPath)) // app/services/MyService.js -console.log(Path.parseExt(jsPath)) // app/services/MyService.js +console.log(Path.parseExt(tsPath)) // src/services/MyService.js +console.log(Path.parseExt(jsPath)) // src/services/MyService.js ``` #### `Path::toURL()` diff --git a/src/components/path.module.css b/src/components/path.module.css index b0e1cf7c..f0913cbc 100644 --- a/src/components/path.module.css +++ b/src/components/path.module.css @@ -11,7 +11,6 @@ .hoverCard { position: absolute; - width: 200px; padding: 10px; background-color: #f6f6f7; border-radius: 5px; diff --git a/src/components/path.tsx b/src/components/path.tsx index 7f5fc321..1abbdd2e 100644 --- a/src/components/path.tsx +++ b/src/components/path.tsx @@ -80,6 +80,9 @@ export default function Path(props: { father: string; child: string }) { case 'facades': father = 'src/facades' break + case 'routes': + father = 'src/routes' + break } return (