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: Document server - redoc #38

Merged
merged 8 commits into from
Aug 31, 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"nunjucks": "^3.2.3",
"openapi3-ts": "^2.0.2",
"ora": "^5.4.1",
"redoc": "2.0.0-rc.76",
"reflect-metadata": "^0.1.13",
"tslib": "^2.3.0",
"tslog": "^3.3.3",
Expand Down
4 changes: 1 addition & 3 deletions packages/build/src/containers/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ export class Container {
schemaParserModule(options['schema-parser'])
);
await this.inversifyContainer.loadAsync(extensionModule(options));
await this.inversifyContainer.loadAsync(
documentGeneratorModule(options['document-generator'])
);
await this.inversifyContainer.loadAsync(documentGeneratorModule());
}

public async unload() {
Expand Down
16 changes: 2 additions & 14 deletions packages/build/src/containers/modules/documentGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
import {
IDocumentGeneratorOptions,
SpecGenerator,
} from '@vulcan-sql/build/models';
import { SpecGenerator } from '@vulcan-sql/build/models';
import { AsyncContainerModule, interfaces } from 'inversify';
import { DocumentGenerator } from '../../lib/document-generator';
import { DocumentGeneratorOptions } from '../../options/documentGenerator';
import { TYPES } from '../types';

export const documentGeneratorModule = (options?: IDocumentGeneratorOptions) =>
export const documentGeneratorModule = () =>
new AsyncContainerModule(async (bind) => {
// Options
bind<IDocumentGeneratorOptions>(
TYPES.DocumentGeneratorInputOptions
).toConstantValue(options || ({} as any));
bind<IDocumentGeneratorOptions>(TYPES.DocumentGeneratorOptions)
.to(DocumentGeneratorOptions)
.inSingletonScope();

// Document generator
bind<DocumentGenerator>(TYPES.DocumentGenerator).to(DocumentGenerator);
bind<interfaces.AutoNamedFactory<SpecGenerator>>(
Expand Down
2 changes: 0 additions & 2 deletions packages/build/src/containers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ export const TYPES = {
SchemaParserMiddleware: Symbol.for('SchemaParserMiddleware'),
// Document
DocumentGenerator: Symbol.for('DocumentGenerator'),
DocumentGeneratorInputOptions: Symbol.for('DocumentGeneratorInputOptions'),
DocumentGeneratorOptions: Symbol.for('DocumentGeneratorOptions'),
Factory_SpecGenerator: Symbol.for('Factory_SpecGenerator'),
// Extension
Extension_SchemaReader: Symbol.for('Extension_SchemaReader'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { APISchema } from '@vulcan-sql/core';
import {
APISchema,
DocumentOptions,
TYPES as CORE_TYPES,
} from '@vulcan-sql/core';
import { inject, injectable, interfaces } from 'inversify';
import { TYPES } from '../../containers/types';
import { SpecGenerator } from '../../models/extensions';
import { DocumentGeneratorOptions } from '../../options/documentGenerator';
import * as jsYAML from 'js-yaml';
import * as path from 'path';
import { promises as fs } from 'fs';
Expand All @@ -15,7 +18,7 @@ export class DocumentGenerator {
constructor(
@inject(TYPES.Factory_SpecGenerator)
specGeneratorFactory: interfaces.AutoNamedFactory<SpecGenerator>,
@inject(TYPES.DocumentGeneratorOptions) options: DocumentGeneratorOptions
@inject(CORE_TYPES.DocumentOptions) options: DocumentOptions
) {
this.specGenerators = [];
for (const spec of options.specs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import {
ResponseProperty,
VulcanExtensionId,
VulcanInternalExtension,
DocumentSpec,
} from '@vulcan-sql/core';
import { isEmpty } from 'lodash';
import { DocumentGeneratorSpec } from '@vulcan-sql/build/models';

@VulcanInternalExtension()
@VulcanExtensionId(DocumentGeneratorSpec.oas3)
@VulcanExtensionId(DocumentSpec.oas3)
export class OAS3SpecGenerator extends SpecGenerator<oas3.OpenAPIObject> {
// Follow the OpenAPI specification version 3.0.3
// see https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md
Expand Down
2 changes: 0 additions & 2 deletions packages/build/src/models/buildOptions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { ICoreOptions } from '@vulcan-sql/core';
import { IDocumentGeneratorOptions } from './documentGeneratorOptions';
import { ISchemaParserOptions } from './schemaParserOptions';

export interface IBuildOptions extends ICoreOptions {
'schema-parser'?: ISchemaParserOptions;
'document-generator'?: IDocumentGeneratorOptions;
}
9 changes: 0 additions & 9 deletions packages/build/src/models/documentGeneratorOptions.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/build/src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './buildOptions';
export * from './schemaParserOptions';
export * from './extensions';
export * from './documentGeneratorOptions';
28 changes: 0 additions & 28 deletions packages/build/src/options/documentGenerator.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/build/src/options/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './schemaParser';
export * from './documentGenerator';
11 changes: 4 additions & 7 deletions packages/build/test/builder/builder.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { VulcanBuilder } from '../../src';
import * as path from 'path';
import {
DocumentGeneratorSpec,
IBuildOptions,
SchemaReaderType,
} from '@vulcan-sql/build/models';
import { IBuildOptions, SchemaReaderType } from '@vulcan-sql/build/models';
import {
ArtifactBuilderProviderType,
ArtifactBuilderSerializerType,
DocumentSpec,
TemplateProviderType,
} from '@vulcan-sql/core';

Expand All @@ -18,8 +15,8 @@ it('Builder.build should work', async () => {
reader: SchemaReaderType.LocalFile,
folderPath: path.resolve(__dirname, 'source'),
},
'document-generator': {
specs: [DocumentGeneratorSpec.oas3],
document: {
specs: [DocumentSpec.oas3],
folderPath: path.resolve(__dirname),
},
artifact: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ it('Document generator should write YAML files while generating documents', asyn
{
specs: ['spec1', 'spec2'],
folderPath: __dirname,
router: [],
}
);

Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/containers/container.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ICoreOptions } from '@vulcan-sql/core/models';
import { Container as InversifyContainer } from 'inversify';
import { ProjectOptions } from '../options';
import { extensionModule } from './modules';
import { documentModule, extensionModule } from './modules';
import {
artifactBuilderModule,
executorModule,
Expand Down Expand Up @@ -33,6 +33,7 @@ export class Container {
);
await this.inversifyContainer.loadAsync(validatorLoaderModule());
await this.inversifyContainer.loadAsync(extensionModule(options));
await this.inversifyContainer.loadAsync(documentModule(options.document));
}

public async unload() {
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/containers/modules/document.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { AsyncContainerModule } from 'inversify';
import { TYPES } from '../types';
import { IDocumentOptions } from '../../models';
import { DocumentOptions } from '../../options';

export const documentModule = (options: IDocumentOptions = {}) =>
new AsyncContainerModule(async (bind) => {
// Options
bind<IDocumentOptions>(TYPES.DocumentInputOptions).toConstantValue(options);
bind<IDocumentOptions>(TYPES.DocumentOptions).to(DocumentOptions);
});
1 change: 1 addition & 0 deletions packages/core/src/containers/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './executor';
export * from './templateEngine';
export * from './validatorLoader';
export * from './extension';
export * from './document';
3 changes: 3 additions & 0 deletions packages/core/src/containers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export const TYPES = {
Factory_DataSource: Symbol.for('Factory_DataSource'),
// Validator
ValidatorLoader: Symbol.for('ValidatorLoader'),
// Document
DocumentInputOptions: Symbol.for('DocumentInputOptions'),
DocumentOptions: Symbol.for('DocumentOptions'),
// Extensions
ExtensionConfig: Symbol.for('ExtensionConfig'),
ExtensionName: Symbol.for('ExtensionName'),
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/models/coreOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IArtifactBuilderOptions } from './artifactBuilderOptions';
import { IDocumentOptions } from './documentOptions';
import { IExecutorOptions } from './executorOptions';
import { ITemplateEngineOptions } from './templateEngineOptions';

Expand All @@ -21,5 +22,6 @@ export interface ICoreOptions {
template?: ITemplateEngineOptions;
executor?: IExecutorOptions;
extensions?: ExtensionAliases;
document?: IDocumentOptions;
[moduleAlias: string]: any;
}
14 changes: 14 additions & 0 deletions packages/core/src/models/documentOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export enum DocumentSpec {
oas3 = 'oas3',
}

export enum DocumentRouterType {
redoc = 'redoc',
}

export interface IDocumentOptions {
/** Target specification of our APIs, e.g. OpenAPI, Tinyspec ...etc. */
specs?: (string | DocumentSpec)[];
folderPath?: string;
router?: (string | DocumentRouterType)[];
}
1 change: 1 addition & 0 deletions packages/core/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './coreOptions';
export * from './templateEngineOptions';
export * from './extensions';
export * from './executorOptions';
export * from './documentOptions';
31 changes: 31 additions & 0 deletions packages/core/src/options/document.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { injectable, inject, optional } from 'inversify';
import { IsOptional, IsArray, validateSync, IsString } from 'class-validator';
import { DocumentRouterType, DocumentSpec, IDocumentOptions } from '../models';
import { TYPES } from '@vulcan-sql/core/types';

@injectable()
export class DocumentOptions implements IDocumentOptions {
@IsArray()
@IsOptional()
public readonly specs: string[] = [DocumentSpec.oas3];

@IsString()
public readonly folderPath: string = '.';

@IsArray()
public readonly router: string[] = [DocumentRouterType.redoc];

constructor(
@inject(TYPES.DocumentInputOptions)
@optional()
options: Partial<IDocumentOptions> = {}
) {
Object.assign(this, options);
const errors = validateSync(this);
if (errors.length > 0) {
throw new Error(
'Invalid document generator options: ' + errors.join(', ')
);
}
}
}
1 change: 1 addition & 0 deletions packages/core/src/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './artifactBuilder';
export * from './templateEngine';
export * from './project';
export * from './executor';
export * from './document';
47 changes: 47 additions & 0 deletions packages/core/test/options/documentOptions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
DocumentRouterType,
DocumentSpec,
IDocumentOptions,
TYPES,
} from '../../src';
import { Container } from 'inversify';
import { DocumentOptions } from '../../src/options';

let container: Container;

beforeEach(() => {
container = new Container();
container.bind(TYPES.DocumentOptions).to(DocumentOptions).inSingletonScope();
});

it('Should provide correct default option values', async () => {
// Action
const options = container.get<DocumentOptions>(TYPES.DocumentOptions);
// Assert
expect(options.folderPath).toBe('.');
expect(options.specs).toEqual([DocumentSpec.oas3]);
expect(options.router).toEqual([DocumentRouterType.redoc]);
});

it('Can override some option properties', async () => {
// Arrange
container
.bind<Partial<IDocumentOptions>>(TYPES.DocumentInputOptions)
.toConstantValue({
folderPath: './some/folder',
});
const options = container.get<DocumentOptions>(TYPES.DocumentOptions);
// Assert
expect(options.folderPath).toBe('./some/folder');
expect(options.specs).toEqual([DocumentSpec.oas3]);
expect(options.router).toEqual([DocumentRouterType.redoc]);
});

it('Schema validation should work', async () => {
// Arrange
container.bind(TYPES.DocumentInputOptions).toConstantValue({
folderPath: true,
});
// Act. Assert
expect(() => container.get<DocumentOptions>(TYPES.DocumentOptions)).toThrow();
});
10 changes: 6 additions & 4 deletions packages/integration-testing/src/example1/buildAndServe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import {
VulcanBuilder,
IBuildOptions,
SchemaReaderType,
DocumentGeneratorSpec,
} from '@vulcan-sql/build';
import {
ArtifactBuilderProviderType,
ArtifactBuilderSerializerType,
TemplateProviderType,
DocumentSpec,
} from '@vulcan-sql/core';
import { VulcanServer, ServeConfig, APIProviderType } from '@vulcan-sql/serve';
import * as path from 'path';
import * as supertest from 'supertest';
import faker from '@faker-js/faker';

const projectConfig: ServeConfig & IBuildOptions = {
name: 'example project 1',
Expand All @@ -30,8 +31,8 @@ const projectConfig: ServeConfig & IBuildOptions = {
reader: SchemaReaderType.LocalFile,
folderPath: path.resolve(__dirname, 'sqls'),
},
'document-generator': {
specs: [DocumentGeneratorSpec.oas3],
document: {
specs: [DocumentSpec.oas3],
folderPath: __dirname,
},
types: [APIProviderType.RESTFUL],
Expand All @@ -47,6 +48,7 @@ const projectConfig: ServeConfig & IBuildOptions = {
'enforce-https': {
enabled: false,
},
port: faker.datatype.number({ min: 20000, max: 30000 }),
};

let server: VulcanServer;
Expand All @@ -63,7 +65,7 @@ it('Example1: Build and serve should work', async () => {

const agent = supertest(httpServer);
const result = await agent.get(
'/user?id=436193eb-f686-4105-ad7b-b5945276c14a'
'/api/user/436193eb-f686-4105-ad7b-b5945276c14a'
);
expect(result.body).toContainEqual({
id: '436193eb-f686-4105-ad7b-b5945276c14a',
Expand Down
Loading