-
-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8c9745b
commit c841070
Showing
1 changed file
with
64 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,19 +2,13 @@ | |
title: Shell Scripts | ||
--- | ||
|
||
Shell scripts are an easy way of executing a piece of code from the command line. They can be used in a variety of scenarios and can be a simple code snippet or be more complex and call application services. | ||
|
||
Sometimes we have to execute some tasks from the command line. These tasks can serve different purposes such as populating a database (user creation, etc) for instance. They often need to access some of the app classes and functions. This is when shell scripts come into play. | ||
## Structure | ||
|
||
## Create Scripts | ||
|
||
A shell script is just a TypeScript file located in the `src/scripts`. It must export a `main` function that is then called when running the script. | ||
|
||
Let's create a new one with the command line: `npx foal g script display-users`. A new file with a default template should appear in you `src/scripts` directory. | ||
|
||
## Write Scripts | ||
|
||
Remove the content of `src/scripts/display-users.ts` and replace it with the code below. | ||
A shell script file is divided into two parts: a `main` function, which contains the code to be executed, and a `schema`, which parses and validates the arguments given on the command line and passes them on to the `main` function. The file must be located in the `src/scripts` directory. | ||
|
||
*Example: src/scripts/create-user.ts* | ||
```typescript | ||
// 3p | ||
import { Logger, ServiceManager } from '@foal/core'; | ||
|
@@ -23,40 +17,84 @@ import { Logger, ServiceManager } from '@foal/core'; | |
import { dataSource } from '../db'; | ||
import { User } from '../app/entities'; | ||
|
||
export async function main(args: any, services: ServiceManager, logger: Logger) { | ||
export const schema = { | ||
type: 'object', | ||
properties: { | ||
email: { type: 'string' }, | ||
}, | ||
required: ['email'], | ||
additionalProperties: false | ||
} | ||
|
||
export async function main(args: { email: string }) { | ||
await dataSource.initialize(); | ||
|
||
try { | ||
const users = await User.find(); | ||
const user = new User(); | ||
user.email = args.email; | ||
|
||
logger.info(`Users: ${JSON.stringify(users, null, 2)}`); | ||
await user.save(); | ||
} finally { | ||
dataSource.destroy(); | ||
} | ||
} | ||
|
||
``` | ||
|
||
As you can see, we can easily establish a connection to the database in the script as well as import some of the app components (the `User` in this case). | ||
|
||
Encapsulating your code in a `main` function without calling it directly in the file has several benefits: | ||
- You can import and test your `main` function in a separate file. | ||
- Using a function lets you easily use async/await keywords when dealing with asynchronous code. | ||
## Generating, Building and Running Shell Scripts | ||
|
||
## Build and Run Scripts | ||
To generate a new script, you can use the CLI `generate` command: | ||
|
||
To run a script you first need to build it. | ||
```bash | ||
npx foal generate script create-user | ||
# or | ||
npx foal g script create-user | ||
``` | ||
|
||
```sh | ||
If you need to build the script once, run this command: | ||
```bash | ||
npm run build | ||
``` | ||
|
||
Then you can execute it with this command: | ||
If you need to build and watch it in dev mode, use this command: | ||
```bash | ||
npm run dev | ||
``` | ||
|
||
```shell | ||
npx foal run my-script | ||
Then you can run the script as follows: | ||
```bash | ||
npx foal run create-user [email protected] | ||
``` | ||
|
||
> You can also provide additionnal arguments to your script (for example: `npx foal run my-script foo=1 bar='[ 3, 4 ]'`). The default template in the generated scripts shows you how to handle such behavior. | ||
## Accessing Services | ||
|
||
If you wish to access a service, you can use the `ServiceManager` instance passed as second argument to the `main` function. | ||
|
||
Example | ||
|
||
```typescript | ||
import { ServiceManager } from '@foal/core'; | ||
|
||
import { MyService } from '../app/services'; | ||
|
||
> If you want your script to recompile each time you save the file, you can run `npm run dev` in a separate terminal. | ||
export function main(args: any, services: ServiceManager) { | ||
const myService = services.get(MyService); | ||
|
||
// Do something with myService. | ||
} | ||
``` | ||
|
||
## Logging | ||
|
||
When a script is executed, a script ID is created and added to the log context. Like the request ID in an HTTP request, the script ID is added as a parameter to every log printed during script execution, including any errors. In this way, it is possible to aggregate all logs from a single script execution in a logging program. | ||
|
||
If you wish to access the logger in the script, it is passed as the third argument to the main function. | ||
|
||
```typescript | ||
import { Logger, ServiceManager } from '@foal/core'; | ||
|
||
|
||
export function main(args: any, services: ServiceManager, logger: Logger) { | ||
logger.info('Hello world!'); | ||
} | ||
``` |