import { OpenAPIV3 } from "openapi-types"; import { Description } from "./app"; import { Collection, Entity, Property } from "./MySchema"; type APISchemas = { [key: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.ArraySchemaObject | OpenAPIV3.NonArraySchemaObject; }; function generateSchemas(entities: Entity[]): APISchemas { return fromEntites(entities.map(generateSchema)); } function generateSchema(entity: Entity): [string, OpenAPIV3.NonArraySchemaObject] { const schema: OpenAPIV3.NonArraySchemaObject = { type: "object", required: entity.properties.filter(p => !p.isNullable).map(p => p.key).concat(["id", "ref"]), properties: fromEntites(addIdProperties(entity.properties.map(generateProperty))), }; return [ entity.name, schema, ]; } function addIdProperties(properties: [string, OpenAPIV3.NonArraySchemaObject][]): [string, OpenAPIV3.NonArraySchemaObject][] { return [ ["id", { type: "number", format: "int32" }], ...properties, ["ref", { type: "string", format: "uri" }], ]; } function fromEntites<T>(list: [string, T][]): { [key: string]: T; } { const obj: { [key: string]: T; } = {}; for (const mapped of list) { obj[mapped[0]] = mapped[1]; } return obj; } function generateProperty(property: Property): [string, OpenAPIV3.NonArraySchemaObject] { const prop: OpenAPIV3.NonArraySchemaObject = { type: property.type, } return [ property.key, prop, ]; } function generatePaths(collections: Collection[]): OpenAPIV3.PathsObject { const paths = [ ...collections.map(generateCollectionPath), ...collections.map(generateEntityPath), ]; return fromEntites(paths); } export function collectionRoute(collection: Collection): string { return "/" + collection.name.toLowerCase(); } function generateCollectionPath(collection: Collection): [string, OpenAPIV3.PathItemObject] { const route = collectionRoute(collection); const path: OpenAPIV3.PathItemObject = { get: generateGetAllOperation(collection), }; return [route, path]; } function generateGetAllOperation(collection: Collection): OpenAPIV3.OperationObject { const operation: OpenAPIV3.OperationObject = { responses: { "200": { description: `Lists all ${collection.name}`, content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/" + collection.entities.name } } } } } } }; return operation; } function generateEntityPath(collection: Collection): [string, OpenAPIV3.PathItemObject] { const route = itemRoute(collection); const path: OpenAPIV3.PathItemObject = { get: generateGetSpecificOperation(collection.entities), }; return [route, path]; } function itemRoute(collection: Collection): string { return collectionRoute(collection) + "/{" + idParameterName(collection.entities) + "}"; } function idParameterName(entity: Entity): string { return entity.name.toLowerCase() + "Id"; } function generateGetSpecificOperation(entity: Entity): OpenAPIV3.OperationObject { const operation: OpenAPIV3.OperationObject = { parameters: [ { in: "path", name: idParameterName(entity), required: true, schema: { type: "integer", }, }, ], responses: { "200": { description: "Returns one specific " + entity.name, content: { "application/json": { schema: { $ref: "#/components/schemas/" + entity.name, } } } }, "404": { $ref: "#/components/responses/NotFound", } } }; return operation; } const NotFoundResponse: OpenAPIV3.ResponseObject = { description: "The specified resource does not exist.", content: { "text/plain": { schema: { type: "string", example: "not found", } } } }; export function generateOpenAPI(description: Description): OpenAPIV3.Document { const doc: OpenAPIV3.Document = { openapi: "3.0.0", info: { title: "API for " + description.collections.map(c => c.name).join(", "), version: "1.0.0", }, servers: [{ url: "http://localhost:8080" }], components: { schemas: generateSchemas(description.collections.map(c => c.entities)), responses: { NotFound: NotFoundResponse } }, paths: generatePaths(description.collections), }; return doc; }