diff --git a/libs/plugin/core/generators.json b/libs/plugin/core/generators.json index 298d1b0..2f7b049 100644 --- a/libs/plugin/core/generators.json +++ b/libs/plugin/core/generators.json @@ -20,6 +20,12 @@ "schema": "./src/generators/presentation/schema.json", "description": "Add a presentation layer", "hidden": true + }, + "repository": { + "factory": "./src/generators/repository/generator.compat#generatorSchematic", + "schema": "./src/generators/repository/schema.json", + "description": "Add a abstract and implementation repository", + "hidden": true } }, "generators": { @@ -37,6 +43,11 @@ "factory": "./src/generators/presentation/generator", "schema": "./src/generators/presentation/schema.json", "description": "Presentation" + }, + "repository": { + "factory": "./src/generators/repository/generator", + "schema": "./src/generators/repository/schema.json", + "description": "Add a abstract and implementation repository" } } } \ No newline at end of file diff --git a/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/__model__.inmemory.repository.spec.ts__template__ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/__model__.inmemory.repository.spec.ts__template__ new file mode 100644 index 0000000..63e1040 --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/__model__.inmemory.repository.spec.ts__template__ @@ -0,0 +1,43 @@ +import { <%= entity.className %>InMemoryRepository } from './<%= entity.propertyName %>.inmemory.repository'; +import { <%= entity.className %>Repository } from '@<%= npmScope %>/<%= projectDomain %>'; +import { forkJoin } from 'rxjs'; + +describe('<%= entity.className %> In Memory Repository', () => { + let repo: <%= entity.className %>Repository; + + beforeEach(() => { + repo = new <%= entity.className %>InMemoryRepository([ + { + id: '1', + title: 'one' + }, + { + id: '2', + title: 'two' + }, + { + id: '3', + title: 'three' + }, + ]); + }); + + it('get all', (done) => { + repo.getAll<%= entity.className %>s().subscribe((<%= entity.propertyName %>s) => { + expect(<%= entity.propertyName %>s.length).toEqual(3); + done(); + }); + }); + + it('add <%= entity.propertyName %>', (done) => { + const name = 'bar'; + const add$ = repo.add<%= entity.className %>({name}); + const all$ = repo.getAll<%= entity.className %>s(); + + forkJoin([add$, all$]).subscribe(([<%= entity.propertyName %>, <%= entity.propertyName %>s]) => { + expect(<%= entity.propertyName %>.name).toEqual('bar'); + expect(<%= entity.propertyName %>s.length).toEqual(4); + done(); + }); + }); +}); diff --git a/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/__model__.inmemory.repository.ts__template__ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/__model__.inmemory.repository.ts__template__ new file mode 100644 index 0000000..9db9e43 --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/__model__.inmemory.repository.ts__template__ @@ -0,0 +1,44 @@ +import { <%= entity.className %>Repository, <%= entity.className %>Entity } from '@<%= npmScope %>/<%= projectDomain %>'; +import { <%= entity.className %>MockMapper } from './mapper/<%= entity.fileName %>-mock.mapper'; +import { <%= entity.className %>MockDto } from './dto/<%= entity.fileName %>-mock.dto'; +import { Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; + +export class <%= entity.className %>InMemoryRepository implements <%= entity.className %>Repository { + constructor(private data: <%= entity.className %>MockDto[] = []) {} + + private mapper = new <%= entity.className %>MockMapper(); + + public getAll<%= entity.className %>s(): Observable<<%= entity.className %>Entity[]> { + return of(this.data).pipe(map((mocks) => mocks.map(this.mapper.mapTo))); + } + + public add<%= entity.className %>({ name }: Pick<<%= entity.className %>Entity, 'name'>): Observable<<%= entity.className %>Entity> { + const id = 'item-' + new Date().getTime(); + const <%= entity.propertyName %>: <%= entity.className %>Entity = <%= entity.className %>Entity.create({ id, name }); + + this.data.push(this.mapper.mapFrom(<%= entity.propertyName %>)); + return of(<%= entity.propertyName %>); + } + + public get<%= entity.className %>ById(id: string): Observable<<%= entity.className %>Entity> { + return of<<%= entity.className %>MockDto>( + this.data.find((<%= entity.propertyName %>) => <%= entity.propertyName %>.id === id) as <%= entity.className %>MockDto + ).pipe(map(this.mapper.mapTo)); + } + + public update<%= entity.className %>(id: string, <%= entity.propertyName %>: <%= entity.className %>Entity): Observable<<%= entity.className %>Entity> { + const record = this.data.findIndex((<%= entity.propertyName %>) => <%= entity.propertyName %>.id === id) + this.data[record] = this.mapper.mapFrom(<%= entity.propertyName %>); + return of(this.mapper.mapTo(this.data[record])); + } + + public remove<%= entity.className %>(id: string): Observable<<%= entity.className %>Entity> { + const idx = this.data.findIndex((t) => t.id === id); + const <%= entity.propertyName %> = this.data.find((t) => t.id === id); + + this.data.splice(idx, 1); + + return of<<%= entity.className %>MockDto>(<%= entity.propertyName %> as <%= entity.className %>MockDto).pipe(map(this.mapper.mapTo)); + } +} diff --git a/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/dto/__model__-mock.dto.ts__template__ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/dto/__model__-mock.dto.ts__template__ new file mode 100644 index 0000000..df7a7df --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/dto/__model__-mock.dto.ts__template__ @@ -0,0 +1,9 @@ +export class <%= entity.className %>MockDto { + id: string; + title: string; + + constructor(params: <%= entity.className %>MockDto) { + this.id = params.id; + this.title = params.title; + } +} diff --git a/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/mapper/__model__-mock.mapper.ts__template__ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/mapper/__model__-mock.mapper.ts__template__ new file mode 100644 index 0000000..1d102df --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/data/files/lib/inmemory/mapper/__model__-mock.mapper.ts__template__ @@ -0,0 +1,21 @@ +import { <%= entity.className %>MockDto } from '../dto/<%= entity.propertyName %>-mock.dto'; +import { <%= entity.className %>Entity } from '@<%= npmScope %>/<%= projectDomain %>'; +import { Mapper } from '@nx-clean/core'; + +export class <%= entity.className %>MockMapper implements Mapper<<%= entity.className %>Entity, <%= entity.className %>MockDto> { + mapFrom(input: <%= entity.className %>Entity): <%= entity.className %>MockDto { + return { + id: input?.id, + title: input?.name + }; + } + + mapTo(input: <%= entity.className %>MockDto): <%= entity.className %>Entity { + const <%= entity.propertyName %> = <%= entity.className %>Entity.create({ + id: input?.id, + name: input?.title + }); + + return <%= entity.propertyName %>; + } +} diff --git a/libs/plugin/core/src/generators/repository/files/domain/files/lib/entity/__model__.entity.spec.ts__template__ b/libs/plugin/core/src/generators/repository/files/domain/files/lib/entity/__model__.entity.spec.ts__template__ new file mode 100644 index 0000000..6cc9677 --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/domain/files/lib/entity/__model__.entity.spec.ts__template__ @@ -0,0 +1,13 @@ +import { <%= entity.className %>Entity } from './<%= entity.fileName %>.entity'; + +describe('<%= entity.className %> Entity', () => { + it('should be properly initialized', () => { + const model = <%= entity.className %>Entity.create({ + id: '1', + name: 'foo', + }); + + expect(model.id).toEqual('1'); + expect(model.name).toEqual('foo'); + }); +}); diff --git a/libs/plugin/core/src/generators/repository/files/domain/files/lib/entity/__model__.entity.ts__template__ b/libs/plugin/core/src/generators/repository/files/domain/files/lib/entity/__model__.entity.ts__template__ new file mode 100644 index 0000000..e3e2597 --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/domain/files/lib/entity/__model__.entity.ts__template__ @@ -0,0 +1,13 @@ +export class <%= entity.className %>Entity { + id: string; + name: string; + + private constructor(params: <%= entity.className %>Entity) { + this.id = params?.id; + this.name = params?.name; + } + + static create(params: <%= entity.className %>Entity) { + return new this(params); + } +} diff --git a/libs/plugin/core/src/generators/repository/files/domain/files/lib/repository/__model__.repository.ts__template__ b/libs/plugin/core/src/generators/repository/files/domain/files/lib/repository/__model__.repository.ts__template__ new file mode 100644 index 0000000..21930aa --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/domain/files/lib/repository/__model__.repository.ts__template__ @@ -0,0 +1,10 @@ +import { <%= entity.className %>Entity } from '../entity/<%= entity.fileName %>.entity'; +import { Observable } from 'rxjs'; + +export abstract class <%= entity.className %>Repository { + public abstract getAll<%= entity.className %>s(): Observable<<%= entity.className %>Entity[]>; + public abstract add<%= entity.className %>(<%= entity.propertyName %>: Pick<<%= entity.className %>Entity, 'name'>): Observable<<%= entity.className %>Entity>; + public abstract update<%= entity.className %>(id: string, <%= entity.propertyName %>: <%= entity.className %>Entity): Observable<<%= entity.className %>Entity>; + public abstract remove<%= entity.className %>(id: string): Observable<<%= entity.className %>Entity>; + public abstract get<%= entity.className %>ById(id: string): Observable<<%= entity.className %>Entity>; +} diff --git a/libs/plugin/core/src/generators/repository/files/src/index.ts__template__ b/libs/plugin/core/src/generators/repository/files/src/index.ts__template__ new file mode 100644 index 0000000..dde3cb6 --- /dev/null +++ b/libs/plugin/core/src/generators/repository/files/src/index.ts__template__ @@ -0,0 +1 @@ +const variable = "<%= projectName %>"; \ No newline at end of file diff --git a/libs/plugin/core/src/generators/repository/generator.compat.ts b/libs/plugin/core/src/generators/repository/generator.compat.ts new file mode 100644 index 0000000..8bdd87d --- /dev/null +++ b/libs/plugin/core/src/generators/repository/generator.compat.ts @@ -0,0 +1,4 @@ +import { convertNxGenerator } from '@nrwl/devkit'; +import generator from './generator'; + +export const generatorSchematic = convertNxGenerator(generator); \ No newline at end of file diff --git a/libs/plugin/core/src/generators/repository/generator.ts b/libs/plugin/core/src/generators/repository/generator.ts new file mode 100644 index 0000000..b8cce9b --- /dev/null +++ b/libs/plugin/core/src/generators/repository/generator.ts @@ -0,0 +1,85 @@ +import { + Tree, + names, + generateFiles, + offsetFromRoot, + readProjectConfiguration, + readWorkspaceConfiguration, +} from '@nrwl/devkit'; +import { join } from 'path'; +import { + ImplRepositoryGeneratorSchema, + NormalizedSchema, + RepositoryGeneratorSchema, + TemplateOptions, +} from './schema'; + +export default function (host: Tree, options: RepositoryGeneratorSchema): void; + +export default function ( + host: Tree, + options: ImplRepositoryGeneratorSchema +): void { + const normalizedOptions = normalizeOptions(host, options); + + if (options.name && options.domain) { + const domain = readProjectConfiguration(host, options.domain); + + addFiles( + host, + normalizedOptions, + __dirname + '/files/domain', + domain.sourceRoot + ); + } + + if (options.impl && options.data) { + if (!options.name) { + throw new Error('You need to add a name'); + } + + const data = readProjectConfiguration(host, options.data); + + addFiles( + host, + normalizedOptions, + __dirname + '/files/data', + data.sourceRoot + ); + } +} + +function normalizeOptions( + host: Tree, + options: ImplRepositoryGeneratorSchema +): NormalizedSchema { + const npmScope = readWorkspaceConfiguration(host).npmScope; + const projectDomain = options.domain; + const projectData = options.data; + return { + ...options, + projectDomain, + projectData, + npmScope, + }; +} + +function addFiles( + host: Tree, + options: NormalizedSchema, + dir: string, + target: string +) { + const entity = names(options.name); + + const templateOptions: TemplateOptions = { + ...options, + ...names(options.name), + model: entity.fileName, + offsetFromRoot: offsetFromRoot(target), + template: '', + entity, + }; + + generateFiles(host, join(dir, 'files'), target, templateOptions); +} diff --git a/libs/plugin/core/src/generators/repository/schema.d.ts b/libs/plugin/core/src/generators/repository/schema.d.ts new file mode 100644 index 0000000..cd1b151 --- /dev/null +++ b/libs/plugin/core/src/generators/repository/schema.d.ts @@ -0,0 +1,31 @@ +export interface RepositoryGeneratorSchema { + name: string; + domain: string; + impl?: boolean; +} + +export interface ImplRepositoryGeneratorSchema extends RepositoryGeneratorSchema { + impl: boolean; + data: string; +} + +export interface NormalizedSchema extends RepositoryGeneratorSchema { + projectDomain: string; + projectData: string; + npmScope: string; +} + +export interface Names { + name: string; + className: string; + propertyName: string; + constantName: string; + fileName: string; +} + +export interface TemplateOptions extends NormalizedSchema, Names { + offsetFromRoot: string; + entity: Names; + model: string; + template: string; +} diff --git a/libs/plugin/core/src/generators/repository/schema.json b/libs/plugin/core/src/generators/repository/schema.json new file mode 100644 index 0000000..7ae279c --- /dev/null +++ b/libs/plugin/core/src/generators/repository/schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "id": "Repository", + "title": "", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What name would you like to use?" + }, + "domain": { + "type": "string", + "description": "The name of the domain library.", + "$default": { + "$source": "projectName" + } + }, + "impl": { + "type": "boolean", + "description": "Would you like to add a implementation?", + "default": false + }, + "data": { + "type": "string", + "description": "The name of the data library.", + "$default": { + "$source": "projectName" + } + } + }, + "required": ["name", "domain"] +} diff --git a/libs/plugin/core/src/utils/add-files.ts b/libs/plugin/core/src/utils/add-files.ts index c211525..c959868 100644 --- a/libs/plugin/core/src/utils/add-files.ts +++ b/libs/plugin/core/src/utils/add-files.ts @@ -10,19 +10,19 @@ export function addFiles( host: Tree, options: DomainPluginCoreNormalizedSchema, dir: string -); +): void export function addFiles( host: Tree, options: DataPluginCoreNormalizedSchema, dir: string -); +): void export function addFiles( host: Tree, options: PresentationPluginCoreNormalizedSchema, dir: string -) { +): void { const libNames = names(options.name); const templateOptions: Record = {