UNPKG

@postman/wsdl-to-postman

Version:

Convert a given WSDL specification (1.1) to Postman Collection

487 lines (462 loc) 17.1 kB
const { getProtocolAndHost } = require('./utils/urlUtils'); const XML = 'xml', { Collection } = require('postman-collection/lib/collection/collection'), { Header } = require('postman-collection/lib/collection/header'), { ItemGroup } = require('postman-collection/lib/collection/item-group'), { Item } = require('postman-collection/lib/collection/item'), { Response } = require('postman-collection/lib/collection/response'), { WsdlToPostmanCollectionBodyMapper } = require('./WsdlToPostmanCollectionBodyMapper'), UserError = require('../lib/UserError'), FOLDER_OPTIONS = { noFolders: 'No folders', portEndpoint: 'Port/Endpoint', service: 'Service' }, keyByOption = { [FOLDER_OPTIONS.portEndpoint]: 'portName', [FOLDER_OPTIONS.service]: 'serviceName' }, DEFAULT_COLLECTION_NAME = 'WSDL To Postman Generated'; /** * Class to map a json parsed WSDL object to a PostmanCollection object * @param {object} wsdlObject Contains parsed data from a xml document to json format */ class WsdlToPostmanCollectionMapper { constructor(wsdlObject) { this.wsdlObject = wsdlObject; this.wsdlToPostmanCollectionBodyMapper = new WsdlToPostmanCollectionBodyMapper(); } /** * Generates a PostmanCollection Object * @param {object} options options from the process * @param {object} xmlParser the parser class to parse xml to object or vice versa * @param {string} collectionName optional collection name * @returns {object} A PostmanCollection object. */ getPostmanCollection(options, xmlParser, collectionName = '') { let name = this.getCollectionName(collectionName), postmanCollectionDefinition = this.generateMappingObject(this.wsdlObject, options, name, xmlParser), collection = new Collection(postmanCollectionDefinition); return collection; } /** * Gets the collection name from the file if the input is not empty * from the target namespaces if the input is emtpy * and a default value if both are empty * @param {string} collectionName optional collection name * @returns {string} collection's name. */ getCollectionName(collectionName = '') { if (!this.wsdlObject.targetNamespace) { return DEFAULT_COLLECTION_NAME; } let name = (collectionName) ? collectionName : this.wsdlObject.targetNamespace.url; return name ? name : DEFAULT_COLLECTION_NAME; } /** * Takes a json wsdl parsed object and generates a postman collection definition format. * @param {object} wsdlObject JSON parsed object from a wsdl document * @param {object} options options from the process * @param {string} name optional collection name * @param {object} xmlParser the parser class to parse xml to object or vice versa * @returns {object} An object with postman collection definition format. */ generateMappingObject(wsdlObject, options, name, xmlParser) { let urlsData = this.getUrlDataFromOperations(wsdlObject.operationsArray), urlVariablesData = this.getVariablesFromUrlDataList(urlsData), urlVariablesOnly = urlVariablesData.map((variableData) => { return { key: variableData.key, value: variableData.value }; }), groupsInfo = this.groupOperations(wsdlObject.operationsArray, options), items = this.createItemsFromOperationsFolderStrategy( groupsInfo, urlVariablesData, wsdlObject.securityPolicyArray, xmlParser ), description = this.getCollectionDescription(wsdlObject, groupsInfo), collectionDefinition = {}; collectionDefinition = { info: { name: name, version: '1.0.0', description: description }, item: items, variable: [...urlVariablesOnly] }; return collectionDefinition; } /** * Groups the operations according to the folder strategy * @param {object} wsdlObject JSON parsed object from a wsdl document * @param {object} groupsInfo the generated information for grouping requests in folders * @returns {string} the collection description in string representation */ getCollectionDescription(wsdlObject, groupsInfo) { let messagesFromGrouping = [], generatedDocumentation = wsdlObject.documentation + '\n' + wsdlObject.log.errors; if (groupsInfo && groupsInfo.isGrouped) { messagesFromGrouping = groupsInfo.groups.filter((groupedRequests) => { if (groupedRequests.message) { return true; } }).map((filtered) => { return filtered.message; }); messagesFromGrouping.forEach((message) => { generatedDocumentation += '\n' + message; }); } return generatedDocumentation; } /** * Groups the operations according to the folder strategy * @param {array} operations An array of parsed WSDL operations in json format. * @param {array} options the options from the process * @returns {object} object with the properties isGrouped if has some * groups by folder and the groups * if no groups returns the original operations */ groupOperations(operations, options) { let key = '', { folderStrategy } = options, defaultOption = FOLDER_OPTIONS.portEndpoint; if (!folderStrategy) { key = keyByOption[defaultOption]; } else if (Object.values(FOLDER_OPTIONS).includes(folderStrategy)) { if (folderStrategy === FOLDER_OPTIONS.noFolders) { return { isGrouped: false, operations: operations }; } key = keyByOption[folderStrategy]; } else { throw new UserError(`Provided folderStrategy option is not supported. Options are ${Object.values(FOLDER_OPTIONS).join(', ')}`); } return this.getGroupsFromKey(operations, key); } /** * Gets the group object from the operations by the key * @param {array} operations An array of parsed WSDL operations in json format. * @param {string} key the key to group operations by * @returns {object} object with the properties isGrouped if has some * groups by folder and the groups */ getGroupsFromKey(operations, key) { const allKeys = [...new Set(operations.map((operation) => { return operation[key]; }))]; let groups = [], messageKey = '', message = ''; allKeys.forEach((valueKey) => { let operationsByKey = operations.filter((operation) => { return operation[key] === valueKey; }); if (valueKey === '') { messageKey = key === 'portName' ? 'Port' : 'Service'; valueKey = `Unnamed ${messageKey}`; message = `Could not get name for folder from ${messageKey}. Using default instead.`; } groups.push({ groupName: valueKey, operations: operationsByKey, message: message }); }); return { isGrouped: true, groups: groups }; } /** * Extract urls from operations array and returns an array of urls * @param {array} operations An array of parsed WSDL operations in json format. * @returns {array} An array of unique urls <string> */ getUrlDataFromOperations(operations) { let urlsData = [], portNames = new Set(); operations.forEach((operation) => { const { portName, // protocol, url } = operation; if (!portNames.has(portName)) { urlsData.push({ portName, url }); } portNames.add(portName); }); return [...urlsData]; } /** * Extract url variables from urls list * @param {array} urlDataList An array of urls with urlData * @returns {array} an array of variables with format {key: <string>, value: <string>} */ getVariablesFromUrlDataList(urlDataList) { let variables = urlDataList.filter((urlData) => { if (urlData.url) { return urlData; } }).map((urlData) => { const { protocol, host } = getProtocolAndHost(urlData.url); return { key: `${urlData.portName}BaseUrl`, value: `${protocol}${host}`, urlData }; }); return variables; } /** * Substitutes a url value by a variable that corresponds with that value * @param {string} operation An operation object * @param {array} variablesData An array of all variables with urlData * @returns {string} a url with values, if any matches, replaced by variables */ replaceVariableInUrl(operation, variablesData) { const { portName, url } = operation; let variableInUrl = variablesData.find((variable) => { const portNameMatch = variable.urlData.portName === portName; return portNameMatch; }); if (variableInUrl) { return url.replace(variableInUrl.value, `{{${variableInUrl.key}}}`); } } /** * Generate an array with Items in postman definition format from an array of operations parsed from * a wsdl file determines if there is a folder strategy and implements * @param {array} groupsInfo the grouped operations information * @param {array} urlVariablesData An array with all variables with urlData from operations * @param {array} securityPolicyArray An array with all security policies * @param {object} xmlParser the parser class to parse xml to object or vice versa * @returns {array} An array with all items generated in postman item definition format */ createItemsFromOperationsFolderStrategy(groupsInfo, urlVariablesData, securityPolicyArray, xmlParser) { let items = []; if (!groupsInfo.isGrouped) { return this.createItemsFromOperations(groupsInfo.operations, urlVariablesData, securityPolicyArray, xmlParser); } else if (groupsInfo.isGrouped && groupsInfo.groups.length === 1) { return this.createItemsFromOperations(groupsInfo.groups[0].operations, urlVariablesData, securityPolicyArray, xmlParser); } groupsInfo.groups.forEach((group) => { let operationItems = this.createItemsFromOperations(group.operations, urlVariablesData, securityPolicyArray, xmlParser); items.push(new ItemGroup({ name: group.groupName, item: operationItems, description: group.operations[0].serviceDescription })); }); return items; } /** * Generate an array with Items in postman definition format from an array of operations parsed from * a wsdl file * @param {object} wsdlObject JSON parsed object from a wsdl document * @param {array} operation WSDLOPeration in json format parsed from a wsdl document * @param {object} xmlParser the parser class to parse xml to object or vice versa * @returns {array} An array with all items generated in postman item definition format */ createSingleItemAndVariablesFromOperation(wsdlObject, operation, xmlParser) { let urlsData = this.getUrlDataFromOperations(wsdlObject.operationsArray), urlVariablesData = this.getVariablesFromUrlDataList(urlsData), urlVariablesOnly = urlVariablesData.map((variableData) => { return { key: variableData.key, value: variableData.value }; }), postmanItem = this.createItemsFromOperations(operation, urlVariablesData, wsdlObject.securityPolicyArray, xmlParser)[0], resultItem = new Item({ name: postmanItem.name, request: postmanItem.request, response: postmanItem.response }), suggestedEndpointJson = resultItem.toJSON(), result = { request: suggestedEndpointJson, variables: [...urlVariablesOnly] }; return result; } /** * Generate an array with Items in postman definition format from an array of operations parsed from * a wsdl file * @param {array} operations An array of operations in json format parsed from a wsdl document * @param {array} urlVariablesData An array with all variables with urlData from operations * @param {array} securityPolicyArray An array with all security policies * @param {object} xmlParser the parser class to parse xml to object or vice versa * @returns {array} An array with all items generated in postman item definition format */ createItemsFromOperations(operations, urlVariablesData, securityPolicyArray, xmlParser) { let items = operations.map((operation) => { let requestBody = this.wsdlToPostmanCollectionBodyMapper .getBody(operation, operation.input[0], securityPolicyArray, xmlParser, operation.header), postmanItem = { name: operation.name, request: { description: operation.description, url: this.replaceVariableInUrl(operation, urlVariablesData), method: operation.method, header: this.getReqHeaders(operation), body: requestBody }, response: this.getResponses(operation, xmlParser, requestBody) }; return postmanItem; }); return items; } /** * Generates the responses also knwon as examples of an operation * if the operation has output and fault returns the corresponding array of * responses * @param {Operation} operation the WSDL Operation * @param {object} xmlParser the parser class to parse xml to object or vice versa * @param {object} requestBody the request body for this response * @returns {Array} the array of responses */ getResponses(operation, xmlParser, requestBody) { let responses = []; if (operation.output) { if (Array.isArray(operation.output)) { let correctResponses = operation.output.map((currentOutput) => { return this.buildResponseObject(' response', 'OK', 200, operation, requestBody, currentOutput, xmlParser); }); responses = responses.concat(correctResponses); } } if (operation.fault) { let faultRequestBody = { mode: 'raw', raw: 'incorrect body message', options: { raw: { language: XML } } }; if (Array.isArray(operation.fault)) { let incorrectResponses = operation.fault.map((currentFault) => { return this.buildResponseObject(` fault - ${currentFault.name}`, 'not OK', 500, operation, faultRequestBody, currentFault, xmlParser); }); responses = responses.concat(incorrectResponses); } } return responses; } /** * build a Postman collection response object * @param {string} sufixName the sufix that will be applied to the response name * @param {string} status the status operation * @param {Number} code the code operation * @param {Operation} operation the WSDL Operation * @param {string} requestBody the request body message in string rep * @param {Element} responseBodyElement the WSDL Operation Element * @param {object} xmlParser the parser class to parse xml to object or vice versa * @returns {Response} the Postman response object */ buildResponseObject(sufixName, status, code, operation, requestBody, responseBodyElement, xmlParser) { let responseBody = this.wsdlToPostmanCollectionBodyMapper .getResponseBody(operation, responseBodyElement, undefined, xmlParser), response = new Response({ name: operation.name + sufixName, status: status, code: code, header: this.getXMLHeader(), originalRequest: { url: operation.url, method: operation.method, header: this.getReqHeaders(operation), body: requestBody }, body: responseBody }); response._postman_previewlanguage = XML; return response; } /** * Returns the request headers * returns the object that represents a header for Postman * @param {Operation} operation the WSDL Operation * @returns {Object} the header object */ getReqHeaders(operation) { let soapActionHeader = this.getSoapActionHeader(operation), xmlHeader = this.getXMLHeader(); if (soapActionHeader) { xmlHeader.push(soapActionHeader); return xmlHeader; } return xmlHeader; } /** * Returns the defined xml header * returns the object that represents a header for Postman * whit the values for xml * @returns {Object} the header object */ getXMLHeader() { let header = new Header({ key: 'Content-Type', value: 'text/xml; charset=utf-8', type: 'text' }); return [header]; } /** * Returns the defined soapActionHeader * returns the object that represents a header for Postman * @param {Operation} operation the WSDL Operation * @returns {Object} the header object */ getSoapActionHeader(operation) { if (operation.soapAction && operation.soapAction !== '') { let header = new Header({ key: 'SOAPAction', value: operation.soapAction, type: 'text' }); return header; } } } module.exports = { WsdlToPostmanCollectionMapper, DEFAULT_COLLECTION_NAME };