Skip to content

Commit

Permalink
feat(agenda): support DIContext and allow manual job creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Dec 22, 2023
1 parent 692bc1d commit 76fbc5e
Show file tree
Hide file tree
Showing 10 changed files with 540 additions and 188 deletions.
67 changes: 57 additions & 10 deletions docs/tutorials/agenda.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,22 @@ method name is used as job name.
Use the `@Define` decorator on methods that you would like to schedule
programmatically via the AgendaService and Agenda instance access.

```typescript
```ts
import {Agenda, Every, Define} from "@tsed/agenda";
import {Job} from "agenda";

@Agenda({ namespace: "email" })
@Agenda({namespace: "email"})
export class EmailJobService {
@Every("60 minutes", {
name: "maintenanceJob",
/* ... and any option you would normally pass to agenda.every/define */ }
name: "maintenanceJob"
/* ... and any option you would normally pass to agenda.every/define */
})
async sendAdminStatistics(job: Job) {
// implement something here
}

@Define({
name: "sendWelcomeEmail",
name: "sendWelcomeEmail"
/* ... and any option you would normally pass to agenda.define(...) */
})
async sendWelcomeEmail(job: Job) {
Expand All @@ -90,19 +90,66 @@ export class EmailJobService {
}
```

## Define manually a job processor

Since Ts.ED 7.53.0, AgendaModule expose methods to define a job processor manually. it can be useful to define a job processor when
you need to fetch data before and build job name / options dynamically.

```typescript
import {Agenda, AgendaModule, Define} from "@tsed/agenda";

@Agenda({namespace: "email"})
export class EmailJobService {
@Inject()
agenda: AgendaModule;

@Inject()
httpClient: HttpClient;

cache: Map<string, Job[]> = new Map();

@Define({
name: "sendWelcomeEmail",
concurrency: 3
/* ... and any option you would normally pass to agenda.define(...) */
})
async sendWelcomeEmail(job: Job) {
// implement something here
console.log(job.attrs.data.locale);
}

async $beforeAgendaStart() {
const locales = await this.httpClient.get("/locales");

this.cache.set(
"sendWelcomeEmail",
locales.map((locale) => {
return this.agenda.create("sendWelcomeEmail", {locale});
})
);
}

async $afterAgendaStart() {
const jobs = this.cache.get("sendWelcomeEmail");

await Promise.all(jobs.map((job) => job.repeatEvery("1 week").save()));
}
}
```

## Inject Agenda

Inject the AgendaService instance to interact with it directly, e.g. to schedule
a job manually.

```typescript
import {Service, AfterRoutesInit} from "@tsed/common";
import {AgendaService} from "@tsed/agenda";
import {AgendaModule} from "@tsed/agenda";

@Service()
export class UsersService implements AfterRoutesInit {
export class UsersService {
@Inject()
private agenda: AgendaService;
private agenda: AgendaModule;

async create(user: User): Promise<User> {
// do something
Expand Down Expand Up @@ -131,7 +178,7 @@ Install the additional dependency.
npm install --save agendash
```

Afterwards create the module `agendash.module.ts` in src/modules so that the dashboard can be exposed using middleware.
Afterward create the module `agendash.module.ts` in src/modules so that the dashboard can be exposed using middleware.

```typescript
import {AfterRoutesInit, Inject, PlatformApplication} from "@tsed/common";
Expand Down Expand Up @@ -165,7 +212,7 @@ export class AgendashModule implements AfterRoutesInit {

## Maintainers

<GithubContributors :users="['ochrstn']"/>
<GithubContributors :users="['ochrstn', 'romakita']"/>

<div class="flex items-center justify-center p-5">
<Button href="/contributing.html" class="rounded-medium">
Expand Down
3 changes: 2 additions & 1 deletion packages/third-parties/agenda/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// https://jestjs.io/docs/en/configuration.html

module.exports = {
roots: ["<rootDir>/src", "<rootDir>/test"],
...require("@tsed/jest-config"),
coverageThreshold: {
global: {
statements: 100,
branches: 100,
branches: 91.48,
functions: 100,
lines: 100
}
Expand Down
98 changes: 94 additions & 4 deletions packages/third-parties/agenda/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ method name is used as job name.
Use the `@Define` decorator on methods that you would like to schedule
programmatically via the AgendaService and Agenda instance access.

```typescript
```ts
import {Agenda, Every, Define} from "@tsed/agenda";
import {Job} from "agenda";

Expand Down Expand Up @@ -111,19 +111,66 @@ export class EmailJobService {
}
```

## Define manually a job processor

Since Ts.ED 7.53.0, AgendaModule expose methods to define a job processor manually. it can be useful to define a job processor when
you need to fetch data before and build job name / options dynamically.

```typescript
import {Agenda, AgendaModule, Define} from "@tsed/agenda";

@Agenda({namespace: "email"})
export class EmailJobService {
@Inject()
agenda: AgendaModule;

@Inject()
httpClient: HttpClient;

cache: Map<string, Job[]> = new Map();

@Define({
name: "sendWelcomeEmail",
concurrency: 3
/* ... and any option you would normally pass to agenda.define(...) */
})
async sendWelcomeEmail(job: Job) {
// implement something here
console.log(job.attrs.data.locale);
}

async $beforeAgendaStart() {
const locales = await this.httpClient.get("/locales");

this.cache.set(
"sendWelcomeEmail",
locales.map((locale) => {
return this.agenda.create("sendWelcomeEmail", {locale});
})
);
}

async $afterAgendaStart() {
const jobs = this.cache.get("sendWelcomeEmail");

await Promise.all(jobs.map((job) => job.repeatEvery("1 week").save()));
}
}
```

## Inject Agenda

Inject the AgendaService instance to interact with it directly, e.g. to schedule
a job manually.

```typescript
import {Service, AfterRoutesInit} from "@tsed/common";
import {AgendaService} from "@tsed/agenda";
import {AgendaModule} from "@tsed/agenda";

@Service()
export class UsersService implements AfterRoutesInit {
export class UsersService {
@Inject()
private agenda: AgendaService;
private agenda: AgendaModule;

async create(user: User): Promise<User> {
// do something
Expand All @@ -137,6 +184,49 @@ export class UsersService implements AfterRoutesInit {
}
```

## Using Agendash

[Agendash](https://github.com/agenda/agendash) provides a job overview dashboard that makes it easy to manage, create and
schedule your jobs.

::: tip Note
This is an optional feature and is not required to use agenda.
:::

Install the additional dependency.

```shell
npm install --save agendash
```

Afterward create the module `agendash.module.ts` in src/modules so that the dashboard can be exposed using middleware.

```typescript
import {AfterRoutesInit, Inject, PlatformApplication} from "@tsed/common";
import {Configuration, Module} from "@tsed/di";
import {Agenda} from "agenda";

const Agendash = require("agendash");

@Module()
export class AgendashModule implements AfterRoutesInit {
@Configuration()
config: Configuration;

@Inject()
agenda: Agenda;

@Inject()
app: PlatformApplication;

$afterRoutesInit() {
if (this.config.agenda?.enabled) {
this.app.use("/agendash", Agendash(this.agenda));
}
}
}
```

## Contributors

Please read [contributing guidelines here](https://tsed.io/contributing.html)
Expand Down
Loading

0 comments on commit 76fbc5e

Please sign in to comment.