Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for nested resources and URI params resolution. #3

Merged
merged 10 commits into from
Sep 27, 2019
39 changes: 28 additions & 11 deletions lib/convert.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const raml = require('raml-1-parser'),
_ = require('lodash'),
SDK = require('postman-collection'),
helper = require('./helper.js');
helper = require('./helper.js'),

// This is the default collection name if one can't be inferred from the RAML 1.0 spec
COLLECTION_NAME = 'Converted from RAML 1.0';

/**
* Used to convert array to a map
Expand Down Expand Up @@ -35,12 +38,12 @@ var converter = {
let collection = new SDK.Collection(),
ramlAPI = raml.parseRAMLSync(ramlString),
ramlJSON = ramlAPI.toJSON(),
baseUrl = new SDK.Variable(),
convertedBaseUrl,
info = {
title: ramlJSON.title || '',
documentation: ramlJSON.documentation || '',
description: ramlJSON.description || '',
version: ramlJSON.version || ''
title: _.get(ramlJSON, 'title', COLLECTION_NAME),
documentation: _.get(ramlJSON, 'documentation', ''),
description: _.get(ramlJSON, 'description', ''),
version: _.get(ramlJSON, 'version', '')
},
RootParameters = {
mediaType: ramlJSON.mediaType,
Expand All @@ -49,19 +52,33 @@ var converter = {
baseUri: ramlJSON.baseUri,
baseUriParameters: ramlJSON.baseUriParameters,
securitySchemes: '',
securedBy: ramlJSON.securedBy || ''
securedBy: _.get(ramlJSON, 'securedBy', '')
};

collection = helper.setCollectionInfo(info, collection);
ramlJSON.types && (RootParameters.types = arrayToMap(ramlJSON.types));
ramlJSON.traits && (RootParameters.traits = arrayToMap(ramlJSON.traits));
ramlJSON.securitySchemes && (RootParameters.securitySchemes = arrayToMap(ramlJSON.securitySchemes));
baseUrl.key = 'baseUrl';
baseUrl.value = helper.addParametersToUrl(ramlJSON.baseUri, ramlJSON.baseUriParameters);
collection.variables.add(baseUrl);

// Returns object containing url and all path variables with SDK compatible format
// Also adds {{baseUrl}} collection variable
helper.convertToPmCollectionVariables(
ramlJSON.baseUriParameters,
'baseUrl',
ramlJSON.baseUri
).forEach((element) => {
collection.variables.add(element);
});

// convrtedBaseUrl is URL containing baseUrl in form of PM collection variable
convertedBaseUrl = new SDK.Variable({
key: 'baseUrl',
value: '{{baseUrl}}',
type: 'string'
});

ramlJSON.resources && ramlJSON.resources.forEach(function (resource) {
collection.items.add(helper.convertResources(baseUrl, resource, RootParameters));
collection.items.add(helper.convertResources(convertedBaseUrl, resource, RootParameters));
});

return cb(null, {
Expand Down
139 changes: 124 additions & 15 deletions lib/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,50 @@ helper = {

collection.name = info.title;
collection.version = info.version;
collection.description = info.documentation.concat(info.description);
collection.description = helper.convertDescription(info.description, info.documentation);

return collection;
},

/**
* RAML 1.0 supports documentation of schema which supports Markdown.
* This function converts it into postman description with some Markdown Formatting.
* https://github.com/raml-org/raml-spec/blob/master/versions/raml-10/raml-10.md#user-documentation
*
* @param {String} ramlDescription - Description of an schema
* @param {Array} ramlDocumentation - Documentation of schema
*
* @returns {String} - Postman collection description.
*/
convertDescription: function(ramlDescription, ramlDocumentation) {
let description;

if (ramlDocumentation) {
description = '# Description\n\n' + ramlDescription + '\n\n';
description += '# Documentation\n\n';

// Handle converted description for invalid documentation
if (_.isArray(ramlDocumentation)) {
_.forEach(ramlDocumentation, (value) => {
if (value.title && value.content) {
description += `## ${value.title}\n\n${value.content}\n\n`;
}
else {
description += 'Invalid Documentaion key-value pair\n';
}
});
}
else {
description += _.toString(ramlDocumentation);
}
}
else {
description = ramlDescription;
}

return description;
},

/**
*
* Used to convert a raml json header into postman sdk header object
Expand Down Expand Up @@ -283,21 +322,69 @@ helper = {
return auth;
},

/**
* Converts the neccessary root level variables to the
* something that can be added to the collection
*
* @param {Array} variablesToAdd - Object containing the root level variables at the root/path-item level
* @param {String} keyName - an additional key to add the baseUrl to the variable list
* @param {String} baseUrl - URL from the baseUrl object
* @returns {Array} modified collection variable array
*/
convertToPmCollectionVariables: function(variablesToAdd, keyName, baseUrl = '') {
var variables = [],
convertedBaseUrl = baseUrl;

if (variablesToAdd) {
_.forOwn(variablesToAdd, (value, key) => {
// 'version' is reserved base URI parameter for RAML 1.0, value of it recieved in enum so special handling
if (key === 'version') {
variables.push(new SDK.Variable({
id: key,
value: _.get(value, 'enum[0]', ''),
description: value.description || 'This is version of API schema.'
}));
}
else {
variables.push(new SDK.Variable({
id: key,
value: value.default || '',
description: value.description + (value.enum ? ' (is one of ' + value.enum + ')' : '')
}));
}
// replace base url variables according to postman variable identification syntax
convertedBaseUrl = convertedBaseUrl.replace('{' + key + '}', '{{' + key + '}}');
});
}
if (keyName) {
variables.push(new SDK.Variable({
id: keyName,
value: convertedBaseUrl,
type: 'string'
}));
}

return variables;
},

/**
*
* Used to convert raml method json to a postman sdk item object
*
* @param {Object} method - raml method in json format
* @param {String} url - url of the raml request
* @param {Object} globalParameters - Object with parameters given at root level of raml spec
* @param {Array} pathVariables - Optional path variables array to add value/description of path varibles in url
*
* @returns {Object} item - postman sdk item object
*/
convertMethod: function(method, url, globalParameters) {
convertMethod: function(method, url, globalParameters, pathVariables) {
let item = new SDK.Item(),
request = new SDK.Request(),
requestUrl = new SDK.Url(),
mediaType = globalParameters.mediaType || 'application/json',
securedBy = method.securedBy || globalParameters.securedBy;
securedBy = method.securedBy || globalParameters.securedBy,
requestName = _.get(method, 'displayName', url.replace('{{baseUrl}}', ''));

method.is && (method = addTraitsToMethod(method, globalParameters.traits));
if (method.queryParameters) {
Expand All @@ -306,18 +393,25 @@ helper = {
if (method.queryString) {
url = url.concat(helper.constructQueryString(method.queryString, globalParameters.types));
}
method.description && (item.description = (method.description));
method.description && (request.description = method.description);
method.body && (request.body = new SDK.RequestBody({
mode: 'raw',
raw: JSON.stringify(helper.convertBody(method.body, globalParameters.types, request))
})) && request.headers.add(helper.getContentTypeHeader(mediaType));
method.responses && (item.responses.add(helper.convertResponse(method.responses, globalParameters.types)));
securedBy && (request.auth = helper.convertSecurityScheme(securedBy[0], globalParameters.securitySchemes));
request.url = url;
request.method = method.method;
requestUrl.update(url);

// Add path variables of url to request
if (pathVariables) {
requestUrl.variables = pathVariables;
}
request.url = requestUrl;
request.method = _.toUpper(method.method);
_.forEach(method.headers, (header) => {
request.headers.add(helper.convertHeader(header, globalParameters.types));
});
item.name = requestName;
item.request = request;

return item;
Expand All @@ -331,16 +425,24 @@ helper = {
* @param {Object} params - raml url parameters object
* @param {Object} types - raml types map
*
* @returns {string} url - postman url string
* @returns {Object} contains url - postman url string, variables - path variable array
*/
addParametersToUrl: function(baseUrl, params, types) {
let paramToReplace,
paramToBeReplaced,
variables = [],
url = baseUrl,
value,
type;

_.forEach(params, (param, key) => {
variables.push({
key: key,
value: param.default || '',
description: (param.description || '') + (param.enum ?
' (This can only be one of ' + param.enum.toString() + ')' : '')
});

paramToReplace = key;
value = param.default || param.example;
if (param.type[0] !== 'string' &&
Expand All @@ -349,12 +451,14 @@ helper = {
type = param.type[0];
value = types[type].default || types[type].example || value;
}
value && (paramToReplace = `${key}=${value}/`);
paramToBeReplaced = '{' + key + '}';
url = url.replace(paramToBeReplaced, ':' + paramToReplace);
});

return url;
return {
url: url,
variables: variables
};
},

/**
Expand All @@ -364,27 +468,32 @@ helper = {
* @param {string} baseUrl - base url string
* @param {String} res - raml resource json
* @param {Object} globalParameters - Object with parameters given at root level of raml spec
* @param {Array} pathVariables - Array with all path variables and it's value and decription.
*
* @returns {Object} folder - postman sdk ItemGroup object
*/
convertResources: function(baseUrl, res, globalParameters) {
convertResources: function(baseUrl, res, globalParameters, pathVariables = []) {
var folder = new SDK.ItemGroup(),
url;
url,
convertedUrlAndVars;

url = '{{' + baseUrl.key + '}}' +
helper.addParametersToUrl(res.displayName, res.uriParameters, globalParameters.types);
convertedUrlAndVars = helper.addParametersToUrl(res.relativeUri, res.uriParameters, globalParameters.types);
url = baseUrl.value + convertedUrlAndVars.url;
pathVariables = _.concat(pathVariables, convertedUrlAndVars.variables);

baseUrl.value = url;
res.displayName && (folder.name = res.displayName);
res.description && (folder.description = res.description);
res.is && res.methods.forEach(function (method) {
method.is = res.is;
});

res.methods && res.methods.forEach(function (method) {
folder.items.add(helper.convertMethod(method, url, globalParameters));
folder.items.add(helper.convertMethod(method, url, globalParameters, pathVariables));
});

res.resources && res.resources.forEach(function (resource) {
folder.items.add(helper.convertResources(baseUrl, resource, globalParameters));
folder.items.add(helper.convertResources(baseUrl, resource, globalParameters, pathVariables));
});

return folder;
Expand Down
41 changes: 35 additions & 6 deletions test/fixtures/valid-raml/ramlSpecBasicCollection.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,20 @@
}
],
"event": [],
"name": "/users/search",
"request": {
"url": "{{baseUrl}}/search",
"method": "get",
"url": {
"path": [
"users",
"search"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variables": []
},
"method": "GET",
"auth": {
"type": "basic",
"description": {
Expand All @@ -39,8 +50,19 @@
{
"response": [],
"event": [],
"name": "/users/search",
"request": {
"url": "{{baseUrl}}/search",
"url": {
"path": [
"users",
"search"
],
"host": [
"{{baseUrl}}"
],
"query": [],
"variables": []
},
"header": [
{
"key": "Content-Type",
Expand All @@ -55,7 +77,7 @@
"value": "OtherExample"
}
],
"method": "post",
"method": "POST",
"body": {
"mode": "raw",
"raw": "{\"firstname\":\"someName\",\"lastname\":\"someLastName\",\"age\":10}"
Expand All @@ -81,9 +103,16 @@
"event": [],
"variable": [
{
"description": {
"content": "This is version of API schema.",
"type": "text/plain"
},
"type": "any",
"value": "https://api.BasicRamlAPI.com/:version",
"key": "baseUrl"
"value": "v3"
},
{
"type": "string",
"value": "https://api.BasicRamlAPI.com/{{version}}"
}
],
"info": {
Expand Down
12 changes: 12 additions & 0 deletions test/fixtures/valid-raml/ramlSpecNameAndDesc.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#%RAML 1.0
title: The Naming And Description One
version: v5
baseUri: https://name.me/{version}
/users:
displayName: Folder named /users
get:
description: Description of /users GET req
/teams:
description: Description of Folder named /teams
get:
displayName: GET req of /admins
Loading