Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: IoC container #9

Merged
merged 8 commits into from
Jul 1, 2022
Merged
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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
"scripts": {},
"private": true,
"dependencies": {
"class-validator": "^0.13.2",
"glob": "^8.0.1",
"inversify": "^6.0.1",
"js-yaml": "^4.1.0",
"koa-compose": "^4.1.0",
"lodash": "^4.17.21",
"nunjucks": "^3.2.3",
"reflect-metadata": "^0.1.13",
"tslib": "^2.3.0"
},
"devDependencies": {
Expand Down
28 changes: 28 additions & 0 deletions packages/build/src/containers/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Container as InversifyContainer } from 'inversify';
import { Container as CoreContainer } from '@vulcan/core';
import { IBuildOptions } from '@vulcan/build/models';
import { schemaParserModule } from './modules';

export class Container {
private inversifyContainer = new InversifyContainer();

public get<T>(type: symbol) {
return this.inversifyContainer.get<T>(type);
}

public load(options: IBuildOptions) {
const coreContainer = new CoreContainer();
coreContainer.load(options);
this.inversifyContainer.parent = coreContainer.getInversifyContainer();
this.inversifyContainer.load(schemaParserModule(options.schemaParser));
}

public unload() {
this.inversifyContainer.parent?.unbindAll();
this.inversifyContainer.unbindAll();
}

public getInversifyContainer() {
return this.inversifyContainer;
}
}
3 changes: 3 additions & 0 deletions packages/build/src/containers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import 'reflect-metadata';
export * from './types';
export * from './container';
1 change: 1 addition & 0 deletions packages/build/src/containers/modules/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './schemaParser';
33 changes: 33 additions & 0 deletions packages/build/src/containers/modules/schemaParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ISchemaParserOptions, SchemaReaderType } from '@vulcan/build/models';
import {
FileSchemaReader,
SchemaParser,
SchemaReader,
} from '@vulcan/build/schema-parser';
import { ContainerModule, interfaces } from 'inversify';
import { SchemaParserOptions } from '../../options/schemaParser';
import { TYPES } from '../types';

export const schemaParserModule = (options: ISchemaParserOptions) =>
new ContainerModule((bind) => {
// Options
bind<ISchemaParserOptions>(TYPES.SchemaParserInputOptions).toConstantValue(
options
);
bind<SchemaParserOptions>(TYPES.SchemaParserOptions)
.to(SchemaParserOptions)
.inSingletonScope();

// Schema reader
bind<SchemaReader>(TYPES.SchemaReader)
.to(FileSchemaReader)
.inSingletonScope()
.whenTargetNamed(SchemaReaderType.LocalFile);

bind<interfaces.AutoNamedFactory<SchemaReader>>(
TYPES.Factory_SchemaReader
).toAutoNamedFactory<SchemaReader>(TYPES.SchemaReader);

// Schema parser
bind<SchemaParser>(TYPES.SchemaParser).to(SchemaParser).inSingletonScope();
});
7 changes: 7 additions & 0 deletions packages/build/src/containers/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const TYPES = {
SchemaParserInputOptions: Symbol.for('SchemaParserInputOptions'),
SchemaParserOptions: Symbol.for('SchemaParserOptions'),
SchemaReader: Symbol.for('SchemaReader'),
Factory_SchemaReader: Symbol.for('Factory_SchemaReader'),
SchemaParser: Symbol.for('SchemaParser'),
};
5 changes: 4 additions & 1 deletion packages/build/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './lib/schema-parser';
export * from './lib/pipelline';
export * from './lib/vulcanBuilder';
export * from './containers';
export * from './options';
export * from './models';
1 change: 0 additions & 1 deletion packages/build/src/lib/pipelline/index.ts

This file was deleted.

75 changes: 0 additions & 75 deletions packages/build/src/lib/pipelline/pipeline.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ValidatorLoader } from '../schemaParser';
import { SchemaParserMiddleware } from './middleware';
import { chain } from 'lodash';
import { APISchema } from '@vulcan/core';
import { APISchema, ValidatorLoader } from '@vulcan/core';

export const checkValidator =
(loader: ValidatorLoader): SchemaParserMiddleware =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface RawRequestParameter
}

export interface RawAPISchema extends DeepPartial<Omit<APISchema, 'request'>> {
/** Indicate the identifier of this schema from the source, it might be uuid, file path, url ...etc, depend on the provider */
sourceName: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding the comment to make the members know the source name is the schema file name or rename it to schemaFileName or fileName ?

Because sourceName may not easy to know where the source from, even we could know the templateSource will be assigned the source name if not foundtemplateSource in getTemapleteSource middleware, we still do not know where the default source name from.

And also, even though we found the sourceName is from name field in the SchemaData, the name is a general word ( maybe we could also rename the name in the SchemaData or just also add a comment ) until we see the code of readSchema in the FileSchemaReader, however, it may spend some time to trace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed these two properties to "sourceName" and added some comments about them.

export interface RawAPISchema extends DeepPartial<Omit<APISchema, 'request'>> {
  /** Indicate the identifier of this schema from the source, it might be uuid, file path, url ...etc, depend on the provider */
  sourceName: string;
  request?: DeepPartial<RawRequestParameter[]>;
}
export interface SchemaData {
  /** The identifier of this schema, we might use this name to mapping SQL sources. */
  sourceName: string;
  content: string;
  type: SchemaFormat;
}

The sourceName here indicated the identifier of the schema, it might be file path (File provider), URL (s3 provider), UUID (database provider) ...etc. in the future, so I didn't rename it the schemaFileName or fileName.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for telling me the sourceName purpose in the future, thanks, no problem and thanks for you comment added 👍

request?: DeepPartial<RawRequestParameter[]>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import { SchemaFormat, SchemaData, SchemaReader } from './schemaReader';
import * as glob from 'glob';
import { promises as fs } from 'fs';
import * as path from 'path';
import { inject, injectable } from 'inversify';
import { TYPES } from '@vulcan/build/containers';
import { SchemaParserOptions } from '@vulcan/build/options';

export interface FileSchemaReaderOptions {
folderPath: string;
}

export class FileSchemaReader extends SchemaReader {
private options: FileSchemaReaderOptions;
@injectable()
export class FileSchemaReader implements SchemaReader {
private options: SchemaParserOptions;

constructor(options: FileSchemaReaderOptions) {
super();
constructor(@inject(TYPES.SchemaParserOptions) options: SchemaParserOptions) {
this.options = options;
}

Expand All @@ -21,9 +24,9 @@ export class FileSchemaReader extends SchemaReader {
for (const file of files) {
const fileName = path.relative(this.options.folderPath, file);
const { ext } = path.parse(fileName);
const name = fileName.replace(new RegExp(`\\${ext}$`), '');
const sourceName = fileName.replace(new RegExp(`\\${ext}$`), '');
yield {
name,
sourceName,
content: await fs.readFile(file, 'utf8'),
type: SchemaFormat.YAML,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ export enum SchemaFormat {
}

export interface SchemaData {
name: string;
/** The identifier of this schema, we might use this name to mapping SQL sources. */
sourceName: string;
content: string;
type: SchemaFormat;
}

export abstract class SchemaReader {
abstract readSchema(): AsyncGenerator<SchemaData>;
export interface SchemaReader {
readSchema(): AsyncGenerator<SchemaData>;
}
37 changes: 19 additions & 18 deletions packages/build/src/lib/schema-parser/schemaParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { APISchema, IValidator, TemplateMetadata } from '@vulcan/core';
import {
APISchema,
TemplateMetadata,
TYPES as CORE_TYPES,
ValidatorLoader,
} from '@vulcan/core';
import { SchemaData, SchemaFormat, SchemaReader } from './schema-reader';
import * as yaml from 'js-yaml';
import {
Expand All @@ -13,31 +18,27 @@ import {
addMissingErrors,
} from './middleware';
import * as compose from 'koa-compose';

/**
* Temporary interface
* @deprecated
*/
export interface ValidatorLoader {
getLoader(name: string): IValidator;
}
import { inject, injectable, interfaces } from 'inversify';
import { TYPES } from '@vulcan/build/containers';
import { SchemaParserOptions } from '@vulcan/build/options';

export interface SchemaParseResult {
schemas: APISchema[];
}

@injectable()
export class SchemaParser {
private schemaReader: SchemaReader;
private middleware: SchemaParserMiddleware[] = [];

constructor({
schemaReader,
validatorLoader,
}: {
schemaReader: SchemaReader;
validatorLoader: ValidatorLoader;
}) {
this.schemaReader = schemaReader;
constructor(
@inject(TYPES.Factory_SchemaReader)
schemaReaderFactory: interfaces.AutoNamedFactory<SchemaReader>,
@inject(TYPES.SchemaParserOptions) schemaParserOptions: SchemaParserOptions,
@inject(CORE_TYPES.ValidatorLoader) validatorLoader: ValidatorLoader
) {
this.schemaReader = schemaReaderFactory(schemaParserOptions.reader);

// Global middleware
this.use(generateUrl());
this.use(generateTemplateSource());
Expand Down Expand Up @@ -76,7 +77,7 @@ export class SchemaParser {
switch (schemaData.type) {
case SchemaFormat.YAML:
return {
sourceName: schemaData.name,
sourceName: schemaData.sourceName,
...(yaml.load(schemaData.content) as object),
} as RawAPISchema;
default:
Expand Down
29 changes: 29 additions & 0 deletions packages/build/src/lib/vulcanBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { IBuildOptions } from '@vulcan/build/models';
import { Container, TYPES } from '@vulcan/build/containers';
import { SchemaParser } from '@vulcan/build/schema-parser';
import {
TemplateEngine,
TYPES as CORE_TYPES,
VulcanArtifactBuilder,
} from '@vulcan/core';

export class VulcanBuilder {
public async build(options: IBuildOptions) {
const container = new Container();
container.load(options);
const schemaParser = container.get<SchemaParser>(TYPES.SchemaParser);
const templateEngine = container.get<TemplateEngine>(
CORE_TYPES.TemplateEngine
);
const artifactBuilder = container.get<VulcanArtifactBuilder>(
CORE_TYPES.ArtifactBuilder
);

const { metadata, templates } = await templateEngine.compile();
const { schemas } = await schemaParser.parse({ metadata });

await artifactBuilder.build({ schemas, templates });

container.unload();
}
}
6 changes: 6 additions & 0 deletions packages/build/src/models/buildOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ICoreOptions } from '@vulcan/core';
import { ISchemaParserOptions } from './schemaParserOptions';

export interface IBuildOptions extends ICoreOptions {
schemaParser: ISchemaParserOptions;
}
2 changes: 2 additions & 0 deletions packages/build/src/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './buildOptions';
export * from './schemaParserOptions';
8 changes: 8 additions & 0 deletions packages/build/src/models/schemaParserOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface ISchemaParserOptions {
reader: SchemaReaderType;
folderPath: string;
}

export enum SchemaReaderType {
LocalFile = 'LocalFile',
}
1 change: 1 addition & 0 deletions packages/build/src/options/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './schemaParser';
26 changes: 26 additions & 0 deletions packages/build/src/options/schemaParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { injectable, inject, optional } from 'inversify';
import { TYPES } from '@vulcan/build/containers';
import { ISchemaParserOptions, SchemaReaderType } from '@vulcan/build/models';
import { IsOptional, IsString, validateSync } from 'class-validator';

@injectable()
export class SchemaParserOptions implements ISchemaParserOptions {
@IsString()
public readonly reader: SchemaReaderType = SchemaReaderType.LocalFile;

@IsString()
@IsOptional()
public readonly folderPath!: string;

constructor(
@inject(TYPES.SchemaParserInputOptions)
@optional()
options: Partial<ISchemaParserOptions> = {}
) {
Object.assign(this, options);
const errors = validateSync(this);
if (errors.length > 0) {
throw new Error('Invalid schema parser options: ' + errors.join(', '));
}
}
}
Loading