Skip to content

Commit

Permalink
feat(schematics): added controller schematics with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Stradivario committed May 14, 2024
1 parent 1b3dbd3 commit 9f3c0f4
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 3 deletions.
7 changes: 6 additions & 1 deletion packages/schematics/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
"factory": "./dist/lib/service/service.factory#main",
"description": "Create a @rhtml service.",
"schema": "./dist/lib/service/schema.json"
},
"controller": {
"factory": "./dist/lib/controller/controller.factory#main",
"description": "Create a @rhtml controller.",
"schema": "./dist/lib/controller/schema.json"
}
}
}
}
92 changes: 92 additions & 0 deletions packages/schematics/src/lib/controller/controller.factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { join, Path, strings } from '@angular-devkit/core';
import {
apply,
branchAndMerge,
chain,
filter,
mergeWith,
move,
noop,
Rule,
SchematicContext,
template,
Tree,
url,
} from '@angular-devkit/schematics';

import {
DeclarationOptions,
ModuleDeclarator,
} from '../../utils/module.declarator';
import { ModuleFinder } from '../../utils/module.finder';
import { Location, NameParser } from '../../utils/name.parser';
import { mergeSourceRoot } from '../../utils/source-root.helpers';
import { DEFAULT_LANGUAGE } from '../defaults';
import { ControllerOptions } from './controller.schema';

const ELEMENT_METADATA = 'bootstrap';
const ELEMENT_TYPE = 'controller';

export function main(options: ControllerOptions): Rule {
options = transform(options);
return (tree: Tree, context: SchematicContext) => {
return branchAndMerge(
chain([
mergeSourceRoot(options),
mergeWith(generate(options)),
addDeclarationToModule(options),
])
)(tree, context);
};
}

function transform(source: ControllerOptions): ControllerOptions {
const target: ControllerOptions = Object.assign({}, source);
target.metadata = ELEMENT_METADATA;
target.type = ELEMENT_TYPE;

const location: Location = new NameParser().parse(target);
target.name = strings.dasherize(location.name);
target.path = strings.dasherize(location.path);
target.language =
target.language !== undefined ? target.language : DEFAULT_LANGUAGE;

target.path = target.flat
? target.path
: join(target.path as Path, target.name);
return target;
}

function generate(options: ControllerOptions) {
return (context: SchematicContext) =>
apply(url(join('./files' as Path, options.language)), [
options.spec ? noop() : filter((path) => !path.endsWith('.spec.ts')),
template({
...strings,
...options,
}),
move(options.path),
])(context);
}

function addDeclarationToModule(options: ControllerOptions): Rule {
return (tree: Tree) => {
if (options.skipImport !== undefined && options.skipImport) {
return tree;
}
options.module = new ModuleFinder(tree).find({
name: options.name,
path: options.path as Path,
});
if (!options.module) {
return tree;
}
const content = tree.read(options.module).toString();
const declarator: ModuleDeclarator = new ModuleDeclarator();
tree.overwrite(
options.module,
declarator.declare(content, options as DeclarationOptions)
);
return tree;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Bootstrap, get, Module } from '@rhtml/di'
import { FastifyModule } from '@rhtml/fastify'
import fastify from 'fastify'

import { <%= classify(name) %>Controller } from './<%= name %>.controller';

describe('<%= classify(name) %> Controller', () => {
let <%= decamelize(name) %>Controller: <%= classify(name) %>Controller

beforeAll(async () => {
@Module({
imports: [
FastifyModule.forRoot(fastify, {
logger: true,
}),
],
bootstrap: [<%= classify(name) %>Controller],
})
class AppModule {}

await Bootstrap(AppModule)
<%= decamelize(name) %>Controller = get(<%= classify(name) %>Controller)

});

it('e2e: get => (<%= classify(name) %>) : Should sucessfully return expected result', async () => {
const response = {
hello: 'world',
}
const result = await <%= decamelize(name) %>Controller.get<%= classify(name) %>()
expect(result).toEqual(response)
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Controller, Route } from '@rhtml/fastify'

@Controller({
route: '/<%= decamelize(name) %>',
})
export class <%= classify(name) %>Controller {

@Route({
method: 'GET',
})
get<%= classify(name) %>() {
return {
hello: 'world'
}
}
}
49 changes: 49 additions & 0 deletions packages/schematics/src/lib/controller/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/schema",
"id": "SchematicsRHTMLController",
"title": "RHTML Controller Options Schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the controller.",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the controller?"
},
"path": {
"type": "string",
"format": "path",
"description": "The path to create the controller."
},
"language": {
"type": "string",
"description": "RHTML controller language (ts/js)."
},
"sourceRoot": {
"type": "string",
"description": "RHTML controller source root directory."
},
"skipImport": {
"description": "Flag to skip the module import.",
"default": false
},
"module": {
"type": "string",
"description": "Allows specification of the declaring module."
},
"flat": {
"default": false,
"description": "Flag to indicate if a directory is created."
},
"spec": {
"default": true,
"description": "Specifies if a spec file is generated."
}
},
"required": [
"name"
]
}
3 changes: 1 addition & 2 deletions packages/schematics/src/lib/service/service.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
Tree,
url,
} from '@angular-devkit/schematics';
import { isNullOrUndefined } from 'util';

import {
DeclarationOptions,
Expand Down Expand Up @@ -52,7 +51,7 @@ function transform(source: ServiceOptions): ServiceOptions {
target.metadata = 'providers';
target.type = 'service';

if (isNullOrUndefined(target.name)) {
if (!target.name) {
throw new SchematicsException('Option (name) is required.');
}
const location: Location = new NameParser().parse(target);
Expand Down

0 comments on commit 9f3c0f4

Please sign in to comment.