Skip to content
This repository has been archived by the owner on Mar 20, 2023. It is now read-only.

feat: create code snippets for api (#342) #396

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const FILES_WITH_DEV_DEPENDENCIES = [
'**/*.stories.tsx',
'scripts/*.js',
'scripts/**',
'generators/*.ts',
'generators/**',
'plopfile.ts',
];

module.exports = {
Expand Down Expand Up @@ -158,7 +161,7 @@ module.exports = {
},
{
// Files that should contain a default export.
files: ['*.config.[tj]s', 'packages/website/src/pages/**/*.tsx', '*.stories.tsx'],
files: ['*.config.[tj]s', 'packages/website/src/pages/**/*.tsx', '*.stories.tsx', 'plopfile.ts'],
rules: { 'import/no-default-export': 0 },
},
{
Expand Down
7 changes: 7 additions & 0 deletions generators/_plugins/inquirerSelectDirectory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-disable @typescript-eslint/no-var-requires */

import { Prompt } from '../types';

const inquirerSelectDirectoryPrompt = require('inquirer-select-directory');

export const inquirerSelectDirectory = inquirerSelectDirectoryPrompt as Prompt;
18 changes: 18 additions & 0 deletions generators/automationModule/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const createAutomationModuleAction = {
type: 'add',
path: '{{directory}}/{{dashCase module}}/{{dashCase module}}.automation-module.ts',
templateFile: './generators/automationModule/module.hbs',
};

export const createAutomationTestModuleAction = {
type: 'add',
path: '{{directory}}/{{dashCase module}}/{{dashCase module}}.test-module.ts',
templateFile: './generators/automationModule/module.test.hbs',
};

export const createAutomationTestFileAction = {
type: 'add',
path: '{{directory}}/{{dashCase module}}/{{dashCase module}}.module.spec.ts',
templateFile: './generators/automationModule/module.spec.hbs',
};

20 changes: 20 additions & 0 deletions generators/automationModule/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createCommandAction } from '../command/actions';
import { commandNamePrompt } from '../command/prompts';
import { eventNamePrompt } from '../event/prompts';
import { createEventHandlerAction } from '../eventHandler/actions';
import {moduleDirectoryPrompt, moduleNamePrompt} from '../module/prompts';
import {createEventAction} from '../event/actions';
import { createAutomationModuleAction, createAutomationTestFileAction, createAutomationTestModuleAction } from './actions';

export const automationModuleGenerator = {
description: 'Create a new automation module',
prompts: [moduleDirectoryPrompt, moduleNamePrompt, commandNamePrompt, eventNamePrompt],
actions: [
{ ...createEventHandlerAction, path: '{{directory}}/{{dashCase module}}/{{dashCase event}}.event-handler.service.ts'},
{ ...createCommandAction, skipIfExists: true },
{ ...createEventAction, skipIfExists: true },
createAutomationModuleAction,
createAutomationTestModuleAction,
createAutomationTestFileAction,
],
};
11 changes: 11 additions & 0 deletions generators/automationModule/module.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';

import { SharedModule } from '@/write/shared/shared.module';

import { {{properCase event}}EventHandler } from './{{dashCase event}}.event-handler.service';

@Module({
imports: [SharedModule],
providers: [{{properCase event}}EventHandler],
})
export class {{properCase module}}AutomationModule {}
32 changes: 32 additions & 0 deletions generators/automationModule/module.spec.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { AsyncReturnType } from 'type-fest';

import { {{camelCase command}}Command } from '@/module/commands/{{dashCase command}}';
import { {{camelCase event}}Event } from '@/module/events/{{dashCase event}}.domain-event';
import { EventStreamName } from '@/write/shared/application/event-stream-name.value-object';

import { {{properCase module}}AutomationTestModule } from './{{dashCase module}}.test-module';

describe('', () => {
let moduleUnderTest: AsyncReturnType<
typeof {{properCase module}}AutomationTestModule
>;

beforeEach(async () => {
moduleUnderTest = await {{properCase module}}AutomationTestModule();
});

afterEach(async () => {
await moduleUnderTest.close();
});

it('', async () => {
const event = {{camelCase event}}Event({});

await moduleUnderTest.eventOccurred(
EventStreamName.from('stream category', `stream id`),
event,
);

await moduleUnderTest.expectCommandExecutedLastly({{camelCase command}}Command({}));
});
});
7 changes: 7 additions & 0 deletions generators/automationModule/module.test.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { initAutomationTestModule } from '@/shared/test-utils';

import { {{properCase module}}AutomationModule } from './{{dashCase module}}.automation-module';

export async function {{properCase module}}AutomationTestModule() {
return initAutomationTestModule([{{properCase module}}AutomationModule]);
}
Empty file.
5 changes: 5 additions & 0 deletions generators/command/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const createCommandAction = {
type: 'add',
path: 'packages/api/src/module/shared/commands/{{dashCase command}}.ts',
templateFile: './generators/command/command.hbs',
};
13 changes: 13 additions & 0 deletions generators/command/command.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { AbstractApplicationCommand } from '@/module/application-command-events';

export type {{properCase command}} = {
type: '{{properCase command}}';
data: {};
};

export const {{camelCase command}}Command = (data: {{properCase command}}['data']): {{properCase command}} => ({
type: '{{properCase command}}',
data,
});

export class {{properCase command}}ApplicationCommand extends AbstractApplicationCommand<{{properCase command}}> {}
8 changes: 8 additions & 0 deletions generators/command/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createCommandAction } from './actions';
import { commandNamePrompt } from './prompts';

export const commandGenerator = {
description: 'Create a new command',
prompts: [commandNamePrompt],
actions: [createCommandAction],
};
6 changes: 6 additions & 0 deletions generators/command/prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const commandNamePrompt = {
type: 'input',
name: 'command',
message: 'command name',
default: 'myCommand',
};
5 changes: 5 additions & 0 deletions generators/commandHandler/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const createCommandHandlerAction = {
type: 'add',
path: '{{directory}}/application/{{dashCase command}}.command-handler.ts',
templateFile: './generators/commandHandler/commandHandler.hbs',
};
30 changes: 30 additions & 0 deletions generators/commandHandler/commandHandler.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Inject } from '@nestjs/common';
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';

import { {{properCase command}}ApplicationCommand } from '@/commands/{{dashCase command}}';
import { {{properCase event}} } from '@/module/events/{{dashCase event}}.domain-event';
import { APPLICATION_SERVICE, ApplicationService } from '@/write/shared/application/application-service';
import { EventStreamName } from '@/write/shared/application/event-stream-name.value-object';

import { {{camelCase domainFunction}} } from '../domain/{{dashCase domainFunction}}';

@CommandHandler({{properCase command}}ApplicationCommand)
export class {{properCase command}}ApplicationCommandHandler implements ICommandHandler<{{properCase command}}ApplicationCommand> {
constructor(
@Inject(APPLICATION_SERVICE)
private readonly applicationService: ApplicationService,
) {}

async execute(command: {{properCase command}}ApplicationCommand): Promise<void> {
const eventStream = EventStreamName.from('{{properCase streamCategory}}', ``);

await this.applicationService.execute<{{properCase event}}>(
eventStream,
{
causationId: command.id,
correlationId: command.metadata.correlationId,
},
{{camelCase domainFunction}}(command),
);
}
}
18 changes: 18 additions & 0 deletions generators/commandHandler/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { commandNamePrompt } from '../command/prompts';
import { domainFunctionNamePrompt } from '../domain-function/prompts';
import { eventNamePrompt } from '../event/prompts';
import { apiDirectoryPrompt } from '../utils/directory.prompt';
import { createCommandHandlerAction } from './actions';
import { streamCategoryPrompt } from './prompts';

export const commandHandlerGenerator = {
description: 'Create a new command handler',
prompts: [
{ ...apiDirectoryPrompt, message: 'Path to module' },
commandNamePrompt,
{ ...eventNamePrompt, message: 'Past event name' },
domainFunctionNamePrompt,
streamCategoryPrompt,
],
actions: [createCommandHandlerAction],
};
6 changes: 6 additions & 0 deletions generators/commandHandler/prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const streamCategoryPrompt = {
type: 'input',
name: 'streamCategory',
message: 'stream category name',
default: 'stream',
};
11 changes: 11 additions & 0 deletions generators/domain-function/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const createDomainFunctionAction = {
type: 'add',
path: '{{directory}}/domain/{{dashCase domainFunction}}.ts',
templateFile: './generators/domain-function/domain-function.hbs',
};

export const createDomainFunctionTestAction = {
type: 'add',
path: '{{directory}}/domain/{{dashCase domainFunction}}.spec.ts',
templateFile: './generators/domain-function/domain-function.spec.hbs',
};
28 changes: 28 additions & 0 deletions generators/domain-function/domain-function.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { {{properCase event}}, {{camelCase event}} } from '@/events/{{dashCase event}}.domain-event';
import { {{properCase command}} } from '@/module/commands/{{dashCase command}}';

export const {{camelCase domainFunction}} =
(command: {{properCase event}}) =>
(pastEvents: {{properCase event}}[]): {{properCase event}}[] => {
const state = pastEvents.reduce<{ completed: boolean }>(
(acc, event) => {
switch (event.type) {
case '{{properCase event}}': {
return { completed: true };
}
default: {
return acc;
}
}
},
{ completed: false },
);

if (state.completed) {
throw new Error('X already completed');
}

const newEvent: {{properCase event}} = {{camelCase event}}({});

return [newEvent];
}
43 changes: 43 additions & 0 deletions generators/domain-function/domain-function.spec.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { {{properCase command}} } from '@/module/commands/{{dashCase command}}';
import { {{properCase event}} } from '@/module/events/{{dashCase event}}.domain-event';

import { {{camelCase domainFunction}} } from './{{dashCase domainFunction}}';

describe('{{camelCase domainFunction}}', () => {
const command: {{properCase command}} = {
type: '{{properCase command}}',
data: {},
};

it('should ', () => {
// Given
const pastEvents: {{properCase event}}[] = [];

// When
const events = {{camelCase domainFunction}}(pastEvents, command);

// Then
expect(events).toStrictEqual([
{
type: '{{properCase event}}',
data: {},
},
]);
});

it('should throw exception if ', () => {
// Given
const pastEvents: {{properCase event}}[] = [
{
type: {{properCase event}},
data: {},
},
];

// When
const events = {{camelCase domainFunction}}(pastEvents, command);

// Then
expect(events).toThrowError('');
});
});
15 changes: 15 additions & 0 deletions generators/domain-function/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { commandNamePrompt } from '../command/prompts';
import { eventNamePrompt } from '../event/prompts';
import { createDomainFunctionAction, createDomainFunctionTestAction } from './actions';
import { domainFunctionDirectoryPrompt, domainFunctionNamePrompt } from './prompts';

export const domainFunctionGenerator = {
description: 'Create a new domain function',
prompts: [
domainFunctionDirectoryPrompt,
domainFunctionNamePrompt,
commandNamePrompt,
{ ...eventNamePrompt, message: 'past event name' },
],
actions: [createDomainFunctionAction, createDomainFunctionTestAction],
};
8 changes: 8 additions & 0 deletions generators/domain-function/prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { apiDirectoryPrompt } from '../utils/directory.prompt';

export const domainFunctionDirectoryPrompt = {
...apiDirectoryPrompt,
message: 'choose directory in which you want to create domain function',
};

export const domainFunctionNamePrompt = { type: 'input', name: 'domainFunction', message: 'domain function name' };
5 changes: 5 additions & 0 deletions generators/event/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const createEventAction = {
type: 'add',
path: 'packages/api/src/module/shared/events/{{dashCase event}}.domain-event.ts',
templateFile: './generators/event/event.hbs',
};
9 changes: 9 additions & 0 deletions generators/event/event.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type {{properCase event}} = {
type: '{{properCase event}}';
data: {};
};

export const {{camelCase event}}Event = (data: {{properCase event}}['data']): {{properCase event}} => ({
type: '{{properCase event}}',
data,
});
8 changes: 8 additions & 0 deletions generators/event/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createEventAction } from './actions';
import { eventNamePrompt } from './prompts';

export const eventGenerator = {
description: 'Create a new event',
prompts: [eventNamePrompt],
actions: [createEventAction],
};
6 changes: 6 additions & 0 deletions generators/event/prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const eventNamePrompt = {
type: 'input',
name: 'event',
message: 'event name',
default: 'myEvent',
};
7 changes: 7 additions & 0 deletions generators/eventHandler/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { eventHandlerDirectory } from './prompts';

export const createEventHandlerAction = {
type: 'add',
path: `{{directory}}/{{moduleNameFromPath ${eventHandlerDirectory.name}}}.ts`,
templateFile: './generators/eventHandler/eventHandler.hbs',
};
Loading