All files / blong-gogo/src/codec/adapter/openapi load.ts

86.02% Statements 117/136
53.33% Branches 16/30
50% Functions 1/2
86.02% Lines 117/136

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 1371x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 46x 46x 46x 133x 90x 46x 46x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 44x 90x           90x 90x 90x 90x 90x                       90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x 90x   90x 90x 90x 90x 90x 90x 90x 90x 90x 224x 134x 134x 460x 460x 134x 134x 134x 224x 90x 90x   90x 90x 1x 1x 1x 1x 1x  
import {library} from '@feasibleone/blong/types';
 
const httpVerbs: string[] = ['post', 'put', 'patch', 'get', 'delete', 'options', 'head', 'trace'];
const responseTypes: Record<string, string> = {
    'application/json': 'json',
    'text/plain': 'text',
    'text/html': 'text',
};
 
type BundleOperation = {
    operationId?: string;
    'x-blong-method'?: string;
    parameters?: unknown[];
    requestBody?: {content?: Record<string, {schema?: unknown}>};
    responses?: Record<string, {content?: Record<string, {schema?: unknown}>}>;
    servers?: {url: string}[];
};
type BundleDoc = {
    paths?: Record<string, Record<string, BundleOperation>>;
    parameters?: unknown[];
    swagger?: string;
    schemes?: string[];
    host?: string;
    basePath?: string;
    openapi?: string;
    servers?: {url: string}[];
};
 
export default library(
    ({lib: {request}, apiSchema}) =>
        async function load(config: object, pattern: RegExp | string, source: string) {
            const test =
                pattern instanceof RegExp
                    ? (key: string) => pattern.test(key)
                    : (key: string) => key.includes(pattern as string);
            const handlers: Record<string, unknown> = {};
            for (const [namespace, locations] of Object.entries(config)) {
                const bundle = (await apiSchema.loadApi(locations, source)) as BundleDoc;
                Object.entries(bundle.paths ?? {}).forEach(([path, methods]) =>
                    Object.entries(methods)
                        .filter(
                            ([method, def]) =>
                                (def.operationId || def['x-blong-method']) &&
                                httpVerbs.includes(method),
                        )
                        .forEach(([method, def]) => {
                            const name = `${namespace}${
                                def['x-blong-method'] || def.operationId
                            }`.toLowerCase();
                            if (!test(name)) return;
                            const formatProps: {
                                method: string;
                                path: string;
                                url: string;
                                requestBody: unknown;
                                responseType: string | undefined;
                                schemas: unknown[];
                            } = {
                                method,
                                path,
                                url: '',
                                requestBody: undefined,
                                responseType: undefined,
                                schemas: ([] as unknown[])
                                    .concat(methods.parameters)
                                    .concat(def.parameters)
                                    .concat(
                                        'requestBody' in def &&
                                            def.requestBody &&
                                            'content' in def.requestBody &&
                                            def.requestBody.content?.['application/json']
                                                ?.schema && {
                                                name: 'body',
                                                in: 'body',
                                                schema: def.requestBody.content['application/json']
                                                    .schema,
                                            },
                                    )
                                    .filter(Boolean),
                            };
                            switch (true) {
                                case 'swagger' in bundle: {
                                    formatProps.url += [
                                        (bundle.schemes && bundle.schemes[0]) || 'http',
                                        '://',
                                        bundle.host,
                                        bundle.basePath,
                                        path,
                                    ]
                                        .filter(Boolean)
                                        .join('');
                                    break;
                                }
                                case 'openapi' in bundle: {
                                    const defUrl =
                                        ('servers' in def &&
                                            def.servers &&
                                            def.servers[0] &&
                                            def.servers[0].url) ||
                                        '';
                                    const docUrl =
                                        (bundle.servers &&
                                            bundle.servers[0] &&
                                            bundle.servers[0].url) ||
                                        '';
                                    formatProps.url =
                                        (defUrl.startsWith('/')
                                            ? docUrl + defUrl
                                            : defUrl || docUrl) + path;
                                    if ('requestBody' in def)
                                        formatProps.requestBody = def.requestBody;
                                    break;
                                }
                            }
                            // get unique response types
                            const responseTypeSet = new Set<string>();
                            Object.values(def.responses || {}).forEach(response => {
                                if (response.content) {
                                    Object.entries(response.content).forEach(
                                        ([type, content]) =>
                                            (content as {schema?: unknown}).schema &&
                                            responseTypes[type] &&
                                            responseTypeSet.add(responseTypes[type]),
                                    );
                                }
                            });
                            if (responseTypeSet.size === 1)
                                formatProps.responseType = responseTypeSet.values().next().value;
                            else formatProps.responseType = 'json';
                            handlers[name] = request(formatProps);
                        }),
                );
            }
            return handlers;
        },
);