UNPKG

insomnia-importers

Version:

Various data importers for Insomnia

446 lines 16.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.convert = exports.description = exports.name = exports.id = void 0; const swagger_parser_1 = __importDefault(require("@apidevtools/swagger-parser")); const crypto_1 = __importDefault(require("crypto")); const yaml_1 = __importDefault(require("yaml")); const utils_1 = require("../utils"); const SUPPORTED_SWAGGER_VERSION = '2.0'; const MIMETYPE_JSON = 'application/json'; const MIMETYPE_URLENCODED = 'application/x-www-form-urlencoded'; const MIMETYPE_MULTIPART = 'multipart/form-data'; const SUPPORTED_MIME_TYPES = [ MIMETYPE_JSON, MIMETYPE_URLENCODED, MIMETYPE_MULTIPART, ]; const WORKSPACE_ID = '__WORKSPACE_ID__'; let requestCount = 1; exports.id = 'swagger2'; exports.name = 'Swagger 2.0'; exports.description = 'Importer for Swagger 2.0 specification (json/yaml)'; /* eslint-disable camelcase -- this file uses camel case too often */ /** * Return Insomnia folder / request group */ const importFolderItem = (parentId) => (item) => { const hash = crypto_1.default .createHash('sha1') .update(item.name) .digest('hex') .slice(0, 8); return { parentId, _id: `fld___WORKSPACE_ID__${hash}`, _type: 'request_group', name: item.name || 'Folder {requestGroupCount}', description: item.description || '', }; }; /** * Parse string data into swagger 2.0 object (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#swagger-object) */ const parseDocument = (rawData) => { try { return (0, utils_1.unthrowableParseJson)(rawData) || yaml_1.default.parse(rawData); } catch (err) { return null; } }; /** * Create request definitions based on swagger document. */ const parseEndpoints = (document) => { var _a; const defaultParent = WORKSPACE_ID; const globalMimeTypes = (_a = document.consumes) !== null && _a !== void 0 ? _a : []; const endpointsSchemas = Object.keys(document.paths) .map((path) => { const schemasPerMethod = document.paths[path]; const methods = Object.keys(schemasPerMethod); return methods .filter(method => method !== 'parameters' && method !== '$ref') .map(method => ({ ...schemasPerMethod[method], path, method, })); }) .flat(); const tags = document.tags || []; const implicitTags = endpointsSchemas .map(endpointSchema => endpointSchema.tags) .flat() .reduce((distinct, value) => { // remove duplicates if (value !== undefined && !distinct.includes(value)) { distinct.push(value); } return distinct; }, []) .filter(tag => !tags.map(tag => tag.name).includes(tag)) .map(name => ({ name, description: '' })); const folders = [...tags, ...implicitTags].map(importFolderItem(defaultParent)); const folderLookup = folders.reduce((accumulator, { _id, name }) => ({ ...accumulator, ...(name === undefined ? {} : { [name]: _id }), }), {}); const requests = []; endpointsSchemas.map(endpointSchema => { let { tags } = endpointSchema; if (!tags || tags.length === 0) { tags = ['']; } tags.forEach((tag, index) => { const requestId = endpointSchema.operationId ? `${endpointSchema.operationId}${index > 0 ? index : ''}` : `__REQUEST_${requestCount++}__`; const parentId = folderLookup[tag] || defaultParent; requests.push(importRequest(document, endpointSchema, globalMimeTypes, requestId, parentId)); }); }); return [...folders, ...requests]; }; const importRequest = (document, endpointSchema, globalMimeTypes, requestId, parentId) => { var _a; const name = endpointSchema.summary || `${endpointSchema.method} ${endpointSchema.path}`; const body = prepareBody(document, endpointSchema, globalMimeTypes); let headers = prepareHeaders(endpointSchema); const noContentTypeHeader = !(headers === null || headers === void 0 ? void 0 : headers.find(header => header.name === 'Content-Type')); if (body.mimeType && noContentTypeHeader) { headers = [ { name: 'Content-Type', disabled: false, value: body.mimeType, }, ...headers, ]; } const request = { _type: 'request', _id: requestId, parentId: parentId, name, method: (_a = endpointSchema.method) === null || _a === void 0 ? void 0 : _a.toUpperCase(), url: `{{ base_url }}${pathWithParamsAsVariables(endpointSchema.path)}`, body, description: endpointSchema.description || '', headers, parameters: prepareQueryParams(endpointSchema), }; return setupAuthentication(document.securityDefinitions, endpointSchema, request); }; /** * Populate Insomnia request with authentication */ const setupAuthentication = (securityDefinitions, endpointSchema, request) => { var _a, _b; if (!securityDefinitions) { return request; } if (!(endpointSchema === null || endpointSchema === void 0 ? void 0 : endpointSchema.security) || endpointSchema.security.length === 0) { return request; } const usedDefinitions = endpointSchema.security.reduce((collect, requirement) => [ ...collect, ...Object.keys(requirement), ], []); const scopes = endpointSchema.security.reduce((accumulator, security) => { for (const defname of Object.keys(security)) { if (security[defname].length === 0) { continue; } return accumulator.concat(security[defname]); } return accumulator; }, []); for (const usedDefinition of usedDefinitions) { const securityScheme = securityDefinitions[usedDefinition]; if (securityScheme.type === 'basic') { request.authentication = { type: 'basic', disabled: false, password: '{{ password }}', username: '{{ username }}', }; } if (securityScheme.type === 'apiKey') { if (securityScheme.in === 'header') { (_a = request.headers) === null || _a === void 0 ? void 0 : _a.push({ name: securityScheme.name, disabled: false, value: '{{ api_key }}', }); } if (securityScheme.in === 'query') { (_b = request.parameters) === null || _b === void 0 ? void 0 : _b.push({ name: securityScheme.name, disabled: false, value: '{{ api_key }}', }); } } if (securityScheme.type === 'oauth2') { if (securityScheme.flow === 'implicit') { request.authentication = { type: 'oauth2', grantType: 'authorization_code', disabled: false, authorizationUrl: securityScheme.authorizationUrl, clientId: '{{ client_id }}', scope: scopes.join(' '), }; } if (securityScheme.flow === 'password') { request.authentication = { type: 'oauth2', grantType: 'password', disabled: false, accessTokenUrl: securityScheme.tokenUrl, username: '{{ username }}', password: '{{ password }}', clientId: '{{ client_id }}', clientSecret: '{{ client_secret }}', scope: scopes.join(' '), }; } if (securityScheme.flow === 'application') { request.authentication = { type: 'oauth2', grantType: 'client_credentials', disabled: false, accessTokenUrl: securityScheme.tokenUrl, clientId: '{{ client_id }}', clientSecret: '{{ client_secret }}', scope: scopes.join(' '), }; } if (securityScheme.flow === 'accessCode') { request.authentication = { type: 'oauth2', grantType: 'authorization_code', disabled: false, accessTokenUrl: securityScheme.tokenUrl, authorizationUrl: securityScheme.authorizationUrl, clientId: '{{ client_id }}', clientSecret: '{{ client_secret }}', scope: scopes.join(' '), }; } } } return request; }; /** * Return path with parameters replaced by insomnia variables * * I.e. "/foo/:bar" => "/foo/{{ bar }}" */ const pathWithParamsAsVariables = (path) => { return path === null || path === void 0 ? void 0 : path.replace(/{([^}]+)}/g, '{{ $1 }}'); }; /** * Imports insomnia definitions of query parameters. */ const prepareQueryParams = (endpointSchema) => { var _a; return (convertParameters((_a = endpointSchema.parameters) === null || _a === void 0 ? void 0 : _a.filter(parameter => parameter.in === 'query')) || []); }; /** * Imports insomnia definitions of header parameters. */ const prepareHeaders = (endpointSchema) => { var _a; return (convertParameters((_a = endpointSchema.parameters) === null || _a === void 0 ? void 0 : _a.filter(parameter => parameter.in === 'header')) || []); }; const resolve$ref = (document, $ref) => { const [, ...parts] = $ref.split('/'); return parts.reduce((accumulator, path) => accumulator[path], document); }; /** * Imports insomnia request body definitions, including data mock (if available) * * If multiple types are available, the one for which an example can be generated will be selected first (i.e. application/json) */ const prepareBody = (document, endpointSchema, globalMimeTypes) => { const mimeTypes = endpointSchema.consumes || globalMimeTypes || []; const supportedMimeType = SUPPORTED_MIME_TYPES.find(mimeType => mimeTypes.includes(mimeType)); if (supportedMimeType === MIMETYPE_JSON) { const parameters = endpointSchema.parameters || []; const bodyParameter = parameters.find(parameter => parameter.in === 'body'); if (!bodyParameter) { return {}; } const type = bodyParameter.type || 'object'; const example = generateParameterExample(type); let text; if (type === 'object') { const { schema } = bodyParameter; if (schema.$ref) { const definition = resolve$ref(document, schema.$ref); text = JSON.stringify(example(definition), null, 2); } else { text = JSON.stringify(example(schema), null, 2); } } else { text = JSON.stringify(example, null, 2); } return { mimeType: supportedMimeType, text, }; } if (supportedMimeType === MIMETYPE_URLENCODED || supportedMimeType === MIMETYPE_MULTIPART) { const parameters = endpointSchema.parameters || []; const formDataParameters = parameters.filter(parameter => parameter.in === 'formData'); if (formDataParameters.length === 0) { return {}; } return { mimeType: supportedMimeType, params: convertParameters(formDataParameters), }; } if (mimeTypes && mimeTypes.length) { return { mimeType: mimeTypes[0] || undefined, }; } else { return {}; } }; /** * Generate example value of parameter based on it's schema. * Returns example / default value of the parameter, if any of those are defined. If not, returns value based on parameter type. */ const generateParameterExample = (parameter, ancestors = []) => { const typeExamples = { string: () => 'string', string_email: () => 'user@example.com', 'string_date-time': () => new Date().toISOString(), string_byte: () => 'ZXhhbXBsZQ==', number: () => 0, number_float: () => 0.0, number_double: () => 0.0, integer: () => 0, boolean: () => true, object: (parameter) => { if (ancestors.indexOf(parameter) !== -1) { return {}; } const example = {}; const { properties } = parameter; if (properties) { ancestors.push(parameter); Object.keys(properties).forEach(propertyName => { // @ts-expect-error there's no way, so far as I'm aware, for TypeScript to know what's actually going on here. example[propertyName] = generateParameterExample(properties[propertyName], ancestors); }); ancestors.pop(); } return example; }, array: ({ items, collectionFormat }) => { const value = generateParameterExample(items, ancestors); if (collectionFormat === 'csv') { return value; } else { return [value]; } }, }; if (typeof parameter === 'string') { return typeExamples[parameter]; } if (typeof parameter === 'object') { const { type, format, example, default: defaultValue } = parameter; if (example) { return example; } if (defaultValue) { return defaultValue; } const factory = typeExamples[`${type}_${format}`] || typeExamples[type]; if (!factory) { return null; } return factory(parameter); } }; /** * Converts swagger schema of parameters into insomnia one. */ const convertParameters = (parameters) => { return parameters === null || parameters === void 0 ? void 0 : parameters.map(parameter => { const { required, name, type } = parameter; if (type === 'file') { return { name, disabled: required !== true, type: 'file', }; } return { name, disabled: required !== true, value: `${generateParameterExample(parameter)}`, }; }); }; const convert = async (rawData) => { requestCount = 1; // Validate let api = await parseDocument(rawData); if (!api || api.swagger !== SUPPORTED_SWAGGER_VERSION) { return null; } // Await here so we catch any exceptions try { api = await swagger_parser_1.default.validate(api); } catch (err) { // We already know it's a Swagger doc so we will try to import it anyway instead // of bailing out here. console.log('[swagger] Import file validation failed', err); } // Import const workspace = { _type: 'workspace', _id: WORKSPACE_ID, parentId: null, name: `${api.info.title} ${api.info.version}`, description: api.info.description || '', // scope is not set because it could be imported for design OR to generate requests }; const baseEnv = { _type: 'environment', _id: '__BASE_ENVIRONMENT_ID__', parentId: WORKSPACE_ID, name: 'Base environment', data: { base_url: '{{ scheme }}://{{ host }}{{ base_path }}', }, }; const swaggerEnv = { _type: 'environment', _id: 'env___BASE_ENVIRONMENT_ID___sub', parentId: baseEnv._id, name: 'Swagger env', data: { base_path: api.basePath || '', scheme: (api.schemes || ['http'])[0], host: api.host || '', }, }; const endpoints = parseEndpoints(api); return [workspace, baseEnv, swaggerEnv, ...endpoints]; }; exports.convert = convert; //# sourceMappingURL=swagger-2.js.map