Repository: postmanlabs/postman-code-generators
Pull request #712 (2023-06-27 08:43:50)

Release version v1.8.0
Hi @dhwaneetbhatt!

This PR was created in response to a manual trigger of the release workflow here: https://github.com/postmanlabs/postman-code-generators/actions/runs/5387694247.
I've updated the changelog and bumped the versions in the manifest files in this commit: 66b87cce6d1af5022c25b24cf4962774e8b5528a.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ad21d8..1f3982d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## [Unreleased]
 
+## [v1.8.0] - 2023-06-27
+
+-   Fix for - [#10521](https://github.com/postmanlabs/postman-app-support/issues/10521) Add support for Dart Dio snippet generation
+
 ## [v1.7.2] - 2023-05-04
 
 ### Fixed
@@ -136,7 +140,9 @@ v1.0.0 (May 29, 2020)
 -   Add ES6 syntax support for NodeJS Request, NodeJS Native and NodeJS Unirest
 -   Fix snippet generation for powershell and jquery, where form data params had no type field
 
-[Unreleased]: https://github.com/postmanlabs/postman-code-generators/compare/v1.7.2...HEAD
+[Unreleased]: https://github.com/postmanlabs/postman-code-generators/compare/v1.8.0...HEAD
+
+[v1.8.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.7.2...v1.8.0
 
 [v1.7.2]: https://github.com/postmanlabs/postman-code-generators/compare/v1.7.1...v1.7.2
 
diff --git a/codegens/dart-dio/.gitignore b/codegens/dart-dio/.gitignore
new file mode 100644
index 0000000..34f4d89
--- /dev/null
+++ b/codegens/dart-dio/.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/dart-dio/.npmignore b/codegens/dart-dio/.npmignore
new file mode 100644
index 0000000..79ad2ba
--- /dev/null
+++ b/codegens/dart-dio/.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/dart-dio/README.md b/codegens/dart-dio/README.md
new file mode 100644
index 0000000..579d735
--- /dev/null
+++ b/codegens/dart-dio/README.md
@@ -0,0 +1,42 @@
+
+> Converts Postman-SDK Request into code snippet for Dart dio.
+
+#### Prerequisites
+To run Code-Gen, ensure that you have NodeJS >= v12. 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 swift 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
+
+* `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
+
+* Since Postman-SDK Request object doesn't provide complete path of the file, it needs to be manually inserted in case of uploading a file.
+
+* This module doesn't support cookies.
\ No newline at end of file
diff --git a/codegens/dart-dio/index.js b/codegens/dart-dio/index.js
new file mode 100644
index 0000000..bb0a047
--- /dev/null
+++ b/codegens/dart-dio/index.js
@@ -0,0 +1,1 @@
+module.exports = require('./lib');
diff --git a/codegens/dart-dio/lib/index.js b/codegens/dart-dio/lib/index.js
new file mode 100644
index 0000000..fef51ca
--- /dev/null
+++ b/codegens/dart-dio/lib/index.js
@@ -0,0 +1,346 @@
+var _ = require('lodash'),
+  sanitizeOptions = require('./util').sanitizeOptions,
+  sanitize = require('./util').sanitize,
+  addFormParam = require('./util').addFormParam,
+  path = require('path'),
+  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 = 'var data = {',
+    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
+ * @param {String} contentType the content-type of request body
+ * @param {Integer} indentCount the number of space to use
+ */
+function parseRawBody (body, trim, contentType, indentCount) {
+  if (contentType && (contentType === 'application/json' || contentType.match(/\+json$/))) {
+    try {
+      let jsonBody = JSON.parse(body);
+      return `var data = json.encode(${JSON.stringify(jsonBody, null, indentCount)});`;
+
+    }
+    catch (error) {
+      // Do nothing
+    }
+  }
+  return `var data = '''${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 ? body.query : '',
+    graphqlVariables;
+  try {
+    graphqlVariables = JSON.parse(body.variables);
+  }
+  catch (e) {
+    graphqlVariables = {};
+  }
+
+  bodySnippet += `var data = '''${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') {
+        var pathArray = data.src.split(path.sep),
+          fileName = pathArray[pathArray.length - 1];
+        formDataFileArray.push(`await MultipartFile.fromFile('${data.src}', filename: '${sanitize(fileName, trim)}')`);
+      }
+      else {
+        formDataArray.push(`${indent}'${sanitize(key)}': '${sanitize(value, trim)}'`);
+      }
+    }
+  });
+
+  if (formDataArray.length > 0 || formDataFileArray.length > 0) {
+    bodySnippet += 'var data = FormData.fromMap({\n';
+    if (formDataFileArray.length > 0) {
+      bodySnippet += `${indent}'files': [\n${indent}${indent}`;
+      bodySnippet += formDataFileArray.join(`,\n${indent}${indent}`);
+      bodySnippet += `\n${indent}],\n`;
+    }
+    bodySnippet += formDataArray.join(',\n');
+    bodySnippet += '\n});\n';
+  }
+
+  return bodySnippet;
+}
+
+/**
+ * Parses Body from the Request
+ *
+ * @param {Object} body body object from request.
+ * @param {String} indent indentation required for code snippet
+ * @param {Boolean} trim indicates whether to trim string or not
+ * @param {String} contentType the content-type of the request body
+ */
+function parseBody (body, indent, trim, contentType) {
+  if (!_.isEmpty(body)) {
+    switch (body.mode) {
+      case 'urlencoded':
+        return parseUrlEncoded(body.urlencoded, indent, trim);
+      case 'raw':
+        return parseRawBody(body.raw, trim, contentType, indent.length);
+      case 'formdata':
+        return parseFormData(body.formdata, indent, trim);
+      case 'graphql':
+        return parseGraphQLBody(body.graphql, trim);
+      case 'file':
+        return 'var data = r\'<file contents here>\';\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,
+      contentType;
+    options = sanitizeOptions(options, self.getOptions());
+
+    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'
+        });
+      }
+    }
+
+    contentType = request.headers.get('Content-Type');
+    if (options.includeBoilerplate) {
+      headerSnippet = 'import \'dart:convert\';\n';
+      headerSnippet += 'import \'package:dio/dio.dart\';\n\n';
+      headerSnippet += 'void main() async {\n';
+      footerSnippet = '}\n';
+    }
+
+    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, contentType);
+
+    codeSnippet += headers;
+
+    if (body !== '') {
+      codeSnippet += body + '\n';
+    }
+
+    codeSnippet += 'var dio = Dio();\n';
+    codeSnippet += 'var response = await dio.request(\n';
+    codeSnippet += `${indent}'${request.url.toString()}',\n`;
+    codeSnippet += `${indent}options: Options(\n`;
+    codeSnippet += `${indent}${indent}method: '${request.method.toUpperCase()}',\n`;
+    codeSnippet += `${headers !== '' ? `${indent}${indent}headers: headers,\n` : ''}`;
+    codeSnippet += `${followRedirect ? '' : `${indent}${indent}followRedirects: false,\n`}`;
+    codeSnippet += `${timeout ? `${indent}${indent}receiveTimeout: ${timeout},\n` : ''}`;
+    codeSnippet += `${indent}),\n`;
+    codeSnippet += `${body !== '' ? `${indent}data: data,\n` : ''}`;
+    codeSnippet += ');';
+
+    codeSnippet += '\n\n';
+    codeSnippet += 'if (response.statusCode == 200) {\n';
+    codeSnippet += `${indent}print(json.encode(response.data));\n`;
+    codeSnippet += '}\nelse {\n';
+    codeSnippet += `${indent}print(response.statusMessage);\n`;
+    codeSnippet += '}';
+
+    (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/dart-dio/lib/util.js b/codegens/dart-dio/lib/util.js
new file mode 100644
index 0000000..4d1801f
--- /dev/null
+++ b/codegens/dart-dio/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/dart-dio/package-lock.json b/codegens/dart-dio/package-lock.json
new file mode 100644
index 0000000..c1166ce
--- /dev/null
+++ b/codegens/dart-dio/package-lock.json
@@ -0,0 +1,13 @@
+{
+  "name": "@postman/codegen-dart-dio",
+  "version": "0.0.1",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    }
+  }
+}
diff --git a/codegens/dart-dio/package.json b/codegens/dart-dio/package.json
new file mode 100644
index 0000000..2dd1dd2
--- /dev/null
+++ b/codegens/dart-dio/package.json
@@ -0,0 +1,36 @@
+{
+  "name": "@postman/codegen-dart-dio",
+  "version": "0.0.1",
+  "description": "Converts Postman SDK Requests to Dart Dio code snippet",
+  "main": "index.js",
+  "com_postman_plugin": {
+    "type": "code_generator",
+    "lang": "Dart",
+    "variant": "dio",
+    "syntax_mode": "dart"
+  },
+  "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 <help@getpostman.com>",
+  "license": "Apache-2.0",
+  "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/dart-dio",
+  "dependencies": {
+    "lodash": "4.17.21"
+  },
+  "devDependencies": {},
+  "engines": {
+    "node": ">=12"
+  }
+}
diff --git a/npm/ci-requirements.sh b/npm/ci-requirements.sh
index 98cac20..69d612a 100755
--- a/npm/ci-requirements.sh
+++ b/npm/ci-requirements.sh
@@ -80,13 +80,28 @@ echo "Installing curl"
 echo "Installing dependencies required for tests in codegens/shell-httpie"
 sudo apt-get install httpie
 
-echo "Installing dependencies required for tests in codegens/dart"
+echo "Installing dependencies required for tests in codegens/dart-http"
 pushd ./codegens/dart-http &>/dev/null;
-  wget -q https://storage.googleapis.com/dart-archive/channels/stable/release/2.10.2/linux_packages/dart_2.10.2-1_amd64.deb
-  sudo dpkg -i dart_2.10.2-1_amd64.deb
+  wget -q https://storage.googleapis.com/dart-archive/channels/stable/release/3.0.4/linux_packages/dart_3.0.4-1_amd64.deb
+  sudo dpkg -i dart_3.0.4-1_amd64.deb
   echo '''name: test
+version: 1.0.0
+environment:
+  sdk: ^3.0.3
 dependencies:
-  http: ^0.12.2
+  http: ^1.0.0
+''' > pubspec.yaml
+  dart pub get
+popd &>/dev/null;
+
+echo "Installing dependencies required for tests in codegens/dart-dio"
+pushd ./codegens/dart-dio &>/dev/null;
+  echo '''name: test
+version: 1.0.0
+environment:
+  sdk: ^3.0.3
+dependencies:
+  dio: ^5.2.0
 ''' > pubspec.yaml
   dart pub get
 popd &>/dev/null;
diff --git a/package-lock.json b/package-lock.json
index 05b9e9d..93339fa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "postman-code-generators",
-  "version": "1.7.2",
+  "version": "1.8.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 465fe33..786c599 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "postman-code-generators",
-  "version": "1.7.2",
+  "version": "1.8.0",
   "description": "Generates code snippets for a postman collection",
   "main": "index.js",
   "directories": {


Test patch:
diff --git a/codegens/dart-dio/npm/test-lint.js b/codegens/dart-dio/npm/test-lint.js
new file mode 100644
index 0000000..2f4db0c
--- /dev/null
+++ b/codegens/dart-dio/npm/test-lint.js
@@ -0,0 +1,56 @@
+#!/usr/bin/env node
+var shell = require('shelljs'),
+  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(shell.exit);
diff --git a/codegens/dart-dio/npm/test-newman.js b/codegens/dart-dio/npm/test-newman.js
new file mode 100644
index 0000000..0c8559a
--- /dev/null
+++ b/codegens/dart-dio/npm/test-newman.js
@@ -0,0 +1,59 @@
+#!/usr/bin/env node
+/* eslint-env node, es6 */
+// ---------------------------------------------------------------------------------------------------------------------
+// This script is intended to execute all unit tests.
+// ---------------------------------------------------------------------------------------------------------------------
+
+var shell = require('shelljs'),
+
+  // set directories and files for test and coverage report
+  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...'));
+
+  shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH);
+  shell.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(shell.exit);
diff --git a/codegens/dart-dio/npm/test.js b/codegens/dart-dio/npm/test.js
new file mode 100644
index 0000000..ee0be8b
--- /dev/null
+++ b/codegens/dart-dio/npm/test.js
@@ -0,0 +1,19 @@
+#!/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')
+  // 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/dart-dio/test/newman/newman.test.js b/codegens/dart-dio/test/newman/newman.test.js
new file mode 100644
index 0000000..e8b2386
--- /dev/null
+++ b/codegens/dart-dio/test/newman/newman.test.js
@@ -0,0 +1,18 @@
+var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest,
+  convert = require('../../lib/index').convert;
+
+
+describe('Dart-dio Converter', function () {
+  describe('convert for different request types', function () {
+    var options = {indentCount: 2, indentType: 'Space', includeBoilerplate: true },
+      testConfig = {
+        runScript: 'dart snippet.dart',
+        fileName: 'snippet.dart',
+        headerSnippet: '',
+        // http uses Map<String, String> to store headers, so there is no way to
+        // keep multiple headers with the same key
+        skipCollections: ['sameNameHeadersCollection', 'unsupportedMethods']
+      };
+    runNewmanTest(convert, options, testConfig);
+  });
+});
diff --git a/codegens/dart-dio/test/unit/convert.test.js b/codegens/dart-dio/test/unit/convert.test.js
new file mode 100644
index 0000000..3936eeb
--- /dev/null
+++ b/codegens/dart-dio/test/unit/convert.test.js
@@ -0,0 +1,121 @@
+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('receiveTimeout: 50000');
+    });
+  });
+
+  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('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:dio/dio.dart\';');
+      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\'');
+    });
+  });
+});