From b14eba88848a475653907916e6cab74d976efa10 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 22 Sep 2022 21:02:25 +0800 Subject: [PATCH 1/8] refactor(core, build, serve): let artifact builder get/set artifacts dynamically --- packages/build/src/lib/vulcanBuilder.ts | 6 ++++- .../lib/artifact-builder/artifactBuilder.ts | 16 ++++++------- .../artifact-builder/vulcanArtifactBuilder.ts | 23 ++++++++++++++----- .../vulcanArtifactBuilder.spec.ts | 17 ++++++++------ .../core/test/containers/continer.spec.ts | 8 +++++-- packages/serve/src/lib/server.ts | 14 ++++++++--- 6 files changed, 57 insertions(+), 27 deletions(-) diff --git a/packages/build/src/lib/vulcanBuilder.ts b/packages/build/src/lib/vulcanBuilder.ts index 85c53876..f4886c9c 100644 --- a/packages/build/src/lib/vulcanBuilder.ts +++ b/packages/build/src/lib/vulcanBuilder.ts @@ -3,6 +3,7 @@ import { Container, TYPES } from '@vulcan-sql/build/containers'; import { SchemaParser } from '@vulcan-sql/build/schema-parser'; import { DataSource, + BuiltInArtifactKeys, TemplateEngine, TYPES as CORE_TYPES, VulcanArtifactBuilder, @@ -44,7 +45,10 @@ export class VulcanBuilder { const { metadata, templates } = await templateEngine.compile(); const { schemas } = await schemaParser.parse({ metadata }); - await artifactBuilder.build({ schemas, templates }); + artifactBuilder.addArtifact(BuiltInArtifactKeys.templates, templates); + artifactBuilder.addArtifact(BuiltInArtifactKeys.schemas, schemas); + + await artifactBuilder.build(); await documentGenerator.generateDocuments(schemas); diff --git a/packages/core/src/lib/artifact-builder/artifactBuilder.ts b/packages/core/src/lib/artifact-builder/artifactBuilder.ts index b691298e..f711afcd 100644 --- a/packages/core/src/lib/artifact-builder/artifactBuilder.ts +++ b/packages/core/src/lib/artifact-builder/artifactBuilder.ts @@ -1,11 +1,11 @@ -import { APISchema } from '../../models'; - -export interface Artifact { - schemas: APISchema[]; - templates: Record; -} +export const BuiltInArtifactKeys = { + templates: 'templates', + schemas: 'schemas', +}; export interface ArtifactBuilder { - build(artifact: Artifact): Promise; - load(): Promise; + build(): Promise; + load(): Promise; + addArtifact(key: string, data: any): void; + getArtifact(key: string): T; } diff --git a/packages/core/src/lib/artifact-builder/vulcanArtifactBuilder.ts b/packages/core/src/lib/artifact-builder/vulcanArtifactBuilder.ts index 37630b5f..7c0574e1 100644 --- a/packages/core/src/lib/artifact-builder/vulcanArtifactBuilder.ts +++ b/packages/core/src/lib/artifact-builder/vulcanArtifactBuilder.ts @@ -1,4 +1,4 @@ -import { Artifact, ArtifactBuilder } from './artifactBuilder'; +import { ArtifactBuilder } from './artifactBuilder'; import { PersistentStore } from '@vulcan-sql/core/models'; import { Serializer } from '@vulcan-sql/core/models'; import { inject, injectable } from 'inversify'; @@ -6,8 +6,9 @@ import { TYPES } from '@vulcan-sql/core/types'; @injectable() export class VulcanArtifactBuilder implements ArtifactBuilder { - private serializer: Serializer; + private serializer: Serializer>; private persistentStore: PersistentStore; + private artifact: Record = {}; constructor( @inject(TYPES.PersistentStore) @@ -19,13 +20,23 @@ export class VulcanArtifactBuilder implements ArtifactBuilder { this.persistentStore = persistentStore; } - public async build(artifact: Artifact): Promise { - const serializedArtifact = this.serializer.serialize(artifact); + public async build(): Promise { + const serializedArtifact = this.serializer.serialize(this.artifact); await this.persistentStore.save(serializedArtifact); } - public async load(): Promise { + public async load(): Promise { const serializedArtifact = await this.persistentStore.load(); - return this.serializer.deserialize(serializedArtifact); + this.artifact = this.serializer.deserialize(serializedArtifact); + } + + public getArtifact(key: string): T { + const target = this.artifact[key]; + if (!target) throw new Error(`Artifact ${key} not found`); + return target as T; + } + + public addArtifact(key: string, data: any): void { + this.artifact[key] = data; } } diff --git a/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts b/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts index 97bb9db7..1b5c354b 100644 --- a/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts +++ b/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts @@ -1,5 +1,5 @@ import { - Artifact, + BuiltInArtifactKeys, JSONSerializer, LocalFilePersistentStore, VulcanArtifactBuilder, @@ -11,9 +11,9 @@ import * as sinon from 'ts-sinon'; let container: Container; let mockPersistentStore: sinon.StubbedInstance; -const mockArtifact: Artifact = { - schemas: [], - templates: {}, +const mockArtifact = { + [BuiltInArtifactKeys.schemas]: [], + [BuiltInArtifactKeys.templates]: {}, }; beforeEach(() => { @@ -32,7 +32,7 @@ it('Should pass serialized data to store while building', async () => { const builder = container.get(TYPES.ArtifactBuilder); // Act - await builder.build(mockArtifact); + await builder.build(); // Assert expect(mockPersistentStore.save.calledOnce).toBe(true); @@ -44,8 +44,11 @@ it('Should load deserialized data while loading', async () => { mockPersistentStore.load.resolves(Buffer.from(JSON.stringify(mockArtifact))); // Act - const artifact = await builder.load(); + await builder.load(); + const template = builder.getArtifact(BuiltInArtifactKeys.templates); + const schemas = builder.getArtifact(BuiltInArtifactKeys.schemas); // Assert - expect(artifact).toEqual(mockArtifact); + expect(template).toEqual(mockArtifact[BuiltInArtifactKeys.templates]); + expect(schemas).toEqual(mockArtifact[BuiltInArtifactKeys.schemas]); }); diff --git a/packages/core/test/containers/continer.spec.ts b/packages/core/test/containers/continer.spec.ts index 2c8ee72f..02f5b8db 100644 --- a/packages/core/test/containers/continer.spec.ts +++ b/packages/core/test/containers/continer.spec.ts @@ -1,4 +1,7 @@ -import { ArtifactBuilder } from '@vulcan-sql/core/artifact-builder'; +import { + ArtifactBuilder, + BuiltInArtifactKeys, +} from '@vulcan-sql/core/artifact-builder'; import { Container, TYPES } from '@vulcan-sql/core/containers'; import { TemplateEngine } from '@vulcan-sql/core/template-engine'; import * as path from 'path'; @@ -32,7 +35,8 @@ it('Container should load options and resolve all dependencies', async () => { const templateEngine = container.get(TYPES.TemplateEngine); const artifactBuilder = container.get(TYPES.ArtifactBuilder); const { templates } = await templateEngine.compile(); - await artifactBuilder.build({ templates, schemas: [] }); + artifactBuilder.addArtifact(BuiltInArtifactKeys.templates, templates); + await artifactBuilder.build(); await container.unload(); // Assert expect(fs.existsSync(resultPath)).toBeTruthy(); diff --git a/packages/serve/src/lib/server.ts b/packages/serve/src/lib/server.ts index 1f3a7751..c464e30b 100644 --- a/packages/serve/src/lib/server.ts +++ b/packages/serve/src/lib/server.ts @@ -3,11 +3,13 @@ import * as fs from 'fs'; import * as http from 'http'; import * as https from 'https'; import { - VulcanArtifactBuilder, TYPES as CORE_TYPES, CodeLoader, DataSource, getLogger, + BuiltInArtifactKeys, + APISchema, + ArtifactBuilder, } from '@vulcan-sql/core'; import { Container, TYPES } from '../containers'; import { ServeConfig, sslFileOptions } from '../models'; @@ -45,12 +47,18 @@ export class VulcanServer { // Load container await this.container.load(this.config); - const artifactBuilder = this.container.get( + const artifactBuilder = this.container.get( CORE_TYPES.ArtifactBuilder ); // Obtain schema and template - const { schemas, templates } = await artifactBuilder.load(); + await artifactBuilder.load(); + const templates = artifactBuilder.getArtifact( + BuiltInArtifactKeys.templates + ); + const schemas = artifactBuilder.getArtifact( + BuiltInArtifactKeys.schemas + ); // Initialized template engine const codeLoader = this.container.get( From 3997886f6c81a3df42ccbff90eb68c4427d0f622 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 22 Sep 2022 21:50:28 +0800 Subject: [PATCH 2/8] refactor(core, build, serve): save api spec to artifact file instead of writing files directly --- labs/playground1/vulcan.yaml | 7 +++++++ .../document-generator/documentGenerator.ts | 21 ++++++++----------- packages/build/src/lib/vulcanBuilder.ts | 8 +++---- .../build/test/document-generator/.gitignore | 1 - .../document-generator.spec.ts | 17 +++++++-------- .../lib/artifact-builder/artifactBuilder.ts | 5 +++-- .../vulcanArtifactBuilder.spec.ts | 12 +++++------ .../core/test/containers/continer.spec.ts | 2 +- .../redocDocumentRouters.ts | 8 +++---- packages/serve/src/lib/server.ts | 4 ++-- .../src/models/extensions/documentRouter.ts | 18 ++++++---------- .../redocDocumentRouter.spec.ts | 15 +++++++------ 12 files changed, 58 insertions(+), 60 deletions(-) delete mode 100644 packages/build/test/document-generator/.gitignore diff --git a/labs/playground1/vulcan.yaml b/labs/playground1/vulcan.yaml index 977e5998..a29d593a 100644 --- a/labs/playground1/vulcan.yaml +++ b/labs/playground1/vulcan.yaml @@ -34,3 +34,10 @@ enforce-https: enabled: false auth: enabled: false +response-format: + enabled: true + options: + default: json + formats: + - json + - csv diff --git a/packages/build/src/lib/document-generator/documentGenerator.ts b/packages/build/src/lib/document-generator/documentGenerator.ts index 75e8c9b3..b5bbc766 100644 --- a/packages/build/src/lib/document-generator/documentGenerator.ts +++ b/packages/build/src/lib/document-generator/documentGenerator.ts @@ -1,40 +1,37 @@ import { APISchema, + ArtifactBuilder, + BuiltInArtifactKeys, 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 * as jsYAML from 'js-yaml'; -import * as path from 'path'; -import { promises as fs } from 'fs'; @injectable() export class DocumentGenerator { private specGenerators: SpecGenerator[]; - private folderPath: string; + private artifactBuilder: ArtifactBuilder; constructor( @inject(TYPES.Factory_SpecGenerator) specGeneratorFactory: interfaces.AutoNamedFactory, - @inject(CORE_TYPES.DocumentOptions) options: DocumentOptions + @inject(CORE_TYPES.DocumentOptions) options: DocumentOptions, + @inject(CORE_TYPES.ArtifactBuilder) artifactBuilder: ArtifactBuilder ) { this.specGenerators = []; for (const spec of options.specs) { this.specGenerators.push(specGeneratorFactory(spec)); } - this.folderPath = options.folderPath; + this.artifactBuilder = artifactBuilder; } public async generateDocuments(schemas: APISchema[]) { + const specs: Record = {}; for (const generator of this.specGenerators) { - const spec = generator.getSpec(schemas); - const filePath = path.resolve( - this.folderPath, - `spec-${generator.getExtensionId()}.yaml` - ); - await fs.writeFile(filePath, jsYAML.dump(spec), 'utf-8'); + specs[generator.getExtensionId()!] = generator.getSpec(schemas); } + this.artifactBuilder.addArtifact(BuiltInArtifactKeys.Specs, specs); } } diff --git a/packages/build/src/lib/vulcanBuilder.ts b/packages/build/src/lib/vulcanBuilder.ts index f4886c9c..96a0400f 100644 --- a/packages/build/src/lib/vulcanBuilder.ts +++ b/packages/build/src/lib/vulcanBuilder.ts @@ -45,13 +45,11 @@ export class VulcanBuilder { const { metadata, templates } = await templateEngine.compile(); const { schemas } = await schemaParser.parse({ metadata }); - artifactBuilder.addArtifact(BuiltInArtifactKeys.templates, templates); - artifactBuilder.addArtifact(BuiltInArtifactKeys.schemas, schemas); - - await artifactBuilder.build(); + artifactBuilder.addArtifact(BuiltInArtifactKeys.Templates, templates); + artifactBuilder.addArtifact(BuiltInArtifactKeys.Schemas, schemas); await documentGenerator.generateDocuments(schemas); - + await artifactBuilder.build(); await container.unload(); } } diff --git a/packages/build/test/document-generator/.gitignore b/packages/build/test/document-generator/.gitignore deleted file mode 100644 index 1265a84a..00000000 --- a/packages/build/test/document-generator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -spec-*.yaml \ No newline at end of file diff --git a/packages/build/test/document-generator/document-generator.spec.ts b/packages/build/test/document-generator/document-generator.spec.ts index 21899a07..e4fd650e 100644 --- a/packages/build/test/document-generator/document-generator.spec.ts +++ b/packages/build/test/document-generator/document-generator.spec.ts @@ -1,9 +1,8 @@ import { SpecGenerator } from '@vulcan-sql/build'; import { DocumentGenerator } from '@vulcan-sql/build/doc-generator'; import * as sinon from 'ts-sinon'; -import * as path from 'path'; -import { promises as fs } from 'fs'; import faker from '@faker-js/faker'; +import { ArtifactBuilder } from '@vulcan-sql/core'; it('Document generator should write YAML files while generating documents', async () => { // Arrange @@ -11,6 +10,7 @@ it('Document generator should write YAML files while generating documents', asyn someSpec: faker.datatype.number(), profile: faker.name.firstName(), }; + const mockArtifactBuilder = sinon.stubInterface(); const documentGenerator = new DocumentGenerator( (id: string) => { const mockSpecGenerator = sinon.stubInterface(); @@ -22,17 +22,16 @@ it('Document generator should write YAML files while generating documents', asyn specs: ['spec1', 'spec2'], folderPath: __dirname, router: [], - } + }, + mockArtifactBuilder ); // Act await documentGenerator.generateDocuments([]); // Arrange - expect( - await fs.readFile(path.resolve(__dirname, 'spec-spec1.yaml'), 'utf-8') - ).toEqual(`someSpec: ${mockSpec.someSpec}\nprofile: ${mockSpec.profile}\n`); - expect( - await fs.readFile(path.resolve(__dirname, 'spec-spec2.yaml'), 'utf-8') - ).toEqual(`someSpec: ${mockSpec.someSpec}\nprofile: ${mockSpec.profile}\n`); + expect(mockArtifactBuilder.addArtifact.firstCall.args[1]).toEqual({ + spec1: mockSpec, + spec2: mockSpec, + }); }); diff --git a/packages/core/src/lib/artifact-builder/artifactBuilder.ts b/packages/core/src/lib/artifact-builder/artifactBuilder.ts index f711afcd..ee0ebd1d 100644 --- a/packages/core/src/lib/artifact-builder/artifactBuilder.ts +++ b/packages/core/src/lib/artifact-builder/artifactBuilder.ts @@ -1,6 +1,7 @@ export const BuiltInArtifactKeys = { - templates: 'templates', - schemas: 'schemas', + Templates: 'templates', + Schemas: 'schemas', + Specs: 'specs', }; export interface ArtifactBuilder { diff --git a/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts b/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts index 1b5c354b..b697545f 100644 --- a/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts +++ b/packages/core/test/artifact-builder/vulcanArtifactBuilder.spec.ts @@ -12,8 +12,8 @@ let container: Container; let mockPersistentStore: sinon.StubbedInstance; const mockArtifact = { - [BuiltInArtifactKeys.schemas]: [], - [BuiltInArtifactKeys.templates]: {}, + [BuiltInArtifactKeys.Schemas]: [], + [BuiltInArtifactKeys.Templates]: {}, }; beforeEach(() => { @@ -45,10 +45,10 @@ it('Should load deserialized data while loading', async () => { // Act await builder.load(); - const template = builder.getArtifact(BuiltInArtifactKeys.templates); - const schemas = builder.getArtifact(BuiltInArtifactKeys.schemas); + const template = builder.getArtifact(BuiltInArtifactKeys.Templates); + const schemas = builder.getArtifact(BuiltInArtifactKeys.Schemas); // Assert - expect(template).toEqual(mockArtifact[BuiltInArtifactKeys.templates]); - expect(schemas).toEqual(mockArtifact[BuiltInArtifactKeys.schemas]); + expect(template).toEqual(mockArtifact[BuiltInArtifactKeys.Templates]); + expect(schemas).toEqual(mockArtifact[BuiltInArtifactKeys.Schemas]); }); diff --git a/packages/core/test/containers/continer.spec.ts b/packages/core/test/containers/continer.spec.ts index 02f5b8db..71055e03 100644 --- a/packages/core/test/containers/continer.spec.ts +++ b/packages/core/test/containers/continer.spec.ts @@ -35,7 +35,7 @@ it('Container should load options and resolve all dependencies', async () => { const templateEngine = container.get(TYPES.TemplateEngine); const artifactBuilder = container.get(TYPES.ArtifactBuilder); const { templates } = await templateEngine.compile(); - artifactBuilder.addArtifact(BuiltInArtifactKeys.templates, templates); + artifactBuilder.addArtifact(BuiltInArtifactKeys.Templates, templates); await artifactBuilder.build(); await container.unload(); // Assert diff --git a/packages/serve/src/lib/document-router/redoc-document-router/redocDocumentRouters.ts b/packages/serve/src/lib/document-router/redoc-document-router/redocDocumentRouters.ts index 1162ab02..1846e98b 100644 --- a/packages/serve/src/lib/document-router/redoc-document-router/redocDocumentRouters.ts +++ b/packages/serve/src/lib/document-router/redoc-document-router/redocDocumentRouters.ts @@ -1,5 +1,5 @@ import { - DocumentOptions, + ArtifactBuilder, DocumentRouterType, DocumentSpec, ProjectOptions, @@ -28,10 +28,10 @@ export class RedocDocumentRouters extends DocumentRouter { constructor( @inject(CORE_TYPES.ExtensionConfig) config: any, @inject(CORE_TYPES.ExtensionName) moduleName: string, - @inject(CORE_TYPES.DocumentOptions) documentOptions: DocumentOptions, + @inject(CORE_TYPES.ArtifactBuilder) artifactBuilder: ArtifactBuilder, @inject(CORE_TYPES.ProjectOptions) projectOption: ProjectOptions ) { - super(config, moduleName, documentOptions); + super(config, moduleName, artifactBuilder); this.projectOption = projectOption; } @@ -43,7 +43,7 @@ export class RedocDocumentRouters extends DocumentRouter { ctx.response.body = this.docContent; }); // spec file - // TODO: it should be spec.yaml but extension will be removed by response-format/middleware, wait for fixing + // TODO: it should be spec.json but extension will be removed by response-format/middleware, wait for fixing const specUrl = `/${this.urlPrefix}/spec`; this.router.get(specUrl, async (ctx, next) => { await next(); diff --git a/packages/serve/src/lib/server.ts b/packages/serve/src/lib/server.ts index c464e30b..eaaf0e1d 100644 --- a/packages/serve/src/lib/server.ts +++ b/packages/serve/src/lib/server.ts @@ -54,10 +54,10 @@ export class VulcanServer { // Obtain schema and template await artifactBuilder.load(); const templates = artifactBuilder.getArtifact( - BuiltInArtifactKeys.templates + BuiltInArtifactKeys.Templates ); const schemas = artifactBuilder.getArtifact( - BuiltInArtifactKeys.schemas + BuiltInArtifactKeys.Schemas ); // Initialized template engine diff --git a/packages/serve/src/models/extensions/documentRouter.ts b/packages/serve/src/models/extensions/documentRouter.ts index 5730ce86..bcabb77a 100644 --- a/packages/serve/src/models/extensions/documentRouter.ts +++ b/packages/serve/src/models/extensions/documentRouter.ts @@ -1,5 +1,6 @@ import { - DocumentOptions, + ArtifactBuilder, + BuiltInArtifactKeys, DocumentSpec, ExtensionBase, VulcanExtension, @@ -8,30 +9,23 @@ import { TYPES } from '@vulcan-sql/serve/types'; import { KoaContext, Next } from '@vulcan-sql/serve/models'; import { TYPES as CORE_TYPES } from '@vulcan-sql/core'; import { inject } from 'inversify'; -import * as fs from 'fs'; -import * as path from 'path'; @VulcanExtension(TYPES.Extension_DocumentRouter) export abstract class DocumentRouter extends ExtensionBase { - private documentOptions: DocumentOptions; + private artifactBuilder: ArtifactBuilder; constructor( @inject(CORE_TYPES.ExtensionConfig) config: any, @inject(CORE_TYPES.ExtensionName) moduleName: string, - @inject(CORE_TYPES.DocumentOptions) documentOptions: DocumentOptions + @inject(CORE_TYPES.ArtifactBuilder) artifactBuilder: ArtifactBuilder ) { super(config, moduleName); - this.documentOptions = documentOptions; + this.artifactBuilder = artifactBuilder; } public abstract handle(context: KoaContext, next: Next): Promise; protected async getSpec(type: string = DocumentSpec.oas3) { - const filePath = path.resolve( - this.documentOptions.folderPath, - `spec-${type}.yaml` - ); - if (!fs.existsSync(filePath)) throw new Error(`File ${filePath} not found`); - return fs.createReadStream(filePath); + return this.artifactBuilder.getArtifact(BuiltInArtifactKeys.Specs)[type]; } } diff --git a/packages/serve/test/document-router/redocDocumentRouter.spec.ts b/packages/serve/test/document-router/redocDocumentRouter.spec.ts index 00316b11..223e206c 100644 --- a/packages/serve/test/document-router/redocDocumentRouter.spec.ts +++ b/packages/serve/test/document-router/redocDocumentRouter.spec.ts @@ -1,9 +1,10 @@ -import { DocumentOptions, ProjectOptions } from '@vulcan-sql/core'; +import { ArtifactBuilder, ProjectOptions } from '@vulcan-sql/core'; import { RedocDocumentRouters } from '../../src/lib/document-router'; import * as Koa from 'koa'; import * as supertest from 'supertest'; import faker from '@faker-js/faker'; import { Server } from 'http'; +import * as sinon from 'ts-sinon'; let http: Server; @@ -13,12 +14,12 @@ afterEach(() => { it('Should serve redoc server with all the required contents', async () => { // Arrange + const mockArtifactBuilder = sinon.stubInterface(); + mockArtifactBuilder.getArtifact.returns({ oas3: { name: 123 } }); const server = new RedocDocumentRouters( {}, '', - new DocumentOptions({ - folderPath: __dirname, - }), + mockArtifactBuilder, new ProjectOptions() ); await server.activate(); @@ -39,17 +40,19 @@ it('Should serve redoc server with all the required contents', async () => { `/*! For license information please see redoc.standalone.js.LICENSE.txt */` ) ).toBeTruthy(); - expect(spec.body.toString()).toEqual(`name: "123"`); + expect(spec.body).toEqual({ name: 123 }); }); it('Should follow the url path we set', async () => { // Arrange + const mockArtifactBuilder = sinon.stubInterface(); + mockArtifactBuilder.getArtifact.returns({ oas3: { name: 123 } }); const server = new RedocDocumentRouters( { url: 'some-path-other-than-doc', }, '', - new DocumentOptions(), + mockArtifactBuilder, new ProjectOptions() ); await server.activate(); From 5c5e21252b526880c66998e784355aab741a2ad5 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 22 Sep 2022 21:57:16 +0800 Subject: [PATCH 3/8] refactor: remove folderPath optoin of document options --- labs/playground1/vulcan.yaml | 1 - packages/build/test/builder/builder.spec.ts | 1 - .../test/document-generator/document-generator.spec.ts | 1 - packages/cli/src/schemas/init/vulcan.yaml | 1 - packages/core/src/models/documentOptions.ts | 1 - packages/core/src/options/document.ts | 5 +---- packages/core/test/options/documentOptions.spec.ts | 8 +++----- .../integration-testing/src/example1/projectConfig.ts | 1 - 8 files changed, 4 insertions(+), 15 deletions(-) diff --git a/labs/playground1/vulcan.yaml b/labs/playground1/vulcan.yaml index a29d593a..258d1e18 100644 --- a/labs/playground1/vulcan.yaml +++ b/labs/playground1/vulcan.yaml @@ -18,7 +18,6 @@ schema-parser: document-generator: specs: - oas3 - folderPath: . types: - RESTFUL extensions: diff --git a/packages/build/test/builder/builder.spec.ts b/packages/build/test/builder/builder.spec.ts index 01d511f3..dfa5dba1 100644 --- a/packages/build/test/builder/builder.spec.ts +++ b/packages/build/test/builder/builder.spec.ts @@ -17,7 +17,6 @@ it('Builder.build should work', async () => { }, document: { specs: [DocumentSpec.oas3], - folderPath: path.resolve(__dirname), }, artifact: { provider: ArtifactBuilderProviderType.LocalFile, diff --git a/packages/build/test/document-generator/document-generator.spec.ts b/packages/build/test/document-generator/document-generator.spec.ts index e4fd650e..1af9d351 100644 --- a/packages/build/test/document-generator/document-generator.spec.ts +++ b/packages/build/test/document-generator/document-generator.spec.ts @@ -20,7 +20,6 @@ it('Document generator should write YAML files while generating documents', asyn }, { specs: ['spec1', 'spec2'], - folderPath: __dirname, router: [], }, mockArtifactBuilder diff --git a/packages/cli/src/schemas/init/vulcan.yaml b/packages/cli/src/schemas/init/vulcan.yaml index f393ec28..c44a9e52 100644 --- a/packages/cli/src/schemas/init/vulcan.yaml +++ b/packages/cli/src/schemas/init/vulcan.yaml @@ -18,7 +18,6 @@ schema-parser: document-generator: specs: - oas3 - folderPath: . types: - RESTFUL enforce-https: diff --git a/packages/core/src/models/documentOptions.ts b/packages/core/src/models/documentOptions.ts index bc615407..96c08395 100644 --- a/packages/core/src/models/documentOptions.ts +++ b/packages/core/src/models/documentOptions.ts @@ -9,6 +9,5 @@ export enum DocumentRouterType { export interface IDocumentOptions { /** Target specification of our APIs, e.g. OpenAPI, Tinyspec ...etc. */ specs?: (string | DocumentSpec)[]; - folderPath?: string; router?: (string | DocumentRouterType)[]; } diff --git a/packages/core/src/options/document.ts b/packages/core/src/options/document.ts index b5ad679e..7d0e2604 100644 --- a/packages/core/src/options/document.ts +++ b/packages/core/src/options/document.ts @@ -1,5 +1,5 @@ import { injectable, inject, optional } from 'inversify'; -import { IsOptional, IsArray, validateSync, IsString } from 'class-validator'; +import { IsOptional, IsArray, validateSync } from 'class-validator'; import { DocumentRouterType, DocumentSpec, IDocumentOptions } from '../models'; import { TYPES } from '@vulcan-sql/core/types'; @@ -9,9 +9,6 @@ export class DocumentOptions implements IDocumentOptions { @IsOptional() public readonly specs: string[] = [DocumentSpec.oas3]; - @IsString() - public readonly folderPath: string = '.'; - @IsArray() public readonly router: string[] = [DocumentRouterType.redoc]; diff --git a/packages/core/test/options/documentOptions.spec.ts b/packages/core/test/options/documentOptions.spec.ts index 6e145f99..9e269c2e 100644 --- a/packages/core/test/options/documentOptions.spec.ts +++ b/packages/core/test/options/documentOptions.spec.ts @@ -18,7 +18,6 @@ it('Should provide correct default option values', async () => { // Action const options = container.get(TYPES.DocumentOptions); // Assert - expect(options.folderPath).toBe('.'); expect(options.specs).toEqual([DocumentSpec.oas3]); expect(options.router).toEqual([DocumentRouterType.redoc]); }); @@ -28,19 +27,18 @@ it('Can override some option properties', async () => { container .bind>(TYPES.DocumentInputOptions) .toConstantValue({ - folderPath: './some/folder', + specs: ['oas4'], }); const options = container.get(TYPES.DocumentOptions); // Assert - expect(options.folderPath).toBe('./some/folder'); - expect(options.specs).toEqual([DocumentSpec.oas3]); + expect(options.specs).toEqual(['oas4']); expect(options.router).toEqual([DocumentRouterType.redoc]); }); it('Schema validation should work', async () => { // Arrange container.bind(TYPES.DocumentInputOptions).toConstantValue({ - folderPath: true, + specs: true, }); // Act. Assert expect(() => container.get(TYPES.DocumentOptions)).toThrow(); diff --git a/packages/integration-testing/src/example1/projectConfig.ts b/packages/integration-testing/src/example1/projectConfig.ts index c491d136..879dfed0 100644 --- a/packages/integration-testing/src/example1/projectConfig.ts +++ b/packages/integration-testing/src/example1/projectConfig.ts @@ -28,7 +28,6 @@ export default { }, document: { specs: [DocumentSpec.oas3], - folderPath: __dirname, }, types: [APIProviderType.RESTFUL], profiles: [path.resolve(__dirname, 'profile.yaml')], From ea3bfab991aa7ea9deef0d816f49da8d849a4fde Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 22 Sep 2022 23:43:44 +0800 Subject: [PATCH 4/8] feat(build): add packager extension and implement node packager --- packages/build/project.json | 5 +- packages/build/src/containers/container.ts | 2 + .../build/src/containers/modules/extension.ts | 4 ++ .../build/src/containers/modules/index.ts | 1 + .../build/src/containers/modules/packager.ts | 11 +++++ packages/build/src/containers/types.ts | 3 ++ packages/build/src/lib/packagers/index.ts | 5 ++ .../build/src/lib/packagers/nodePackager.ts | 49 +++++++++++++++++++ packages/build/src/lib/vulcanBuilder.ts | 20 +++++++- packages/build/src/models/extensions/index.ts | 1 + .../extensions/packager/assets/entry.js | 7 +++ .../src/models/extensions/packager/index.ts | 1 + .../models/extensions/packager/packager.ts | 28 +++++++++++ 13 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 packages/build/src/containers/modules/packager.ts create mode 100644 packages/build/src/lib/packagers/index.ts create mode 100644 packages/build/src/lib/packagers/nodePackager.ts create mode 100644 packages/build/src/models/extensions/packager/assets/entry.js create mode 100644 packages/build/src/models/extensions/packager/index.ts create mode 100644 packages/build/src/models/extensions/packager/packager.ts diff --git a/packages/build/project.json b/packages/build/project.json index ed28adf9..ee35c80d 100644 --- a/packages/build/project.json +++ b/packages/build/project.json @@ -21,7 +21,10 @@ "outputPath": "dist/packages/build", "main": "packages/build/src/index.ts", "tsConfig": "packages/build/tsconfig.lib.json", - "assets": ["packages/build/*.md"], + "assets": [ + "packages/build/*.md", + "packages/build/src/models/extensions/packager/assets/**/*" + ], "buildableProjectDepsInPackageJsonType": "dependencies" }, "dependsOn": [ diff --git a/packages/build/src/containers/container.ts b/packages/build/src/containers/container.ts index 81b75c0d..18d8a996 100644 --- a/packages/build/src/containers/container.ts +++ b/packages/build/src/containers/container.ts @@ -4,6 +4,7 @@ import { IBuildOptions } from '@vulcan-sql/build/models'; import { documentGeneratorModule, extensionModule, + packagerModule, schemaParserModule, } from './modules'; @@ -32,6 +33,7 @@ export class Container { ); await this.inversifyContainer.loadAsync(extensionModule(options)); await this.inversifyContainer.loadAsync(documentGeneratorModule()); + await this.inversifyContainer.loadAsync(packagerModule()); } public async unload() { diff --git a/packages/build/src/containers/modules/extension.ts b/packages/build/src/containers/modules/extension.ts index fc00cf7e..f74acd36 100644 --- a/packages/build/src/containers/modules/extension.ts +++ b/packages/build/src/containers/modules/extension.ts @@ -2,6 +2,7 @@ import { builtInSchemaReader } from '@vulcan-sql/build/schema-parser'; import { ExtensionLoader } from '@vulcan-sql/core'; import { AsyncContainerModule } from 'inversify'; import { builtInSpecGenerator } from '../../lib/document-generator'; +import { builtInPackager } from '../../lib/packagers'; import { IBuildOptions } from '../../models/buildOptions'; export const extensionModule = (options: IBuildOptions) => @@ -15,5 +16,8 @@ export const extensionModule = (options: IBuildOptions) => // Spec generator loader.loadInternalExtensionModule(builtInSpecGenerator); + // Packager + loader.loadInternalExtensionModule(builtInPackager); + loader.bindExtensions(bind); }); diff --git a/packages/build/src/containers/modules/index.ts b/packages/build/src/containers/modules/index.ts index 08532140..fabb3fed 100644 --- a/packages/build/src/containers/modules/index.ts +++ b/packages/build/src/containers/modules/index.ts @@ -1,3 +1,4 @@ export * from './schemaParser'; export * from './extension'; export * from './documentGenerator'; +export * from './packager'; diff --git a/packages/build/src/containers/modules/packager.ts b/packages/build/src/containers/modules/packager.ts new file mode 100644 index 00000000..738808ac --- /dev/null +++ b/packages/build/src/containers/modules/packager.ts @@ -0,0 +1,11 @@ +import { AsyncContainerModule, interfaces } from 'inversify'; +import { Packager } from '../../models/extensions'; +import { TYPES } from '../types'; + +export const packagerModule = () => + new AsyncContainerModule(async (bind) => { + // Document generator + bind>( + TYPES.Factory_Packager + ).toAutoNamedFactory(TYPES.Extension_Packager); + }); diff --git a/packages/build/src/containers/types.ts b/packages/build/src/containers/types.ts index a9ea01f3..9cd9f481 100644 --- a/packages/build/src/containers/types.ts +++ b/packages/build/src/containers/types.ts @@ -9,7 +9,10 @@ export const TYPES = { // Document DocumentGenerator: Symbol.for('DocumentGenerator'), Factory_SpecGenerator: Symbol.for('Factory_SpecGenerator'), + // Packager + Factory_Packager: Symbol.for('Factory_Packager'), // Extension Extension_SchemaReader: Symbol.for('Extension_SchemaReader'), Extension_SpecGenerator: Symbol.for('Extension_SpecGenerator'), + Extension_Packager: Symbol.for('Extension_Packager'), }; diff --git a/packages/build/src/lib/packagers/index.ts b/packages/build/src/lib/packagers/index.ts new file mode 100644 index 00000000..533f725f --- /dev/null +++ b/packages/build/src/lib/packagers/index.ts @@ -0,0 +1,5 @@ +import { NodePackager } from './nodePackager'; + +export * from './nodePackager'; + +export const builtInPackager = [NodePackager]; diff --git a/packages/build/src/lib/packagers/nodePackager.ts b/packages/build/src/lib/packagers/nodePackager.ts new file mode 100644 index 00000000..87c04f21 --- /dev/null +++ b/packages/build/src/lib/packagers/nodePackager.ts @@ -0,0 +1,49 @@ +import { + ArtifactBuilderProviderType, + VulcanExtensionId, + VulcanInternalExtension, +} from '@vulcan-sql/core'; +import { IBuildOptions } from '../../models/buildOptions'; +import { Packager, PackagerType } from '../../models/extensions'; +import * as path from 'path'; +import { promises as fs } from 'fs'; + +export interface NodePackagerConfig { + folderPath?: string; +} + +@VulcanExtensionId(PackagerType.Node) +@VulcanInternalExtension('node-packager') +export class NodePackager extends Packager { + public async package(option: IBuildOptions): Promise { + const { folderPath = 'dist' } = this.getConfig() || {}; + const distFolder = path.resolve(process.cwd(), folderPath); + await fs.rm(distFolder, { recursive: true, force: true }); + await fs.mkdir(distFolder, { recursive: true }); + // package.json + await fs.writeFile( + path.resolve(distFolder, 'package.json'), + JSON.stringify(await this.getPackageJson(), null, 4), + 'utf-8' + ); + // config.json (vulcan config) + await fs.writeFile( + path.resolve(distFolder, 'config.json'), + JSON.stringify(option), + 'utf-8' + ); + // entrypoint + await fs.writeFile( + path.resolve(distFolder, 'index.js'), + await this.getEntryJS(), + 'utf-8' + ); + // result.json + if (option.artifact.provider === ArtifactBuilderProviderType.LocalFile) { + await fs.copyFile( + path.resolve(process.cwd(), option.artifact.filePath), + path.resolve(distFolder, option.artifact.filePath) + ); + } + } +} diff --git a/packages/build/src/lib/vulcanBuilder.ts b/packages/build/src/lib/vulcanBuilder.ts index 96a0400f..4fe9ba48 100644 --- a/packages/build/src/lib/vulcanBuilder.ts +++ b/packages/build/src/lib/vulcanBuilder.ts @@ -1,4 +1,8 @@ -import { IBuildOptions } from '@vulcan-sql/build/models'; +import { + IBuildOptions, + Packager, + PackagerType, +} from '@vulcan-sql/build/models'; import { Container, TYPES } from '@vulcan-sql/build/containers'; import { SchemaParser } from '@vulcan-sql/build/schema-parser'; import { @@ -10,6 +14,7 @@ import { getLogger, } from '@vulcan-sql/core'; import { DocumentGenerator } from './document-generator'; +import { interfaces } from 'inversify'; const logger = getLogger({ scopeName: 'BUILD' }); @@ -19,7 +24,7 @@ export class VulcanBuilder { this.options = options; } - public async build() { + public async build(packagerName?: PackagerType | string) { const container = new Container(); await container.load(this.options); const schemaParser = container.get(TYPES.SchemaParser); @@ -50,6 +55,17 @@ export class VulcanBuilder { await documentGenerator.generateDocuments(schemas); await artifactBuilder.build(); + + // Package + if (packagerName) { + const packagerFactory = container.get< + interfaces.AutoNamedFactory + >(TYPES.Factory_Packager); + const packager = packagerFactory(packagerName); + await packager.activate(); + await packager.package(this.options); + } + await container.unload(); } } diff --git a/packages/build/src/models/extensions/index.ts b/packages/build/src/models/extensions/index.ts index b25ba372..0e536f8d 100644 --- a/packages/build/src/models/extensions/index.ts +++ b/packages/build/src/models/extensions/index.ts @@ -1,2 +1,3 @@ export * from './schemaReader'; export * from './specGenerator'; +export * from './packager'; diff --git a/packages/build/src/models/extensions/packager/assets/entry.js b/packages/build/src/models/extensions/packager/assets/entry.js new file mode 100644 index 00000000..e45060a9 --- /dev/null +++ b/packages/build/src/models/extensions/packager/assets/entry.js @@ -0,0 +1,7 @@ +const { VulcanServer } = require('@vulcan-sql/serve'); +const { getLogger } = require('@vulcan-sql/core'); +const logger = getLogger({ scopeName: 'CORE' }); +const fs = require('fs'); +const config = JSON.parse(fs.readFileSync('config.json', 'utf-8')); +const server = new VulcanServer(config); +server.start().then(() => logger.info('server started')); \ No newline at end of file diff --git a/packages/build/src/models/extensions/packager/index.ts b/packages/build/src/models/extensions/packager/index.ts new file mode 100644 index 00000000..86e1fe2d --- /dev/null +++ b/packages/build/src/models/extensions/packager/index.ts @@ -0,0 +1 @@ +export * from './packager'; diff --git a/packages/build/src/models/extensions/packager/packager.ts b/packages/build/src/models/extensions/packager/packager.ts new file mode 100644 index 00000000..c283ec6c --- /dev/null +++ b/packages/build/src/models/extensions/packager/packager.ts @@ -0,0 +1,28 @@ +import { ExtensionBase, VulcanExtension } from '@vulcan-sql/core'; +import { TYPES } from '../../../containers/types'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { IBuildOptions } from '../../buildOptions'; + +export enum PackagerType { + Node = 'node', +} + +@VulcanExtension(TYPES.Extension_Packager, { enforcedId: true }) +export abstract class Packager extends ExtensionBase { + abstract package(options: IBuildOptions): Promise; + + protected async getPackageJson() { + const packageJson: Record = {}; + const projectPackageJson = JSON.parse( + await fs.readFile(path.resolve(process.cwd(), 'package.json'), 'utf-8') + ); + packageJson['dependencies'] = projectPackageJson['dependencies']; + packageJson['main'] = 'index.js'; + return packageJson; + } + + protected async getEntryJS() { + return fs.readFile(path.resolve(__dirname, 'assets', 'entry.js'), 'utf-8'); + } +} From c669a0e8ee6a1fa6b485d400e6929c5b8840c65e Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Thu, 22 Sep 2022 23:44:05 +0800 Subject: [PATCH 5/8] feat(cli): add vulcan package command --- labs/playground1/.gitignore | 3 +- packages/cli/src/cli.ts | 24 +++++++++++++-- packages/cli/src/commands/index.ts | 3 ++ packages/cli/src/commands/package.ts | 46 ++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 packages/cli/src/commands/package.ts diff --git a/labs/playground1/.gitignore b/labs/playground1/.gitignore index 886829bd..d05c459f 100644 --- a/labs/playground1/.gitignore +++ b/labs/playground1/.gitignore @@ -1,4 +1,5 @@ node_modules result.json spec-oas3.yaml -.vulcan-debug \ No newline at end of file +.vulcan-debug +dist \ No newline at end of file diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 83ac1631..4f70d878 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -1,7 +1,12 @@ import { program } from 'commander'; -import { handleInit, handleStart, handleVersion } from './commands'; -import { handleBuild } from './commands/build'; -import { handleServe } from './commands/serve'; +import { + handleInit, + handlePackage, + handleStart, + handleVersion, + handleBuild, + handleServe, +} from './commands'; program.exitOverride(); @@ -60,4 +65,17 @@ program await handleStart(options); }); +program + .command('package') + .description('package Vulcan project for production environments') + .option( + '-c --config ', + 'path to Vulcan config file', + './vulcan.yaml' + ) + .option('-o --output ', 'package output type', 'node') + .action(async (options) => { + await handlePackage(options); + }); + export { program }; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index f9f1cf5a..07ad64e6 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -1,3 +1,6 @@ export * from './init'; export * from './start'; export * from './version'; +export * from './build'; +export * from './package'; +export * from './serve'; diff --git a/packages/cli/src/commands/package.ts b/packages/cli/src/commands/package.ts new file mode 100644 index 00000000..929a2717 --- /dev/null +++ b/packages/cli/src/commands/package.ts @@ -0,0 +1,46 @@ +import * as jsYAML from 'js-yaml'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import * as ora from 'ora'; +import { localModulePath } from '../utils'; + +export interface PackageCommandOptions { + config: string; + output: string; +} + +const defaultOptions: PackageCommandOptions = { + config: './vulcan.yaml', + output: 'node', +}; + +export const packageVulcan = async (options: PackageCommandOptions) => { + const configPath = path.resolve(process.cwd(), options.config); + const config: any = jsYAML.load(await fs.readFile(configPath, 'utf-8')); + + // Import dependencies. We use dynamic import here to import dependencies at runtime. + const { VulcanBuilder } = await import(localModulePath('@vulcan-sql/build')); + + // Build project + const spinner = ora('Packaging project...').start(); + try { + const builder = new VulcanBuilder(config); + await builder.build(options.output); + spinner.succeed('Package successfully.'); + } catch (e) { + spinner.fail(); + throw e; + } finally { + spinner.stop(); + } +}; + +export const handlePackage = async ( + options: Partial +): Promise => { + options = { + ...defaultOptions, + ...options, + }; + await packageVulcan(options as PackageCommandOptions); +}; From 2edcaf373174e509ed92c41156e265409a345ded Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 23 Sep 2022 10:55:31 +0800 Subject: [PATCH 6/8] test(build): add test cases for node packager --- .../build/src/containers/modules/extension.ts | 2 +- .../src/lib/{packagers => packager}/index.ts | 0 .../{packagers => packager}/nodePackager.ts | 5 +++- packages/build/test/builder/.gitignore | 3 ++- packages/build/test/builder/builder.spec.ts | 15 +++++++---- packages/build/test/builder/package.json | 5 ++++ packages/build/test/packager/.gitignore | 1 + .../build/test/packager/nodePackager.spec.ts | 25 +++++++++++++++++++ packages/build/test/packager/package.json | 5 ++++ packages/build/test/packager/result.json | 1 + 10 files changed, 54 insertions(+), 8 deletions(-) rename packages/build/src/lib/{packagers => packager}/index.ts (100%) rename packages/build/src/lib/{packagers => packager}/nodePackager.ts (92%) create mode 100644 packages/build/test/builder/package.json create mode 100644 packages/build/test/packager/.gitignore create mode 100644 packages/build/test/packager/nodePackager.spec.ts create mode 100644 packages/build/test/packager/package.json create mode 100644 packages/build/test/packager/result.json diff --git a/packages/build/src/containers/modules/extension.ts b/packages/build/src/containers/modules/extension.ts index f74acd36..f5e882d8 100644 --- a/packages/build/src/containers/modules/extension.ts +++ b/packages/build/src/containers/modules/extension.ts @@ -2,7 +2,7 @@ import { builtInSchemaReader } from '@vulcan-sql/build/schema-parser'; import { ExtensionLoader } from '@vulcan-sql/core'; import { AsyncContainerModule } from 'inversify'; import { builtInSpecGenerator } from '../../lib/document-generator'; -import { builtInPackager } from '../../lib/packagers'; +import { builtInPackager } from '../../lib/packager'; import { IBuildOptions } from '../../models/buildOptions'; export const extensionModule = (options: IBuildOptions) => diff --git a/packages/build/src/lib/packagers/index.ts b/packages/build/src/lib/packager/index.ts similarity index 100% rename from packages/build/src/lib/packagers/index.ts rename to packages/build/src/lib/packager/index.ts diff --git a/packages/build/src/lib/packagers/nodePackager.ts b/packages/build/src/lib/packager/nodePackager.ts similarity index 92% rename from packages/build/src/lib/packagers/nodePackager.ts rename to packages/build/src/lib/packager/nodePackager.ts index 87c04f21..7ebe59c5 100644 --- a/packages/build/src/lib/packagers/nodePackager.ts +++ b/packages/build/src/lib/packager/nodePackager.ts @@ -39,7 +39,10 @@ export class NodePackager extends Packager { 'utf-8' ); // result.json - if (option.artifact.provider === ArtifactBuilderProviderType.LocalFile) { + if ( + option.artifact.provider === ArtifactBuilderProviderType.LocalFile && + option.artifact.filePath + ) { await fs.copyFile( path.resolve(process.cwd(), option.artifact.filePath), path.resolve(distFolder, option.artifact.filePath) diff --git a/packages/build/test/builder/.gitignore b/packages/build/test/builder/.gitignore index 860deb70..7c98324f 100644 --- a/packages/build/test/builder/.gitignore +++ b/packages/build/test/builder/.gitignore @@ -1,2 +1,3 @@ result.json -spec-*.yaml \ No newline at end of file +spec-*.yaml +dist \ No newline at end of file diff --git a/packages/build/test/builder/builder.spec.ts b/packages/build/test/builder/builder.spec.ts index dfa5dba1..d7db956d 100644 --- a/packages/build/test/builder/builder.spec.ts +++ b/packages/build/test/builder/builder.spec.ts @@ -1,6 +1,10 @@ import { VulcanBuilder } from '../../src'; import * as path from 'path'; -import { IBuildOptions, SchemaReaderType } from '@vulcan-sql/build/models'; +import { + IBuildOptions, + PackagerType, + SchemaReaderType, +} from '@vulcan-sql/build/models'; import { ArtifactBuilderProviderType, ArtifactBuilderSerializerType, @@ -10,10 +14,11 @@ import { it('Builder.build should work', async () => { // Arrange + process.chdir(__dirname); const options: IBuildOptions = { 'schema-parser': { reader: SchemaReaderType.LocalFile, - folderPath: path.resolve(__dirname, 'source'), + folderPath: 'source', }, document: { specs: [DocumentSpec.oas3], @@ -21,16 +26,16 @@ it('Builder.build should work', async () => { artifact: { provider: ArtifactBuilderProviderType.LocalFile, serializer: ArtifactBuilderSerializerType.JSON, - filePath: path.resolve(__dirname, 'result.json'), + filePath: 'result.json', }, template: { provider: TemplateProviderType.LocalFile, - folderPath: path.resolve(__dirname, 'source'), + folderPath: 'source', }, profiles: [path.resolve(__dirname, 'profile.yaml')], }; const builder = new VulcanBuilder(options); // Act, Assert - await expect(builder.build()).resolves.not.toThrow(); + await expect(builder.build(PackagerType.Node)).resolves.not.toThrow(); }); diff --git a/packages/build/test/builder/package.json b/packages/build/test/builder/package.json new file mode 100644 index 00000000..2c475137 --- /dev/null +++ b/packages/build/test/builder/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "some-package": "1.2.3" + } +} diff --git a/packages/build/test/packager/.gitignore b/packages/build/test/packager/.gitignore new file mode 100644 index 00000000..53c37a16 --- /dev/null +++ b/packages/build/test/packager/.gitignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/packages/build/test/packager/nodePackager.spec.ts b/packages/build/test/packager/nodePackager.spec.ts new file mode 100644 index 00000000..39ebdb21 --- /dev/null +++ b/packages/build/test/packager/nodePackager.spec.ts @@ -0,0 +1,25 @@ +import { ArtifactBuilderProviderType } from '@vulcan-sql/core'; +import { NodePackager } from '../../src/lib/packager'; +import * as fs from 'fs'; + +it('NodePackager should create package.json, config.json, index.js, and result.json', async () => { + // Arrange + process.chdir(__dirname); + const nodePackager = new NodePackager( + { + folderPath: 'dist', + }, + '' + ); + const options: any = { + artifact: { + provider: ArtifactBuilderProviderType.LocalFile, + filePath: 'result.json', + }, + }; + // Act + await nodePackager.package(options); + const result = fs.readdirSync('dist'); + // Assert + expect(result.length).toBe(4); +}); diff --git a/packages/build/test/packager/package.json b/packages/build/test/packager/package.json new file mode 100644 index 00000000..2c475137 --- /dev/null +++ b/packages/build/test/packager/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "some-package": "1.2.3" + } +} diff --git a/packages/build/test/packager/result.json b/packages/build/test/packager/result.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/build/test/packager/result.json @@ -0,0 +1 @@ +{} From d97e4d976f5fa916d12a8de216d4afc7a3a8db7a Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 23 Sep 2022 11:00:42 +0800 Subject: [PATCH 7/8] test(cli): add test cases for package command --- packages/cli/test/cli.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/cli/test/cli.spec.ts b/packages/cli/test/cli.spec.ts index d6ab905d..3bcdbf40 100644 --- a/packages/cli/test/cli.spec.ts +++ b/packages/cli/test/cli.spec.ts @@ -89,3 +89,13 @@ it('Version command should execute without error', async () => { program.parseAsync(['node', 'vulcan', 'version']) ).resolves.not.toThrow(); }); + +// TODO: We can't test package result until the new version of build package release +it('Package command should make result.json', async () => { + // Action + await program.parseAsync(['node', 'vulcan', 'package']); + // Assert + expect( + fs.readFile(path.resolve(projectRoot, 'result.json'), 'utf-8') + ).resolves.not.toThrow(); +}); From 406ebeda654c2f7574a5e37cbc9d14a1f57fd725 Mon Sep 17 00:00:00 2001 From: Ivan Tsai <9553914+oscar60310@users.noreply.github.com> Date: Fri, 23 Sep 2022 11:42:07 +0800 Subject: [PATCH 8/8] feat(build): add docker packager --- labs/playground1/Makefile | 6 +- labs/playground1/package.json | 8 +- labs/playground1/yarn.lock | 607 ------------------ packages/build/project.json | 3 +- .../packager/dockerPackager/assets/Dockerfile | 19 + .../packager/dockerPackager/dockerPackager.ts | 62 ++ .../src/lib/packager/dockerPackager/index.ts | 1 + packages/build/src/lib/packager/index.ts | 4 +- .../build/src/lib/packager/nodePackager.ts | 5 + .../models/extensions/packager/packager.ts | 1 + packages/build/test/packager/.gitignore | 2 +- .../test/packager/dockerPackager.spec.ts | 25 + .../build/test/packager/nodePackager.spec.ts | 4 +- 13 files changed, 128 insertions(+), 619 deletions(-) delete mode 100644 labs/playground1/yarn.lock create mode 100644 packages/build/src/lib/packager/dockerPackager/assets/Dockerfile create mode 100644 packages/build/src/lib/packager/dockerPackager/dockerPackager.ts create mode 100644 packages/build/src/lib/packager/dockerPackager/index.ts create mode 100644 packages/build/test/packager/dockerPackager.spec.ts diff --git a/labs/playground1/Makefile b/labs/playground1/Makefile index 491be631..8de7d896 100644 --- a/labs/playground1/Makefile +++ b/labs/playground1/Makefile @@ -2,13 +2,9 @@ .PHONY: install build start pkg-core pkg-build pkg-serve pkg-cli clean pkg-extension-driver-duckdb # start vulcan server (default goal) -start: install build test-data/moma.db ../../node_modules +start: build test-data/moma.db ../../node_modules @vulcan start -# install node modules and like playground extensions -install: - @yarn; - # build the required packages build: pkg-core pkg-build pkg-serve pkg-cli pkg-extension-driver-duckdb diff --git a/labs/playground1/package.json b/labs/playground1/package.json index 382eff41..7dae4729 100644 --- a/labs/playground1/package.json +++ b/labs/playground1/package.json @@ -1,7 +1,11 @@ { "name": "my-first-vulcan-project", "dependencies": { - "lodash": "^4.17.21", - "redoc": "^2.0.0-rc.76" + "@vulcan-sql/core": "0.2.0-dev.20220922.0", + "@vulcan-sql/extension-driver-duckdb": "0.2.0-dev.20220919.0", + "@vulcan-sql/serve": "0.2.0-dev.20220922.0" + }, + "devDependencies": { + "@vulcan-sql/build": "0.2.0-dev.20220922.0" } } diff --git a/labs/playground1/yarn.lock b/labs/playground1/yarn.lock deleted file mode 100644 index cd588d81..00000000 --- a/labs/playground1/yarn.lock +++ /dev/null @@ -1,607 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/runtime@^7.17.8": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - -"@exodus/schemasafe@^1.0.0-rc.2": - version "1.0.0-rc.7" - resolved "https://registry.yarnpkg.com/@exodus/schemasafe/-/schemasafe-1.0.0-rc.7.tgz#aded6839c2369883dafa46608a135c82b42ed76b" - integrity sha512-+1mBLsa+vvlV0lwEAP1hwgmOPkjMnoJ8hyCMfCCJga0sVDwDzrPJjnxZwdDaUmOh/vbFHQGBTk+FxsVjoI/CjQ== - -"@redocly/ajv@^8.6.5": - version "8.6.5" - resolved "https://registry.yarnpkg.com/@redocly/ajv/-/ajv-8.6.5.tgz#b6e737248b791905b3f600fb329779a807f0f774" - integrity sha512-3P2TY/u4c6OBqkP+1cTH1iGAEv0O34PV3vV2Wnos/nNHu62OTrtC4zcaxttG0pHtPtn42StrhGq7SsiFgP4Bfw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -"@redocly/openapi-core@^1.0.0-beta.104": - version "1.0.0-beta.108" - resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.0.0-beta.108.tgz#fbf1b4e31c148f8816d2d63aa37b7831e305ec0f" - integrity sha512-4Lq7KB+XiBvVzpaY/M0a8qog/Zr8kGrvJbRW2z7Sk2Zpc/m+8LTuZbRh15eMoneVc13M9qbHFIRh3PG18g3Tng== - dependencies: - "@redocly/ajv" "^8.6.5" - "@types/node" "^14.11.8" - colorette "^1.2.0" - js-levenshtein "^1.1.6" - js-yaml "^4.1.0" - lodash.isequal "^4.5.0" - minimatch "^5.0.1" - node-fetch "^2.6.1" - pluralize "^8.0.0" - yaml-ast-parser "0.0.43" - -"@types/json-schema@^7.0.7": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== - -"@types/node@^14.11.8": - version "14.18.26" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.26.tgz#239e19f8b4ea1a9eb710528061c1d733dc561996" - integrity sha512-0b+utRBSYj8L7XAp0d+DX7lI4cSmowNaaTkk6/1SKzbKkG+doLuPusB9EOvzLJ8ahJSk03bTLIL6cWaEd4dBKA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - integrity sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw== - -classnames@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -clsx@^1.1.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" - integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" - integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== - -decko@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decko/-/decko-1.2.0.tgz#fd43c735e967b8013306884a56fbe665996b6817" - integrity sha512-m8FnyHXV1QX+S1cl+KPFDIl6NMkxtKsy6+U/aYyjrOqWMuwAwYWu7ePqrsUHtDR5Y8Yk2pi/KIDSgF+vT4cPOQ== - -dompurify@^2.2.8: - version "2.3.12" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.12.tgz#0c8a5cabc5d7a7cf2635645cfd5061deab73f3d2" - integrity sha512-KkCEdpWPXBkF9IP9afmYPDB24nRZRUsThsjuWKM8Cpp9+WXKJmC6FZxSyPIiiQezkYa3+Mm1oo6dh60NxBXkyw== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -es6-promise@^3.2.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" - integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -eventemitter3@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-safe-stringify@^2.0.7: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" - integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== - -foreach@^2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e" - integrity sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -http2-client@^1.2.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/http2-client/-/http2-client-1.3.5.tgz#20c9dc909e3cc98284dd20af2432c524086df181" - integrity sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -js-levenshtein@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" - integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== - -"js-tokens@^3.0.0 || ^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-pointer@0.6.2, json-pointer@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.2.tgz#f97bd7550be5e9ea901f8c9264c9d436a22a93cd" - integrity sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw== - dependencies: - foreach "^2.0.4" - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lunr@^2.3.9: - version "2.3.9" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" - integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== - -mark.js@^8.11.1: - version "8.11.1" - resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5" - integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== - -marked@^4.0.15: - version "4.0.19" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.19.tgz#d36198d1ac1255525153c351c68c75bc1d7aee46" - integrity sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ== - -minimatch@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== - dependencies: - brace-expansion "^2.0.1" - -mobx-react-lite@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.4.0.tgz#d59156a96889cdadad751e5e4dab95f28926dfff" - integrity sha512-bRuZp3C0itgLKHu/VNxi66DN/XVkQG7xtoBVWxpvC5FhAqbOCP21+nPhULjnzEqd7xBMybp6KwytdUpZKEgpIQ== - -mobx-react@^7.2.0: - version "7.5.2" - resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-7.5.2.tgz#362d6dc7271698caf3b56229c3c68fb0b30e682e" - integrity sha512-NP44ONwSqTy+3KlD7y9k7xbsuGD+8mgUj3IeI65SbxF1IOB42/j9TbosgUEDn//CCuU6OmQ7k9oiu9eSpRBHnw== - dependencies: - mobx-react-lite "^3.4.0" - -node-fetch-h2@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz#c6188325f9bd3d834020bf0f2d6dc17ced2241ac" - integrity sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg== - dependencies: - http2-client "^1.2.5" - -node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-readfiles@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/node-readfiles/-/node-readfiles-0.2.0.tgz#dbbd4af12134e2e635c245ef93ffcf6f60673a5d" - integrity sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA== - dependencies: - es6-promise "^3.2.1" - -oas-kit-common@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/oas-kit-common/-/oas-kit-common-1.0.8.tgz#6d8cacf6e9097967a4c7ea8bcbcbd77018e1f535" - integrity sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ== - dependencies: - fast-safe-stringify "^2.0.7" - -oas-linter@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/oas-linter/-/oas-linter-3.2.2.tgz#ab6a33736313490659035ca6802dc4b35d48aa1e" - integrity sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ== - dependencies: - "@exodus/schemasafe" "^1.0.0-rc.2" - should "^13.2.1" - yaml "^1.10.0" - -oas-resolver@^2.5.6: - version "2.5.6" - resolved "https://registry.yarnpkg.com/oas-resolver/-/oas-resolver-2.5.6.tgz#10430569cb7daca56115c915e611ebc5515c561b" - integrity sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ== - dependencies: - node-fetch-h2 "^2.3.0" - oas-kit-common "^1.0.8" - reftools "^1.1.9" - yaml "^1.10.0" - yargs "^17.0.1" - -oas-schema-walker@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz#74c3cd47b70ff8e0b19adada14455b5d3ac38a22" - integrity sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ== - -oas-validator@^5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/oas-validator/-/oas-validator-5.0.8.tgz#387e90df7cafa2d3ffc83b5fb976052b87e73c28" - integrity sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw== - dependencies: - call-me-maybe "^1.0.1" - oas-kit-common "^1.0.8" - oas-linter "^3.2.2" - oas-resolver "^2.5.6" - oas-schema-walker "^1.1.5" - reftools "^1.1.9" - should "^13.2.1" - yaml "^1.10.0" - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -openapi-sampler@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/openapi-sampler/-/openapi-sampler-1.3.0.tgz#5b99ceb4156b00d2aa3f860e52ccb768a5695793" - integrity sha512-2QfjK1oM9Sv0q82Ae1RrUe3yfFmAyjF548+6eAeb+h/cL1Uj51TW4UezraBEvwEdzoBgfo4AaTLVFGTKj+yYDw== - dependencies: - "@types/json-schema" "^7.0.7" - json-pointer "0.6.2" - -path-browserify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" - integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== - -perfect-scrollbar@^1.5.5: - version "1.5.5" - resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz#41a211a2fb52a7191eff301432134ea47052b27f" - integrity sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g== - -pluralize@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" - integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== - -polished@^4.1.3: - version "4.2.2" - resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" - integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ== - dependencies: - "@babel/runtime" "^7.17.8" - -prismjs@^1.27.0: - version "1.29.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" - integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== - -prop-types@^15.5.0, prop-types@^15.7.2: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-tabs@^3.2.2: - version "3.2.3" - resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-3.2.3.tgz#ccbb3e1241ad3f601047305c75db661239977f2f" - integrity sha512-jx325RhRVnS9DdFbeF511z0T0WEqEoMl1uCE3LoZ6VaZZm7ytatxbum0B8bCTmaiV0KsU+4TtLGTGevCic7SWg== - dependencies: - clsx "^1.1.0" - prop-types "^15.5.0" - -redoc@^2.0.0-rc.76: - version "2.0.0-rc.76" - resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.0.0-rc.76.tgz#1d8644f8cd5be5bee1781fc6c4104a9a3abab124" - integrity sha512-7PbH/0GUaH9GoGjaCI8tfCvkzVQY3BGhiBX9lLgpJ6oGAhvioiac7kjTwltRxl5+sDr58AyyB/4XyTBEPqbupw== - dependencies: - "@redocly/openapi-core" "^1.0.0-beta.104" - classnames "^2.3.1" - decko "^1.2.0" - dompurify "^2.2.8" - eventemitter3 "^4.0.7" - json-pointer "^0.6.2" - lunr "^2.3.9" - mark.js "^8.11.1" - marked "^4.0.15" - mobx-react "^7.2.0" - openapi-sampler "^1.3.0" - path-browserify "^1.0.1" - perfect-scrollbar "^1.5.5" - polished "^4.1.3" - prismjs "^1.27.0" - prop-types "^15.7.2" - react-tabs "^3.2.2" - slugify "~1.4.7" - stickyfill "^1.1.1" - style-loader "^3.3.1" - swagger2openapi "^7.0.6" - url-template "^2.0.8" - -reftools@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/reftools/-/reftools-1.1.9.tgz#e16e19f662ccd4648605312c06d34e5da3a2b77e" - integrity sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w== - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -should-equal@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" - integrity sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA== - dependencies: - should-type "^1.4.0" - -should-format@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/should-format/-/should-format-3.0.3.tgz#9bfc8f74fa39205c53d38c34d717303e277124f1" - integrity sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q== - dependencies: - should-type "^1.3.0" - should-type-adaptors "^1.0.1" - -should-type-adaptors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz#401e7f33b5533033944d5cd8bf2b65027792e27a" - integrity sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA== - dependencies: - should-type "^1.3.0" - should-util "^1.0.0" - -should-type@^1.3.0, should-type@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/should-type/-/should-type-1.4.0.tgz#0756d8ce846dfd09843a6947719dfa0d4cff5cf3" - integrity sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ== - -should-util@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.1.tgz#fb0d71338f532a3a149213639e2d32cbea8bcb28" - integrity sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g== - -should@^13.2.1: - version "13.2.3" - resolved "https://registry.yarnpkg.com/should/-/should-13.2.3.tgz#96d8e5acf3e97b49d89b51feaa5ae8d07ef58f10" - integrity sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ== - dependencies: - should-equal "^2.0.0" - should-format "^3.0.3" - should-type "^1.4.0" - should-type-adaptors "^1.0.1" - should-util "^1.0.0" - -slugify@~1.4.7: - version "1.4.7" - resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.7.tgz#e42359d505afd84a44513280868e31202a79a628" - integrity sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg== - -stickyfill@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stickyfill/-/stickyfill-1.1.1.tgz#39413fee9d025c74a7e59ceecb23784cc0f17f02" - integrity sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA== - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -style-loader@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575" - integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ== - -swagger2openapi@^7.0.6: - version "7.0.8" - resolved "https://registry.yarnpkg.com/swagger2openapi/-/swagger2openapi-7.0.8.tgz#12c88d5de776cb1cbba758994930f40ad0afac59" - integrity sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g== - dependencies: - call-me-maybe "^1.0.1" - node-fetch "^2.6.1" - node-fetch-h2 "^2.3.0" - node-readfiles "^0.2.0" - oas-kit-common "^1.0.8" - oas-resolver "^2.5.6" - oas-schema-walker "^1.1.5" - oas-validator "^5.0.8" - reftools "^1.1.9" - yaml "^1.10.0" - yargs "^17.0.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -url-template@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" - integrity sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yaml-ast-parser@0.0.43: - version "0.0.43" - resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" - integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== - -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yargs-parser@^21.0.0: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^17.0.1: - version "17.5.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" - integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.0.0" diff --git a/packages/build/project.json b/packages/build/project.json index ee35c80d..2f75f578 100644 --- a/packages/build/project.json +++ b/packages/build/project.json @@ -23,7 +23,8 @@ "tsConfig": "packages/build/tsconfig.lib.json", "assets": [ "packages/build/*.md", - "packages/build/src/models/extensions/packager/assets/**/*" + "packages/build/src/models/extensions/packager/assets/**/*", + "packages/build/src/lib/packager/dockerPackager/assets/**/*" ], "buildableProjectDepsInPackageJsonType": "dependencies" }, diff --git a/packages/build/src/lib/packager/dockerPackager/assets/Dockerfile b/packages/build/src/lib/packager/dockerPackager/assets/Dockerfile new file mode 100644 index 00000000..32d66298 --- /dev/null +++ b/packages/build/src/lib/packager/dockerPackager/assets/Dockerfile @@ -0,0 +1,19 @@ +# Use bullseye for glibc > 2.29 +FROM node:16-bullseye AS build + +# create app directory +WORKDIR /usr/app + +# install dependencies +COPY package.json . +RUN npm install --omit=dev + +FROM node:16-bullseye-slim +WORKDIR /usr/app +COPY --from=build /usr/app /usr/app +COPY config.json . +COPY index.js . +COPY result.json . +ENV NODE_ENV production + +CMD [ "node", "index.js" ] \ No newline at end of file diff --git a/packages/build/src/lib/packager/dockerPackager/dockerPackager.ts b/packages/build/src/lib/packager/dockerPackager/dockerPackager.ts new file mode 100644 index 00000000..3b6a0dd0 --- /dev/null +++ b/packages/build/src/lib/packager/dockerPackager/dockerPackager.ts @@ -0,0 +1,62 @@ +import { + ArtifactBuilderProviderType, + VulcanExtensionId, + VulcanInternalExtension, +} from '@vulcan-sql/core'; +import { IBuildOptions } from '../../../models/buildOptions'; +import { Packager, PackagerType } from '../../../models/extensions'; +import * as path from 'path'; +import { promises as fs } from 'fs'; + +export interface DockerPackagerConfig { + folderPath?: string; +} + +@VulcanExtensionId(PackagerType.Docker) +@VulcanInternalExtension('docker-packager') +export class DockerPackager extends Packager { + private logger = this.getLogger(); + + public async package(option: IBuildOptions): Promise { + const { folderPath = 'dist' } = this.getConfig() || {}; + const distFolder = path.resolve(process.cwd(), folderPath); + await fs.rm(distFolder, { recursive: true, force: true }); + await fs.mkdir(distFolder, { recursive: true }); + // package.json + await fs.writeFile( + path.resolve(distFolder, 'package.json'), + JSON.stringify(await this.getPackageJson(), null, 4), + 'utf-8' + ); + // config.json (vulcan config) + await fs.writeFile( + path.resolve(distFolder, 'config.json'), + JSON.stringify(option), + 'utf-8' + ); + // entrypoint + await fs.writeFile( + path.resolve(distFolder, 'index.js'), + await this.getEntryJS(), + 'utf-8' + ); + // result.json + if ( + option.artifact.provider === ArtifactBuilderProviderType.LocalFile && + option.artifact.filePath + ) { + await fs.copyFile( + path.resolve(process.cwd(), option.artifact.filePath), + path.resolve(distFolder, option.artifact.filePath) + ); + } + // Dockerfile + await fs.copyFile( + path.resolve(__dirname, 'assets', 'Dockerfile'), + path.resolve(distFolder, 'Dockerfile') + ); + this.logger.info( + `Package successfully, you can go to "${folderPath}" folder and run "docker build ." to build the image.` + ); + } +} diff --git a/packages/build/src/lib/packager/dockerPackager/index.ts b/packages/build/src/lib/packager/dockerPackager/index.ts new file mode 100644 index 00000000..3f21bcb6 --- /dev/null +++ b/packages/build/src/lib/packager/dockerPackager/index.ts @@ -0,0 +1 @@ +export * from './dockerPackager'; diff --git a/packages/build/src/lib/packager/index.ts b/packages/build/src/lib/packager/index.ts index 533f725f..6292b8d2 100644 --- a/packages/build/src/lib/packager/index.ts +++ b/packages/build/src/lib/packager/index.ts @@ -1,5 +1,7 @@ +import { DockerPackager } from './dockerPackager'; import { NodePackager } from './nodePackager'; export * from './nodePackager'; +export * from './dockerPackager'; -export const builtInPackager = [NodePackager]; +export const builtInPackager = [NodePackager, DockerPackager]; diff --git a/packages/build/src/lib/packager/nodePackager.ts b/packages/build/src/lib/packager/nodePackager.ts index 7ebe59c5..dd01d747 100644 --- a/packages/build/src/lib/packager/nodePackager.ts +++ b/packages/build/src/lib/packager/nodePackager.ts @@ -15,6 +15,8 @@ export interface NodePackagerConfig { @VulcanExtensionId(PackagerType.Node) @VulcanInternalExtension('node-packager') export class NodePackager extends Packager { + private logger = this.getLogger(); + public async package(option: IBuildOptions): Promise { const { folderPath = 'dist' } = this.getConfig() || {}; const distFolder = path.resolve(process.cwd(), folderPath); @@ -48,5 +50,8 @@ export class NodePackager extends Packager { path.resolve(distFolder, option.artifact.filePath) ); } + this.logger.info( + `Package successfully, you can go to "${folderPath}" folder and run "npm install && node index.js" to start the server` + ); } } diff --git a/packages/build/src/models/extensions/packager/packager.ts b/packages/build/src/models/extensions/packager/packager.ts index c283ec6c..62672774 100644 --- a/packages/build/src/models/extensions/packager/packager.ts +++ b/packages/build/src/models/extensions/packager/packager.ts @@ -6,6 +6,7 @@ import { IBuildOptions } from '../../buildOptions'; export enum PackagerType { Node = 'node', + Docker = 'docker', } @VulcanExtension(TYPES.Extension_Packager, { enforcedId: true }) diff --git a/packages/build/test/packager/.gitignore b/packages/build/test/packager/.gitignore index 53c37a16..84b7166a 100644 --- a/packages/build/test/packager/.gitignore +++ b/packages/build/test/packager/.gitignore @@ -1 +1 @@ -dist \ No newline at end of file +dist-* \ No newline at end of file diff --git a/packages/build/test/packager/dockerPackager.spec.ts b/packages/build/test/packager/dockerPackager.spec.ts new file mode 100644 index 00000000..dae975dd --- /dev/null +++ b/packages/build/test/packager/dockerPackager.spec.ts @@ -0,0 +1,25 @@ +import { ArtifactBuilderProviderType } from '@vulcan-sql/core'; +import { DockerPackager } from '../../src/lib/packager'; +import * as fs from 'fs'; + +it('DockerPackager should create package.json, config.json, index.js, result.json, and Dockerfile', async () => { + // Arrange + process.chdir(__dirname); + const dockerPackager = new DockerPackager( + { + folderPath: 'dist-docker', + }, + '' + ); + const options: any = { + artifact: { + provider: ArtifactBuilderProviderType.LocalFile, + filePath: 'result.json', + }, + }; + // Act + await dockerPackager.package(options); + const result = fs.readdirSync('dist-docker'); + // Assert + expect(result.length).toBe(5); +}); diff --git a/packages/build/test/packager/nodePackager.spec.ts b/packages/build/test/packager/nodePackager.spec.ts index 39ebdb21..1de46a1c 100644 --- a/packages/build/test/packager/nodePackager.spec.ts +++ b/packages/build/test/packager/nodePackager.spec.ts @@ -7,7 +7,7 @@ it('NodePackager should create package.json, config.json, index.js, and result.j process.chdir(__dirname); const nodePackager = new NodePackager( { - folderPath: 'dist', + folderPath: 'dist-node', }, '' ); @@ -19,7 +19,7 @@ it('NodePackager should create package.json, config.json, index.js, and result.j }; // Act await nodePackager.package(options); - const result = fs.readdirSync('dist'); + const result = fs.readdirSync('dist-node'); // Assert expect(result.length).toBe(4); });