Skip to content

Commit

Permalink
feat(cmd-api-server): aggregate swagger.json endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
petermetz committed Mar 15, 2022
1 parent 23ded0f commit f813149
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
25 changes: 25 additions & 0 deletions packages/cactus-cmd-api-server/src/main/json/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,31 @@
}
}
}
},
"/api/v1/api-server/get-aggregate-openapi-json": {
"get": {
"summary": "Returns the combined openapi.json document of all plugins currently installed in the API server.",
"description": "The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one.",
"x-hyperledger-cactus": {
"http": {
"verbLowerCase": "get",
"path": "/api/v1/api-server/get-aggregate-openapi-json"
}
},
"operationId": "getAggregateOpenapiJsonV1",
"parameters": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
}
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,36 @@ export enum WatchHealthcheckV1 {
*/
export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
* The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one.
* @summary Returns the combined openapi.json document of all plugins currently installed in the API server.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getAggregateOpenapiJsonV1: async (options: any = {}): Promise<RequestArgs> => {
const localVarPath = `/api/v1/api-server/get-aggregate-openapi-json`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}

const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;



setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Returns the current timestamp of the API server as proof of health/liveness
* @summary Can be used to verify liveness of an API server instance
Expand Down Expand Up @@ -174,6 +204,16 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati
export const DefaultApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration)
return {
/**
* The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one.
* @summary Returns the combined openapi.json document of all plugins currently installed in the API server.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getAggregateOpenapiJsonV1(options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<any>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getAggregateOpenapiJsonV1(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
* Returns the current timestamp of the API server as proof of health/liveness
* @summary Can be used to verify liveness of an API server instance
Expand Down Expand Up @@ -204,6 +244,15 @@ export const DefaultApiFp = function(configuration?: Configuration) {
export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = DefaultApiFp(configuration)
return {
/**
* The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one.
* @summary Returns the combined openapi.json document of all plugins currently installed in the API server.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getAggregateOpenapiJsonV1(options?: any): AxiosPromise<any> {
return localVarFp.getAggregateOpenapiJsonV1(options).then((request) => request(axios, basePath));
},
/**
* Returns the current timestamp of the API server as proof of health/liveness
* @summary Can be used to verify liveness of an API server instance
Expand Down Expand Up @@ -232,6 +281,17 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa
* @extends {BaseAPI}
*/
export class DefaultApi extends BaseAPI {
/**
* The various distinct openapi.json documents (formerly called swagger.json) are flattened into a single one.
* @summary Returns the combined openapi.json document of all plugins currently installed in the API server.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof DefaultApi
*/
public getAggregateOpenapiJsonV1(options?: any) {
return DefaultApiFp(this.configuration).getAggregateOpenapiJsonV1(options).then((request) => request(this.axios, this.basePath));
}

/**
* Returns the current timestamp of the API server as proof of health/liveness
* @summary Can be used to verify liveness of an API server instance
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { OpenAPIV3 } from "express-openapi-validator/dist/framework/types";

import type { PluginRegistry } from "@hyperledger/cactus-core";
import type { ICactusPlugin } from "@hyperledger/cactus-core-api";
import type { IPluginWebService } from "@hyperledger/cactus-core-api";
import { isIPluginWebService } from "@hyperledger/cactus-core-api";
import { Checks } from "@hyperledger/cactus-common";

export async function collectOpenapiJsonDocs(
pr: PluginRegistry,
): Promise<OpenAPIV3.Document[]> {
Checks.truthy(pr, `collectOpenapiJsonDocs() pr (PluginRegistry)`);

const openApiJsonDocsPromises = pr
.getPlugins()
.filter((pluginInstance) => isIPluginWebService(pluginInstance))
.map(async (plugin: ICactusPlugin) => {
const webSvc = plugin as IPluginWebService;
const openApiJson = (await webSvc.getOpenApiSpec()) as OpenAPIV3.Document;
return openApiJson;
});

const openApiJsonDocs = await Promise.all(openApiJsonDocsPromises);

// Filter out falsy results where the plugin did not return anything.
return openApiJsonDocs.filter((d) => !!d);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Express, Request, Response } from "express";
import HttpStatus from "http-status-codes";

import {
Logger,
Checks,
LogLevelDesc,
LoggerProvider,
IAsyncProvider,
} from "@hyperledger/cactus-common";

import {
IWebServiceEndpoint,
IExpressRequestHandler,
IEndpointAuthzOptions,
} from "@hyperledger/cactus-core-api";

import {
PluginRegistry,
registerWebServiceEndpoint,
} from "@hyperledger/cactus-core";

import OAS from "../../../json/openapi.json";
import { collectOpenapiJsonDocs } from "./collect-openapi-json-docs";

export interface IGetAggregateOpenapiJsonEndpointV1Options {
logLevel?: LogLevelDesc;
pluginRegistry: PluginRegistry;
}

export class GetAggregateOpenapiJsonEndpointV1 implements IWebServiceEndpoint {
public static readonly CLASS_NAME = "GetAggregateOpenapiJsonEndpointV1";

private readonly log: Logger;

public get className(): string {
return GetAggregateOpenapiJsonEndpointV1.CLASS_NAME;
}

constructor(public readonly opts: IGetAggregateOpenapiJsonEndpointV1Options) {
const fnTag = `${this.className}#constructor()`;
Checks.truthy(opts, `${fnTag} arg options`);
Checks.truthy(opts.pluginRegistry, `${fnTag} arg options.pluginRegistry`);

const level = this.opts.logLevel || "INFO";
const label = this.className;
this.log = LoggerProvider.getOrCreate({ level, label });
}

public getExpressRequestHandler(): IExpressRequestHandler {
return this.handleRequest.bind(this);
}

public get oasPath(): typeof OAS.paths["/api/v1/api-server/get-aggregate-openapi-json"] {
return OAS.paths["/api/v1/api-server/get-aggregate-openapi-json"];
}

public getPath(): string {
return this.oasPath.get["x-hyperledger-cactus"].http.path;
}

public getVerbLowerCase(): string {
return this.oasPath.get["x-hyperledger-cactus"].http.verbLowerCase;
}

public getOperationId(): string {
return this.oasPath.get.operationId;
}

public async registerExpress(
expressApp: Express,
): Promise<IWebServiceEndpoint> {
await registerWebServiceEndpoint(expressApp, this);
return this;
}

getAuthorizationOptionsProvider(): IAsyncProvider<IEndpointAuthzOptions> {
// TODO: make this an injectable dependency in the constructor
return {
get: async () => ({
isProtected: true,
requiredRoles: [],
}),
};
}

async handleRequest(req: Request, res: Response): Promise<void> {
const fnTag = `${this.className}#handleRequest()`;
const verbUpper = this.getVerbLowerCase().toUpperCase();
this.log.debug(`${verbUpper} ${this.getPath()}`);

try {
const resBody = await collectOpenapiJsonDocs(this.opts.pluginRegistry);
res.status(HttpStatus.OK);
res.json(resBody);
} catch (ex) {
this.log.error(`${fnTag} failed to serve contract deploy request`, ex);
res.status(HttpStatus.INTERNAL_SERVER_ERROR);
res.statusMessage = ex.message;
res.json({ error: ex.stack });
}
}

public async getAggregateOpenapiJson(): Promise<unknown> {
return {};
}
}

0 comments on commit f813149

Please sign in to comment.