0

I need to obtain the documentation for all the APIs that I have in ApiGateway, but I can only see the documentation for the last API. This is my class:

import Auth from '@aws-amplify/auth';
import { config } from './config';
import APIGateway, { GetExportRequest } from 'aws-sdk/clients/apigateway';
import { ICredentials } from '@aws-amplify/core';
import { Spec, SwaggerUIBundle } from 'swagger-ui-dist'

export const initSwagger = async (): Promise<void> => {
    const credentials = await Auth.currentCredentials();
    const apiGateway = createAPIGatewayClient(credentials);
    const specs = await getAPIGatewaySpec(apiGateway);
    renderUI(specs);
  
};

const createAPIGatewayClient = (credentials: ICredentials): APIGateway => new APIGateway({
    region: config.region,
    accessKeyId: credentials.accessKeyId,
    secretAccessKey: credentials.secretAccessKey,
    sessionToken: credentials.sessionToken,
});

const getAPIGatewaySpec = async (apiGateway: APIGateway): Promise<Spec[]> => {
    const { items: apis } = await apiGateway.getRestApis().promise();
    const data = await getAPIExports(apiGateway, apis);

    if (!data) {
        throw new Error('No documentation body received');
    }

    const blobToJson = function (binArray: any) {
        const jsons = new Array();
        for (let j = 0; j < binArray.length; j++) {    
            let str = '';
            for (let i = 0; i < binArray[j].body.length; i++) {
                str += String.fromCharCode(parseInt(binArray[j].body[i]));
            }
            jsons.push(JSON.parse(str));
        } 
        return jsons;
    };

    const specs = blobToJson(data) as Spec[];
    console.log('spec: ', specs)
    console.log('spec.length: ', specs.length)
    specs.forEach((spec) => {
        spec.servers.forEach((server: { variables: { basePath: { default: string } } }) => {
            const basePath = server.variables.basePath.default;
            if (basePath.startsWith('/')) {
                server.variables.basePath.default = basePath.substr(1);
            }
        });
    });

    return specs;
};


const getAPIExports = async (apiGateway: APIGateway, apis: APIGateway.ListOfRestApi | undefined) => {
    if (!apis) return;
    return Promise.all(
        apis.map(async (api) => {
            const req: GetExportRequest = {
                restApiId: api.id,
                stageName: config.apiGateway.stageName,
                exportType: 'oas30',
                accepts: 'application/json',
            } as GetExportRequest;
            return await apiGateway.getExport(req).promise();
        }),
    );
};

const renderUI = (specs?: Spec[]): void => {
    if (!specs) {
      return;
    }
  
    specs.forEach((spec) => {
      SwaggerUIBundle({      
        spec: spec,
        'dom_id': '#swagger',
        deepLinking: true,
        openapi: '3.0.0',
      });
    });
  };

The spec contains all the specifications of my API and it looks like this:

spec:

(4) [{…}, {…}, {…}, {…}]
0: {openapi: '3.0.1', info: {…}, servers: Array(1), paths: {…}, components: {…}}
1: {openapi: '3.0.1', info: {…}, servers: Array(1), paths: {…}, components: {…}}
2: {openapi: '3.0.1', info: {…}, servers: Array(1), paths: {…}, components: {…}}
3: {openapi: '3.0.1', info: {…}, servers: Array(1), paths: {…}, components: {…}}

However, when I try to access the documentation for the other APIs, I can only see the specification for the last position in the array (position 3). This leads me to believe that SwaggerUIBundle is being overwritten during iteration. Even if I don't iterate the array, I still get the same result. Is there a way to solve this? Thank you in advance.

EDIT:

By the way, I'm using this implementation with some modifications: https://github.com/m-radzikowski/aws-swaggerui

David D
  • 23
  • 3
  • I'm not sure if it's possible to render multiple Swagger UI instances on the same page; they'll probably need different `dom_id`s at the very least. Instead, consider using a single Swagger UI with the [`urls`](https://stackoverflow.com/a/44819386/113116) parameter that lets you specify a list of API definitions to switch between. – Helen Apr 02 '23 at 10:37
  • I decided to use the URL parameter as you indicated, however, should I save the specification in JSON format and then pass the URL by calling these files? I tried to pass the URL as 'data:text/json;charset=utf-8', but in the URL field it shows something like this: [link](data:text/json;charset=utf-8,%7B%22openapi%22%3A%223.0.1%22%2C%22info%22%3A%7B%22title%22%3A%22dev-swaggerui%22%2C%22version%22%3A%222023-04-03T04%3A16%3A17Z%22%7D%2C%22servers%22%3A%5B%7B%22url%2F%7BbasePath%7D%22%) It seems like Swagger won't accept this approach. Is there any way for Swagger to accept this approach? – David D Apr 03 '23 at 04:40
  • Oh I see, your original example uses `spec` to pass the spec content instead of a URL. I think the `urls` config only accepts `http(s)://` URLs and cannot be used to pass the spec content like `spec` does. – Helen Apr 03 '23 at 06:50
  • Looks like your original example _can_ work, but it needs to have a unique `dom_id` for each Swagger UI instance. Check out [this codepen](https://codepen.io/kyleshockey/pen/wrqarZ) (found [here on GitHub](https://github.com/swagger-api/swagger-ui/issues/3372#issuecomment-333339657)) to get an idea. – Helen Apr 03 '23 at 06:53

0 Answers0