-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Complete refactor of job configuration as NestJS modules
- Developing new actions is documented in README.md for developers - Move everything related to job config to src/config/job-config - Major refactor of parsing from jobConfig.yaml to JobConfig objects - Create module, interface, and factory for each JobAction - Add handlebar-utils to help consistently applying templates to jobs and Dtos. - In test contexts the handlebars helpers may not be registered, so avoid using custom helpers or register them in the test - Add options to LogJobAction. This is also good for templating tests - Change MailService.sendMail to match nest's MailerService.sendMail - Accept empty jobConfigurationFile as no jobs - CaslModule now depends indirectly on the MailerModule. Thus all controllers need to either mock CaslAbilityFactory or add a mock MailerModule before importing CaslModule. - Most tests disable jobConfig except for those that directly test it. These use test/config/jobconfig.yaml
- Loading branch information
Showing
65 changed files
with
1,157 additions
and
790 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
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
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 |
---|---|---|
@@ -1,7 +1,24 @@ | ||
import { MailerModule, MailerService } from "@nestjs-modules/mailer"; | ||
import { CaslAbilityFactory } from "./casl-ability.factory"; | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import { CaslModule } from "./casl.module"; | ||
|
||
class MailerServiceMock {} | ||
|
||
describe("CaslAbilityFactory", () => { | ||
let casl: CaslAbilityFactory; | ||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
imports: [MailerModule.forRoot(), CaslModule], | ||
}) | ||
.overrideProvider(MailerService) | ||
.useClass(MailerServiceMock) | ||
.compile(); | ||
|
||
casl = module.get<CaslAbilityFactory>(CaslAbilityFactory); | ||
}); | ||
|
||
it("should be defined", () => { | ||
expect(new CaslAbilityFactory()).toBeDefined(); | ||
expect(casl).toBeDefined(); | ||
}); | ||
}); |
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
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
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
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
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
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 |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Job Configuration | ||
|
||
## Background | ||
|
||
Jobs are SciCat's main way of interacting with external systems. Thus the actions which | ||
should be performed when a job is created or updated (with POST or PATCH requests; see | ||
[job.controller.ts](../../jobs/job.controller.ts)) tend to be facility specific. To | ||
facilitate this, the allowed jobs are configured in a YAML or JSON file. An example file | ||
is available at [jobConfig.example.yaml](../../../jobConfig.example.yaml). The location | ||
of the job config file is configurable in with the `JOB_CONFIGURATION_FILE` environment | ||
variable. | ||
|
||
The file is parsed when the application starts and converted to a `JobConfigService` | ||
instance. Job types are arbitrary and facility-specific, but `archive` and `retrieve` | ||
are traditional for interacting with the data storage system. Authorization can be | ||
configured for each job type for *create* and *update* requests, and then a list of | ||
actions are provided which should run after the request. | ||
|
||
## Implementing an Action | ||
|
||
Implementing an Action requires four (short) files: | ||
1. `action.ts` contains a class implementing `JobAction`. It's constructor can take any arguments, but the existing actions take an `options` argument mirroring the expected yaml config. It does not need to be `@Injectable()`, since it is constructed by the factory. | ||
2. `action.interface.ts` can contain additional types, e.g. the definition of the expected `options` and a type guard for casting to the options. | ||
3. `action.factory.ts` implements `JobActionFactory`. The factory is provided by NestJS as a singleton, so it must be `@Injectable()`. This means that dependencies can be injected into the factory. It has a `create(options)` method, which constructs the action itself by combining the options from the yaml file with any dependencies injected by NestJS. | ||
4. `action.module.ts` is an NestJS module that provides the factory. | ||
|
||
The lists of known factories are provided to Nest with the `CREATE_JOB_ACTION_FACTORIES` and `UPDATE_JOB_ACTION_FACTORIES` symbols. The top-level AppModule imports built-in actions from `DefaultJobActionFactories`. Core actions should be added to this list. Plugins can use the NestJS dependency injection system to extend the lists if needed. | ||
|
||
## Accessing the jobConfig | ||
|
||
Parsing `jobConfig.yaml` is handled by the JobConfigService. NestJS injects the lists of factories (and the file path) during construction. When parsing reaches a new action, the correct factory is used to create an instance of the JobAction with the current list of options. | ||
|
||
Code which needs the configuration for a particular job type (eg jobs.controller.ts) injects the `JobConfigService` and can then call `jobConfigService.get(jobType)`. |
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 |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { Module } from "@nestjs/common"; | ||
import { LogJobActionFactory } from "./logaction/logaction.factory"; | ||
import { LogJobActionModule } from "./logaction/logaction.module"; | ||
import { EmailJobActionFactory } from "./emailaction/emailaction.factory"; | ||
import { EmailJobActionModule } from "./emailaction/emailaction.module"; | ||
import { actionType as logActionType } from "./logaction/logaction.interface"; | ||
import { actionType as emailActionType } from "./emailaction/emailaction.interface"; | ||
import { ValidateJobActionModule } from "./validateaction/validateaction.module"; | ||
import { actionType as validateActionType } from "./validateaction/validateaction.interface"; | ||
import { ValidateJobActionFactory } from "./validateaction/validateaction.factory"; | ||
import { URLJobActionModule } from "./urlaction/urlaction.module"; | ||
import { URLJobActionFactory } from "./urlaction/urlaction.factory"; | ||
import { actionType as urlActionType } from "./urlaction/urlaction.interface"; | ||
import { RabbitMQJobActionModule } from "./rabbitmqaction/rabbitmqaction.module"; | ||
import { actionType as rabbitmqActionType } from "./rabbitmqaction/rabbitmqaction.interface"; | ||
import { RabbitMQJobActionFactory } from "./rabbitmqaction/rabbitmqaction.factory"; | ||
import { | ||
CREATE_JOB_ACTION_FACTORIES, | ||
UPDATE_JOB_ACTION_FACTORIES, | ||
} from "../jobconfig.interface"; | ||
|
||
/** | ||
* Provide a list of built-in job action factories. | ||
* | ||
* CREATE_JOB_ACTION_FACTORIES and UPDATE_JOB_ACTION_FACTORIES be extended (eg by a | ||
* plugin) with additional factories. | ||
*/ | ||
@Module({ | ||
imports: [ | ||
EmailJobActionModule, | ||
LogJobActionModule, | ||
ValidateJobActionModule, | ||
URLJobActionModule, | ||
RabbitMQJobActionModule, | ||
], | ||
providers: [ | ||
{ | ||
provide: CREATE_JOB_ACTION_FACTORIES, | ||
useFactory: ( | ||
logJobActionFactory, | ||
emailJobActionFactory, | ||
validateJobActionFactory, | ||
urlJobActionFactory, | ||
rabbitMQJobActionFactory, | ||
) => { | ||
return { | ||
[logActionType]: logJobActionFactory, | ||
[emailActionType]: emailJobActionFactory, | ||
[validateActionType]: validateJobActionFactory, | ||
[urlActionType]: urlJobActionFactory, | ||
[rabbitmqActionType]: rabbitMQJobActionFactory, | ||
}; | ||
}, | ||
inject: [ | ||
LogJobActionFactory, | ||
EmailJobActionFactory, | ||
ValidateJobActionFactory, | ||
URLJobActionFactory, | ||
RabbitMQJobActionFactory, | ||
], | ||
}, | ||
{ | ||
provide: UPDATE_JOB_ACTION_FACTORIES, | ||
useFactory: ( | ||
logJobActionFactory, | ||
emailJobActionFactory, | ||
validateJobActionFactory, | ||
urlJobActionFactory, | ||
rabbitMQJobActionFactory, | ||
) => { | ||
return { | ||
[logActionType]: logJobActionFactory, | ||
[emailActionType]: emailJobActionFactory, | ||
[validateActionType]: validateJobActionFactory, | ||
[urlActionType]: urlJobActionFactory, | ||
[rabbitmqActionType]: rabbitMQJobActionFactory, | ||
}; | ||
}, | ||
inject: [ | ||
LogJobActionFactory, | ||
EmailJobActionFactory, | ||
ValidateJobActionFactory, | ||
URLJobActionFactory, | ||
RabbitMQJobActionFactory, | ||
], | ||
}, | ||
], | ||
exports: [CREATE_JOB_ACTION_FACTORIES, UPDATE_JOB_ACTION_FACTORIES], | ||
}) | ||
export class DefaultJobActionFactories {} |
Oops, something went wrong.