From dd776bf4e83382b55a7af466278a81bc3221b76a Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Thu, 5 Nov 2020 15:49:06 +0330 Subject: [PATCH 01/35] Initial commit for Kotlin-Retrofit2 plugin --- codegens/kotlin-retrofit2/.gitignore | 50 ++ codegens/kotlin-retrofit2/.npmignore | 76 +++ codegens/kotlin-retrofit2/README.md | 42 ++ codegens/kotlin-retrofit2/index.js | 1 + codegens/kotlin-retrofit2/lib/index.js | 345 +++++++++++++ codegens/kotlin-retrofit2/lib/lodash.js | 455 ++++++++++++++++++ codegens/kotlin-retrofit2/lib/util.js | 123 +++++ codegens/kotlin-retrofit2/npm-shrinkwrap.json | 5 + codegens/kotlin-retrofit2/npm/test-lint.js | 57 +++ codegens/kotlin-retrofit2/npm/test-newman.js | 59 +++ codegens/kotlin-retrofit2/npm/test-unit.js | 59 +++ codegens/kotlin-retrofit2/npm/test.js | 20 + codegens/kotlin-retrofit2/package.json | 34 ++ .../test/newman/newman.test.js | 16 + .../test/unit/convert.test.js | 150 ++++++ 15 files changed, 1492 insertions(+) create mode 100644 codegens/kotlin-retrofit2/.gitignore create mode 100644 codegens/kotlin-retrofit2/.npmignore create mode 100644 codegens/kotlin-retrofit2/README.md create mode 100644 codegens/kotlin-retrofit2/index.js create mode 100644 codegens/kotlin-retrofit2/lib/index.js create mode 100644 codegens/kotlin-retrofit2/lib/lodash.js create mode 100644 codegens/kotlin-retrofit2/lib/util.js create mode 100644 codegens/kotlin-retrofit2/npm-shrinkwrap.json create mode 100644 codegens/kotlin-retrofit2/npm/test-lint.js create mode 100644 codegens/kotlin-retrofit2/npm/test-newman.js create mode 100644 codegens/kotlin-retrofit2/npm/test-unit.js create mode 100644 codegens/kotlin-retrofit2/npm/test.js create mode 100644 codegens/kotlin-retrofit2/package.json create mode 100644 codegens/kotlin-retrofit2/test/newman/newman.test.js create mode 100644 codegens/kotlin-retrofit2/test/unit/convert.test.js diff --git a/codegens/kotlin-retrofit2/.gitignore b/codegens/kotlin-retrofit2/.gitignore new file mode 100644 index 000000000..34f4d897c --- /dev/null +++ b/codegens/kotlin-retrofit2/.gitignore @@ -0,0 +1,50 @@ +.DS_Store + +#Obj-c files +*.m +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Coverage directory used by tools like istanbul +.coverage + +# node-waf configuration +.lock-wscript + + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ +/.idea/ +pubspec.lock +pubspec.yaml +.packages +snippet.dart +.dart_tool/ diff --git a/codegens/kotlin-retrofit2/.npmignore b/codegens/kotlin-retrofit2/.npmignore new file mode 100644 index 000000000..79ad2ba5f --- /dev/null +++ b/codegens/kotlin-retrofit2/.npmignore @@ -0,0 +1,76 @@ +### NPM Specific: Disregard recursive project files +### =============================================== +/.editorconfig +/.gitmodules +/test + +### Borrowed from .gitignore +### ======================== + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +snippet.swift + +out/ diff --git a/codegens/kotlin-retrofit2/README.md b/codegens/kotlin-retrofit2/README.md new file mode 100644 index 000000000..909745533 --- /dev/null +++ b/codegens/kotlin-retrofit2/README.md @@ -0,0 +1,42 @@ + +> Converts Postman-SDK Request into code snippet for kotlin retrofit2. + +#### Prerequisites +To run Code-Gen, ensure that you have NodeJS >= v8. A copy of the NodeJS installable can be downloaded from https://nodejs.org/en/download/package-manager. + +## Using the Module +The module will expose an object which will have property `convert` which is the function for converting the Postman-SDK request to kotlin retrofit2 code snippet. + +### convert function +Convert function takes three parameters + +* `request` - Postman-SDK Request Object + +* `options` - options is an object which hsa following properties + * `indentType` - String denoting type of indentation for code snippet. eg: 'Space', 'Tab' + * `indentCount` - The number of indentation characters to add per code level + * `trimRequestBody` - Whether or not request body fields should be trimmed + * `includeBoilerplate` - Include class definition and import statements in snippet + * `followRedirect` - Automatically follow HTTP redirects + +* `callback` - callback function with first parameter as error and second parameter as string for code snippet + +##### Example: +```js +var request = new sdk.Request('www.google.com'), //using postman sdk to create request + options = { + indentCount: 3, + indentType: 'Space', + requestTimeout: 200, + trimRequestBody: true + }; +convert(request, options, function(error, snippet) { + if (error) { + // handle error + } + // handle snippet +}); +``` +### Guidelines for using generated snippet + +* This module doesn't support cookies. diff --git a/codegens/kotlin-retrofit2/index.js b/codegens/kotlin-retrofit2/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/kotlin-retrofit2/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js new file mode 100644 index 000000000..e8a68aa8c --- /dev/null +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -0,0 +1,345 @@ +var _ = require('./lodash'), + sanitizeOptions = require('./util').sanitizeOptions, + sanitize = require('./util').sanitize, + addFormParam = require('./util').addFormParam, + self; + +/** + * Parses Url encoded data + * + * @param {Object} body body data + * @param {String} indent indentation required for code snippet + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseUrlEncoded (body, indent, trim) { + var bodySnippet = 'request.bodyFields = {', + enabledBodyList = _.reject(body, 'disabled'), + bodyDataMap; + if (!_.isEmpty(enabledBodyList)) { + bodyDataMap = _.map(enabledBodyList, function (value) { + return `${indent}'${sanitize(value.key, trim)}': '${sanitize(value.value, trim)}'`; + }); + bodySnippet += '\n' + bodyDataMap.join(',\n') + '\n'; + } + bodySnippet += '};'; + return bodySnippet; +} + +/** + * Parses Raw data + * + * @param {Object} body Raw body data + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseRawBody (body, trim) { + return `request.body = '''${sanitize(body, trim)}''';`; +} + +/** + * Parses GraphQL body + * + * @param {Object} body GraphQL body + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseGraphQLBody (body, trim) { + var bodySnippet = '', + query = body.query, + graphqlVariables; + try { + graphqlVariables = JSON.parse(body.variables); + } + catch (e) { + graphqlVariables = {}; + } + + bodySnippet += `request.body = '''${sanitize(JSON.stringify({ + query: query, + variables: graphqlVariables + }), trim)}''';\n`; + + return bodySnippet; +} + +/** + * Parses form data body from request + * + * @param {Object} body form data Body + * @param {String} indent indentation required for code snippet + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseFormData (body, indent, trim) { + let bodySnippet = '', + formDataArray = [], + formDataFileArray = [], + key, + value; + + if (_.isEmpty(body)) { + return bodySnippet; + } + + _.forEach(body, function (data) { + key = trim ? data.key.trim() : data.key; + value = trim ? data.value.trim() : data.value; + if (!data.disabled) { + if (data.type === 'file') { + formDataFileArray.push(`request.files.add(await http.MultipartFile.fromPath('${key}', '${data.src}'));`); + } + else { + formDataArray.push(`${indent}'${sanitize(key)}': '${sanitize(value, trim)}'`); + } + } + }); + + if (formDataArray.length > 0) { + bodySnippet += 'request.fields.addAll({\n'; + bodySnippet += formDataArray.join(',\n'); + bodySnippet += '\n});\n'; + } + + if (formDataFileArray.length > 0) { + bodySnippet += formDataFileArray.join('\n'); + } + + return bodySnippet; +} + +/** + * Parses Body from the Request + * + * @param {Object} body body object from request. + * @param {String} indent indentation required for code snippet + * @param {trim} trim indicates whether to trim string or not + */ +function parseBody (body, indent, trim) { + if (!_.isEmpty(body)) { + switch (body.mode) { + case 'urlencoded': + return parseUrlEncoded(body.urlencoded, indent, trim); + case 'raw': + return parseRawBody(body.raw, trim); + case 'formdata': + return parseFormData(body.formdata, indent, trim); + case 'graphql': + return parseGraphQLBody(body.graphql, trim); + case 'file': + return 'request.body = r\'\';\n'; + default: + return ''; + } + } + return ''; +} + +/** + * Parses headers from the request. + * + * @param {Object} headersArray array containing headers + * @param {String} indent indentation required for code snippet + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseHeaders (headersArray, indent, trim) { + var headerString = '', + headerDictionary = []; + if (_.isEmpty(headersArray)) { + return headerString; + } + + headerString += 'var headers = {\n'; + + _.forEach(headersArray, function (header) { + if (!header.disabled) { + headerDictionary.push(indent + '\'' + header.key + '\': \'' + sanitize(header.value, trim) + '\''); + } + }); + + headerString += headerDictionary.join(',\n'); + headerString += '\n};\n'; + + return headerString; +} + +self = module.exports = { + convert: function (request, options, callback) { + var indent, + codeSnippet = '', + headerSnippet = '', + footerSnippet = '', + trim, + timeout, + followRedirect; + options = sanitizeOptions(options, self.getOptions()); + if (options.includeBoilerplate) { + headerSnippet = 'import retrofit2.Call\n'; + headerSnippet = 'import retrofit2.Callback\n'; + headerSnippet = 'import retrofit2.Response\n'; + headerSnippet = 'import retrofit2.Retrofit\n'; + headerSnippet = 'import retrofit2.http.GET\n'; + headerSnippet = 'import retrofit2.http.Path\n'; + headerSnippet = 'import retrofit2.converter.gson.GsonConverterFactory\n\n'; + + // TODO: add interface implementation in here + + headerSnippet += 'fun main() {\n'; + footerSnippet = '}\n'; + } + trim = options.trimRequestBody; + indent = options.indentType === 'Tab' ? '\t' : ' '; + indent = indent.repeat(options.indentCount); + timeout = options.requestTimeout; + followRedirect = options.followRedirect; + + if (!_.isFunction(callback)) { + throw new Error('Callback is not valid function'); + } + + if (request.body && !request.headers.has('Content-Type')) { + if (request.body.mode === 'file') { + request.addHeader({ + key: 'Content-Type', + value: 'text/plain' + }); + } + else if (request.body.mode === 'graphql') { + request.addHeader({ + key: 'Content-Type', + value: 'application/json' + }); + } + } + + // The following code handles multiple files in the same formdata param. + // It removes the form data params where the src property is an array of filepath strings + // Splits that array into different form data params with src set as a single filepath string + if (request.body && request.body.mode === 'formdata') { + let formdata = request.body.formdata, + formdataArray = []; + formdata.members.forEach((param) => { + let key = param.key, + type = param.type, + disabled = param.disabled, + contentType = param.contentType; + // check if type is file or text + if (type === 'file') { + // if src is not of type string we check for array(multiple files) + if (typeof param.src !== 'string') { + // if src is an array(not empty), iterate over it and add files as separate form fields + if (Array.isArray(param.src) && param.src.length) { + param.src.forEach((filePath) => { + addFormParam(formdataArray, key, param.type, filePath, disabled, contentType); + }); + } + // if src is not an array or string, or is an empty array, add a placeholder for file path(no files case) + else { + addFormParam(formdataArray, key, param.type, '/path/to/file', disabled, contentType); + } + } + // if src is string, directly add the param with src as filepath + else { + addFormParam(formdataArray, key, param.type, param.src, disabled, contentType); + } + } + // if type is text, directly add it to formdata array + else { + addFormParam(formdataArray, key, param.type, param.value, disabled, contentType); + } + }); + request.body.update({ + mode: 'formdata', + formdata: formdataArray + }); + } + + const headers = parseHeaders(request.headers.toJSON(), indent, trim), + requestBody = request.body ? request.body.toJSON() : {}, + body = parseBody(requestBody, indent, trim) + '\n'; + + codeSnippet += headers; + + if (requestBody && requestBody.mode === 'formdata') { + codeSnippet += `var request = http.MultipartRequest('${request.method.toUpperCase()}',` + + ` Uri.parse('${request.url.toString()}'));\n`; + } + else { + codeSnippet += `var request = http.Request('${request.method.toUpperCase()}',` + + ` Uri.parse('${request.url.toString()}'));\n`; + } + + if (body !== '') { + codeSnippet += body; + } + if (headers !== '') { + codeSnippet += 'request.headers.addAll(headers);\n'; + } + if (!followRedirect) { + codeSnippet += 'request.followRedirects = false;\n'; + } + + codeSnippet += '\n'; + + codeSnippet += 'http.StreamedResponse response = await request.send()'; + if (timeout > 0) { + codeSnippet += `.timeout(Duration(milliseconds: ${timeout}))`; + } + codeSnippet += ';\n'; + codeSnippet += 'if (response.statusCode == 200) {\n'; + codeSnippet += `${indent}print(await response.stream.bytesToString());\n`; + codeSnippet += '} else {\n'; + codeSnippet += `${indent}print(response.reasonPhrase);\n`; + codeSnippet += '}\n'; + + // if boilerplate is included then two more indent needs to be added in snippet + (options.includeBoilerplate) && + (codeSnippet = indent + codeSnippet.split('\n').join('\n' + indent) + '\n'); + + callback(null, headerSnippet + codeSnippet + footerSnippet); + }, + getOptions: function () { + return [ + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 2, + description: 'Set the number of indentation characters to add per code level' + }, + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Set request timeout', + id: 'requestTimeout', + type: 'positiveInteger', + default: 0, + description: 'Set number of milliseconds the request should wait for a response' + + ' before timing out (use 0 for infinity)' + }, + { + name: 'Trim request body fields', + id: 'trimRequestBody', + type: 'boolean', + default: false, + description: 'Remove white space and additional lines that may affect the server\'s response' + }, + { + name: 'Include boilerplate', + id: 'includeBoilerplate', + type: 'boolean', + default: false, + description: 'Include class definition and import statements in snippet' + }, + { + name: 'Follow redirects', + id: 'followRedirect', + type: 'boolean', + default: true, + description: 'Automatically follow HTTP redirects' + } + ]; + } +}; diff --git a/codegens/kotlin-retrofit2/lib/lodash.js b/codegens/kotlin-retrofit2/lib/lodash.js new file mode 100644 index 000000000..5be147afd --- /dev/null +++ b/codegens/kotlin-retrofit2/lib/lodash.js @@ -0,0 +1,455 @@ +/* istanbul ignore next */ +module.exports = { + + /** + * Checks if `value` is an empty object, array or string. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Values such as strings, arrays are considered empty if they have a `length` of `0`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * isEmpty(null) + * // => true + * + * isEmpty(true) + * // => true + * + * isEmpty(1) + * // => true + * + * isEmpty([1, 2, 3]) + * // => false + * + * isEmpty('abc') + * // => false + * + * isEmpty({ 'a': 1 }) + * // => false + */ + isEmpty: function (value) { + // eslint-disable-next-line lodash/prefer-is-nil + if (value === null || value === undefined) { + return true; + } + if (Array.isArray(value) || typeof value === 'string' || typeof value.splice === 'function') { + return !value.length; + } + + for (const key in value) { + if (Object.prototype.hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + }, + + /** + * Checks if `value` is `undefined`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * isUndefined(void 0) + * // => true + * + * isUndefined(null) + * // => false + */ + isUndefined: function (value) { + return value === undefined; + }, + + /** + * Checks if `func` is classified as a `Function` object. + * + * @param {*} func The value to check. + * @returns {boolean} Returns `true` if `func` is a function, else `false`. + * @example + * + * isFunction(self.isEmpty) + * // => true + * + * isFunction(/abc/) + * // => false + */ + isFunction: function (func) { + return typeof func === 'function'; + }, + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * capitalize('FRED') + * // => 'Fred' + * + * capitalize('john') + * // => 'John' + */ + + capitalize: function (string) { + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + }, + + /** + * Reduces `array` to a value which is the accumulated result of running + * each element in `array` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `array` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, array). + * + * @param {Array} array The Array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @example + * + * reduce([1, 2], (sum, n) => sum + n, 0) + * // => 3 + * + */ + reduce: function (array, iteratee, accumulator) { + return array.reduce(iteratee, accumulator); + }, + + /** + * Iterates over elements of `array`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function|object} predicate The function/object invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * filter(users, ({ active }) => active) + * // => object for ['barney'] + */ + filter: function (array, predicate) { + if (typeof predicate === 'function') { + return array.filter(predicate); + } + var key = Object.keys(predicate), + val = predicate[key], + res = []; + array.forEach(function (item) { + if (item[key] && item[key] === val) { + res.push(item); + } + }); + return res; + }, + + /** + * The opposite of `filter` this method returns the elements of `array` + * that `predicate` does **not** return truthy for. + * + * @param {Array} array collection to iterate over. + * @param {String} predicate The String that needs to have truthy value, invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * reject(users, 'active') + * // => object for ['fred'] + */ + reject: function (array, predicate) { + var res = []; + array.forEach((object) => { + if (!object[predicate]) { + res.push(object); + } + }); + return res; + }, + + /** + * Creates an array of values by running each element of `array` thru `iteratee`. + * The iteratee is invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n + * } + * + * map([4, 8], square) + * // => [16, 64] + */ + map: function (array, iteratee) { + return array.map(iteratee); + }, + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @example + * + * forEach([1, 2], value => console.log(value)) + * // => Logs `1` then `2`. + * + * forEach({ 'a': 1, 'b': 2 }, (value, key) => console.log(key)) + * // => Logs 'a' then 'b' + */ + + forEach: function (collection, iteratee) { + if (collection === null) { + return null; + } + + if (Array.isArray(collection)) { + return collection.forEach(iteratee); + } + const iterable = Object(collection), + props = Object.keys(collection); + var index = -1, + key, i; + + for (i = 0; i < props.length; i++) { + key = props[++index]; + iteratee(iterable[key], key, iterable); + } + return collection; + }, + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise it checks if the `value` is present + * as a key in a `collection` object. + * + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + includes: function (collection, value) { + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.includes(value); + } + for (var key in collection) { + if (collection.hasOwnProperty(key)) { + if (collection[key] === value) { + return true; + } + } + } + return false; + }, + + /** + * Gets the size of `collection` by returning its length for array and strings. + * For objects it returns the number of enumerable string keyed + * properties. + * + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * size([1, 2, 3]) + * // => 3 + * + * size({ 'a': 1, 'b': 2 }) + * // => 2 + * + * size('pebbles') + * // => 7 + */ + size: function (collection) { + // eslint-disable-next-line lodash/prefer-is-nil + if (collection === null || collection === undefined) { + return 0; + } + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.length; + } + + return Object.keys(collection).length; + }, + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + join: function (array, separator) { + if (array === null) { + return ''; + } + return array.join(separator); + }, + + /** + * Removes trailing whitespace or specified characters from `string`. + * + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + * @example + * + * trimEnd(' abc ') + * // => ' abc' + * + * trimEnd('-_-abc-_-', '_-') + * // => '-_-abc' + */ + trimEnd: function (string, chars) { + if (!string) { + return ''; + } + if (string && !chars) { + return string.replace(/\s*$/, ''); + } + chars += '$'; + return string.replace(new RegExp(chars, 'g'), ''); + }, + + /** + * Returns the index of the first + * element `predicate` returns truthy for. + * + * @param {Array} array The array to inspect. + * @param {Object} predicate The exact object to be searched for in the array. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * _.findIndex(users, {'active' : false}); + * // => 0 + * + */ + findIndex: function (array, predicate) { + var length = array === null ? 0 : array.length, + index = -1, + keys = Object.keys(predicate), + found, i; + if (!length) { + return -1; + } + for (i = 0; i < array.length; i++) { + found = true; + // eslint-disable-next-line no-loop-func + keys.forEach((key) => { + if (!(array[i][key] && array[i][key] === predicate[key])) { + found = false; + } + }); + if (found) { + index = i; + break; + } + } + return index; + }, + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @param {Object} object The object to query. + * @param {string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * const object = { a: {b : 'c'} } + * + * + * get(object, 'a.b.c', 'default') + * // => 'default' + * + * get(object, 'a.b', 'default') + * // => 'c' + */ + get: function (object, path, defaultValue) { + if (object === null) { + return undefined; + } + var arr = path.split('.'), + res = object, + i; + for (i = 0; i < arr.length; i++) { + res = res[arr[i]]; + if (res === undefined) { + return defaultValue; + } + } + return res; + }, + + /** + * Checks if `predicate` returns truthy for **all** elements of `array`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * every([true, 1, null, 'yes'], Boolean) + * // => false + */ + every: function (array, predicate) { + var index = -1, + length = array === null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + +}; diff --git a/codegens/kotlin-retrofit2/lib/util.js b/codegens/kotlin-retrofit2/lib/util.js new file mode 100644 index 000000000..4d1801f91 --- /dev/null +++ b/codegens/kotlin-retrofit2/lib/util.js @@ -0,0 +1,123 @@ +module.exports = { + /** + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' + * and trim input if required + * + * @param {String} inputString + * @param {Boolean} [trim] - indicates whether to trim string or not + * @returns {String} + */ + sanitize: function (inputString, trim) { + if (typeof inputString !== 'string') { + return ''; + } + inputString = inputString.replace(/\\/g, '\\\\') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t') + .replace(/'/g, '\\\'') + .replace(/\$/g, '\\$'); + return trim ? inputString.trim() : inputString; + + }, + + /** + * sanitizes input options + * + * @param {Object} options - Options provided by the user + * @param {Array} optionsArray - options array received from getOptions function + * + * @returns {Object} - Sanitized options object + */ + sanitizeOptions: function (options, optionsArray) { + var result = {}, + defaultOptions = {}, + id; + optionsArray.forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + return result; + }, + + /** + * + * @param {Array} array - form data array + * @param {String} key - key of form data param + * @param {String} type - type of form data param(file/text) + * @param {String} val - value/src property of form data param + * @param {String} disabled - Boolean denoting whether the param is disabled or not + * @param {String} contentType - content type header of the param + * + * Appends a single param to form data array + */ + addFormParam: function (array, key, type, val, disabled, contentType) { + if (type === 'file') { + array.push({ + key: key, + type: type, + src: val, + disabled: disabled, + contentType: contentType + }); + } + else { + array.push({ + key: key, + type: type, + value: val, + disabled: disabled, + contentType: contentType + }); + } + } +}; diff --git a/codegens/kotlin-retrofit2/npm-shrinkwrap.json b/codegens/kotlin-retrofit2/npm-shrinkwrap.json new file mode 100644 index 000000000..9339a9b6c --- /dev/null +++ b/codegens/kotlin-retrofit2/npm-shrinkwrap.json @@ -0,0 +1,5 @@ +{ + "name": "@postman/codegen-dart", + "version": "0.0.1", + "lockfileVersion": 1 +} diff --git a/codegens/kotlin-retrofit2/npm/test-lint.js b/codegens/kotlin-retrofit2/npm/test-lint.js new file mode 100644 index 000000000..91bdef168 --- /dev/null +++ b/codegens/kotlin-retrofit2/npm/test-lint.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node +require('shelljs/global'); + +var chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(exit); diff --git a/codegens/kotlin-retrofit2/npm/test-newman.js b/codegens/kotlin-retrofit2/npm/test-newman.js new file mode 100644 index 000000000..e8f970755 --- /dev/null +++ b/codegens/kotlin-retrofit2/npm/test-newman.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all newman tests. +// --------------------------------------------------------------------------------------------------------------------- + +require('shelljs/global'); + +// set directories and files for test and coverage report +var path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + test('-d', COV_REPORT_PATH) && rm('-rf', COV_REPORT_PATH); + mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(exit); diff --git a/codegens/kotlin-retrofit2/npm/test-unit.js b/codegens/kotlin-retrofit2/npm/test-unit.js new file mode 100644 index 000000000..1bd787adb --- /dev/null +++ b/codegens/kotlin-retrofit2/npm/test-unit.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +require('shelljs/global'); + +// set directories and files for test and coverage report +var path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running unit tests using mocha on node...')); + + test('-d', COV_REPORT_PATH) && rm('-rf', COV_REPORT_PATH); + mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(exit); diff --git a/codegens/kotlin-retrofit2/npm/test.js b/codegens/kotlin-retrofit2/npm/test.js new file mode 100644 index 000000000..cff8a6c18 --- /dev/null +++ b/codegens/kotlin-retrofit2/npm/test.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-newman'), + require('./test-unit') + // Add a separate folder for every new suite of tests + // require('./test-unit') + // require('./test-browser') + // require('./test-integration') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/kotlin-retrofit2/package.json b/codegens/kotlin-retrofit2/package.json new file mode 100644 index 000000000..74710303c --- /dev/null +++ b/codegens/kotlin-retrofit2/package.json @@ -0,0 +1,34 @@ +{ + "name": "@postman/codegen-kotlin-retrofit2", + "version": "0.0.1", + "description": "Converts postman request into kotlin retrofit2 code snippet", + "main": "index.js", + "com_postman_plugin": { + "type": "code_generator", + "lang": "Kotlin", + "variant": "retrofit2", + "syntax_mode": "kotlin" + }, + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-unit": "node npm/test-unit.js", + "test-newman": "node npm/test-newman.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/kotlin-retrofit2", + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=8" + } +} diff --git a/codegens/kotlin-retrofit2/test/newman/newman.test.js b/codegens/kotlin-retrofit2/test/newman/newman.test.js new file mode 100644 index 000000000..2b4e3e1ca --- /dev/null +++ b/codegens/kotlin-retrofit2/test/newman/newman.test.js @@ -0,0 +1,16 @@ +var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../lib/index').convert; + +describe('Convert for different types of request', function () { + var options = {indentCount: 2, indentType: 'Space', includeBoilerplate: true }, + testConfig = { + runScript: 'dart snippet.dart', + fileName: 'snippet.dart', + headerSnippet: '', + // http uses Map to store headers, so there is no way to + // keep multiple headers with the same key + skipCollections: ['sameNameHeadersCollection'] + }; + + runNewmanTest(convert, options, testConfig); +}); diff --git a/codegens/kotlin-retrofit2/test/unit/convert.test.js b/codegens/kotlin-retrofit2/test/unit/convert.test.js new file mode 100644 index 000000000..05e943c10 --- /dev/null +++ b/codegens/kotlin-retrofit2/test/unit/convert.test.js @@ -0,0 +1,150 @@ +var convert = require('../../index').convert, + expect = require('chai').expect, + sdk = require('postman-collection'); + +// Disable check with expected snippets as we now have proper newman tests +describe('Dart Converter', function () { + it('should add timeout if requestTimeout options is used', function () { + var request = new sdk.Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'body': { + 'mode': 'raw', + 'raw': '{\n "json": "Test-Test"\n}' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, {requestTimeout: 5000}, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('.timeout(Duration(milliseconds: 5000))'); + }); + }); + + it('should use http.MultipartRequest for formdata requests', function () { + var request = new sdk.Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'formdata', + 'formdata': [] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, {}, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('http.MultipartRequest'); + }); + }); + + it('should add code for followRedirects if given in the option', function () { + var request = new sdk.Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ] + } + }); + convert(request, { followRedirect: false }, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('request.followRedirects = false;'); + }); + }); + + it('should add boilerplate if given in the option', function () { + var request = new sdk.Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ] + } + }); + convert(request, { includeBoilerplate: true }, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('import \'package:http/http.dart\' as http;'); + expect(snippet).to.contain('void main() async {'); + }); + }); + + it('should add correct indentation', function () { + var request = new sdk.Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'hello', + 'value': 'world', + 'type': 'text' + } + ] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, { includeBoilerplate: true, indentType: 'Tab' }, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('\t\t\'hello\': \'world\''); + }); + }); +}); From 545f7305e8f2593e3bcbc731833a753c428d3876 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:15:41 +0330 Subject: [PATCH 02/35] Add double quote in sanitize method --- codegens/kotlin-retrofit2/lib/util.js | 1 + 1 file changed, 1 insertion(+) diff --git a/codegens/kotlin-retrofit2/lib/util.js b/codegens/kotlin-retrofit2/lib/util.js index 4d1801f91..7e71b7ee2 100644 --- a/codegens/kotlin-retrofit2/lib/util.js +++ b/codegens/kotlin-retrofit2/lib/util.js @@ -16,6 +16,7 @@ module.exports = { .replace(/\r/g, '\\r') .replace(/\t/g, '\\t') .replace(/'/g, '\\\'') + .replace(/"/g, '\\"') .replace(/\$/g, '\\$'); return trim ? inputString.trim() : inputString; From 5d5d2b72279ee15bb90f065df0852f2cc52db859 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:17:03 +0330 Subject: [PATCH 03/35] Parse body fo Kotlin Retrofit --- codegens/kotlin-retrofit2/lib/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index e8a68aa8c..387a23cc1 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -12,16 +12,16 @@ var _ = require('./lodash'), * @param {Boolean} trim indicates whether to trim string or not */ function parseUrlEncoded (body, indent, trim) { - var bodySnippet = 'request.bodyFields = {', + var bodySnippet = 'val body = mapOf(', enabledBodyList = _.reject(body, 'disabled'), bodyDataMap; if (!_.isEmpty(enabledBodyList)) { bodyDataMap = _.map(enabledBodyList, function (value) { - return `${indent}'${sanitize(value.key, trim)}': '${sanitize(value.value, trim)}'`; + return `${indent}"${sanitize(value.key, trim)} to "${sanitize(value.value, trim)}`; }); bodySnippet += '\n' + bodyDataMap.join(',\n') + '\n'; } - bodySnippet += '};'; + bodySnippet += ')'; return bodySnippet; } @@ -32,7 +32,7 @@ function parseUrlEncoded (body, indent, trim) { * @param {Boolean} trim indicates whether to trim string or not */ function parseRawBody (body, trim) { - return `request.body = '''${sanitize(body, trim)}''';`; + return `val body = '''${sanitize(body, trim)}'''`; } /** From 5a269106bc3e0a6f246c8a640bd701d3147f2ca2 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:17:29 +0330 Subject: [PATCH 04/35] Add header generator for kotlin retrofit --- codegens/kotlin-retrofit2/lib/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 387a23cc1..7612aff83 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -145,16 +145,16 @@ function parseHeaders (headersArray, indent, trim) { return headerString; } - headerString += 'var headers = {\n'; + headerString += 'val headers = mapOf(\n'; _.forEach(headersArray, function (header) { if (!header.disabled) { - headerDictionary.push(indent + '\'' + header.key + '\': \'' + sanitize(header.value, trim) + '\''); + headerDictionary.push(indent + '"' + sanitize(header.key) + '" to "' + sanitize(header.value, trim) + '"'); } }); headerString += headerDictionary.join(',\n'); - headerString += '\n};\n'; + headerString += '\n)\n'; return headerString; } From 4aed922769c810d31f8f8e616f1524748a0886fd Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:19:06 +0330 Subject: [PATCH 05/35] Add getInterfaceMethodParams method --- codegens/kotlin-retrofit2/lib/index.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 7612aff83..3712c4f14 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -159,6 +159,23 @@ function parseHeaders (headersArray, indent, trim) { return headerString; } +/** + * Generate arguments for interface method + * + * @param {Array} variables in route path + */ +function getInterfaceMethodParams (variables) { + var variablesArray = []; + if (!variables || variables.members.length === 0) { + return ''; + } + + variables.members.forEach((variable) => { + variablesArray.push(`@Path("${variable.key}") ${variable.key}: ${_.capitalize(variable.type)}`); + }); + + return variablesArray.join(', '); +} self = module.exports = { convert: function (request, options, callback) { var indent, From 6c234c64775b9f25cba16b6f18df933856497f55 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:19:27 +0330 Subject: [PATCH 06/35] Add getInterfaceFunctionName method --- codegens/kotlin-retrofit2/lib/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 3712c4f14..f3cd22474 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -176,6 +176,18 @@ function getInterfaceMethodParams (variables) { return variablesArray.join(', '); } + +/** + * Parse interface method based on http method and web service path + * + * @param {String} httpMethod of web service request + * @param {String} path of web service request + */ +function getInterfaceFunctionName (httpMethod, path) { + const route = path.slice(-1).toString().replace(':', ''); + + return `${httpMethod.toLowerCase()}${_.capitalize(route)}`; +} self = module.exports = { convert: function (request, options, callback) { var indent, From 94e664bb387d30ba72f65929d42134086c0c6383 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:20:06 +0330 Subject: [PATCH 07/35] Add getServiceInterfaceName method --- codegens/kotlin-retrofit2/lib/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index f3cd22474..f45025775 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -188,6 +188,22 @@ function getInterfaceFunctionName (httpMethod, path) { return `${httpMethod.toLowerCase()}${_.capitalize(route)}`; } + +/** + * Get service name for interface name based one domain name. + * + * @param {String} domainName of web service request + */ +function getServiceInterfaceName (domainName) { + const serviceArray = []; + + domainName.split('-').forEach((service) => { + serviceArray.push(_.capitalize(service)); + }); + + return `${serviceArray.join('')}Service`; +} + self = module.exports = { convert: function (request, options, callback) { var indent, From f3c1710d62cd03269281c0365f3bc650de4e8107 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:20:37 +0330 Subject: [PATCH 08/35] Add generateRetrofitClientFactory method --- codegens/kotlin-retrofit2/lib/index.js | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index f45025775..28c380ef0 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -204,6 +204,34 @@ function getServiceInterfaceName (domainName) { return `${serviceArray.join('')}Service`; } +/** + * Generate retrofit client factory for configure timeout and follow redirect + * + * @param {String} timeout web service request + * @param {String} followRedirect of web service request + * @param {String} indent indentation required for code snippet + */ +function generateRetrofitClientFactory (timeout, followRedirect, indent) { + var timeoutFactoryString = 'val okHttpClient = OkHttpClient().newBuilder()\n'; + + if (timeout === 0 || followRedirect) { + return ''; + } + + timeoutFactoryString += `${indent}.connectTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; + timeoutFactoryString += `${indent}.readTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; + timeoutFactoryString += `${indent}.writeTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; + + if (!followRedirect) { + timeoutFactoryString += `${indent}.followRedirects(false)\n`; + timeoutFactoryString += `${indent}.followSslRedirects(false)\n`; + } + + timeoutFactoryString += `${indent}.build()\n\n`; + + return timeoutFactoryString; +} + self = module.exports = { convert: function (request, options, callback) { var indent, From 871670f58613ee1903351e2a256455c781774bf7 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:21:03 +0330 Subject: [PATCH 09/35] Add generateInterface method --- codegens/kotlin-retrofit2/lib/index.js | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 28c380ef0..becef0786 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -232,6 +232,45 @@ function generateRetrofitClientFactory (timeout, followRedirect, indent) { return timeoutFactoryString; } +/** + * Parses headers from the request. + * + * @param {String} name of web service request + * @param {String} method of web service request + * @param {String} path of web service request + * @param {String} variables in path + * @param {boolean} hasHeader in web service request + * @param {boolean} hasBody in web service request + * @param {String} indent indentation required for code snippet + */ +function generateInterface (name, method, path, variables, hasHeader, hasBody, indent) { + var interfaceString = '@JvmSuppressWildcards\n'; + + interfaceString += `interface ${getServiceInterfaceName(name)} {\n`; + interfaceString += `${indent}@${method.toUpperCase()}("${path}")\n`; + interfaceString += `${indent}fun `; + interfaceString += `${getInterfaceFunctionName(method, path)}(`; + + const functionArguments = []; + let paramString = getInterfaceMethodParams(variables); + if (hasHeader) { + functionArguments.push('@HeaderMap headers: Map'); + } + + if (hasBody) { + functionArguments.push('@Body body: Map'); + } + + if (paramString !== '') { + functionArguments.push(paramString); + } + + interfaceString += `${functionArguments.join(', ')}): Call`; + interfaceString += '\n}\n'; + + return interfaceString; +} + self = module.exports = { convert: function (request, options, callback) { var indent, From d6b59eff7050ff3c5b56940b24383aedb15a6d82 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:21:16 +0330 Subject: [PATCH 10/35] Add getRetrofitImportByMethod method --- codegens/kotlin-retrofit2/lib/index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index becef0786..552d4b797 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -271,6 +271,15 @@ function generateInterface (name, method, path, variables, hasHeader, hasBody, i return interfaceString; } +/** + * Parse retrofit import module based on request method + * + * @param {String} method of web service request + */ +function getRetrofitImportByMethod (method) { + return `import retrofit2.http.${method.toUpperCase()}`; +} + self = module.exports = { convert: function (request, options, callback) { var indent, From 9dc1ec7af45ab9d3583a6c33a3cf2ad5736e8ae8 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:21:42 +0330 Subject: [PATCH 11/35] Refactor kotlin-retrofit2 import modules --- codegens/kotlin-retrofit2/lib/index.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 552d4b797..b4f835646 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -292,17 +292,17 @@ self = module.exports = { options = sanitizeOptions(options, self.getOptions()); if (options.includeBoilerplate) { headerSnippet = 'import retrofit2.Call\n'; - headerSnippet = 'import retrofit2.Callback\n'; - headerSnippet = 'import retrofit2.Response\n'; - headerSnippet = 'import retrofit2.Retrofit\n'; - headerSnippet = 'import retrofit2.http.GET\n'; - headerSnippet = 'import retrofit2.http.Path\n'; - headerSnippet = 'import retrofit2.converter.gson.GsonConverterFactory\n\n'; - - // TODO: add interface implementation in here - + headerSnippet += 'import retrofit2.Callback\n'; + headerSnippet += 'import retrofit2.Response\n'; + headerSnippet += 'import retrofit2.Retrofit\n'; + headerSnippet += 'import retrofit2.http.Path\n'; + headerSnippet += `${getRetrofitImportByMethod(request.method)}\n`; + headerSnippet += 'import retrofit2.http.HeaderMap\n'; + headerSnippet += 'import retrofit2.converter.gson.GsonConverterFactory\n\n'; headerSnippet += 'fun main() {\n'; - footerSnippet = '}\n'; + + footerSnippet = '}\n\n'; + // TODO: add interface implementation in footerSnippet } trim = options.trimRequestBody; indent = options.indentType === 'Tab' ? '\t' : ' '; From eced72fcaf26e2db04efc9868d801a342422f5b5 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:21:58 +0330 Subject: [PATCH 12/35] Append body to snippet code --- codegens/kotlin-retrofit2/lib/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index b4f835646..d01c6272b 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -376,6 +376,7 @@ self = module.exports = { body = parseBody(requestBody, indent, trim) + '\n'; codeSnippet += headers; + codeSnippet += body; if (requestBody && requestBody.mode === 'formdata') { codeSnippet += `var request = http.MultipartRequest('${request.method.toUpperCase()}',` + From 9457d324b96d41e995858918849b90c05a9f064e Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:22:46 +0330 Subject: [PATCH 13/35] Generate snippet code for kotlin-retrofit just for raw body and simple request --- codegens/kotlin-retrofit2/lib/index.js | 64 ++++++++++++++++++-------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index d01c6272b..4fe3857a1 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -383,32 +383,58 @@ self = module.exports = { ` Uri.parse('${request.url.toString()}'));\n`; } else { - codeSnippet += `var request = http.Request('${request.method.toUpperCase()}',` + - ` Uri.parse('${request.url.toString()}'));\n`; - } + codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; - if (body !== '') { - codeSnippet += body; + codeSnippet += 'val retrofit = Retrofit.Builder()\n'; + codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; + codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; + + if (timeout > 0) { + codeSnippet += `${indent}.client(okHttpClient)\n`; + } + + codeSnippet += `${indent}.build()\n\n`; } + + let serviceName = request.url.host.slice(-2)[0]; + footerSnippet += generateInterface( + serviceName, + request.method, + request.url.path, + request.url.variables, + headers !== '', + body !== '', + indent + ); + + codeSnippet += `val service = retrofit.create(${getServiceInterfaceName(serviceName)}::class.java)\n`; + codeSnippet += `val serviceCall = service.${getInterfaceFunctionName(request.method, request.url.path)}(`; + if (headers !== '') { - codeSnippet += 'request.headers.addAll(headers);\n'; + codeSnippet += 'headers'; + + if (body !== '') { + codeSnippet += ', '; + } + else { + codeSnippet += ')\n'; + } } - if (!followRedirect) { - codeSnippet += 'request.followRedirects = false;\n'; + if (body !== '') { + codeSnippet += 'body)'; } + codeSnippet += '\n\n'; - codeSnippet += '\n'; + codeSnippet += 'serviceCall.enqueue(object : Callback {\n'; + codeSnippet += `${indent}override fun onResponse(call: Call, response: Response) {\n`; + codeSnippet += `${indent}${indent}println("Request success with response: \${response.body()}")\n`; + codeSnippet += `${indent}}\n\n`; - codeSnippet += 'http.StreamedResponse response = await request.send()'; - if (timeout > 0) { - codeSnippet += `.timeout(Duration(milliseconds: ${timeout}))`; - } - codeSnippet += ';\n'; - codeSnippet += 'if (response.statusCode == 200) {\n'; - codeSnippet += `${indent}print(await response.stream.bytesToString());\n`; - codeSnippet += '} else {\n'; - codeSnippet += `${indent}print(response.reasonPhrase);\n`; - codeSnippet += '}\n'; + codeSnippet += `${indent}override fun onFailure(call: Call, t: Throwable) {\n`; + codeSnippet += `${indent}${indent}println("Request has been failed for \${t.message} reason. $t")\n`; + codeSnippet += `${indent}}\n`; + + codeSnippet += '})\n'; // if boilerplate is included then two more indent needs to be added in snippet (options.includeBoilerplate) && From cf3f6b7efa2a8d001037c512c7ab6e9e06380b41 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:15:41 +0330 Subject: [PATCH 14/35] Add double quote in sanitize method From f146c4a50d61204d5854943632d6da1262e44b5f Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 13:17:03 +0330 Subject: [PATCH 15/35] Parse body fo Kotlin Retrofit From 0768d266032307aac669494f0efcf2c57593005e Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 14:40:41 +0330 Subject: [PATCH 16/35] Support for form-data format --- codegens/kotlin-retrofit2/lib/index.js | 74 ++++++++++++++++---------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 4fe3857a1..28945918a 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -70,6 +70,7 @@ function parseGraphQLBody (body, trim) { function parseFormData (body, indent, trim) { let bodySnippet = '', formDataArray = [], + bodyDataArray = [], formDataFileArray = [], key, value; @@ -78,27 +79,39 @@ function parseFormData (body, indent, trim) { return bodySnippet; } - _.forEach(body, function (data) { + formDataArray.push('val body = HashMap()'); + + _.forEach(body, function (data, index) { key = trim ? data.key.trim() : data.key; value = trim ? data.value.trim() : data.value; if (!data.disabled) { + let trimKey = `${key.replace(/\s+/g, '')}${index}`; if (data.type === 'file') { - formDataFileArray.push(`request.files.add(await http.MultipartFile.fromPath('${key}', '${data.src}'));`); + formDataFileArray.push(`val ${sanitize(trimKey)} = ` + + `RequestBody.create(MediaType.parse("multipart/form-data"), "${data.src}");`); } else { - formDataArray.push(`${indent}'${sanitize(key)}': '${sanitize(value, trim)}'`); + formDataArray.push(`val ${sanitize(trimKey)} =` + + ` RequestBody.create(MediaType.parse("text/plain"), "${sanitize(value, trim)}")`); } + + bodyDataArray.push(`body.put("${sanitize(key.trim())}", ${sanitize(trimKey)})`); } }); if (formDataArray.length > 0) { - bodySnippet += 'request.fields.addAll({\n'; - bodySnippet += formDataArray.join(',\n'); - bodySnippet += '\n});\n'; + bodySnippet += formDataArray.join('\n'); + bodySnippet += '\n\n'; } if (formDataFileArray.length > 0) { bodySnippet += formDataFileArray.join('\n'); + bodySnippet += '\n\n'; + } + + if (bodyDataArray.length > 0) { + bodySnippet += bodyDataArray.join('\n'); + bodySnippet += '\n'; } return bodySnippet; @@ -241,12 +254,17 @@ function generateRetrofitClientFactory (timeout, followRedirect, indent) { * @param {String} variables in path * @param {boolean} hasHeader in web service request * @param {boolean} hasBody in web service request + * @param {boolean} isFormData web service request * @param {String} indent indentation required for code snippet */ -function generateInterface (name, method, path, variables, hasHeader, hasBody, indent) { +function generateInterface (name, method, path, variables, + hasHeader, hasBody, isFormData, indent) { var interfaceString = '@JvmSuppressWildcards\n'; interfaceString += `interface ${getServiceInterfaceName(name)} {\n`; + if (isFormData) { + interfaceString += `${indent}@Multipart\n`; + } interfaceString += `${indent}@${method.toUpperCase()}("${path}")\n`; interfaceString += `${indent}fun `; interfaceString += `${getInterfaceFunctionName(method, path)}(`; @@ -257,9 +275,12 @@ function generateInterface (name, method, path, variables, hasHeader, hasBody, i functionArguments.push('@HeaderMap headers: Map'); } - if (hasBody) { + if (hasBody && !isFormData) { functionArguments.push('@Body body: Map'); } + else if (isFormData) { + functionArguments.push('@PartMap body: Map'); + } if (paramString !== '') { functionArguments.push(paramString); @@ -288,16 +309,15 @@ self = module.exports = { footerSnippet = '', trim, timeout, - followRedirect; + followRedirect, + isFormData = false; options = sanitizeOptions(options, self.getOptions()); if (options.includeBoilerplate) { headerSnippet = 'import retrofit2.Call\n'; + headerSnippet += `import retrofit2.http.*\n`; headerSnippet += 'import retrofit2.Callback\n'; headerSnippet += 'import retrofit2.Response\n'; headerSnippet += 'import retrofit2.Retrofit\n'; - headerSnippet += 'import retrofit2.http.Path\n'; - headerSnippet += `${getRetrofitImportByMethod(request.method)}\n`; - headerSnippet += 'import retrofit2.http.HeaderMap\n'; headerSnippet += 'import retrofit2.converter.gson.GsonConverterFactory\n\n'; headerSnippet += 'fun main() {\n'; @@ -373,16 +393,17 @@ self = module.exports = { const headers = parseHeaders(request.headers.toJSON(), indent, trim), requestBody = request.body ? request.body.toJSON() : {}, - body = parseBody(requestBody, indent, trim) + '\n'; + body = parseBody(requestBody, indent, trim); codeSnippet += headers; codeSnippet += body; if (requestBody && requestBody.mode === 'formdata') { - codeSnippet += `var request = http.MultipartRequest('${request.method.toUpperCase()}',` + - ` Uri.parse('${request.url.toString()}'));\n`; + // codeSnippet += `var request = http.MultipartRequest('${request.method.toUpperCase()}',` + + // ` Uri.parse('${request.url.toString()}'));\n`; + isFormData = true; } - else { + codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; codeSnippet += 'val retrofit = Retrofit.Builder()\n'; @@ -393,8 +414,8 @@ self = module.exports = { codeSnippet += `${indent}.client(okHttpClient)\n`; } - codeSnippet += `${indent}.build()\n\n`; - } + codeSnippet += `${indent}.build()\n\n`; + let serviceName = request.url.host.slice(-2)[0]; footerSnippet += generateInterface( @@ -404,26 +425,23 @@ self = module.exports = { request.url.variables, headers !== '', body !== '', + isFormData, indent ); codeSnippet += `val service = retrofit.create(${getServiceInterfaceName(serviceName)}::class.java)\n`; codeSnippet += `val serviceCall = service.${getInterfaceFunctionName(request.method, request.url.path)}(`; + const serviceCallParamsArray = []; if (headers !== '') { - codeSnippet += 'headers'; - - if (body !== '') { - codeSnippet += ', '; - } - else { - codeSnippet += ')\n'; - } + serviceCallParamsArray.push('headers'); } if (body !== '') { - codeSnippet += 'body)'; + serviceCallParamsArray.push('body'); } - codeSnippet += '\n\n'; + + codeSnippet += serviceCallParamsArray.join(', '); + codeSnippet += ')\n\n'; codeSnippet += 'serviceCall.enqueue(object : Callback {\n'; codeSnippet += `${indent}override fun onResponse(call: Call, response: Response) {\n`; From d74f46f350984dd0d307cbc71af5e4b914bf313d Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 14:41:11 +0330 Subject: [PATCH 17/35] Remove generate retrofit module import --- codegens/kotlin-retrofit2/lib/index.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 28945918a..333a450fb 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -292,15 +292,6 @@ function generateInterface (name, method, path, variables, return interfaceString; } -/** - * Parse retrofit import module based on request method - * - * @param {String} method of web service request - */ -function getRetrofitImportByMethod (method) { - return `import retrofit2.http.${method.toUpperCase()}`; -} - self = module.exports = { convert: function (request, options, callback) { var indent, From 097f91c98c043a0875e66c163ec0cd93f40778ed Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 14:58:46 +0330 Subject: [PATCH 18/35] Complete form-data request --- codegens/kotlin-retrofit2/lib/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 333a450fb..fb51322ce 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -395,15 +395,15 @@ self = module.exports = { isFormData = true; } - codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; + codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; - codeSnippet += 'val retrofit = Retrofit.Builder()\n'; - codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; - codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; + codeSnippet += 'val retrofit = Retrofit.Builder()\n'; + codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; + codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; - if (timeout > 0) { - codeSnippet += `${indent}.client(okHttpClient)\n`; - } + if (timeout > 0) { + codeSnippet += `${indent}.client(okHttpClient)\n`; + } codeSnippet += `${indent}.build()\n\n`; From 7b9dc94c8f2be217a72caa8431fa362020c47690 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 15:00:33 +0330 Subject: [PATCH 19/35] Fix ESLint errors --- codegens/kotlin-retrofit2/lib/index.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index fb51322ce..8676d4a6e 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -301,11 +301,12 @@ self = module.exports = { trim, timeout, followRedirect, - isFormData = false; + isFormData = false, + serviceCallParamsArray = []; options = sanitizeOptions(options, self.getOptions()); if (options.includeBoilerplate) { headerSnippet = 'import retrofit2.Call\n'; - headerSnippet += `import retrofit2.http.*\n`; + headerSnippet += 'import retrofit2.http.*\n'; headerSnippet += 'import retrofit2.Callback\n'; headerSnippet += 'import retrofit2.Response\n'; headerSnippet += 'import retrofit2.Retrofit\n'; @@ -395,15 +396,15 @@ self = module.exports = { isFormData = true; } - codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; + codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; - codeSnippet += 'val retrofit = Retrofit.Builder()\n'; - codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; - codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; + codeSnippet += 'val retrofit = Retrofit.Builder()\n'; + codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; + codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; - if (timeout > 0) { - codeSnippet += `${indent}.client(okHttpClient)\n`; - } + if (timeout > 0) { + codeSnippet += `${indent}.client(okHttpClient)\n`; + } codeSnippet += `${indent}.build()\n\n`; @@ -423,7 +424,6 @@ self = module.exports = { codeSnippet += `val service = retrofit.create(${getServiceInterfaceName(serviceName)}::class.java)\n`; codeSnippet += `val serviceCall = service.${getInterfaceFunctionName(request.method, request.url.path)}(`; - const serviceCallParamsArray = []; if (headers !== '') { serviceCallParamsArray.push('headers'); } From a311db9ea9ee2c837dc1970a167302586f1cf063 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sat, 7 Nov 2020 20:15:01 +0330 Subject: [PATCH 20/35] Support for raw body in kotlin-retrofit2 --- codegens/kotlin-retrofit2/lib/index.js | 83 +++++++++++++++++++------- 1 file changed, 60 insertions(+), 23 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 8676d4a6e..ee122c0a9 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -32,7 +32,7 @@ function parseUrlEncoded (body, indent, trim) { * @param {Boolean} trim indicates whether to trim string or not */ function parseRawBody (body, trim) { - return `val body = '''${sanitize(body, trim)}'''`; + return `val body = """${sanitize(body, trim)}"""`; } /** @@ -245,6 +245,29 @@ function generateRetrofitClientFactory (timeout, followRedirect, indent) { return timeoutFactoryString; } +/** + * Change url parameter from :something to {something} based on retrofit2 documentation + * + * @param {Array} paths in web service url + */ +function changeUrlParamToTemplate (paths) { + const pathArray = []; + if (!paths) { + return pathArray; + } + + paths.forEach((p) => { + if (p.startsWith(':')) { + pathArray.push(`{${p.replace(':', '')}}`); + } + else { + pathArray.push(p); + } + }); + + return pathArray; +} + /** * Parses headers from the request. * @@ -254,33 +277,38 @@ function generateRetrofitClientFactory (timeout, followRedirect, indent) { * @param {String} variables in path * @param {boolean} hasHeader in web service request * @param {boolean} hasBody in web service request - * @param {boolean} isFormData web service request + * @param {String} bodyType of web service request * @param {String} indent indentation required for code snippet */ function generateInterface (name, method, path, variables, - hasHeader, hasBody, isFormData, indent) { + hasHeader, hasBody, bodyType, indent) { var interfaceString = '@JvmSuppressWildcards\n'; + const pathTemplate = changeUrlParamToTemplate(path), + functionArguments = []; + interfaceString += `interface ${getServiceInterfaceName(name)} {\n`; - if (isFormData) { + if (bodyType === 'formdata') { interfaceString += `${indent}@Multipart\n`; } - interfaceString += `${indent}@${method.toUpperCase()}("${path}")\n`; + interfaceString += `${indent}@${method.toUpperCase()}("${pathTemplate}")\n`; interfaceString += `${indent}fun `; interfaceString += `${getInterfaceFunctionName(method, path)}(`; - const functionArguments = []; let paramString = getInterfaceMethodParams(variables); if (hasHeader) { functionArguments.push('@HeaderMap headers: Map'); } - if (hasBody && !isFormData) { - functionArguments.push('@Body body: Map'); - } - else if (isFormData) { + if (bodyType === 'formdata') { functionArguments.push('@PartMap body: Map'); } + else if (bodyType === 'raw') { + functionArguments.push('@Body body: String'); + } + else if (hasBody) { + functionArguments.push('@Body body: Map'); + } if (paramString !== '') { functionArguments.push(paramString); @@ -301,7 +329,7 @@ self = module.exports = { trim, timeout, followRedirect, - isFormData = false, + bodyType, serviceCallParamsArray = []; options = sanitizeOptions(options, self.getOptions()); if (options.includeBoilerplate) { @@ -390,21 +418,19 @@ self = module.exports = { codeSnippet += headers; codeSnippet += body; - if (requestBody && requestBody.mode === 'formdata') { - // codeSnippet += `var request = http.MultipartRequest('${request.method.toUpperCase()}',` + - // ` Uri.parse('${request.url.toString()}'));\n`; - isFormData = true; + if (requestBody) { + bodyType = requestBody.mode; } - codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; + codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; - codeSnippet += 'val retrofit = Retrofit.Builder()\n'; - codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; - codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; + codeSnippet += 'val retrofit = Retrofit.Builder()\n'; + codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; + codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; - if (timeout > 0) { - codeSnippet += `${indent}.client(okHttpClient)\n`; - } + if (timeout > 0) { + codeSnippet += `${indent}.client(okHttpClient)\n`; + } codeSnippet += `${indent}.build()\n\n`; @@ -417,7 +443,7 @@ self = module.exports = { request.url.variables, headers !== '', body !== '', - isFormData, + bodyType, indent ); @@ -431,6 +457,17 @@ self = module.exports = { serviceCallParamsArray.push('body'); } + if (request.url.variables && request.url.variables.members.length > 0) { + request.url.variables.members.forEach((variable) => { + if (['any', 'text', 'string'].includes(variable.type.toLowerCase())) { + serviceCallParamsArray.push(`"${sanitize(variable.value)}"`); + } + else { + serviceCallParamsArray.push(`${variable.value}`); + } + }); + } + codeSnippet += serviceCallParamsArray.join(', '); codeSnippet += ')\n\n'; From 51424605af8c7d21ca21e435c2a1da86ec5d5d66 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 01:19:10 +0330 Subject: [PATCH 21/35] Fix bug forgotten double quote for raw data --- codegens/kotlin-retrofit2/lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index ee122c0a9..ac5fd2b94 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -17,7 +17,7 @@ function parseUrlEncoded (body, indent, trim) { bodyDataMap; if (!_.isEmpty(enabledBodyList)) { bodyDataMap = _.map(enabledBodyList, function (value) { - return `${indent}"${sanitize(value.key, trim)} to "${sanitize(value.value, trim)}`; + return `${indent}"${sanitize(value.key, trim)}" to "${sanitize(value.value, trim)}"`; }); bodySnippet += '\n' + bodyDataMap.join(',\n') + '\n'; } From d414a111c0e0f5e1b0e3ebd0043a8d46bff1d6c5 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 01:19:41 +0330 Subject: [PATCH 22/35] Support Graphql --- codegens/kotlin-retrofit2/lib/index.js | 31 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index ac5fd2b94..9012412e0 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -39,10 +39,10 @@ function parseRawBody (body, trim) { * Parses GraphQL body * * @param {Object} body GraphQL body - * @param {Boolean} trim indicates whether to trim string or not + * @param {String} indent indentation required for code snippet */ -function parseGraphQLBody (body, trim) { - var bodySnippet = '', +function parseGraphQLBody (body, indent) { + var snippet = '', query = body.query, graphqlVariables; try { @@ -52,12 +52,13 @@ function parseGraphQLBody (body, trim) { graphqlVariables = {}; } - bodySnippet += `request.body = '''${sanitize(JSON.stringify({ - query: query, - variables: graphqlVariables - }), trim)}''';\n`; + snippet += 'class GraphqlQuery() {\n'; + snippet += `${indent}val query = "${sanitize(query)}"\n`; + snippet += `${indent}val variables = "${sanitize(JSON.stringify(graphqlVariables))}"\n`; - return bodySnippet; + snippet += '}\n\n'; + + return snippet; } /** @@ -133,8 +134,6 @@ function parseBody (body, indent, trim) { return parseRawBody(body.raw, trim); case 'formdata': return parseFormData(body.formdata, indent, trim); - case 'graphql': - return parseGraphQLBody(body.graphql, trim); case 'file': return 'request.body = r\'\';\n'; default: @@ -306,6 +305,9 @@ function generateInterface (name, method, path, variables, else if (bodyType === 'raw') { functionArguments.push('@Body body: String'); } + else if (bodyType === 'graphql') { + functionArguments.push('@Body body: GraphqlQuery'); + } else if (hasBody) { functionArguments.push('@Body body: Map'); } @@ -342,7 +344,6 @@ self = module.exports = { headerSnippet += 'fun main() {\n'; footerSnippet = '}\n\n'; - // TODO: add interface implementation in footerSnippet } trim = options.trimRequestBody; indent = options.indentType === 'Tab' ? '\t' : ' '; @@ -428,6 +429,10 @@ self = module.exports = { codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; + if (requestBody.mode === 'graphql') { + footerSnippet += parseGraphQLBody(requestBody.graphql, indent); + } + if (timeout > 0) { codeSnippet += `${indent}.client(okHttpClient)\n`; } @@ -457,6 +462,10 @@ self = module.exports = { serviceCallParamsArray.push('body'); } + if (requestBody && requestBody.mode === 'graphql') { + serviceCallParamsArray.push('GraphqlQuery()'); + } + if (request.url.variables && request.url.variables.members.length > 0) { request.url.variables.members.forEach((variable) => { if (['any', 'text', 'string'].includes(variable.type.toLowerCase())) { From 14e4a41602a30142f8ffa33883d4b638354180b4 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 01:21:54 +0330 Subject: [PATCH 23/35] Test for Kotlin-Retrofit2 --- .../test/unit/convert.test.js | 167 ++- .../test/unit/fixtures/collection.json | 1321 +++++++++++++++++ .../test/unit/fixtures/snippets.json | 32 + 3 files changed, 1444 insertions(+), 76 deletions(-) create mode 100644 codegens/kotlin-retrofit2/test/unit/fixtures/collection.json create mode 100644 codegens/kotlin-retrofit2/test/unit/fixtures/snippets.json diff --git a/codegens/kotlin-retrofit2/test/unit/convert.test.js b/codegens/kotlin-retrofit2/test/unit/convert.test.js index 05e943c10..63b5ac063 100644 --- a/codegens/kotlin-retrofit2/test/unit/convert.test.js +++ b/codegens/kotlin-retrofit2/test/unit/convert.test.js @@ -1,51 +1,61 @@ var convert = require('../../index').convert, expect = require('chai').expect, - sdk = require('postman-collection'); + collection = require('./fixtures/collection.json'), + sdk = require('postman-collection'), + expectedSnippets = require('./fixtures/snippets.json'); -// Disable check with expected snippets as we now have proper newman tests -describe('Dart Converter', function () { - it('should add timeout if requestTimeout options is used', function () { - var request = new sdk.Request({ - 'method': 'POST', - 'header': [ - { - 'key': 'Content-Type', - 'value': 'application/json' - } - ], - 'body': { - 'mode': 'raw', - 'raw': '{\n "json": "Test-Test"\n}' - }, - 'url': { - 'raw': 'https://postman-echo.com/post', - 'protocol': 'https', - 'host': [ - 'postman-echo', - 'com' - ], - 'path': [ - 'post' - ] - } +describe('Kotlin-Retrofit2 Converter', function () { + describe('convert for different request types', function () { + collection.item.forEach((item) => { + it(item.name, function (done) { + const request = new sdk.Request(item.request); + convert(request, { + includeBoilerplate: true + }, (err, snippet) => { + if (err) { + expect.fail(null, null, err); + return done(); + } + + expect(snippet).to.equal(expectedSnippets[item.name]); + return done(); + }); + }); }); + }); +}); - convert(request, {requestTimeout: 5000}, function (err, snippet) { - if (err) { - expect.fail(err); +describe('Options Tests', function () { + it('should indent snippet with type and count specified', function () { + var request = new sdk.Request(collection.item[0].request); + convert(request, { + indentType: 'Tab', + indentCount: 2, + includeBoilerplate: true + }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.contain('.timeout(Duration(milliseconds: 5000))'); + var snippetArray = snippet.split('\n'), + i; + for (i = 0; i < snippetArray.length; i++) { + if (snippetArray[i].startsWith('cachePolicy:NSURLRequestUseProtocolCachePolicy')) { + expect(snippetArray[i + 1].charAt(0)).to.equal('\t'); + expect(snippetArray[i + 1].charAt(1)).to.equal('\t'); + expect(snippetArray[i + 1].charAt(2)).to.not.equal(' '); + } + } }); }); - it('should use http.MultipartRequest for formdata requests', function () { + it('should use all the default options', function () { var request = new sdk.Request({ 'method': 'POST', 'header': [], 'body': { - 'mode': 'formdata', - 'formdata': [] + 'mode': 'raw', + 'raw': ' trim this body ' }, 'url': { 'raw': 'https://postman-echo.com/post', @@ -59,73 +69,76 @@ describe('Dart Converter', function () { ] } }); - convert(request, {}, function (err, snippet) { - if (err) { - expect.fail(err); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.contain('http.MultipartRequest'); + var snippetArray = snippet.split('\n'), + i; + for (i = 0; i < snippetArray.length; i++) { + if (snippetArray[i].startsWith('cachePolicy:NSURLRequestUseProtocolCachePolicy')) { + expect(snippetArray[i + 1].charAt(0)).to.equal(' '); + expect(snippetArray[i + 1].charAt(1)).to.equal(' '); + expect(snippet).to.include('timeoutInterval:10'); + expect(snippet).to.include(' trim this body '); + } + } }); }); - it('should add code for followRedirects if given in the option', function () { - var request = new sdk.Request({ - 'method': 'GET', - 'header': [], - 'url': { - 'raw': 'https://postman-echo.com/', - 'protocol': 'https', - 'host': [ - 'postman-echo', - 'com' - ] - } - }); - convert(request, { followRedirect: false }, function (err, snippet) { - if (err) { - expect.fail(err); + it('should add code for requestTimeout option', function () { + var request = new sdk.Request(collection.item[0].request); + convert(request, { + requestTimeout: 5000 + }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.contain('request.followRedirects = false;'); + expect(snippet).to.include('timeoutInterval:5'); }); }); - it('should add boilerplate if given in the option', function () { + it('should trim request body when trimRequestBody is set to true', function () { var request = new sdk.Request({ - 'method': 'GET', + 'method': 'POST', 'header': [], + 'body': { + 'mode': 'raw', + 'raw': ' trim this body ' + }, 'url': { - 'raw': 'https://postman-echo.com/', + 'raw': 'https://postman-echo.com/post', 'protocol': 'https', 'host': [ 'postman-echo', 'com' + ], + 'path': [ + 'post' ] } }); - convert(request, { includeBoilerplate: true }, function (err, snippet) { - if (err) { - expect.fail(err); + convert(request, { + trimRequestBody: true + }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.contain('import \'package:http/http.dart\' as http;'); - expect(snippet).to.contain('void main() async {'); + expect(snippet).to.not.include(' trim this body '); + expect(snippet).to.include('trim this body'); }); }); - it('should add correct indentation', function () { + it('should include boiler plate if includeBoilerplate is set to true', function () { var request = new sdk.Request({ 'method': 'POST', 'header': [], 'body': { - 'mode': 'formdata', - 'formdata': [ - { - 'key': 'hello', - 'value': 'world', - 'type': 'text' - } - ] + 'mode': 'raw', + 'raw': ' trim this body ' }, 'url': { 'raw': 'https://postman-echo.com/post', @@ -139,12 +152,14 @@ describe('Dart Converter', function () { ] } }); - convert(request, { includeBoilerplate: true, indentType: 'Tab' }, function (err, snippet) { - if (err) { - expect.fail(err); + convert(request, { + includeBoilerplate: true + }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.contain('\t\t\'hello\': \'world\''); + expect(snippet).to.include('int main(int argc, const char * argv[])'); }); }); }); diff --git a/codegens/kotlin-retrofit2/test/unit/fixtures/collection.json b/codegens/kotlin-retrofit2/test/unit/fixtures/collection.json new file mode 100644 index 000000000..938de241f --- /dev/null +++ b/codegens/kotlin-retrofit2/test/unit/fixtures/collection.json @@ -0,0 +1,1321 @@ +{ + "info": { + "_postman_id": "f52ee07d-6345-4220-89af-e6696b3c0122", + "name": "Code-Gen Test Cases", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Request Headers", + "event": [ + { + "listen": "test", + "script": { + "id": "a402fc3d-0dac-4471-8505-6df9166afbbf", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "testing", + "value": "'singlequotes'" + }, + { + "key": "TEST", + "value": "\"doublequotes\"" + } + ], + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers (With special Characters)", + "event": [ + { + "listen": "test", + "script": { + "id": "34edbfa7-7d32-42d6-8397-af2378c3aaa4", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "TEST", + "value": "@#$%^&*()" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\" + } + ], + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers with disabled headers", + "event": [ + { + "listen": "test", + "script": { + "id": "e150d55b-0273-430a-9e1d-11969b433734", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "not-disabled-header", + "value": "ENABLED" + }, + { + "key": "disabled header", + "value": "DISABLED", + "disabled": true + } + ], + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "GET Request with disabled query", + "event": [ + { + "listen": "test", + "script": { + "id": "1bfe1fc3-c244-4a42-83c5-1a0d94d56ffd", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get?test=123&anotherone=232", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "test", + "value": "123" + }, + { + "key": "anotherone", + "value": "232" + }, + { + "key": "anotheroneone", + "value": "sdfsdf", + "disabled": true + } + ] + }, + "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested." + }, + "response": [] + }, + { + "name": "POST form data with special characters", + "event": [ + { + "listen": "test", + "script": { + "id": "111c859c-4a8c-41e6-b404-9c404609811b", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "pl", + "value": "'a'", + "type": "text" + }, + { + "key": "qu", + "value": "\"b\"", + "type": "text" + }, + { + "key": "hdjkljh", + "value": "c", + "type": "text" + }, + { + "key": "sa", + "value": "d", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%&*()^_+=`~", + "type": "text" + }, + { + "key": "Not Select", + "value": "Disabled", + "type": "text", + "disabled": true + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL (Quotes + Special Characters) Copy", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "https://postman-echo.com/:action?a=!@$^*()_-`%26&b=,./';[]}{\":/?><||", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "query": [ + { + "key": "a", + "value": "!@$^*()_-`%26" + }, + { + "key": "b", + "value": ",./';[]}{\":/?><||" + } + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + } + }, + "response": [] + }, + { + "name": "POST Raw Text", + "event": [ + { + "listen": "test", + "script": { + "id": "a3ddecd1-e89d-426d-995c-0d6a678caa91", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 281);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "text/plain", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.!@#$%^&*()+POL:},'';,[;[;" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST urlencoded data", + "event": [ + { + "listen": "test", + "script": { + "id": "48da0505-470f-4cf3-bb77-30665415af60", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "1", + "value": "'a'", + "type": "text" + }, + { + "key": "2", + "value": "\"b\"", + "type": "text" + }, + { + "key": "'3'", + "value": "c", + "type": "text" + }, + { + "key": "\"4\"", + "value": "d", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%&*()^_=`~ ", + "type": "text" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\ ", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST json with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "e926912d-1c99-4c54-9b53-91c8f63acef0", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test!@#$%^&*()+POL:},'';,[;[;:>\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST javascript with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "d211bdad-60b3-4cd6-869f-853377bf03ef", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ], + "body": { + "mode": "raw", + "raw": "var val = 6;\nconsole.log(val);" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST textxml with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "532fef57-48fd-4ffe-ac7e-f5a7e32facc2", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/xml" + } + ], + "body": { + "mode": "raw", + "raw": "\n\tTest Test!@#$%^&*()+POL:},'';,[;[;\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST texthtml with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "8bbbbc5b-2983-4979-8347-3ced95a69f7e", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/html" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test !@#$%^&*()+POL:},'';,[;[;\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "https://postman-echo.com/:action?a=''&b=\"\"", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "query": [ + { + "key": "a", + "value": "''" + }, + { + "key": "b", + "value": "\"\"" + }, + { + "key": "more", + "value": "", + "disabled": true + } + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + } + }, + "response": [] + }, + { + "name": "PUT Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + }, + "description": "The HTTP `PUT` request method is similar to HTTP `POST`. It too is meant to \ntransfer data to a server (and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `PUT` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following \nraw HTTP request,\n\n> PUT /hi/there?hand=wave\n>\n> \n\n\n" + }, + "response": [] + }, + { + "name": "PATCH Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." + }, + "url": { + "raw": "https://postman-echo.com/patch", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "patch" + ] + }, + "description": "The HTTP `PATCH` method is used to update resources on a server. The exact\nuse of `PATCH` requests depends on the server in question. There are a number\nof server implementations which handle `PATCH` differently. Technically, \n`PATCH` supports both Query String parameters and a Request Body.\n\nThis endpoint accepts an HTTP `PATCH` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "DELETE Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Donec fermentum, nisi sed cursus eleifend, nulla tortor ultricies tellus, ut vehicula orci arcu ut velit. In volutpat egestas dapibus.\nMorbi condimentum vestibulum sapien. Etiam dignissim diam quis eros lobortis gravida vel lobortis est. Etiam gravida sed." + }, + "url": { + "raw": "https://postman-echo.com/delete", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "delete" + ] + }, + "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "OPTIONS to postman echo", + "event": [ + { + "listen": "test", + "script": { + "id": "26034e86-3b4b-4445-bc81-f1edd810c90e", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "OPTIONS", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "LINK request", + "request": { + "method": "LINK", + "header": [], + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + } + }, + "response": [] + }, + { + "name": "UNLINK request", + "request": { + "method": "UNLINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + } + }, + "response": [] + }, + { + "name": "LOCK request", + "request": { + "method": "LOCK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + } + }, + "response": [] + }, + { + "name": "UNLOCK request", + "request": { + "method": "UNLOCK", + "header": [], + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + } + }, + "response": [] + }, + { + "name": "PROPFIND request", + "request": { + "method": "PROPFIND", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + } + }, + "response": [] + }, + { + "name": "PURGE Request", + "request": { + "method": "PURGE", + "header": [], + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + } + }, + "response": [] + }, + { + "name": "COPY Request", + "request": { + "method": "COPY", + "header": [], + "url": { + "raw": "https://mockbin.org/request", + "protocol": "https", + "host": [ + "mockbin", + "org" + ], + "path": [ + "request" + ] + } + }, + "response": [ + { + "name": "COPY Request", + "originalRequest": { + "method": "COPY", + "header": [], + "url": { + "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "protocol": "https", + "host": [ + "9c76407d-5b8d-4b22-99fb-8c47a85d9848", + "mock", + "pstmn", + "io" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "59", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Mon, 05 Feb 2018 07:48:41 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"af-MmpVeTvfnSW88c4riXD0uw\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + } + ], + "cookie": [], + "body": "{\n \"status\": 200,\n \"method\": \"COPY\"\n}" + } + ] + }, + { + "name": "Post file", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "uohou", + "type": "file", + "src": "/Users/umesh/Desktop/Screenshot 2019-09-29 at 10.50.30 AM.png" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "Post multiple files in the same parameter via form-data", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "multiple files", + "type": "file", + "src": [ + "/Users/shreyshah/Desktop/openapi3.json", + "/Users/shreyshah/Desktop/openapi3.yaml" + ] + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "Post a file via form-data, without file src specified", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "key", + "type": "file", + "src": [] + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "Multiple form-data fields with same names", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "key", + "value": "value1", + "type": "text" + }, + { + "key": "key", + "value": "value2", + "type": "text" + }, + { + "key": "file", + "type": "file", + "src": "/Users/shreyshah/Desktop/openapi3.json" + }, + { + "key": "file", + "type": "file", + "src": "/Users/shreyshah/Desktop/test.c" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "Multiple query params with same names", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get?query=value1&query=value2", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "query", + "value": "value1" + }, + { + "key": "query", + "value": "value2" + } + ] + } + }, + "response": [] + }, + { + "name": "GraphQL query", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "graphql", + "graphql": { + "query": "query {\n __schema\n}", + "variables": "" + } + }, + "url": { + "raw": "https://api.github.com/graphql", + "protocol": "https", + "host": [ + "api", + "github", + "com" + ], + "path": [ + "graphql" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "e80b6162-6c90-4150-bfa1-7f42f11c8f64", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "538efa04-97ce-456c-a5a1-772c466591d5", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] +} diff --git a/codegens/kotlin-retrofit2/test/unit/fixtures/snippets.json b/codegens/kotlin-retrofit2/test/unit/fixtures/snippets.json new file mode 100644 index 000000000..16c90d855 --- /dev/null +++ b/codegens/kotlin-retrofit2/test/unit/fixtures/snippets.json @@ -0,0 +1,32 @@ +{ + "Request Headers": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"my-sample-header\" to \"Lorem ipsum dolor sit amet\",\n \"testing\" to \"\\'singlequotes\\'\",\n \"TEST\" to \"\\\"doublequotes\\\"\"\n )\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.postRequest(headers)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @POST(\"request\")\n fun postRequest(@HeaderMap headers: Map): Call\n}\n", + "Request Headers (With special Characters)": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"my-sample-header\" to \"Lorem ipsum dolor sit amet\",\n \"TEST\" to \"@#\\$%^&*()\",\n \"more\" to \",./\\';[]}{\\\":?><|\\\\\\\\\"\n )\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.getHeaders(headers)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @GET(\"headers\")\n fun getHeaders(@HeaderMap headers: Map): Call\n}\n", + "Request Headers with disabled headers": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"my-sample-header\" to \"Lorem ipsum dolor sit amet\",\n \"not-disabled-header\" to \"ENABLED\"\n )\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.getHeaders(headers)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @GET(\"headers\")\n fun getHeaders(@HeaderMap headers: Map): Call\n}\n", + "GET Request with disabled query": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.getGet()\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @GET(\"get\")\n fun getGet(): Call\n}\n", + "POST form data with special characters": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val body = HashMap()\n val pl0 = RequestBody.create(MediaType.parse(\"text/plain\"), \"\\'a\\'\")\n val qu1 = RequestBody.create(MediaType.parse(\"text/plain\"), \"\\\"b\\\"\")\n val hdjkljh2 = RequestBody.create(MediaType.parse(\"text/plain\"), \"c\")\n val sa3 = RequestBody.create(MediaType.parse(\"text/plain\"), \"d\")\n val Special4 = RequestBody.create(MediaType.parse(\"text/plain\"), \"!@#\\$%&*()^_+=`~\")\n val more6 = RequestBody.create(MediaType.parse(\"text/plain\"), \",./\\';[]}{\\\":?><|\\\\\\\\\")\n \n body.put(\"pl\", pl0)\n body.put(\"qu\", qu1)\n body.put(\"hdjkljh\", hdjkljh2)\n body.put(\"sa\", sa3)\n body.put(\"Special\", Special4)\n body.put(\"more\", more6)\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @Multipart\n @POST(\"post\")\n fun postPost(@PartMap body: Map): Call\n}\n", + "Resolve URL (Quotes + Special Characters) Copy": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val body = \"\"\"\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postAction(body, \"post\")\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"{action}\")\n fun postAction(@Body body: String, @Path(\"action\") action: Any): Call\n}\n", + "POST Raw Text": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.!@#\\$%^&*()+POL:},\\'\\';,[;[;\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"post\")\n fun postPost(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "POST urlencoded data": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"application/x-www-form-urlencoded\"\n )\n val body = mapOf(\n \"1\" to \"\\'a\\'\",\n \"2\" to \"\\\"b\\\"\",\n \"\\'3\\'\" to \"c\",\n \"\\\"4\\\"\" to \"d\",\n \"Special\" to \"!@#\\$%&*()^_=`~ \",\n \"more\" to \",./\\';[]}{\\\":?><|\\\\\\\\ \"\n )\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"post\")\n fun postPost(@HeaderMap headers: Map, @Body body: Map): Call\n}\n", + "POST json with raw": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"application/json\"\n )\n val body = \"\"\"{\\n \\\"json\\\": \\\"Test-Test!@#\\$%^&*()+POL:},\\'\\';,[;[;:>\\\"\\n}\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"post\")\n fun postPost(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "POST javascript with raw": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"application/javascript\"\n )\n val body = \"\"\"var val = 6;\\nconsole.log(val);\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"post\")\n fun postPost(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "Resolve URL": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val body = \"\"\"\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postAction(body, \"post\")\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"{action}\")\n fun postAction(@Body body: String, @Path(\"action\") action: Any): Call\n}\n", + "PUT Request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus.\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.putPut(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @PUT(\"put\")\n fun putPut(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "PATCH Request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.patchPatch(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @PATCH(\"patch\")\n fun patchPatch(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "DELETE Request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Donec fermentum, nisi sed cursus eleifend, nulla tortor ultricies tellus, ut vehicula orci arcu ut velit. In volutpat egestas dapibus.\\nMorbi condimentum vestibulum sapien. Etiam dignissim diam quis eros lobortis gravida vel lobortis est. Etiam gravida sed.\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.deleteDelete(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @DELETE(\"delete\")\n fun deleteDelete(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "OPTIONS to postman echo": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.optionsPost(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @OPTIONS(\"post\")\n fun optionsPost(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "LINK request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.linkRequest()\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @LINK(\"request\")\n fun linkRequest(): Call\n}\n", + "UNLINK request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.unlinkRequest(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @UNLINK(\"request\")\n fun unlinkRequest(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "LOCK request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.lockRequest(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @LOCK(\"request\")\n fun lockRequest(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "UNLOCK request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.unlockRequest()\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @UNLOCK(\"request\")\n fun unlockRequest(): Call\n}\n", + "PROPFIND request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/plain\"\n )\n val body = \"\"\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.propfindRequest(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @PROPFIND(\"request\")\n fun propfindRequest(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "PURGE Request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.purgeRequest()\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @PURGE(\"request\")\n fun purgeRequest(): Call\n}\n", + "COPY Request": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://mockbin.org\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(MockbinService::class.java)\n val serviceCall = service.copyRequest()\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface MockbinService {\n @COPY(\"request\")\n fun copyRequest(): Call\n}\n", + "Post file": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val body = HashMap()\n \n val uohou0 = RequestBody.create(MediaType.parse(\"multipart/form-data\"), \"/Users/umesh/Desktop/Screenshot 2019-09-29 at 10.50.30 AM.png\");\n \n body.put(\"uohou\", uohou0)\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @Multipart\n @POST(\"post\")\n fun postPost(@PartMap body: Map): Call\n}\n", + "Post multiple files in the same parameter via form-data": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val body = HashMap()\n \n val multiplefiles0 = RequestBody.create(MediaType.parse(\"multipart/form-data\"), \"/Users/shreyshah/Desktop/openapi3.json\");\n val multiplefiles1 = RequestBody.create(MediaType.parse(\"multipart/form-data\"), \"/Users/shreyshah/Desktop/openapi3.yaml\");\n \n body.put(\"multiple files\", multiplefiles0)\n body.put(\"multiple files\", multiplefiles1)\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @Multipart\n @POST(\"post\")\n fun postPost(@PartMap body: Map): Call\n}\n", + "Post a file via form-data, without file src specified": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val body = HashMap()\n \n val key0 = RequestBody.create(MediaType.parse(\"multipart/form-data\"), \"/path/to/file\");\n \n body.put(\"key\", key0)\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @Multipart\n @POST(\"post\")\n fun postPost(@PartMap body: Map): Call\n}\n", + "Multiple form-data fields with same names": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val body = HashMap()\n val key0 = RequestBody.create(MediaType.parse(\"text/plain\"), \"value1\")\n val key1 = RequestBody.create(MediaType.parse(\"text/plain\"), \"value2\")\n \n val file2 = RequestBody.create(MediaType.parse(\"multipart/form-data\"), \"/Users/shreyshah/Desktop/openapi3.json\");\n val file3 = RequestBody.create(MediaType.parse(\"multipart/form-data\"), \"/Users/shreyshah/Desktop/test.c\");\n \n body.put(\"key\", key0)\n body.put(\"key\", key1)\n body.put(\"file\", file2)\n body.put(\"file\", file3)\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @Multipart\n @POST(\"post\")\n fun postPost(@PartMap body: Map): Call\n}\n", + "Multiple query params with same names": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.getGet()\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @GET(\"get\")\n fun getGet(): Call\n}\n", + "GraphQL query": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"application/json\"\n )\n \n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://api.github.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(GithubService::class.java)\n val serviceCall = service.postGraphql(headers, GraphqlQuery())\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\nclass GraphqlQuery() {\n val query = \"query {\\n __schema\\n}\"\n val variables = \"{}\"\n}\n\n@JvmSuppressWildcards\ninterface GithubService {\n @POST(\"graphql\")\n fun postGraphql(@HeaderMap headers: Map, @Body body: GraphqlQuery): Call\n}\n", + "POST textxml with raw": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/xml\"\n )\n val body = \"\"\"\\n\\tTest Test!@#\\$%^&*()+POL:},\\'\\';,[;[;\\n\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"post\")\n fun postPost(@HeaderMap headers: Map, @Body body: String): Call\n}\n", + "POST texthtml with raw": "import retrofit2.Call\nimport retrofit2.http.*\nimport retrofit2.Callback\nimport retrofit2.Response\nimport retrofit2.Retrofit\nimport retrofit2.converter.gson.GsonConverterFactory\n\nfun main() {\n val headers = mapOf(\n \"Content-Type\" to \"text/html\"\n )\n val body = \"\"\"\\n Test Test !@#\\$%^&*()+POL:},\\'\\';,[;[;\\n\"\"\"\n val retrofit = Retrofit.Builder()\n .baseUrl(\"https://postman-echo.com\")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n \n val service = retrofit.create(PostmanEchoService::class.java)\n val serviceCall = service.postPost(headers, body)\n \n serviceCall.enqueue(object : Callback {\n override fun onResponse(call: Call, response: Response) {\n println(\"Request success with response: ${response.body()}\")\n }\n \n override fun onFailure(call: Call, t: Throwable) {\n println(\"Request has been failed for ${t.message} reason. $t\")\n }\n })\n \n}\n\n@JvmSuppressWildcards\ninterface PostmanEchoService {\n @POST(\"post\")\n fun postPost(@HeaderMap headers: Map, @Body body: String): Call\n}\n" +} From 5d91bee5c7dccbb3a83ec4099522aa699df49c32 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 01:26:27 +0330 Subject: [PATCH 24/35] Add Kotlin-Retrofit2 to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ac32c2ea3..e1c4f1213 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ List of supported code generators: | JavaScript | Fetch | | JavaScript | jQuery | | JavaScript | XHR | +| Kotlin | Retrofit2 | | NodeJs | Axios | | NodeJs | Native | | NodeJs | Request | From 8707ad92f648cdd780a042a761262e797dc6e2c1 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 01:27:33 +0330 Subject: [PATCH 25/35] Add kotlin to language label --- lib/assets/languageLabels.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/assets/languageLabels.json b/lib/assets/languageLabels.json index 9709e331a..3567eaf77 100644 --- a/lib/assets/languageLabels.json +++ b/lib/assets/languageLabels.json @@ -18,5 +18,6 @@ "nodejs": "NodeJs", "ocaml": "OCaml", "shell": "Shell", - "dart": "Dart" + "dart": "Dart", + "kotlin": "Kotlin" } From 7a33ec8122f0f4be4ad8a7dd365e70afec7fc006 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 01:36:03 +0330 Subject: [PATCH 26/35] Add kotlin snippet file --- codegens/kotlin-retrofit2/.gitignore | 3 +-- codegens/kotlin-retrofit2/npm-shrinkwrap.json | 2 +- codegens/kotlin-retrofit2/test/newman/newman.test.js | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/codegens/kotlin-retrofit2/.gitignore b/codegens/kotlin-retrofit2/.gitignore index 34f4d897c..105c6c629 100644 --- a/codegens/kotlin-retrofit2/.gitignore +++ b/codegens/kotlin-retrofit2/.gitignore @@ -46,5 +46,4 @@ out/ pubspec.lock pubspec.yaml .packages -snippet.dart -.dart_tool/ +snippet.kt diff --git a/codegens/kotlin-retrofit2/npm-shrinkwrap.json b/codegens/kotlin-retrofit2/npm-shrinkwrap.json index 9339a9b6c..fd85b8532 100644 --- a/codegens/kotlin-retrofit2/npm-shrinkwrap.json +++ b/codegens/kotlin-retrofit2/npm-shrinkwrap.json @@ -1,5 +1,5 @@ { - "name": "@postman/codegen-dart", + "name": "@postman/codegen-kotlin", "version": "0.0.1", "lockfileVersion": 1 } diff --git a/codegens/kotlin-retrofit2/test/newman/newman.test.js b/codegens/kotlin-retrofit2/test/newman/newman.test.js index 2b4e3e1ca..91a1434f3 100644 --- a/codegens/kotlin-retrofit2/test/newman/newman.test.js +++ b/codegens/kotlin-retrofit2/test/newman/newman.test.js @@ -4,8 +4,8 @@ var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').ru describe('Convert for different types of request', function () { var options = {indentCount: 2, indentType: 'Space', includeBoilerplate: true }, testConfig = { - runScript: 'dart snippet.dart', - fileName: 'snippet.dart', + runScript: 'kotlin snippet.kt', + fileName: 'snippet.kt', headerSnippet: '', // http uses Map to store headers, so there is no way to // keep multiple headers with the same key From da82f65b2862038cb1ce2aa5a0026c615fbbdc36 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 01:57:15 +0330 Subject: [PATCH 27/35] Fix issue for run test URL is not defined --- codegens/kotlin-retrofit2/lib/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 9012412e0..8279fa465 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -2,6 +2,7 @@ var _ = require('./lodash'), sanitizeOptions = require('./util').sanitizeOptions, sanitize = require('./util').sanitize, addFormParam = require('./util').addFormParam, + sdk = require('postman-collection'), self; /** @@ -426,7 +427,12 @@ self = module.exports = { codeSnippet += `\n${generateRetrofitClientFactory(timeout, followRedirect, indent)}`; codeSnippet += 'val retrofit = Retrofit.Builder()\n'; - codeSnippet += `${indent}.baseUrl("${new URL(request.url.toString()).origin}")\n`; + + let url = sdk.Url.parse(request.url.toString()), + baseUrl = url.host ? url.host.join('.') : ''; + baseUrl += url.port ? ':' + url.port : ''; + + codeSnippet += `${indent}.baseUrl("${baseUrl}")\n`; codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; if (requestBody.mode === 'graphql') { From 988ca32c619a9d6da14bd9dbd902183a31034d0c Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 02:06:31 +0330 Subject: [PATCH 28/35] Fix bug get function name of service interface --- codegens/kotlin-retrofit2/lib/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 8279fa465..037ddb85d 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -197,6 +197,9 @@ function getInterfaceMethodParams (variables) { * @param {String} path of web service request */ function getInterfaceFunctionName (httpMethod, path) { + if (!Array.isArray(path)) { + return `${httpMethod.toLowerCase()}`; + } const route = path.slice(-1).toString().replace(':', ''); return `${httpMethod.toLowerCase()}${_.capitalize(route)}`; From 7c01b38d27d37ada316108c679e27c59954406fc Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 02:10:30 +0330 Subject: [PATCH 29/35] Fix bug in follow redirect --- codegens/kotlin-retrofit2/lib/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 037ddb85d..467a55079 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -230,13 +230,15 @@ function getServiceInterfaceName (domainName) { function generateRetrofitClientFactory (timeout, followRedirect, indent) { var timeoutFactoryString = 'val okHttpClient = OkHttpClient().newBuilder()\n'; - if (timeout === 0 || followRedirect) { + if (timeout === 0 && followRedirect) { return ''; } - timeoutFactoryString += `${indent}.connectTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; - timeoutFactoryString += `${indent}.readTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; - timeoutFactoryString += `${indent}.writeTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; + if (timeout > 0) { + timeoutFactoryString += `${indent}.connectTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; + timeoutFactoryString += `${indent}.readTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; + timeoutFactoryString += `${indent}.writeTimeout(${timeout}, TimeUnit.MILLISECONDS)\n`; + } if (!followRedirect) { timeoutFactoryString += `${indent}.followRedirects(false)\n`; @@ -442,7 +444,7 @@ self = module.exports = { footerSnippet += parseGraphQLBody(requestBody.graphql, indent); } - if (timeout > 0) { + if (timeout > 0 || !followRedirect) { codeSnippet += `${indent}.client(okHttpClient)\n`; } From 59e28aef03a8fa3213a9a8130cd4ebfd751f5190 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 02:13:43 +0330 Subject: [PATCH 30/35] Add new test file --- .../test/unit/convert.test.js | 167 ++++++++---------- 1 file changed, 76 insertions(+), 91 deletions(-) diff --git a/codegens/kotlin-retrofit2/test/unit/convert.test.js b/codegens/kotlin-retrofit2/test/unit/convert.test.js index 63b5ac063..523d3ba2d 100644 --- a/codegens/kotlin-retrofit2/test/unit/convert.test.js +++ b/codegens/kotlin-retrofit2/test/unit/convert.test.js @@ -1,61 +1,51 @@ var convert = require('../../index').convert, expect = require('chai').expect, - collection = require('./fixtures/collection.json'), - sdk = require('postman-collection'), - expectedSnippets = require('./fixtures/snippets.json'); + sdk = require('postman-collection'); -describe('Kotlin-Retrofit2 Converter', function () { - describe('convert for different request types', function () { - collection.item.forEach((item) => { - it(item.name, function (done) { - const request = new sdk.Request(item.request); - convert(request, { - includeBoilerplate: true - }, (err, snippet) => { - if (err) { - expect.fail(null, null, err); - return done(); - } - - expect(snippet).to.equal(expectedSnippets[item.name]); - return done(); - }); - }); +// Disable check with expected snippets as we now have proper newman tests +describe('Kotlin Converter', function () { + it('should add timeout if requestTimeout options is used', function () { + var request = new sdk.Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'body': { + 'mode': 'raw', + 'raw': '{\n "json": "Test-Test"\n}' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } }); - }); -}); -describe('Options Tests', function () { - it('should indent snippet with type and count specified', function () { - var request = new sdk.Request(collection.item[0].request); - convert(request, { - indentType: 'Tab', - indentCount: 2, - includeBoilerplate: true - }, function (error, snippet) { - if (error) { - expect.fail(null, null, error); + convert(request, {requestTimeout: 5000}, function (err, snippet) { + if (err) { + expect.fail(err); } expect(snippet).to.be.a('string'); - var snippetArray = snippet.split('\n'), - i; - for (i = 0; i < snippetArray.length; i++) { - if (snippetArray[i].startsWith('cachePolicy:NSURLRequestUseProtocolCachePolicy')) { - expect(snippetArray[i + 1].charAt(0)).to.equal('\t'); - expect(snippetArray[i + 1].charAt(1)).to.equal('\t'); - expect(snippetArray[i + 1].charAt(2)).to.not.equal(' '); - } - } + expect(snippet).to.contain('.connectTimeout(5000, TimeUnit.MILLISECONDS)'); }); }); - it('should use all the default options', function () { + it('should use http.MultipartRequest for formdata requests', function () { var request = new sdk.Request({ 'method': 'POST', 'header': [], 'body': { - 'mode': 'raw', - 'raw': ' trim this body ' + 'mode': 'formdata', + 'formdata': [] }, 'url': { 'raw': 'https://postman-echo.com/post', @@ -69,76 +59,73 @@ describe('Options Tests', function () { ] } }); - convert(request, {}, function (error, snippet) { - if (error) { - expect.fail(null, null, error); + convert(request, {}, function (err, snippet) { + if (err) { + expect.fail(err); } expect(snippet).to.be.a('string'); - var snippetArray = snippet.split('\n'), - i; - for (i = 0; i < snippetArray.length; i++) { - if (snippetArray[i].startsWith('cachePolicy:NSURLRequestUseProtocolCachePolicy')) { - expect(snippetArray[i + 1].charAt(0)).to.equal(' '); - expect(snippetArray[i + 1].charAt(1)).to.equal(' '); - expect(snippet).to.include('timeoutInterval:10'); - expect(snippet).to.include(' trim this body '); - } - } + expect(snippet).to.contain('@Multipart'); }); }); - it('should add code for requestTimeout option', function () { - var request = new sdk.Request(collection.item[0].request); - convert(request, { - requestTimeout: 5000 - }, function (error, snippet) { - if (error) { - expect.fail(null, null, error); + it('should add code for followRedirects if given in the option', function () { + var request = new sdk.Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ] + } + }); + convert(request, { followRedirect: false }, function (err, snippet) { + if (err) { + expect.fail(err); } expect(snippet).to.be.a('string'); - expect(snippet).to.include('timeoutInterval:5'); + expect(snippet).to.contain('.followRedirects(false)'); }); }); - it('should trim request body when trimRequestBody is set to true', function () { + it('should add boilerplate if given in the option', function () { var request = new sdk.Request({ - 'method': 'POST', + 'method': 'GET', 'header': [], - 'body': { - 'mode': 'raw', - 'raw': ' trim this body ' - }, 'url': { - 'raw': 'https://postman-echo.com/post', + 'raw': 'https://postman-echo.com/', 'protocol': 'https', 'host': [ 'postman-echo', 'com' - ], - 'path': [ - 'post' ] } }); - convert(request, { - trimRequestBody: true - }, function (error, snippet) { - if (error) { - expect.fail(null, null, error); + convert(request, { includeBoilerplate: true }, function (err, snippet) { + if (err) { + expect.fail(err); } expect(snippet).to.be.a('string'); - expect(snippet).to.not.include(' trim this body '); - expect(snippet).to.include('trim this body'); + expect(snippet).to.contain('import retrofit2.Call'); + expect(snippet).to.contain('fun main() {'); }); }); - it('should include boiler plate if includeBoilerplate is set to true', function () { + it('should add correct indentation', function () { var request = new sdk.Request({ 'method': 'POST', 'header': [], 'body': { - 'mode': 'raw', - 'raw': ' trim this body ' + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'hello', + 'value': 'world', + 'type': 'text' + } + ] }, 'url': { 'raw': 'https://postman-echo.com/post', @@ -152,14 +139,12 @@ describe('Options Tests', function () { ] } }); - convert(request, { - includeBoilerplate: true - }, function (error, snippet) { - if (error) { - expect.fail(null, null, error); + convert(request, { includeBoilerplate: true, indentType: 'Tab' }, function (err, snippet) { + if (err) { + expect.fail(err); } expect(snippet).to.be.a('string'); - expect(snippet).to.include('int main(int argc, const char * argv[])'); + expect(snippet).to.contain('val hello0 = RequestBody.create(MediaType.parse("text/plain"), "world")'); }); }); }); From 635aeec5d13783f089288fe29ef7fab33648cf30 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 02:28:13 +0330 Subject: [PATCH 31/35] Fix ESLint error --- codegens/kotlin-retrofit2/lib/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index 467a55079..aba7c147c 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -434,7 +434,8 @@ self = module.exports = { codeSnippet += 'val retrofit = Retrofit.Builder()\n'; let url = sdk.Url.parse(request.url.toString()), - baseUrl = url.host ? url.host.join('.') : ''; + baseUrl = url.host ? url.host.join('.') : '', + serviceName = request.url.host.slice(-2)[0]; baseUrl += url.port ? ':' + url.port : ''; codeSnippet += `${indent}.baseUrl("${baseUrl}")\n`; @@ -450,8 +451,6 @@ self = module.exports = { codeSnippet += `${indent}.build()\n\n`; - - let serviceName = request.url.host.slice(-2)[0]; footerSnippet += generateInterface( serviceName, request.method, From 411af339c7f1fbc84ceef0bdcf4e8caeeb2b6d42 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 20:07:17 +0330 Subject: [PATCH 32/35] Add compile script to kotlin test --- codegens/kotlin-retrofit2/test/newman/newman.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/codegens/kotlin-retrofit2/test/newman/newman.test.js b/codegens/kotlin-retrofit2/test/newman/newman.test.js index 91a1434f3..82eba0393 100644 --- a/codegens/kotlin-retrofit2/test/newman/newman.test.js +++ b/codegens/kotlin-retrofit2/test/newman/newman.test.js @@ -4,11 +4,10 @@ var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').ru describe('Convert for different types of request', function () { var options = {indentCount: 2, indentType: 'Space', includeBoilerplate: true }, testConfig = { - runScript: 'kotlin snippet.kt', + compileScript: 'kotlinc snippet.kt -include-runtime -d snippet.jar', + runScript: 'kotlin snippet.jar', fileName: 'snippet.kt', headerSnippet: '', - // http uses Map to store headers, so there is no way to - // keep multiple headers with the same key skipCollections: ['sameNameHeadersCollection'] }; From 928c2a9d394e99525c90a436deac109bdb5b666e Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Sun, 8 Nov 2020 20:08:02 +0330 Subject: [PATCH 33/35] Fix show protocol in baseUrl of request --- codegens/kotlin-retrofit2/lib/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegens/kotlin-retrofit2/lib/index.js b/codegens/kotlin-retrofit2/lib/index.js index aba7c147c..88a375936 100644 --- a/codegens/kotlin-retrofit2/lib/index.js +++ b/codegens/kotlin-retrofit2/lib/index.js @@ -438,7 +438,8 @@ self = module.exports = { serviceName = request.url.host.slice(-2)[0]; baseUrl += url.port ? ':' + url.port : ''; - codeSnippet += `${indent}.baseUrl("${baseUrl}")\n`; + + codeSnippet += `${indent}.baseUrl("${url.protocol}://${baseUrl}")\n`; codeSnippet += `${indent}.addConverterFactory(GsonConverterFactory.create())\n`; if (requestBody.mode === 'graphql') { From 994fd1fbeb94128b67ad43c681ac72b9a1e59ce3 Mon Sep 17 00:00:00 2001 From: Husseinhj Date: Wed, 11 Nov 2020 15:50:33 +0330 Subject: [PATCH 34/35] Build for sign commit From b9904619c02e7d56e498c905d646e2d2e5bce061 Mon Sep 17 00:00:00 2001 From: Hussein Habibi Juybari Date: Wed, 15 Nov 2023 11:33:39 +0100 Subject: [PATCH 35/35] Fixed issue regarding resolve conflicts languageLabels.json --- lib/assets/languageLabels.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assets/languageLabels.json b/lib/assets/languageLabels.json index 00bf3f58d..aa9d5e2fa 100644 --- a/lib/assets/languageLabels.json +++ b/lib/assets/languageLabels.json @@ -20,7 +20,7 @@ "ocaml": "OCaml", "shell": "Shell", "dart": "Dart", - "kotlin": "Kotlin" + "kotlin": "Kotlin", "r": "R", "rust": "Rust" }