insomnia-importers
Version:
Various data importers for Insomnia
446 lines • 16.5 kB
JavaScript
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
;