├── .nvmrc ├── .node-version ├── .npmrc ├── test ├── data │ ├── class-templates │ │ └── test.ejs │ └── dto-2-class │ │ ├── normalize_02.js │ │ ├── normalize_03.js │ │ └── normalize_01.js ├── expected │ ├── get-response │ │ ├── 03.json │ │ ├── 01.json │ │ └── 02.json │ ├── mock-server │ │ ├── 01.json │ │ ├── 04.json │ │ ├── 07-2.json │ │ ├── 07.json │ │ ├── 02.json │ │ ├── 02-2.json │ │ ├── 07-1.json │ │ ├── 06.json │ │ ├── 05.json │ │ └── 03.json │ ├── dto-import │ │ ├── AddressResponseWsDTO.json │ │ ├── CountryWsDTO.json │ │ ├── ComplexDTO.json │ │ └── AddressWsDTO.json │ ├── swagger-import │ │ ├── response_schema_02.json │ │ ├── request_schema_02.json │ │ ├── response_schema_01.json │ │ ├── desc_01.json │ │ └── desc_02.json │ ├── dto-2-class │ │ ├── normalize_03.js │ │ ├── ComplexDTO-es6flow.js │ │ ├── normalize_01.js │ │ ├── AddressWsDTO-es6flow-4.js │ │ └── ComplexDTO-es6.js │ └── dto-response-func │ │ ├── RuleWsDTO.js │ │ ├── success-faker.json │ │ └── ResponseFuncTestDTO.js ├── tests-dto-response-func.js ├── tests-validator-responses.js ├── tests-dto-import.js ├── tests-preferences.js ├── tests-swagger-import.js ├── tests.js └── tests-get-response.js ├── demo ├── rest │ ├── products │ │ ├── # │ │ │ ├── GET │ │ │ │ ├── request_schema.json │ │ │ │ ├── mock │ │ │ │ │ ├── success.json │ │ │ │ │ ├── tunnel-latest.json │ │ │ │ │ ├── success.headers.json │ │ │ │ │ └── error.json │ │ │ │ ├── response_schema.json │ │ │ │ ├── desc.json │ │ │ │ └── .store.json │ │ │ └── OPTIONS │ │ │ │ ├── request_schema.json │ │ │ │ ├── mock │ │ │ │ ├── success.json │ │ │ │ └── error.json │ │ │ │ ├── response_schema.json │ │ │ │ ├── .store.json │ │ │ │ └── desc.json │ │ ├── #search │ │ │ └── GET │ │ │ │ ├── request_schema.json │ │ │ │ ├── mock │ │ │ │ ├── success.json │ │ │ │ └── error.json │ │ │ │ ├── response_schema.json │ │ │ │ ├── .store.json │ │ │ │ └── desc.json │ │ ├── #{productCode} │ │ │ ├── GET │ │ │ │ ├── mock │ │ │ │ │ ├── error.json │ │ │ │ │ ├── success-2.json │ │ │ │ │ ├── success-207.json │ │ │ │ │ ├── success-default.json │ │ │ │ │ ├── .request_data.json │ │ │ │ │ ├── func.json │ │ │ │ │ ├── request-data.json │ │ │ │ │ ├── success-1.json │ │ │ │ │ ├── faker.json │ │ │ │ │ ├── success.json │ │ │ │ │ └── deep-validation.json │ │ │ │ ├── request_schema.json │ │ │ │ ├── desc.json │ │ │ │ ├── .store.json │ │ │ │ └── response_schema.json │ │ │ └── POST │ │ │ │ ├── mock │ │ │ │ ├── error.json │ │ │ │ ├── .success_validation │ │ │ │ ├── tunnel-latest.json │ │ │ │ └── success.json │ │ │ │ ├── request_schema.json │ │ │ │ ├── response_schema.json │ │ │ │ ├── .store.json │ │ │ │ └── desc.json │ │ └── #{productCode}#static │ │ │ └── GET │ │ │ ├── mock │ │ │ ├── error.json │ │ │ ├── document.txt │ │ │ ├── success.json │ │ │ ├── javascript.js │ │ │ ├── styles.css │ │ │ ├── example.gif │ │ │ ├── example.jpg │ │ │ ├── example.png │ │ │ └── example-202.png │ │ │ ├── request_schema.json │ │ │ ├── response_schema.json │ │ │ ├── desc.json │ │ │ └── .store.json │ ├── _DTO │ │ ├── RuleWsDTO.json │ │ ├── AddressResponseWsDTO.json │ │ ├── PriceDTO.json │ │ ├── ResponseFuncLoopDTO.json │ │ ├── CountryWsDTO.json │ │ ├── Geocode.json │ │ ├── AddressComponent.json │ │ ├── GeocodeResult.json │ │ ├── ComplexDTO.json │ │ ├── AddressWsDTO.json │ │ ├── ResponseFuncTestDTO.json │ │ └── CardDTO.json │ ├── search │ │ ├── #search.{query}.results.json │ │ │ └── GET │ │ │ │ ├── request_schema.json │ │ │ │ ├── response_schema.json │ │ │ │ ├── mock │ │ │ │ ├── success.json │ │ │ │ ├── error-401.json │ │ │ │ └── error.json │ │ │ │ ├── .store.json │ │ │ │ └── desc.json │ │ └── #users#{userId}#products#{productCode}#available │ │ │ └── GET │ │ │ ├── request_schema.json │ │ │ ├── response_schema.json │ │ │ ├── mock │ │ │ ├── success.json │ │ │ └── error.json │ │ │ ├── .store.json │ │ │ └── desc.json │ ├── _fallbacks │ │ └── # │ │ │ └── OPTIONS │ │ │ ├── mock │ │ │ └── success.json │ │ │ └── desc.json │ └── _collections │ │ └── bug12.json ├── public │ └── js │ │ └── javascript.js ├── index.js ├── func2 │ └── image.js ├── func │ └── price.js ├── options.js └── tunnel.js ├── .travis.yml ├── init.js ├── src ├── templates │ ├── func-array.tpl │ ├── response_func_header.tpl │ ├── func.tpl │ └── dto_es6.ejs ├── libraries │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── css │ │ ├── highlight.min.css │ │ └── json-formatter-js.min.css └── app │ └── css │ └── styles.css ├── .eslintignore ├── lib ├── constants │ ├── ignore-general.js │ └── ignore-in-rest-root.js ├── cli │ ├── swagger-import-cli.js │ ├── version-cli.js │ ├── ask │ │ ├── func-path.js │ │ ├── path.js │ │ ├── naming.js │ │ ├── default-config.js │ │ ├── ssl.js │ │ ├── url-settings.js │ │ ├── tunnel.js │ │ ├── swagger-import.js │ │ └── headers.js │ ├── validate-cli.js │ ├── help-cli.js │ └── collections-cli.js ├── getCertificate.js ├── commands │ ├── get-collection.js │ ├── delete-collection.js │ ├── get-currently-selected-responses.js │ ├── create-defined-directories.js │ └── activate-collection.js ├── defaults │ └── options-defaults.js ├── controller │ ├── FileOpenController.js │ ├── PreferencesController.js │ ├── SwaggerImportController.js │ ├── DTOController.js │ ├── UiController.js │ ├── ValidatorController.js │ ├── SchemaController.js │ └── CollectionController.js ├── SwaggerDefinition.js ├── SwaggerUtils.js ├── SwaggerPath.js ├── SwaggerLog.js ├── ValidatorLog.js └── ValidatorResponse.js ├── doc ├── readme-faker.md ├── readme-collections.md ├── readme-mock-functions.md ├── readme-query-params.md ├── readme-path-params.md ├── readme-response-header.md ├── readme-response-validation.md ├── readme-expected-response.md ├── readme-tunnel.md ├── readme-express-middleware.md ├── readme-dto-2-class.md ├── readme-swagger-import.md ├── readme-middleware.md ├── readme-ui-documentation.md ├── readme-folder-structure.md └── readme-usage-examples.md ├── .gitignore ├── .eslintrc.js ├── views ├── menu.ejs ├── method-panel.ejs ├── method-badge.ejs ├── scripts.ejs ├── group.ejs ├── head.ejs ├── validate-result-modal.ejs ├── parameter-desc.ejs ├── validate-modal.ejs ├── validate-single-modal.ejs ├── swagger-import-modal.ejs ├── tab-pane-response.ejs ├── service.ejs ├── collection-modal.ejs ├── collection-new-modal.ejs ├── preferences-modal.ejs ├── tab-pane-request.ejs ├── service-schema.ejs ├── add-endpoint-modal.ejs ├── default.ejs ├── method-modal.ejs ├── default-page-header.ejs ├── dto-modal.ejs └── tab-pane-mock.ejs ├── .editorconfig ├── .npmignore ├── LICENSE ├── dash4.config.js ├── package.json ├── mock-server.js └── readme.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 12.14.0 -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 12.14.0 -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true -------------------------------------------------------------------------------- /test/data/class-templates/test.ejs: -------------------------------------------------------------------------------- 1 | Test 2 | -------------------------------------------------------------------------------- /demo/rest/products/#/GET/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/rest/products/#/OPTIONS/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/rest/products/#search/GET/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/error.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/expected/get-response/03.json: -------------------------------------------------------------------------------- 1 | {"success":true} -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/mock/error.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "12" 4 | -------------------------------------------------------------------------------- /demo/public/js/javascript.js: -------------------------------------------------------------------------------- 1 | console.log('hello world'); 2 | -------------------------------------------------------------------------------- /demo/rest/_DTO/RuleWsDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": "string" 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/error.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/response_schema.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/expected/mock-server/01.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /test/expected/mock-server/04.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/response_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/expected/get-response/01.json: -------------------------------------------------------------------------------- 1 | {"success":true,"productData":{}} -------------------------------------------------------------------------------- /demo/rest/products/#/GET/mock/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/document.txt: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/success.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /demo/rest/search/#search.{query}.results.json/GET/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /init.js: -------------------------------------------------------------------------------- 1 | var initCli = require('./lib/cli/init-cli'); 2 | initCli(); 3 | -------------------------------------------------------------------------------- /demo/rest/_DTO/AddressResponseWsDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": "boolean" 3 | } -------------------------------------------------------------------------------- /demo/rest/_fallbacks/#/OPTIONS/mock/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "ok": true 3 | } 4 | -------------------------------------------------------------------------------- /demo/rest/products/#/GET/mock/tunnel-latest.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#/OPTIONS/mock/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#search/GET/mock/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /test/expected/mock-server/07-2.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | {"success":true,"productCode":2} -------------------------------------------------------------------------------- /test/expected/mock-server/07.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | {"success":true,"productData":{}} -------------------------------------------------------------------------------- /demo/rest/products/#/GET/response_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": "boolean" 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#/OPTIONS/response_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": "boolean" 3 | } -------------------------------------------------------------------------------- /demo/rest/search/#search.{query}.results.json/GET/response_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /src/templates/func-array.tpl: -------------------------------------------------------------------------------- 1 | _getArray(function () { 2 | return <%=data%>; 3 | }) -------------------------------------------------------------------------------- /demo/rest/_DTO/PriceDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "currency": "string", 3 | "price": "number" 4 | } -------------------------------------------------------------------------------- /demo/rest/products/#search/GET/response_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": "boolean" 3 | } -------------------------------------------------------------------------------- /test/expected/dto-import/AddressResponseWsDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": "boolean" 3 | } -------------------------------------------------------------------------------- /test/expected/swagger-import/response_schema_02.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": "boolean" 3 | } -------------------------------------------------------------------------------- /demo/rest/_DTO/ResponseFuncLoopDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "funcLoop": "$ref-ResponseFuncLoopDTO" 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/mock/.success_validation: -------------------------------------------------------------------------------- 1 | { 2 | "counter": 1 3 | } -------------------------------------------------------------------------------- /demo/rest/search/#users#{userId}#products#{productCode}#available/GET/request_schema.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/expected/mock-server/02.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "productData": {} 4 | } 5 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/javascript.js: -------------------------------------------------------------------------------- 1 | console.log('hello world'); 2 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/styles.css: -------------------------------------------------------------------------------- 1 | .a-button { 2 | border: 0; 3 | } 4 | -------------------------------------------------------------------------------- /demo/rest/search/#search.{query}.results.json/GET/mock/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /demo/rest/search/#users#{userId}#products#{productCode}#available/GET/response_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/success-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "productCode": 2 4 | } -------------------------------------------------------------------------------- /demo/rest/_DTO/CountryWsDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": "string", 3 | "name": "string", 4 | "value": "string" 5 | } -------------------------------------------------------------------------------- /demo/rest/_DTO/Geocode.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | "$ref-GeocodeResult" 4 | ], 5 | "status": "string" 6 | } -------------------------------------------------------------------------------- /demo/rest/products/#/GET/mock/success.headers.json: -------------------------------------------------------------------------------- 1 | { 2 | "Response-Custom-Header": "Response-Custom-Header" 3 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/success-207.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "productData": {} 4 | } 5 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/success-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "productData": {} 4 | } -------------------------------------------------------------------------------- /demo/rest/search/#users#{userId}#products#{productCode}#available/GET/mock/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true 3 | } -------------------------------------------------------------------------------- /test/expected/dto-import/CountryWsDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": "string", 3 | "name": "string", 4 | "value": "string" 5 | } -------------------------------------------------------------------------------- /test/expected/mock-server/02-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "currentPage": 12, 4 | "productCode": "31221" 5 | } 6 | -------------------------------------------------------------------------------- /test/expected/mock-server/07-1.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | {"success":true,"productCode":1,"productCodeDynamic":"1","productData":{}} -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /node_modules/ 3 | /test/ 4 | /src/libraries/ 5 | /src/swagger/ 6 | /src/templates/ 7 | /demo/ 8 | -------------------------------------------------------------------------------- /demo/rest/_DTO/AddressComponent.json: -------------------------------------------------------------------------------- 1 | { 2 | "long_name": "string", 3 | "short_name": "string", 4 | "types": [ "string" ] 5 | } -------------------------------------------------------------------------------- /lib/constants/ignore-general.js: -------------------------------------------------------------------------------- 1 | 2 | var ignoreGeneral = [ 3 | '.DS_Store', 4 | ]; 5 | 6 | module.exports = ignoreGeneral; 7 | -------------------------------------------------------------------------------- /demo/rest/products/#/OPTIONS/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "success": { 4 | "counter": 0 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /demo/rest/products/#search/GET/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "success": { 4 | "counter": 0 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | 2 | var mockServer = require('./../mock-server.js'); 3 | var options = require('./options'); 4 | 5 | mockServer(options); 6 | -------------------------------------------------------------------------------- /demo/rest/_DTO/GeocodeResult.json: -------------------------------------------------------------------------------- 1 | { 2 | "address_components": [ 3 | "$ref-AddressComponent" 4 | ], 5 | "formatted_address": "string" 6 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/.request_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "currentPage": 3, 3 | "params": { 4 | "productCode": "test" 5 | } 6 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/func.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": <%-image();%>, 3 | "highlight": true, 4 | "quantity": 3423 5 | } 6 | -------------------------------------------------------------------------------- /demo/rest/search/#search.{query}.results.json/GET/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "success": { 4 | "counter": 1 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /test/expected/mock-server/06.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "Unauthorized", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /src/libraries/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/src/libraries/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/libraries/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/src/libraries/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/mock/tunnel-latest.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "dsfsd", 3 | "productCode": "12", 4 | "query": { 5 | "queryKey": "value2" 6 | } 7 | } -------------------------------------------------------------------------------- /demo/rest/search/#users#{userId}#products#{productCode}#available/GET/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "success": { 4 | "counter": 1 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/libraries/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/src/libraries/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/libraries/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/src/libraries/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/demo/rest/products/#{productCode}#static/GET/mock/example.gif -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/demo/rest/products/#{productCode}#static/GET/mock/example.jpg -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/demo/rest/products/#{productCode}#static/GET/mock/example.png -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/request-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "currentPage": <%=query.currentPage%>, 4 | "productCode": "<%=params.productCode%>" 5 | } 6 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/mock/example-202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smollweide/node-mock-server/HEAD/demo/rest/products/#{productCode}#static/GET/mock/example-202.png -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/success-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "productCode": 1, 4 | "productCodeDynamic": "<%-params.productCode%>", 5 | "productData": {} 6 | } 7 | -------------------------------------------------------------------------------- /demo/rest/search/#search.{query}.results.json/GET/mock/error-401.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "Unauthorized", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /doc/readme-faker.md: -------------------------------------------------------------------------------- 1 | # Faker in mock data 2 | 3 | - [Faker](https://www.npmjs.com/package/faker) 4 | - [Use Faker in mock data](/demo/rest/products/%23%7BproductCode%7D/GET/mock/faker.json#L4) 5 | -------------------------------------------------------------------------------- /test/expected/mock-server/05.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "No valid SearchConfig found for the current context", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | yarn.lock 3 | node_modules 4 | /preferences.json 5 | /demo/rest/*/*/*/mock/response.txt 6 | /test/tmp 7 | /test/func-imported 8 | /tmp 9 | npm-debug.log 10 | /func-imported 11 | -------------------------------------------------------------------------------- /demo/rest/products/#/GET/mock/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "No valid SearchConfig found for the current context", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /demo/rest/products/#/OPTIONS/mock/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "No valid SearchConfig found for the current context", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /demo/rest/products/#search/GET/mock/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "No valid SearchConfig found for the current context", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/faker.json: -------------------------------------------------------------------------------- 1 | { 2 | "price": <%-price(12,'CHF');%>, 3 | "email": "<%-faker.internet.email();%>", 4 | "cards": [<%-JSON.stringify(faker.helpers.createCard());%>] 5 | } 6 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "success": { 4 | "counter": 9 5 | }, 6 | "tunnel-latest": { 7 | "counter": 4 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /demo/rest/search/#search.{query}.results.json/GET/mock/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "No valid SearchConfig found for the current context", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /doc/readme-collections.md: -------------------------------------------------------------------------------- 1 | 2 | # Collections 3 | 4 | Collections allow you to store your current selected responses and share it with your team members. 5 | For that you can use the UI or the both [CLI commands](/readme.md#L121). 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'extends': '@namics/eslint-config/configurations/es5-node.js', 3 | 'rules': { 4 | 'complexity': 0, 5 | 'valid-jsdoc': 0, 6 | 'require-jsdoc': 0, 7 | 'no-useless-escape': 0, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /test/data/dto-2-class/normalize_02.js: -------------------------------------------------------------------------------- 1 | module.export = { 2 | 'results': [ 3 | { 4 | 'address_components': [ 5 | '$ref-AddressComponent' 6 | ], 7 | 'formatted_address': 'string' 8 | } 9 | ], 10 | 'status': 'string' 11 | }; -------------------------------------------------------------------------------- /demo/rest/_collections/bug12.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NUM-12", 3 | "description": "Sticky header - see spec for details", 4 | "selectedResponses": { 5 | "products/#{productCode}/GET": "success", 6 | "products/#search/GET": "error" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo/rest/search/#users#{userId}#products#{productCode}#available/GET/mock/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "No valid SearchConfig found for the current context", 5 | "type": "ConversionError" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /doc/readme-mock-functions.md: -------------------------------------------------------------------------------- 1 | # Functions in mock data 2 | 3 | - [Add function](/demo/func/price.js) 4 | - [Add function folder to option](/demo/options.js#L15) 5 | - [Use function in mock data](/demo/rest/products/%23%7BproductCode%7D/GET/mock/func.json#L2) 6 | -------------------------------------------------------------------------------- /test/expected/get-response/02.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "body": "", 4 | "query": "", 5 | "param": "test", 6 | "faker": "Benjamin8@hotmail.com", 7 | "price": { 8 | "currency": "CHF", 9 | "price": { 10 | "value": 12 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demo/rest/_DTO/ComplexDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "list": [ 3 | { 4 | "country": "$ref-CountryWsDTO", 5 | "email": "string" 6 | } 7 | ], 8 | "items": [ 9 | "$ref-ImageDTO" 10 | ], 11 | "parameters": { 12 | "additional": "string" 13 | } 14 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/mock/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "body": "<%-body.test%>", 4 | "query": "<%-query.test%>", 5 | "param": "<%-params.productCode%>", 6 | "faker": "<%-faker.internet.email();%>", 7 | "price": <%-price(12,'CHF');%> 8 | } 9 | -------------------------------------------------------------------------------- /demo/func2/image.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | image: function (url, alt) { 5 | 6 | url = url || 'http://lorempixel.com/100/100/people'; 7 | alt = alt || 'Alt'; 8 | 9 | return JSON.stringify({ 10 | url: url, 11 | alt: alt 12 | }); 13 | } 14 | 15 | }; -------------------------------------------------------------------------------- /demo/rest/products/#/GET/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Returns a list of products", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "response": { 7 | "statusCode": 200, 8 | "schema": { 9 | "type": "application/json" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/data/dto-2-class/normalize_03.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'list': [ 3 | { 4 | 'country': '$ref-CountryWsDTO', 5 | 'email': 'string' 6 | } 7 | ], 8 | 'items': [ 9 | '$ref-ImageDTO' 10 | ], 11 | 'parameters': { 12 | 'additional': 'string' 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /test/expected/dto-import/ComplexDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "list": [ 3 | { 4 | "country": "$ref-CountryWsDTO", 5 | "email": "string" 6 | } 7 | ], 8 | "items": [ 9 | "$ref-ImageDTO" 10 | ], 11 | "parameters": { 12 | "additional": "string" 13 | } 14 | } -------------------------------------------------------------------------------- /views/menu.ejs: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /demo/rest/_fallbacks/#/OPTIONS/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Default Response for OPTIONS request", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "response": { 7 | "statusCode": 200, 8 | "schema": { 9 | "type": "application/json" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo/rest/products/#/OPTIONS/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Returns a list of products (Options)", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "response": { 7 | "statusCode": 200, 8 | "schema": { 9 | "type": "application/json" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demo/func/price.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | price: function (price, currency) { 5 | 6 | price = price || 0; 7 | currency = currency || '€'; 8 | 9 | return JSON.stringify({ 10 | currency: currency, 11 | price: { 12 | value: price 13 | } 14 | }); 15 | } 16 | 17 | }; -------------------------------------------------------------------------------- /views/method-panel.ejs: -------------------------------------------------------------------------------- 1 |
2 | <% include method-badge %> 3 |
4 |

<%=method.desc.desc%>

5 |
6 |
7 |
-------------------------------------------------------------------------------- /lib/constants/ignore-in-rest-root.js: -------------------------------------------------------------------------------- 1 | var ignoreGeneral = require('./ignore-general'); 2 | var ignoreInRestRoot = ignoreGeneral.concat([ 3 | '_DTO', 4 | '_collections', 5 | 'preferences.json', 6 | '.swagger_import.json', 7 | '.validation.json', 8 | ]); 9 | 10 | module.exports = ignoreInRestRoot; 11 | -------------------------------------------------------------------------------- /lib/cli/swagger-import-cli.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | 3 | var SwaggerImport = require('../SwaggerImport'); 4 | 5 | function swaggerImportCli(options) { 6 | var swaggerImporter = new SwaggerImport(options.swaggerImport); 7 | swaggerImporter.doImport(); 8 | } 9 | 10 | module.exports = swaggerImportCli; 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [package.json] 15 | indent_style = space 16 | indent_size = 4 17 | -------------------------------------------------------------------------------- /test/expected/mock-server/03.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "body": "test", 4 | "query": "", 5 | "param": "31221", 6 | "faker": "Lauriane.Greenholt@gmail.com", 7 | "price": { 8 | "currency": "CHF", 9 | "price": { 10 | "value": 12 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /doc/readme-query-params.md: -------------------------------------------------------------------------------- 1 | 2 | # Query params in mock data 3 | 4 | For example call GET "/products/superProductCode/?currentPage=1" 5 | [Config in mock response](/demo/rest/products/%23%7BproductCode%7D/GET/mock/request-data.json#L3) 6 | The response will be: 7 | ``` 8 | { 9 | "currentPage": 1, 10 | ... 11 | } 12 | ``` 13 | -------------------------------------------------------------------------------- /lib/getCertificate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./Utils'); 4 | var utils = new Utils(); 5 | 6 | function getCertificate(pathOrFile) { 7 | if (pathOrFile.split('\n').length > 1) { 8 | return pathOrFile; 9 | } 10 | return utils.readFile(pathOrFile); 11 | } 12 | 13 | module.exports = getCertificate; 14 | -------------------------------------------------------------------------------- /doc/readme-path-params.md: -------------------------------------------------------------------------------- 1 | # Dynamic path params in mock data 2 | 3 | For example call GET "/products/superProductCode/?currentPage=2" 4 | [Config in mock response](/demo/rest/products/%23%7BproductCode%7D/GET/mock/request-data.json#L4) 5 | Response will be: 6 | ``` 7 | { 8 | "productCode": "superProductCode" 9 | ... 10 | } 11 | ``` 12 | -------------------------------------------------------------------------------- /doc/readme-response-header.md: -------------------------------------------------------------------------------- 1 | # Response Header 2 | 3 | * Use the `options.headers` object to define global response header 4 | 5 | ``` 6 | headers: { 7 | 'Global-Custom-Header': 'Global-Custom-Header' 8 | } 9 | ``` 10 | 11 | * Add an [`*.headers.json`](/demo/rest/products/%23/GET/mock/success.headers.json) beside the expected response file 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /demo/func 2 | demo/func 3 | /demo/func-imported 4 | demo/func-imported 5 | /demo/func2 6 | demo/func2 7 | /demo/public 8 | demo/public 9 | /demo/rest 10 | demo/rest 11 | /test 12 | test 13 | /doc 14 | doc 15 | /.idea 16 | .idea 17 | /src/swagger 18 | src/swagger 19 | /tmp 20 | tmp 21 | npm-debug.log 22 | .eslintrc 23 | package-lock.json 24 | -------------------------------------------------------------------------------- /views/method-badge.ejs: -------------------------------------------------------------------------------- 1 | <% 2 | var classDepc = method.isDeprecated ? ' is-deprecated' : ''; 3 | %> 4 | 5 | <%=method.name %> 6 | <%if (method.isProtected) { %> 7 |   8 | <% } %> 9 | -------------------------------------------------------------------------------- /lib/cli/version-cli.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | 3 | var Utils = require('./../Utils'); 4 | var utils = new Utils(); 5 | var packageData = JSON.parse(utils.readFile(__dirname + '/../../package.json')); 6 | 7 | function versionCli() { 8 | console.log('node-mock-server version ' + packageData.version); 9 | } 10 | 11 | module.exports = versionCli; 12 | -------------------------------------------------------------------------------- /demo/rest/products/#/GET/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "success": { 4 | "counter": 0 5 | }, 6 | "success.headers": { 7 | "counter": 1 8 | }, 9 | "tunnel-latest.json": { 10 | "counter": 0 11 | }, 12 | "success.json": { 13 | "counter": 0 14 | }, 15 | "tunnel-latest": { 16 | "counter": 0 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/mock/success.json: -------------------------------------------------------------------------------- 1 | <% 2 | var data = {}; 3 | 4 | switch (params.productCode) { 5 | case '1': 6 | data = response['success-1']; 7 | break; 8 | case '2': 9 | data = response['success-2']; 10 | break; 11 | default: 12 | data = response['success-default']; 13 | break; 14 | } 15 | 16 | %> 17 | 18 | <%-JSON.stringify(data);%> -------------------------------------------------------------------------------- /test/data/dto-2-class/normalize_01.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'string', 3 | address: { 4 | 'street': 'string', 5 | 'postalCode': 'number', 6 | 'primaryAddress': 'boolean', 7 | 'company': { 8 | 'name': 'string' 9 | } 10 | }, 11 | entries: [ 12 | '$ref-EntryDTO' 13 | ], 14 | card: '$ref-CardDTO', 15 | user: '$ref-UserDTO', 16 | posts: [ 17 | { 18 | words: 'string' 19 | } 20 | ] 21 | }; 22 | -------------------------------------------------------------------------------- /views/scripts.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /doc/readme-response-validation.md: -------------------------------------------------------------------------------- 1 | 2 | # Mock response validation 3 | 4 | - Use the UI to validate each mock response against the schema. 5 | - It's also possible to validate all responses by clicking "Validate all mock responses" in UI. 6 | - In case of you using params (query or path params) in mock data, you can simulate them by adding an [".request_data.json"](/demo/rest/products/%23%7BproductCode%7D/GET/mock/.request_data.json) file. 7 | -------------------------------------------------------------------------------- /views/group.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |

/<%=group.name%>

4 |
5 |
6 |
7 | <% for(var k=0; k 8 | <% var service = group.services[k]; %> 9 | <% include service %> 10 | <% } %> 11 |
12 |
13 |
-------------------------------------------------------------------------------- /demo/rest/products/#search/GET/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Returns a list of products", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "request": { 7 | "query": { 8 | "parameters": [ 9 | { 10 | "name": "query", 11 | "required": false, 12 | "type": "string", 13 | "desc": "Search query" 14 | } 15 | ] 16 | } 17 | }, 18 | "response": { 19 | "statusCode": 200, 20 | "schema": { 21 | "type": "application/json" 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /lib/cli/ask/func-path.js: -------------------------------------------------------------------------------- 1 | var inquirer = require('inquirer'); 2 | var Promise = require('es6-promise-polyfill').Promise; 3 | 4 | function askForFuncPath() { 5 | return new Promise((resolve) => { 6 | inquirer.prompt([ 7 | { 8 | type: 'input', 9 | name: 'funcPath', 10 | message: 'Enter the desired path (from the mock server directory) to store the mock functions', 11 | default: '/func', 12 | }, 13 | ]).then(resolve); 14 | }); 15 | } 16 | 17 | module.exports = askForFuncPath; 18 | -------------------------------------------------------------------------------- /demo/rest/search/#search.{query}.results.json/GET/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Returns a list of products", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "request": { 7 | "query": { 8 | "parameters": [ 9 | { 10 | "name": "query", 11 | "required": false, 12 | "type": "string", 13 | "desc": "Search query" 14 | } 15 | ] 16 | } 17 | }, 18 | "response": { 19 | "statusCode": 200, 20 | "schema": { 21 | "type": "application/json" 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /views/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%=title %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Returns product image", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "request": { 7 | "uri": { 8 | "parameters": [ 9 | { 10 | "name": "productCode", 11 | "required": true, 12 | "type": "string", 13 | "desc": "Product identifier" 14 | } 15 | ] 16 | } 17 | }, 18 | "response": { 19 | "statusCode": 200, 20 | "schema": { 21 | "type": "application/json" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Returns details of a single product.", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "request": { 7 | "uri": { 8 | "parameters": [ 9 | { 10 | "name": "productCode", 11 | "required": true, 12 | "type": "string", 13 | "desc": "Product identifier" 14 | } 15 | ] 16 | } 17 | }, 18 | "response": { 19 | "statusCode": 200, 20 | "schema": { 21 | "type": "application/json" 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /doc/readme-expected-response.md: -------------------------------------------------------------------------------- 1 | # Expected response 2 | 3 | - Use the UI to configure the expected response for each call 4 | - Use the get param "_expected" 5 | - Use the request header "_expected" 6 | - If an dynamic path param is empty or in placeholder format an "400 bad request" will be the response 7 | - To simulate responses with status like "500" you have to add an "error.json" into the response folder. 8 | - To simulate responses with specific status like "400" you have to add an "error-401.json" into the response folder. 9 | -------------------------------------------------------------------------------- /demo/rest/search/#users#{userId}#products#{productCode}#available/GET/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Returns a list of products", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "request": { 7 | "query": { 8 | "parameters": [ 9 | { 10 | "name": "query", 11 | "required": false, 12 | "type": "string", 13 | "desc": "Search query" 14 | } 15 | ] 16 | } 17 | }, 18 | "response": { 19 | "statusCode": 200, 20 | "schema": { 21 | "type": "application/json" 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /lib/cli/ask/path.js: -------------------------------------------------------------------------------- 1 | var inquirer = require('inquirer'); 2 | var Promise = require('es6-promise-polyfill').Promise; 3 | 4 | function askForPath() { 5 | return new Promise((resolve) => { 6 | inquirer.prompt([ 7 | { 8 | type: 'input', 9 | name: 'path', 10 | message: 'Enter the desired path (from your current directory) to store the mock server data', 11 | default: 'mock', 12 | }, 13 | ]).then(function (answers) { 14 | resolve(answers); 15 | }); 16 | }); 17 | } 18 | 19 | module.exports = askForPath; 20 | -------------------------------------------------------------------------------- /src/templates/response_func_header.tpl: -------------------------------------------------------------------------------- 1 | <% 2 | 3 | var _dirname = __dirname, 4 | _require = require; 5 | 6 | function _getArray(getData) { 7 | var out = [], 8 | len = 2, 9 | i; 10 | 11 | for (i = 0; i < len; i += 1) { 12 | out.push(getData()); 13 | } 14 | 15 | return JSON.stringify(out, null, 2); 16 | } 17 | 18 | function _getRef(name) { 19 | try { 20 | return JSON.parse(_require(_dirname + '<%funcPath%>/' + name + '.js')['imported' + name]()); 21 | } catch (err) {} 22 | 23 | return {}; 24 | } 25 | 26 | %> -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/POST/desc.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "Writes a single product.", 3 | "security": [], 4 | "protected": false, 5 | "status": "open", 6 | "request": { 7 | "uri": { 8 | "parameters": [ 9 | { 10 | "name": "productCode", 11 | "required": true, 12 | "type": "", 13 | "desc": "Product code" 14 | } 15 | ] 16 | }, 17 | "schema": { 18 | "type": "application/xml, application/json " 19 | } 20 | }, 21 | "response": { 22 | "statusCode": 200, 23 | "schema": { 24 | "type": "application/json" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /lib/commands/get-collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var log = require('chip')(); 4 | var Utils = require('../Utils'); 5 | var utils = new Utils(); 6 | 7 | function getCollection(id, options) { 8 | 9 | if (!utils.isFilledString(id)) { 10 | return false; 11 | } 12 | 13 | var path = `${options.restPath}/_collections/${id}.json`; 14 | 15 | if (!utils.existFile(path)) { 16 | log.error(`cannot read collection ${id}: file "${path}" don\'t exist!`); 17 | return false; 18 | } 19 | 20 | return JSON.parse(utils.readFile(path)); 21 | } 22 | 23 | module.exports = getCollection; 24 | -------------------------------------------------------------------------------- /lib/commands/delete-collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var log = require('chip')(); 4 | var Utils = require('../Utils'); 5 | var utils = new Utils(); 6 | 7 | function deleteCollection(id, options) { 8 | if (!utils.isFilledString(id)) { 9 | return false; 10 | } 11 | 12 | var path = `${options.restPath}/_collections/${id}.json`; 13 | 14 | if (!utils.existFile(path)) { 15 | log.error(`cannot delete collection ${id}: file "${path}" don\'t exist!`); 16 | return false; 17 | } 18 | 19 | utils.removeFile(path); 20 | return true; 21 | } 22 | 23 | module.exports = deleteCollection; 24 | -------------------------------------------------------------------------------- /lib/cli/ask/naming.js: -------------------------------------------------------------------------------- 1 | var inquirer = require('inquirer'); 2 | var Promise = require('es6-promise-polyfill').Promise; 3 | 4 | function askForNaming() { 5 | return new Promise((resolve) => { 6 | inquirer.prompt([ 7 | { 8 | type: 'input', 9 | name: 'title', 10 | message: 'Enter the title of the API', 11 | default: 'Api mock server', 12 | }, 13 | { 14 | type: 'input', 15 | name: 'version', 16 | message: 'Enter the version number of the API', 17 | default: '1', 18 | }, 19 | ]).then(resolve); 20 | }); 21 | } 22 | 23 | module.exports = askForNaming; 24 | -------------------------------------------------------------------------------- /lib/defaults/options-defaults.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | restPath: './rest', 4 | uiPath: '/', 5 | title: 'Api mock server', 6 | version: 1, 7 | urlBase: 'http://localhost:3001', 8 | urlPath: '/rest/v1', 9 | port: 3001, 10 | contentType: 'application/json', 11 | accessControlExposeHeaders: 'X-Total-Count', 12 | accessControlAllowOrigin: '*', 13 | accessControlAllowMethods: 'GET, POST, PUT, OPTIONS, DELETE, PATCH, HEAD', 14 | accessControlAllowHeaders: 'origin, x-requested-with, content-type', 15 | accessControlAllowCredentials: 'true', 16 | headers: {}, 17 | open: true, 18 | }; 19 | -------------------------------------------------------------------------------- /doc/readme-tunnel.md: -------------------------------------------------------------------------------- 1 | # Tunnel 2 | 3 | The tunnel enables you to fetch, update ... data directly to the API. 4 | The response from the API server will be stored in a "tunnel-latest.json" and "tunnel-latest-.json" response file. 5 | By using the collections you can easily switch between tunneling or not. 6 | 7 | * `$ node collections tunnel` will activate the tunnel everwhere 8 | * `$ node collections tunnel-latest` will activate all latest tunnel responses 9 | * `$ node collections reset` set all responses to default 10 | 11 | [example](/demo/options.js#L57) 12 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}#static/GET/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "example.png": { 4 | "counter": 0 5 | }, 6 | "success": { 7 | "counter": 0 8 | }, 9 | "document.txt": { 10 | "counter": 0 11 | }, 12 | "example.gif": { 13 | "counter": 0 14 | }, 15 | "example.jpg": { 16 | "counter": 0 17 | }, 18 | "example.xml": { 19 | "counter": 0 20 | }, 21 | "javascript.js": { 22 | "counter": 0 23 | }, 24 | "success.json": { 25 | "counter": 0 26 | }, 27 | "styles.css": { 28 | "counter": 0 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /demo/rest/_DTO/AddressWsDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "$ref-CountryWsDTO", 3 | "countryIsocode": "string", 4 | "country2": "$ref-CountryWsDTO", 5 | "email": "string", 6 | "firstName": "string", 7 | "id": "string", 8 | "lastName": "string", 9 | "line1": "string", 10 | "phone": "string", 11 | "postalCode": "string", 12 | "streetName": "string", 13 | "streetNumber": "string", 14 | "title": "string", 15 | "titleCode": "string", 16 | "town": "string", 17 | "list": [ 18 | { 19 | "country": "$ref-CountryWsDTO", 20 | "email": "string" 21 | } 22 | ], 23 | "items": [ 24 | "$ref-CountryWsDTO" 25 | ] 26 | } -------------------------------------------------------------------------------- /views/validate-result-modal.ejs: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /test/expected/dto-import/AddressWsDTO.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "$ref-CountryWsDTO", 3 | "countryIsocode": "string", 4 | "country2": "$ref-CountryWsDTO", 5 | "email": "string", 6 | "firstName": "string", 7 | "id": "string", 8 | "lastName": "string", 9 | "line1": "string", 10 | "phone": "string", 11 | "postalCode": "string", 12 | "streetName": "string", 13 | "streetNumber": "string", 14 | "title": "string", 15 | "titleCode": "string", 16 | "town": "string", 17 | "list": [ 18 | { 19 | "country": "$ref-CountryWsDTO", 20 | "email": "string" 21 | } 22 | ], 23 | "items": [ 24 | "$ref-CountryWsDTO" 25 | ] 26 | } -------------------------------------------------------------------------------- /test/expected/swagger-import/request_schema_02.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "$ref-CountryWsDTO", 3 | "countryIsocode": "string", 4 | "country2": "$ref-CountryWsDTO", 5 | "email": "string", 6 | "firstName": "string", 7 | "id": "string", 8 | "lastName": "string", 9 | "line1": "string", 10 | "phone": "string", 11 | "postalCode": "string", 12 | "streetName": "string", 13 | "streetNumber": "string", 14 | "title": "string", 15 | "titleCode": "string", 16 | "town": "string", 17 | "list": [ 18 | { 19 | "country": "$ref-CountryWsDTO", 20 | "email": "string" 21 | } 22 | ], 23 | "items": [ 24 | "$ref-CountryWsDTO" 25 | ] 26 | } -------------------------------------------------------------------------------- /test/expected/swagger-import/response_schema_01.json: -------------------------------------------------------------------------------- 1 | { 2 | "country": "$ref-CountryWsDTO", 3 | "countryIsocode": "string", 4 | "country2": "$ref-CountryWsDTO", 5 | "email": "string", 6 | "firstName": "string", 7 | "id": "string", 8 | "lastName": "string", 9 | "line1": "string", 10 | "phone": "string", 11 | "postalCode": "string", 12 | "streetName": "string", 13 | "streetNumber": "string", 14 | "title": "string", 15 | "titleCode": "string", 16 | "town": "string", 17 | "list": [ 18 | { 19 | "country": "$ref-CountryWsDTO", 20 | "email": "string" 21 | } 22 | ], 23 | "items": [ 24 | "$ref-CountryWsDTO" 25 | ] 26 | } -------------------------------------------------------------------------------- /lib/cli/ask/default-config.js: -------------------------------------------------------------------------------- 1 | var inquirer = require('inquirer'); 2 | var Promise = require('es6-promise-polyfill').Promise; 3 | 4 | function askForDefaultConfig() { 5 | return new Promise((resolve, reject) => { 6 | inquirer.prompt([ 7 | { 8 | type: 'list', 9 | name: 'defaultConfig', 10 | choices: ['Yes', 'No'], 11 | message: 'Do you want to use the default configuration?', 12 | default: 'Yes', 13 | }, 14 | ]).then(function (answers) { 15 | if (answers.defaultConfig.toUpperCase() === 'YES') { 16 | reject(); 17 | return; 18 | } 19 | resolve({}); 20 | }); 21 | }); 22 | } 23 | 24 | module.exports = askForDefaultConfig; 25 | -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/.store.json: -------------------------------------------------------------------------------- 1 | { 2 | "validation": { 3 | "faker": { 4 | "counter": 6 5 | }, 6 | "func": { 7 | "counter": 0 8 | }, 9 | "request-data": { 10 | "counter": 1 11 | }, 12 | "success-1": { 13 | "counter": 3 14 | }, 15 | "success-2": { 16 | "counter": 1 17 | }, 18 | "success-default": { 19 | "counter": 1 20 | }, 21 | "success": { 22 | "counter": 1 23 | }, 24 | "deep-validation": { 25 | "counter": 0 26 | }, 27 | "deep-validation.json": { 28 | "counter": 1 29 | }, 30 | "success-207": { 31 | "counter": 1 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /demo/rest/products/#{productCode}/GET/response_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "productCode": "string", 3 | "price": "$ref-PriceDTO", 4 | "email": "string", 5 | "success": "boolean", 6 | "highlight": "boolean", 7 | "quantity": "integer", 8 | "currentPage": "string", 9 | "cards": [ 10 | "$ref-CardDTO" 11 | ], 12 | "arrayString": [ 13 | "string" 14 | ], 15 | "arrayNumber": [ 16 | "number" 17 | ], 18 | "arrayObject": [ 19 | { 20 | "name": "string" 21 | } 22 | ], 23 | "arrayArray": [ 24 | [ 25 | { 26 | "name": "string" 27 | } 28 | ] 29 | ], 30 | "image": { 31 | "url": "string", 32 | "alt": "string" 33 | } 34 | } -------------------------------------------------------------------------------- /views/parameter-desc.ejs: -------------------------------------------------------------------------------- 1 | <% if (itemsObj && items.length > 0) { %> 2 |

<%=itemsTitle%>

3 |
    4 | <% for(var m=0; m 5 | <% var item = items[m]; %> 6 |
  • 7 | <%=item.name%>: 8 | 9 | <% if (item.required) { %> 10 | required 11 | <% } %> 12 | (<%=item.type%>) 13 | 14 |

    <%-item.desc%>

    15 |
      16 | <% if (item.descList && item.descList.length > 0) { %> 17 | <% for(var n=0; n 18 |
    • <%-item.descList[n]%>
    • 19 | <% } %> 20 | <% } %> 21 |
    22 |
  • 23 | <% } %> 24 |
25 | <% } %> -------------------------------------------------------------------------------- /lib/cli/validate-cli.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | 3 | var util = require('util'); 4 | var extend = util._extend; 5 | var _defaults = require('../defaults/options-defaults'); 6 | var ValidatorResponses = require('../ValidatorResponses'); 7 | 8 | function validateCli(runServer, options) { 9 | 10 | options = extend(_defaults, options || {}); 11 | options = extend(options, { 12 | onServerStarted: function (app, server) { 13 | var validatorResponses = new ValidatorResponses({ 14 | restPath: options.restPath, 15 | }, options); 16 | 17 | server.close(); 18 | return validatorResponses; 19 | }, 20 | }); 21 | 22 | runServer(options); 23 | } 24 | 25 | module.exports = validateCli; 26 | -------------------------------------------------------------------------------- /views/validate-modal.ejs: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /lib/cli/help-cli.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | 3 | function helpCli() { 4 | console.log(''); 5 | console.log('usage: node 21 |

22 | ( 23 | Errors: <%=validation.counter.error%>, 24 | Warnings: <%=validation.counter.warn%>, 25 | Infos: <%=validation.counter.info%>, 26 | Success: <%=validation.counter.success%> 27 | ) 28 |

29 | 35 | Open Result 36 | 37 | 38 | <% } %> 39 | <% for(var i=0; i 40 | <% var group = apiData[i]; %> 41 | <% include group.ejs %> 42 | <% } %> 43 | 49 | + add new endpoint 50 | 51 |
52 |
53 | 54 |
55 | 58 |
59 | 60 | 61 | <% include add-endpoint-modal.ejs %> 62 | <% include swagger-import-modal.ejs %> 63 | <% include preferences-modal.ejs %> 64 | <% include validate-result-modal.ejs %> 65 | <% include validate-single-modal.ejs %> 66 | <% include validate-modal.ejs %> 67 | <% include scripts.ejs %> 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /lib/controller/ValidatorController.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var Utils = require('../Utils'); 5 | var util = require('util'); 6 | var extend = util._extend; 7 | var AppControllerSingleton = require('./AppController'); 8 | var appController = AppControllerSingleton.getInstance(); 9 | var ValidatorResponse = require('../ValidatorResponse'); 10 | var ValidatorResponses = require('../ValidatorResponses'); 11 | var validatorLog = require('../ValidatorLog'); 12 | 13 | /** 14 | * 15 | * @class ValidatorController 16 | * @constructor 17 | * 18 | */ 19 | function ValidatorController() { 20 | this.init(); 21 | } 22 | 23 | ValidatorController.prototype = extend(ValidatorController.prototype, Utils.prototype); 24 | ValidatorController.prototype = extend(ValidatorController.prototype, { 25 | 26 | constructor: ValidatorController, 27 | 28 | /** 29 | * 30 | * @method init 31 | * called by constructor 32 | * @public 33 | */ 34 | init: function () { 35 | 36 | this.options = appController.options; 37 | 38 | appController.app.get('/service/validation/response', this._serviceRunValidatorForResponse.bind(this)); 39 | appController.app.get('/service/validation/responses', this._serviceRunValidatorForResponses.bind(this)); 40 | }, 41 | 42 | /** 43 | * @method _serviceRunValidatorForResponse 44 | * @param {object} req 45 | * @param {object} res 46 | * @private 47 | */ 48 | _serviceRunValidatorForResponse: function (req, res) { 49 | 50 | validatorLog.clear(); 51 | 52 | this.validatorResponse = new ValidatorResponse({ 53 | path: req.query.path, 54 | method: req.query.method, 55 | expected: req.query.expected, 56 | }, this.options); 57 | 58 | res.send(validatorLog.get()); 59 | res.end(); 60 | }, 61 | 62 | /** 63 | * @method _serviceRunValidatorForResponses 64 | * @param {object} req 65 | * @param {object} res 66 | * @private 67 | */ 68 | _serviceRunValidatorForResponses: function (req, res) { 69 | 70 | validatorLog.clear(); 71 | 72 | this.validatorResponses = new ValidatorResponses({ 73 | restPath: this.options.restPath, 74 | }, this.options); 75 | 76 | res.send(validatorLog.get()); 77 | res.end(); 78 | }, 79 | 80 | }); 81 | 82 | module.exports = ValidatorController; 83 | -------------------------------------------------------------------------------- /lib/controller/SchemaController.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var Utils = require('../Utils'); 5 | var util = require('util'); 6 | var extend = util._extend; 7 | var AppControllerSingleton = require('./AppController'); 8 | var appController = AppControllerSingleton.getInstance(); 9 | 10 | /** 11 | * 12 | * @class ViewSchemaController 13 | * @constructor 14 | * 15 | */ 16 | function ViewSchemaController() { 17 | this.init(); 18 | } 19 | 20 | ViewSchemaController.prototype = extend(ViewSchemaController.prototype, Utils.prototype); 21 | ViewSchemaController.prototype = extend(ViewSchemaController.prototype, { 22 | 23 | constructor: ViewSchemaController, 24 | 25 | /** 26 | * 27 | * @method init 28 | * called by constructor 29 | * @public 30 | */ 31 | init: function () { 32 | 33 | this.options = appController.options; 34 | 35 | appController.app.get('/view/schema', this._viewFormatted.bind(this)); 36 | appController.app.get('/view/schema/file', this._viewFile.bind(this)); 37 | }, 38 | 39 | /** 40 | * @method _viewFile 41 | * @param {object} req 42 | * @param {object} res 43 | * @private 44 | */ 45 | _viewFile: function (req, res) { 46 | if (!req.query || !req.query.url) { 47 | res.send('Not Found'); 48 | return; 49 | } 50 | 51 | try { 52 | res.send(this.readFile(req.query.url)); 53 | } catch (err) { 54 | res.send('Not Found'); 55 | } 56 | }, 57 | 58 | /** 59 | * @method _viewFormatted 60 | * @param {object} req 61 | * @param {object} res 62 | * @private 63 | */ 64 | _viewFormatted: function (req, res) { 65 | if (!req.query || !req.query.url) { 66 | res.send('Not Found'); 67 | return; 68 | } 69 | 70 | try { 71 | var method = req.query.method || 'GET'; 72 | var title = (req.query.type || '') + ' Schema'; 73 | var path = (req.query.path || ''); 74 | 75 | res.render('service-schema.ejs', { 76 | pageTitle: title, 77 | title: title + ' | ' + method + ' | ' + path, 78 | subTitle: path, 79 | method: method, 80 | methodLower: method.toLowerCase(), 81 | schemaJSON: this.readFile(req.query.url), 82 | }); 83 | 84 | } catch (err) { 85 | res.send('Not Found'); 86 | } 87 | }, 88 | 89 | }); 90 | 91 | module.exports = ViewSchemaController; 92 | -------------------------------------------------------------------------------- /src/libraries/css/highlight.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#f0f0f0}.hljs,.hljs-subst,.hljs-tag .hljs-title,.lisp .hljs-title,.clojure .hljs-built_in,.nginx .hljs-title{color:black}.hljs-string,.hljs-title,.hljs-constant,.hljs-parent,.hljs-tag .hljs-value,.hljs-rules .hljs-value,.hljs-preprocessor,.hljs-pragma,.haml .hljs-symbol,.ruby .hljs-symbol,.ruby .hljs-symbol .hljs-string,.hljs-template_tag,.django .hljs-variable,.smalltalk .hljs-class,.hljs-addition,.hljs-flow,.hljs-stream,.bash .hljs-variable,.apache .hljs-tag,.apache .hljs-cbracket,.tex .hljs-command,.tex .hljs-special,.erlang_repl .hljs-function_or_atom,.asciidoc .hljs-header,.markdown .hljs-header,.coffeescript .hljs-attribute{color:#800}.smartquote,.hljs-comment,.hljs-annotation,.hljs-template_comment,.diff .hljs-header,.hljs-chunk,.asciidoc .hljs-blockquote,.markdown .hljs-blockquote{color:#888}.hljs-number,.hljs-date,.hljs-regexp,.hljs-literal,.hljs-hexcolor,.smalltalk .hljs-symbol,.smalltalk .hljs-char,.go .hljs-constant,.hljs-change,.lasso .hljs-variable,.makefile .hljs-variable,.asciidoc .hljs-bullet,.markdown .hljs-bullet,.asciidoc .hljs-link_url,.markdown .hljs-link_url{color:#080}.hljs-label,.hljs-javadoc,.ruby .hljs-string,.hljs-decorator,.hljs-filter .hljs-argument,.hljs-localvars,.hljs-array,.hljs-attr_selector,.hljs-important,.hljs-pseudo,.hljs-pi,.haml .hljs-bullet,.hljs-doctype,.hljs-deletion,.hljs-envvar,.hljs-shebang,.apache .hljs-sqbracket,.nginx .hljs-built_in,.tex .hljs-formula,.erlang_repl .hljs-reserved,.hljs-prompt,.asciidoc .hljs-link_label,.markdown .hljs-link_label,.vhdl .hljs-attribute,.clojure .hljs-attribute,.asciidoc .hljs-attribute,.lasso .hljs-attribute,.coffeescript .hljs-property,.hljs-phony{color:#88f}.hljs-keyword,.hljs-id,.hljs-title,.hljs-built_in,.css .hljs-tag,.hljs-javadoctag,.hljs-phpdoc,.hljs-yardoctag,.smalltalk .hljs-class,.hljs-winutils,.bash .hljs-variable,.apache .hljs-tag,.go .hljs-typename,.tex .hljs-command,.asciidoc .hljs-strong,.markdown .hljs-strong,.hljs-request,.hljs-status{font-weight:bold}.asciidoc .hljs-emphasis,.markdown .hljs-emphasis{font-style:italic}.nginx .hljs-built_in{font-weight:normal}.coffeescript .javascript,.javascript .xml,.lasso .markup,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5} -------------------------------------------------------------------------------- /views/method-modal.ejs: -------------------------------------------------------------------------------- 1 | 69 | -------------------------------------------------------------------------------- /test/expected/dto-response-func/ResponseFuncTestDTO.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* jshint ignore:start */ 3 | 'use strict'; 4 | 5 | var faker = require('faker'); 6 | 7 | function _getArray(getData) { 8 | var out = [], 9 | len = faker.random.number(10) + 1, 10 | i; 11 | 12 | for (i = 0; i < len; i += 1) { 13 | out.push(getData()); 14 | } 15 | 16 | return out; 17 | } 18 | 19 | 20 | function _getRes(name, loopArr) { 21 | try { 22 | return JSON.parse(require(__dirname + '/' + name + '.js')['imported' + name](loopArr.slice())); 23 | } catch (err) {} 24 | 25 | return {}; 26 | } 27 | 28 | module.exports = { 29 | 30 | importedResponseFuncTestDTO: function (loopArr) { 31 | 32 | var found1; 33 | 34 | if (!(loopArr instanceof Array)) { 35 | loopArr = []; 36 | } 37 | 38 | found1 = loopArr.indexOf('importedResponseFuncTestDTO'); 39 | 40 | if (found1 >= 0) { 41 | if (loopArr.indexOf('importedResponseFuncTestDTO', found1 + 1) >= 0) { 42 | return '{}'; 43 | } 44 | } 45 | 46 | loopArr.push('importedResponseFuncTestDTO'); 47 | 48 | return JSON.stringify({ 49 | "list": _getArray(function () {return { "country": _getRes('CountryWsDTO', loopArr), "email": faker.internet.email()};}), 50 | "items": _getArray(function () {return _getRes('CountryWsDTO', loopArr);}), 51 | "image": _getRes('CountryWsDTO', loopArr), 52 | "exampleString": faker.lorem.word(), 53 | "exampleNumber": faker.random.number(), 54 | "exampleInteger": faker.random.number(), 55 | "name": faker.name.findName(), 56 | "firstName": faker.name.firstName(), 57 | "lastName": faker.name.lastName(), 58 | "country": _getRes('CountryWsDTO', loopArr), 59 | "countryIsocode": faker.random.arrayElement(["CH","DE","AT","FR","UK","US","JP"]), 60 | "email": faker.internet.email(), 61 | "phone": faker.phone.phoneNumber(), 62 | "postalCode": faker.address.zipCode(), 63 | "streetName": faker.address.streetName(), 64 | "streetNumber": faker.random.number(), 65 | "title": faker.name.prefix(), 66 | "titleCode": faker.random.arrayElement(["mr","ms"]), 67 | "town": faker.address.city(), 68 | "parameters": { 69 | "additional": faker.lorem.word() 70 | } 71 | }, null, 2); 72 | } 73 | 74 | }; 75 | 76 | /* jshint ignore:end */ 77 | /* eslint-enable */ -------------------------------------------------------------------------------- /lib/commands/activate-collection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var log = require('chip')(); 4 | var Utils = require('../Utils'); 5 | var utils = new Utils(); 6 | var getCollection = require('./get-collection'); 7 | var ignoreInRestRoot = require('../constants/ignore-in-rest-root'); 8 | 9 | function activateCollection(id, options) { 10 | 11 | if (!utils.isFilledString(id)) { 12 | return false; 13 | } 14 | 15 | var collection; 16 | 17 | if (id === 'reset' || id === 'tunnel' || id === 'tunnel-latest') { 18 | collection = { 19 | selectedResponses: {}, 20 | }; 21 | } else { 22 | collection = getCollection(id, options); 23 | } 24 | 25 | if (!collection || !collection.selectedResponses) { 26 | return true; 27 | } 28 | 29 | var selectedResponses = collection.selectedResponses; 30 | 31 | var path = `${options.restPath}`; 32 | var serviceGroups = utils.readDir(path, ignoreInRestRoot); 33 | 34 | serviceGroups.forEach(function (serviceGroup) { 35 | var endPoints = utils.readDir(serviceGroup.path); 36 | endPoints.forEach(function (endPoint) { 37 | var services = utils.readDir(endPoint.path); 38 | services.forEach(function (service) { 39 | var name = `${serviceGroup.file}/${endPoint.file}/${service.file}`; 40 | var pathSelectedFile = `${service.path}/mock/response.txt`; 41 | 42 | if (id === 'tunnel') { 43 | // if tunnel save tunnel as selected response 44 | utils.writeFile(pathSelectedFile, 'tunnel'); 45 | log(`Wrote file "${pathSelectedFile.replace(options.restPath, '')}"`); 46 | } else if (id === 'tunnel-latest') { 47 | // if tunnel-latest save tunnel-latest.json as selected response 48 | utils.writeFile(pathSelectedFile, 'tunnel-latest'); 49 | log(`Wrote file "${pathSelectedFile.replace(options.restPath, '')}"`); 50 | } else if (typeof selectedResponses[name] === 'string') { 51 | // if nothing is selected remove file -> fallback success 52 | utils.writeFile(pathSelectedFile, selectedResponses[name]); 53 | log(`Wrote file "${pathSelectedFile.replace(options.restPath, '')}"`); 54 | } else if (utils.existFile(pathSelectedFile)) { 55 | utils.removeFile(pathSelectedFile); 56 | log(`Removed file "${pathSelectedFile.replace(options.restPath, '')}"`); 57 | } 58 | }); 59 | }); 60 | }); 61 | 62 | return true; 63 | } 64 | 65 | module.exports = activateCollection; 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-mock-server", 3 | "version": "0.25.4", 4 | "description": "File based Node REST API mock server", 5 | "email": "simon.mollweide@web.de", 6 | "author": "Simon Mollweide ", 7 | "url": "https://github.com/smollweide/node-mock-server", 8 | "repository": { 9 | "type": "github", 10 | "url": "https://github.com/smollweide/node-mock-server" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/smollweide/node-mock-server/issues" 14 | }, 15 | "scripts": { 16 | "demo": "node demo/index.js", 17 | "demo-tunnel": "node demo/tunnel.js", 18 | "test": "mocha --exit test/tests.js", 19 | "test:watch": "mocha --colors test/tests.js", 20 | "posttest": "npm run lint", 21 | "lint": "npm run lint:js", 22 | "lint:js": "node_modules/.bin/eslint .", 23 | "start": "dash4" 24 | }, 25 | "main": "mock-server.js", 26 | "keywords": [ 27 | "node", 28 | "node.js", 29 | "npm", 30 | "mock", 31 | "rest", 32 | "REST", 33 | "rest API", 34 | "REST API", 35 | "api", 36 | "API", 37 | "json", 38 | "DTO", 39 | "Datatransferobject", 40 | "Swagger", 41 | "documentation", 42 | "REST API documentation", 43 | "cli" 44 | ], 45 | "engines": { 46 | "node": ">=6" 47 | }, 48 | "preferGlobal": false, 49 | "private": false, 50 | "license": "MIT", 51 | "dependencies": { 52 | "chip": "0.0.5", 53 | "ejs": "2.6.1", 54 | "cookie-parser": "1.4.5", 55 | "deasync": "0.1.20", 56 | "es6-promise-polyfill": "1.2.0", 57 | "express": "4.17.1", 58 | "extend": "3.0.2", 59 | "faker": "4.1.0", 60 | "inflect": "0.4.1", 61 | "inquirer": "5.2.0", 62 | "make-dir": "1.3.0", 63 | "mime-types": "2.1.27", 64 | "minimatch": "3.0.4", 65 | "opener": "1.5.1", 66 | "p-series": "2.1.0", 67 | "request": "2.88.2", 68 | "yamljs": "0.3.0", 69 | "react-dev-utils": "5.0.2" 70 | }, 71 | "devDependencies": { 72 | "@dash4/plugin-dependencies": "0.8.4", 73 | "@dash4/plugin-npm-scripts": "0.8.4", 74 | "@dash4/plugin-readme": "0.8.4", 75 | "@dash4/plugin-terminal": "0.8.4", 76 | "@dash4/server": "0.8.4", 77 | "@namics/eslint-config": "8.0.7", 78 | "ajv": "6.12.2", 79 | "babel-eslint": "10.1.0", 80 | "eslint": "7.0.0", 81 | "eslint-plugin-import": "2.20.2", 82 | "mocha": "7.1.2" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /demo/tunnel.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var express = require('express'); 5 | var https = require('https'); 6 | var openBrowser = require('react-dev-utils/openBrowser'); 7 | var jQueryExtend = require('extend'); 8 | var cookieParser = require('cookie-parser'); 9 | var log = require('chip')(); 10 | var defaultOptions = require('../lib/defaults/options-defaults'); 11 | var options = require('./options'); 12 | var getCertificate = require('../lib/Utils'); 13 | 14 | var app = express(); 15 | var server; 16 | options = jQueryExtend(defaultOptions, options); 17 | 18 | var logFunc = function () { 19 | if (process.env.NODE_ENV !== 'test') { 20 | log.info('server started at port ' + options.tunnel.port); 21 | if (options.open) { 22 | openBrowser('http://localhost:' + options.tunnel.port); 23 | } 24 | } 25 | }; 26 | 27 | app.use(express.json({ limit: '50mb' })); 28 | app.use(express.urlencoded({ extended: true })); 29 | app.use(cookieParser()); 30 | 31 | function setHeaders(res) { 32 | res.setHeader('Test', "test"); 33 | res.setHeader('Content-Type', options.contentType); 34 | res.setHeader('Access-Control-Expose-Headers', options.accessControlExposeHeaders); 35 | res.setHeader('Access-Control-Allow-Origin', options.accessControlAllowOrigin); 36 | res.setHeader('Access-Control-Allow-Methods', options.accessControlAllowMethods); 37 | res.setHeader('Access-Control-Allow-Headers', options.accessControlAllowHeaders); 38 | res.setHeader('Access-Control-Allow-Credentials', options.accessControlAllowCredentials); 39 | } 40 | 41 | app.get('/rest/v1/products', (req, res) => { 42 | setHeaders(res); 43 | var response = { tunnelResponse: { success: true, headers: req.headers } }; 44 | res.send(JSON.stringify(response, null, 2)); 45 | res.end(); 46 | }); 47 | app.post('/rest/v1/products/:productCode', (req, res) => { 48 | setHeaders(res); 49 | var body = jQueryExtend(req.body, { 50 | productCode: req.params.productCode, 51 | query: req.query 52 | }); 53 | res.cookie('cookieName', 'my-cookie', { maxAge: 900000, httpOnly: true }); 54 | res.send(JSON.stringify(body, null, 2)); 55 | res.end(); 56 | }); 57 | 58 | if (options.tunnel.privateKey && options.tunnel.certificate) { 59 | server = https.createServer({ 60 | key: getCertificate(options.tunnel.privateKey), 61 | cert: getCertificate(options.tunnel.certificate), 62 | }, app).listen(options.tunnel.port, logFunc); 63 | } else { 64 | server = app.listen(options.tunnel.port, logFunc); 65 | } 66 | -------------------------------------------------------------------------------- /views/default-page-header.ejs: -------------------------------------------------------------------------------- 1 | 27 |
28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | <% for(var i=0; i 41 | <% var dto = dataDto[i]; %> 42 | 43 | <%= dto.nameClean %> 44 | 45 | <% include dto-modal.ejs %> 46 | <% } %> 47 |
48 |
49 |
50 |
51 | 52 | + add new collection 53 | 54 | <% include collection-new-modal.ejs %> 55 |
56 |
57 |
58 |
59 | 60 | <% for(var i=0; i 61 | <% var collection = dataCollections[i]; %> 62 | 63 | <%= collection.name %> (<%= collection.description %>) 64 | 65 | <% include collection-modal.ejs %> 66 | <% } %> 67 |
68 |
69 |
70 |
71 | -------------------------------------------------------------------------------- /lib/SwaggerDefinition.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var SwaggerUtils = require('./SwaggerUtils'); 5 | var Utils = require('./Utils'); 6 | var extend = require('util')._extend; 7 | var DTOToResponseFuncConverter = require('./DTOToResponseFuncConverter'); 8 | 9 | /** 10 | * 11 | * @class SwaggerDefinition 12 | * @namespace SwaggerImport 13 | * @param {string} name 14 | * @param {object} definition 15 | * @param {object} options 16 | * @constructor 17 | * 18 | * Swagger importer 19 | */ 20 | function SwaggerDefinition(name, definition, options) { 21 | this.init(name, definition, options); 22 | } 23 | 24 | SwaggerDefinition.prototype = extend(SwaggerDefinition.prototype, Utils.prototype); 25 | SwaggerDefinition.prototype = extend(SwaggerDefinition.prototype, SwaggerUtils.prototype); 26 | SwaggerDefinition.prototype = extend(SwaggerDefinition.prototype, { 27 | 28 | constructor: SwaggerDefinition, 29 | 30 | /** 31 | * 32 | * @method init 33 | * called by constructor 34 | * @param {string} name 35 | * @param {object} definition 36 | * @param {object} options 37 | * @returns void 38 | * @public 39 | */ 40 | init: function (name, definition, options) { 41 | 42 | this._name = name; 43 | this._str = ''; 44 | this._def = {}; 45 | this._options = options; 46 | 47 | if (typeof definition === 'object') { 48 | this._def = this.restructureSchema(definition); 49 | this._str = JSON.stringify(this._def); 50 | } 51 | }, 52 | 53 | /** 54 | * @method mapReferences 55 | * @returns {void} 56 | * @public 57 | */ 58 | mapReferences: function () { 59 | this._def = JSON.parse(this._str); 60 | }, 61 | 62 | /** 63 | * 64 | * @method setSwaggerDefinitions 65 | * @param {object} swaggerDefinitions 66 | * @public 67 | */ 68 | setSwaggerDefinitions: function (swaggerDefinitions) { 69 | this._swaggerDefinitions = swaggerDefinitions; 70 | }, 71 | 72 | /** 73 | * 74 | * @method get 75 | * @returns {object} definition 76 | */ 77 | get: function () { 78 | return this._def; 79 | }, 80 | 81 | /** 82 | * 83 | * @method write 84 | * @returns {void} 85 | * @public 86 | */ 87 | write: function () { 88 | var dir = this._options.dest + '/_DTO'; 89 | var file = dir + '/' + this._name + '.json'; 90 | var dtoToResponseFuncConverter; 91 | 92 | dtoToResponseFuncConverter = new DTOToResponseFuncConverter( 93 | this._name, 94 | this._def, 95 | this._options, 96 | false 97 | ); 98 | dtoToResponseFuncConverter.create(); 99 | dtoToResponseFuncConverter.write(); 100 | 101 | this.writeDir(dir); 102 | this.writeFile(file, JSON.stringify(this._def, null, 2)); 103 | }, 104 | 105 | }); 106 | 107 | module.exports = SwaggerDefinition; 108 | -------------------------------------------------------------------------------- /lib/SwaggerUtils.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var Utils = require('../lib/Utils'); 5 | var extend = require('util')._extend; 6 | 7 | /** 8 | * 9 | * @class SwaggerUtils 10 | * @namespace SwaggerImport 11 | * @constructor 12 | * 13 | * Swagger importer 14 | */ 15 | function SwaggerUtils() { 16 | this.init(); 17 | } 18 | 19 | SwaggerUtils.prototype = extend(SwaggerUtils.prototype, Utils.prototype); 20 | SwaggerUtils.prototype = extend(SwaggerUtils.prototype, { 21 | 22 | constructor: SwaggerUtils, 23 | 24 | /** 25 | * 26 | * @method init 27 | * called by constructor 28 | * @returns void 29 | * @public 30 | */ 31 | init: function () { 32 | 33 | }, 34 | 35 | /** 36 | * @method restructureSchema 37 | * @param {object} swaggerSchema 38 | * @returns {object} 39 | * @public 40 | */ 41 | restructureSchema: function (swaggerSchema) { 42 | return this._map(swaggerSchema); 43 | }, 44 | 45 | /** 46 | * @method _map 47 | * @param {object} object 48 | * @returns {object} 49 | * @privat 50 | */ 51 | _map: function (object) { 52 | if (typeof object.properties === 'object') { 53 | return this._mapObject(object.properties); 54 | } 55 | if (typeof object.$ref === 'string') { 56 | return this._mapRef(object.$ref); 57 | } 58 | return {}; 59 | }, 60 | 61 | /** 62 | * @method _mapObject 63 | * @param {object} object 64 | * @returns {object} 65 | * @privat 66 | */ 67 | _mapObject: function (object) { 68 | var out = {}; 69 | 70 | this.forIn(object, function (key, value) { 71 | 72 | if (value.type && value.type === 'object') { 73 | out[key] = this._mapObject(value); 74 | return; 75 | } 76 | 77 | if (value.type && value.type === 'array' && value.items) { 78 | out[key] = [this._map(value.items)]; 79 | return; 80 | } 81 | 82 | if (value.$ref && typeof value.$ref === 'string') { 83 | out[key] = this._mapRef(value.$ref); 84 | return; 85 | } 86 | 87 | if (value.type && typeof value.type === 'string') { 88 | out[key] = this._mapType(value.type); 89 | } 90 | }); 91 | 92 | return out; 93 | }, 94 | 95 | /** 96 | * @method _mapType 97 | * @param {*} type 98 | * @returns {string} type 99 | * @private 100 | */ 101 | _mapType: function (type) { 102 | 103 | switch (type) { 104 | case 'integer': return 'number'; 105 | case 'float': return 'number'; 106 | case 'bigdezimal': return 'number'; 107 | default: return type; 108 | } 109 | 110 | }, 111 | 112 | /** 113 | * @method _mapRef 114 | * @param {string} value 115 | * @returns {string} 116 | * @privat 117 | */ 118 | _mapRef: function (value) { 119 | return '$ref-' + value.replace('#/definitions/', ''); 120 | }, 121 | 122 | }); 123 | 124 | module.exports = SwaggerUtils; 125 | -------------------------------------------------------------------------------- /mock-server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint global-require: 0 */ 4 | var AppControllerSingleton = require('./lib/controller/AppController'); 5 | var versionCli = require('./lib/cli/version-cli'); 6 | var helpCli = require('./lib/cli/help-cli'); 7 | var swaggerImportCli = require('./lib/cli/swagger-import-cli'); 8 | var validateCli = require('./lib/cli/validate-cli'); 9 | var collectionsCli = require('./lib/cli/collections-cli'); 10 | var createDefinedDirectories = require('./lib/commands/create-defined-directories'); 11 | 12 | var processVersionIndex = process.argv.indexOf('--version'); 13 | var processHelpIndex = process.argv.indexOf('--help'); 14 | var processSwaggerImportIndex = process.argv.indexOf('swagger-import'); 15 | var processValidateIndex = process.argv.indexOf('validate'); 16 | var processCollectionsIndex = process.argv.indexOf('collections'); 17 | var doExport; 18 | 19 | function runServer(options) { 20 | 21 | var appController = AppControllerSingleton.getInstance(options); 22 | 23 | createDefinedDirectories(appController.options); 24 | 25 | var UiController = require('./lib/controller/UiController'); 26 | var SchemaController = require('./lib/controller/SchemaController'); 27 | var SwaggerImportController = require('./lib/controller/SwaggerImportController'); 28 | var DTOController = require('./lib/controller/DTOController'); 29 | var ResponseController = require('./lib/controller/ResponseController'); 30 | var PreferencesController = require('./lib/controller/PreferencesController'); 31 | var ValidatorController = require('./lib/controller/ValidatorController'); 32 | var CollectionController = require('./lib/controller/CollectionController'); 33 | var FileOpenController = require('./lib/controller/FileOpenController'); 34 | 35 | var MockController = require('./lib/controller/MockController'); 36 | 37 | return { 38 | appController: appController, 39 | uiController: new UiController(), 40 | schemaController: new SchemaController(), 41 | swaggerImportController: new SwaggerImportController(), 42 | dtoController: new DTOController(), 43 | ResponseController: new ResponseController(), 44 | preferencesController: new PreferencesController(), 45 | validatorController: new ValidatorController(), 46 | collectionController: new CollectionController(), 47 | fileOpenController: new FileOpenController(), 48 | mockController: new MockController(), 49 | }; 50 | } 51 | 52 | if (processVersionIndex >= 0) { 53 | doExport = versionCli; 54 | } else if (processHelpIndex >= 0) { 55 | doExport = helpCli; 56 | } else if (processCollectionsIndex >= 0) { 57 | doExport = collectionsCli; 58 | } else if (processSwaggerImportIndex >= 0) { 59 | doExport = swaggerImportCli; 60 | } else if (processValidateIndex >= 0) { 61 | doExport = validateCli.bind(null, runServer); 62 | } else { 63 | doExport = runServer; 64 | } 65 | 66 | module.exports = doExport; 67 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | var mockServer = require('../mock-server.js'), 2 | SwaggerImport = require('../lib/SwaggerImport'), 3 | ValidatorResponses = require('../lib/ValidatorResponses'), 4 | Utils = require('../lib/Utils'), 5 | utils = new Utils(), 6 | serverOptions; 7 | 8 | process.env.NODE_ENV = 'test'; 9 | 10 | utils.writeDir('./test/tmp'); 11 | 12 | serverOptions = { 13 | urlBase: 'http://localhost:8888', 14 | urlPath: '/rest/v1', 15 | dirName: __dirname, 16 | port: 8888, 17 | restPath: __dirname + '/../demo/rest', 18 | funcPath: [ 19 | __dirname + '/../demo/func', 20 | __dirname + '/../demo/func2' 21 | ], 22 | headers: { 23 | 'Global-Custom-Header': 'Global-Custom-Header' 24 | }, 25 | customDTOToClassTemplate: __dirname + '/data/class-templates/dto_es6flow.ejs', 26 | middleware: { 27 | '/../demo/rest/products/#{productCode}/GET'(serverOptions, requestOptions) { 28 | 29 | var productCode = requestOptions.req.params[0].split('/')[3]; 30 | 31 | if (productCode === '1234') { 32 | requestOptions.res.statusCode = 201; 33 | requestOptions.res.end('product 1234'); 34 | return null; 35 | } 36 | 37 | requestOptions.res.end('middware response'); 38 | return null; 39 | } 40 | }, 41 | swaggerImport: { 42 | protocol: 'http', 43 | dirName: __dirname, 44 | authUser: undefined, 45 | authPass: undefined, 46 | host: 'localhost', 47 | port: 8888, 48 | path: '/src/swagger/swagger-api-docs.json', 49 | dest: __dirname + '/../test/tmp/swagger-import', 50 | replacePathsStr: '/v2/{id}', 51 | createErrorFile: true, 52 | createEmptyFile: true, 53 | overwriteExistingDescriptions: true, 54 | isTest: true, 55 | responseFuncPath: __dirname + '/tmp/func-imported' 56 | } 57 | }; 58 | 59 | function _startMockServer () { 60 | mockServer(serverOptions); 61 | } 62 | 63 | function _getFile(path) { 64 | return utils.readFile(path); 65 | } 66 | 67 | _startMockServer(); 68 | 69 | var swaggerImporter = new SwaggerImport(serverOptions.swaggerImport); 70 | 71 | new ValidatorResponses({ 72 | restPath: serverOptions.restPath 73 | }, serverOptions); 74 | 75 | swaggerImporter.doImport(function () { 76 | 77 | describe('MockServer', require('./tests-mock-server').bind(this, serverOptions, _getFile)); 78 | describe('Preferences', require('./tests-preferences').bind(this, serverOptions, _getFile)); 79 | describe('SwaggerImport', require('./tests-swagger-import').bind(this, serverOptions, _getFile)); 80 | describe('GetResponse', require('./tests-get-response').bind(this, serverOptions, _getFile)); 81 | describe('ValidatorResponses', require('./tests-validator-responses').bind(this, serverOptions, _getFile)); 82 | describe('DTOImport', require('./tests-dto-import').bind(this, serverOptions, _getFile)); 83 | describe('DTOToClassConverter', require('./tests-dto-2-class').bind(this, serverOptions, _getFile)); 84 | describe('DTOToResponseFuncConverter', require('./tests-dto-response-func').bind(this, serverOptions, _getFile)); 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /doc/readme-ui-documentation.md: -------------------------------------------------------------------------------- 1 | # Using the node-mock-server UI 2 | 3 | The mock server UI is available at _http://localhost:3001_ (or at the port you have defined via options). 4 | 5 | If the service you want to mock provides a Swagger definition and you have provided a [`swaggerImport` configuration item](https://github.com/smollweide/node-mock-server/blob/c52adcf2a80999dd6876062006cf72c1ef124a78/demo/index.js#L41-L53), you may perform a Swagger import via the small triangle button in the top right of the UI page. 6 | 7 | In the same location you find general preferences such as a response delay and you can trigger a validation of all responses. 8 | 9 | In addition to Swagger imports, you may create resources manually using the **+add new endpoint** button at the bottom of the mock server UI page. You will be asked to enter a path, a http method and a description. The path must always begin with a slash and a path segment denoting the base url of the api. After the base url you can enter a sequence of more path segments separated by /. If path segments should be variables, use a parameter name in curly braces: 10 | 11 | /users points to the users api base url 12 | /users/{userid}/address contains a path variable userid 13 | 14 | Once a resource has been defined that way, you can continue to work with the resource and edit the possible responses. 15 | 16 | The UI shows you a list of known resources. Alongside each resource there are buttons which allow you to work with the responses for that method. 17 | 18 | Click the button for a ReST method (e.g. GET) to define possible responses for that method. A popup appears that shows the known responses and allows to add new responses. A newly created resource always has a set of default responses: 19 | 20 | * empty.json 21 | * error.json 22 | * success.json 23 | * middleware 24 | 25 | Initially, empty.json, error.json and success.json contain an empty object as response. Click **open** to fill in the required response data. 26 | The node-mock-server can also respond through a [middleware definition](https://github.com/smollweide/node-mock-server/blob/master/doc/readme-middleware.md); therefore you cannot edit a json file for it. 27 | 28 | To enter a new response, hit *+add new response* to add a new response, e.g. `foo.json`. Your favorite text editor is fired up where you can enter the response. It will be stored as _foo.json_ inside the _mock_ directory. 29 | 30 | For an error response the default HTTP status code is 500, but you can specify a different status code by suffixing the name of your error response with the appropriate status code, like `error-423.json`. 31 | 32 | After storing the response you can select the radiobutton in front of your new response. A file _response.txt_ will be created alongside your possible responses, which stores your decision. All decisions can be captured, restored, reset and shared by means of a [collection](https://github.com/smollweide/node-mock-server/blob/master/doc/readme-collections.md). 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/expected/dto-2-class/AddressWsDTO-es6flow-4.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import CountryWsDTO from './CountryWsDTO.class';const _array = (arr: Array): Array => arr || []; 4 | 5 | class AddressWsDTO { 6 | 7 | country: CountryWsDTO; 8 | countryIsocode: string; 9 | country2: CountryWsDTO; 10 | email: string; 11 | firstName: string; 12 | id: string; 13 | lastName: string; 14 | line1: string; 15 | phone: string; 16 | postalCode: string; 17 | streetName: string; 18 | streetNumber: string; 19 | title: string; 20 | titleCode: string; 21 | town: string; 22 | list: Array; 23 | items: Array; 24 | 25 | constructor(addressWsDTO: AddressWsDTO) { 26 | 27 | addressWsDTO = addressWsDTO || {}; 28 | 29 | this.country = new CountryWsDTO(addressWsDTO.country); 30 | this.countryIsocode = addressWsDTO.countryIsocode; 31 | this.country2 = new CountryWsDTO(addressWsDTO.country2); 32 | this.email = addressWsDTO.email; 33 | this.firstName = addressWsDTO.firstName; 34 | this.id = addressWsDTO.id; 35 | this.lastName = addressWsDTO.lastName; 36 | this.line1 = addressWsDTO.line1; 37 | this.phone = addressWsDTO.phone; 38 | this.postalCode = addressWsDTO.postalCode; 39 | this.streetName = addressWsDTO.streetName; 40 | this.streetNumber = addressWsDTO.streetNumber; 41 | this.title = addressWsDTO.title; 42 | this.titleCode = addressWsDTO.titleCode; 43 | this.town = addressWsDTO.town; 44 | this.list = addressWsDTO.list; 45 | this.items = _array(addressWsDTO.items).map(this.mapItems); 46 | } 47 | 48 | mapItems = (countryWsDTO: CountryWsDTO): CountryWsDTO => new CountryWsDTO(countryWsDTO); 49 | 50 | get country(): CountryWsDTO { 51 | return this.country; 52 | } 53 | 54 | get countryIsocode(): string { 55 | return this.countryIsocode; 56 | } 57 | 58 | get country2(): CountryWsDTO { 59 | return this.country2; 60 | } 61 | 62 | get email(): string { 63 | return this.email; 64 | } 65 | 66 | get firstName(): string { 67 | return this.firstName; 68 | } 69 | 70 | get id(): string { 71 | return this.id; 72 | } 73 | 74 | get lastName(): string { 75 | return this.lastName; 76 | } 77 | 78 | get line1(): string { 79 | return this.line1; 80 | } 81 | 82 | get phone(): string { 83 | return this.phone; 84 | } 85 | 86 | get postalCode(): string { 87 | return this.postalCode; 88 | } 89 | 90 | get streetName(): string { 91 | return this.streetName; 92 | } 93 | 94 | get streetNumber(): string { 95 | return this.streetNumber; 96 | } 97 | 98 | get title(): string { 99 | return this.title; 100 | } 101 | 102 | get titleCode(): string { 103 | return this.titleCode; 104 | } 105 | 106 | get town(): string { 107 | return this.town; 108 | } 109 | 110 | get list(): Array { 111 | return this.list; 112 | } 113 | 114 | get items(): Array { 115 | return this.items; 116 | } 117 | } 118 | 119 | export default AddressWsDTO; 120 | -------------------------------------------------------------------------------- /views/dto-modal.ejs: -------------------------------------------------------------------------------- 1 | 90 | -------------------------------------------------------------------------------- /lib/SwaggerPath.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var SwaggerPathMethod = require('./SwaggerPathMethod'); 5 | var Utils = require('./Utils'); 6 | var extend = require('util')._extend; 7 | 8 | /** 9 | * 10 | * @class SwaggerPath 11 | * @namespace SwaggerImport 12 | * @param {string} name 13 | * @param {object} pathObject 14 | * @param {object} options 15 | * @constructor 16 | * 17 | * Swagger importer 18 | */ 19 | function SwaggerPath(name, pathObject, options) { 20 | this.init(name, pathObject, options); 21 | } 22 | 23 | SwaggerPath.prototype = extend(SwaggerPath.prototype, Utils.prototype); 24 | SwaggerPath.prototype = extend(SwaggerPath.prototype, { 25 | 26 | constructor: SwaggerPath, 27 | 28 | /** 29 | * 30 | * @method init 31 | * called by constructor 32 | * @param {string} name 33 | * @param {object} pathObject 34 | * @param {object} options 35 | * @returns void 36 | * @public 37 | */ 38 | init: function (name, pathObject, options) { 39 | 40 | this._options = options; 41 | this._name = name; 42 | this._cleanedName = name.replace(this._options.replacePathsStr, ''); 43 | this._groupName = this._cleanedName.split('/')[1]; 44 | this._pathName = this._cleanedName.split('/'); 45 | this._pathName = '#' + this._pathName.slice(2, this._pathName.length).join('#'); 46 | this._swaggerPathMethods = {}; 47 | this._pathObject = pathObject; 48 | this._swaggerDefinitions = {}; 49 | 50 | }, 51 | 52 | /** 53 | * 54 | * @method setDefinitions 55 | * @param {object} swaggerDefinitions 56 | * @returns {void} 57 | * @protected 58 | */ 59 | setDefinitions: function (swaggerDefinitions) { 60 | this._swaggerDefinitions = swaggerDefinitions; 61 | }, 62 | 63 | /** 64 | * 65 | * @method getDefinitions 66 | * @returns {object} swaggerDefinitions 67 | * @protected 68 | */ 69 | getDefinitions: function () { 70 | return this._swaggerDefinitions || {}; 71 | }, 72 | 73 | /** 74 | * 75 | * @method write 76 | * @returns void 77 | * @public 78 | */ 79 | write: function () { 80 | 81 | var dirGroup = this._options.dest + '/' + this._groupName; 82 | var dirPath = dirGroup + '/' + this._pathName; 83 | 84 | this._storeMethods(); 85 | this.writeDir(this._options.dest); 86 | this.writeDir(dirGroup); 87 | this.writeDir(dirPath); 88 | this.forIn(this._swaggerPathMethods, function (name, swaggerPathMethod) { 89 | swaggerPathMethod.write(dirPath); 90 | }); 91 | }, 92 | 93 | /** 94 | * 95 | * @method _storeMethods 96 | * @returns void 97 | * @private 98 | */ 99 | _storeMethods: function () { 100 | this.forIn(this._pathObject, function (key, value) { 101 | var name = key.toUpperCase(); 102 | var swaggerPathMethod; 103 | 104 | swaggerPathMethod = new SwaggerPathMethod(name, value, this._options); 105 | swaggerPathMethod.setDefinitions(this.getDefinitions()); 106 | 107 | this._swaggerPathMethods[name] = swaggerPathMethod; 108 | }); 109 | }, 110 | 111 | }); 112 | 113 | module.exports = SwaggerPath; 114 | -------------------------------------------------------------------------------- /lib/cli/ask/tunnel.js: -------------------------------------------------------------------------------- 1 | var inquirer = require('inquirer'); 2 | var jQueryExtend = require('extend'); 3 | var Promise = require('es6-promise-polyfill').Promise; 4 | 5 | function askForTunnelRequestHeader(headerCallback) { 6 | inquirer.prompt([ 7 | { 8 | type: 'input', 9 | name: 'name', 10 | message: 'Enter request header key', 11 | }, 12 | { 13 | type: 'input', 14 | name: 'value', 15 | message: 'Enter request header value', 16 | }, 17 | ]).then(function (headerAnswer) { 18 | var headers = {}; 19 | headers[headerAnswer.name] = headerAnswer.value; 20 | headerCallback(headers); 21 | }); 22 | } 23 | function askForTunnelRequestAddHeader(headers, headerCallback) { 24 | inquirer.prompt([ 25 | { 26 | type: 'list', 27 | name: 'addTunnelRequestHeader', 28 | choices: ['Yes', 'No'], 29 | message: 'Do you want to add a tunnel request header?', 30 | default: 'Yes', 31 | }, 32 | ]).then(function (answers) { 33 | if (answers.addTunnelRequestHeader.toUpperCase() === 'YES') { 34 | askForTunnelRequestHeader(function (headerResult) { 35 | headers = jQueryExtend(true, headers, headerResult); 36 | askForTunnelRequestAddHeader(headers, headerCallback); 37 | }); 38 | return; 39 | } 40 | headerCallback({ headers: headers }); 41 | }); 42 | } 43 | function askForTunnel() { 44 | return new Promise((resolve) => { 45 | inquirer.prompt([ 46 | { 47 | type: 'list', 48 | name: 'useTunnel', 49 | choices: ['Yes', 'No'], 50 | message: 'Want to use a tunnel?', 51 | default: 'Yes', 52 | }, 53 | ]).then(function (answers) { 54 | if (answers.useTunnel.toUpperCase() === 'NO') { 55 | resolve({}); 56 | return; 57 | } 58 | 59 | inquirer.prompt([ 60 | { 61 | type: 'input', 62 | name: 'protocol', 63 | message: 'Enter the tunnel url protocol', 64 | default: 'http', 65 | }, 66 | { 67 | type: 'input', 68 | name: 'authUser', 69 | message: 'Enter the tunnel url basic auth user name', 70 | }, 71 | { 72 | type: 'input', 73 | name: 'authPass', 74 | message: 'Enter the tunnel url basic auth password', 75 | }, 76 | { 77 | type: 'input', 78 | name: 'host', 79 | message: 'Enter the tunnel url host', 80 | default: 'localhost', 81 | }, 82 | { 83 | type: 'input', 84 | name: 'port', 85 | message: 'Enter the tunnel url port', 86 | default: '80', 87 | }, 88 | ]).then(function (answersOptions) { 89 | var options = { tunnel: answersOptions }; 90 | options.tunnel.headers = {}; 91 | askForTunnelRequestAddHeader({}, function (answerHeaders) { 92 | options.tunnel.headers = answerHeaders.headers; 93 | if (options.tunnel.authUser === '') { 94 | delete options.tunnel.authUser; 95 | } 96 | if (options.tunnel.authPass === '') { 97 | delete options.tunnel.authPass; 98 | } 99 | resolve(options); 100 | }); 101 | }); 102 | }); 103 | }); 104 | } 105 | 106 | module.exports = askForTunnel; 107 | -------------------------------------------------------------------------------- /doc/readme-folder-structure.md: -------------------------------------------------------------------------------- 1 | 2 | # Adding new ReST Resources 3 | The node-mock-server supports importing swagger descriptions for an API. However, you can create mock resources manually, too. 4 | 5 | 6 | ## Folder structure 7 | The node-mock-server expects you to follow the path conventions below for the content of your `restPath` folder. 8 | 9 | ``` 10 | |- group 11 | |--- # 12 | |--- #path 13 | |--- #path#{param} 14 | |----- method (GET, POST, DELETE ...) 15 | |------- mock 16 | |--------- success.json 17 | |--------- success.headers.json 18 | |--------- error.json 19 | |--------- error-401.json 20 | |------- desc.json 21 | |------- request_schema.json 22 | |------- response_schema.json 23 | ``` 24 | See [/demo/rest](https://github.com/smollweide/node-mock-server/tree/master/demo/rest) for an executable example. 25 | 26 | The top level folder becomes the url path segment immediately after your API `urlPath`: if your `urlPath` is _/rest/v2_, then you would get a _/rest/v2/group_ resource in your mock API for the folder structure above. 27 | 28 | ### Path folders 29 | The _#_ represents a slash in the url path of your response. In other words, the _#_ folder above describes the responses to requests for _/rest/v2/group/_ (trailing slash) whereas the _#path_ folder will become the _/rest/v2/group/path_ resource in your API. 30 | 31 | If you want to use dynamic path segments, you can enclose them in curly brackets, as shown above for the _#path#{param}_ folder. The node-mock-server looks for a success response called success-1 if the requested url is /path/1, success-2 is for /path/2 etc., and success-default is for all other cases. For examples how the actual value can be used inside responses, look at the success files for _products_ [in the demo](https://github.com/smollweide/node-mock-server/blob/master/demo/rest/products/%23%7BproductCode%7D/GET/mock). 32 | 33 | ### Method folders 34 | A path folder with _#_ must contain one or more method folders, e.g. _GET_ or _POST_ which require to contain a _mock_ directory in turn - that directory will later contain the actual API responses. 35 | 36 | ### Description of Response 37 | The file _desc.json_ is optional and can be used to describe the response to a method call on a resource in more detail. Consider the sample _desc.json_ below. 38 | ``` 39 | { 40 | "desc": "Returns a list of products", 41 | "security": [], 42 | "protected": false, 43 | "status": "open", 44 | "request": { 45 | "query": { 46 | "parameters": [ 47 | { 48 | "name": "query", 49 | "required": false, 50 | "type": "string", 51 | "desc": "Search query" 52 | } 53 | ] 54 | } 55 | }, 56 | "response": { 57 | "statusCode": 200, 58 | "schema": { 59 | "type": "application/json" 60 | } 61 | } 62 | } 63 | ``` 64 | The example above provides a descriptive string in the `desc` attribute and specifies expected requests and possible responses. 65 | 66 | ### JSON Schema for Request and Response 67 | 68 | The files _request_schema.json_ and _response_schema.json_ are optional and may contain json schema definitions for your requests and responses which allow for validation. 69 | -------------------------------------------------------------------------------- /test/expected/dto-2-class/ComplexDTO-es6.js: -------------------------------------------------------------------------------- 1 | 2 | import CountryWsDTO from './CountryWsDTO.class'; 3 | import ImageDTO from './ImageDTO.class'; 4 | 5 | /** 6 | * @param {string} type - the typeof the value 7 | * @param {*} value - the value 8 | * @returns {boolean} returns true if type is correct 9 | * @private 10 | */ 11 | const _isValidType = (type, value) => { 12 | switch (type) { 13 | case 'Array': return (value instanceof Array); 14 | default: return (typeof value === type.toLowerCase()); 15 | } 16 | }; 17 | const _array = (arr) => arr || []; 18 | 19 | /** 20 | * @constructor complexDTO 21 | */ 22 | class ComplexDTO { 23 | 24 | /** 25 | * @param {Object} complexDTO - initial data 26 | * @returns {void} 27 | * @public 28 | */ 29 | constructor(complexDTO) { 30 | 31 | complexDTO = complexDTO || {}; 32 | complexDTO.parameters = complexDTO.parameters || {}; 33 | 34 | this.list = complexDTO.list; 35 | this.items = _array(complexDTO.items).map((imageDTO) => new ImageDTO(imageDTO)); 36 | this.parametersAdditional = complexDTO.parameters.additional; 37 | } 38 | 39 | /** 40 | * @returns {Array} returns list 41 | * @public 42 | */ 43 | get list() { 44 | return this.list; 45 | } 46 | 47 | /** 48 | * @param {Array} value - the set value 49 | * @returns {void} 50 | * @public 51 | */ 52 | set list(value) { 53 | this.list = value; 54 | } 55 | 56 | /** 57 | * @returns {boolean} returns true if List is valid 58 | * @public 59 | */ 60 | validList() { 61 | try { 62 | const v = this.list; 63 | return (v && _isValidType('Array', v)); 64 | } catch (err) { 65 | return false; 66 | } 67 | } 68 | 69 | /** 70 | * @returns {Array} returns items 71 | * @public 72 | */ 73 | get items() { 74 | return this.items; 75 | } 76 | 77 | /** 78 | * @param {Array} value - the set value 79 | * @returns {void} 80 | * @public 81 | */ 82 | set items(value) { 83 | this.items = value; 84 | } 85 | 86 | /** 87 | * @returns {boolean} returns true if Items is valid 88 | * @public 89 | */ 90 | validItems() { 91 | try { 92 | const v = this.items; 93 | return (v && _isValidType('Array', v)); 94 | } catch (err) { 95 | return false; 96 | } 97 | } 98 | 99 | /** 100 | * @returns {string} returns parametersAdditional 101 | * @public 102 | */ 103 | get parametersAdditional() { 104 | return this.parametersAdditional; 105 | } 106 | 107 | /** 108 | * @param {string} value - the set value 109 | * @returns {void} 110 | * @public 111 | */ 112 | set parametersAdditional(value) { 113 | this.parametersAdditional = value; 114 | } 115 | 116 | /** 117 | * @returns {boolean} returns true if ParametersAdditional is valid 118 | * @public 119 | */ 120 | validParametersAdditional() { 121 | try { 122 | const v = this.parameters.additional; 123 | return (v && _isValidType('string', v)); 124 | } catch (err) { 125 | return false; 126 | } 127 | } 128 | 129 | } 130 | 131 | export default ComplexDTO; 132 | -------------------------------------------------------------------------------- /lib/controller/CollectionController.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var Utils = require('../Utils'); 5 | var util = require('util'); 6 | var log = require('chip')(); 7 | var extend = util._extend; 8 | var AppControllerSingleton = require('./AppController'); 9 | var appController = AppControllerSingleton.getInstance(); 10 | var deleteCollection = require('../commands/delete-collection'); 11 | var activateCollection = require('../commands/activate-collection'); 12 | var getCurrentlySelectedResponses = require('../commands/get-currently-selected-responses'); 13 | 14 | /** 15 | * 16 | * @class CollectionController 17 | * @constructor 18 | * 19 | */ 20 | function CollectionController() { 21 | this.init(); 22 | } 23 | 24 | CollectionController.prototype = extend(CollectionController.prototype, Utils.prototype); 25 | CollectionController.prototype = extend(CollectionController.prototype, { 26 | 27 | constructor: CollectionController, 28 | 29 | /** 30 | * 31 | * @method init 32 | * called by constructor 33 | * @public 34 | */ 35 | init: function () { 36 | 37 | this.options = appController.options; 38 | 39 | appController.app.delete('/service/collection/:id', this.handleDeleteCollection.bind(this)); 40 | appController.app.post('/service/collection/:id/activate', this.handleActivateCollection.bind(this)); 41 | appController.app.post('/service/collection', this.handleSubmitNewCollection.bind(this)); 42 | }, 43 | 44 | /** 45 | * @method handleSubmitNewCollection 46 | * @param {object} req 47 | * @param {object} res 48 | * @returns {void} 49 | */ 50 | handleSubmitNewCollection: function (req, res) { 51 | 52 | var options = this.options; 53 | var name = req.body.name; 54 | var id = this.cleanString(name); 55 | var description = req.body.description || ''; 56 | 57 | if (!this.isFilledString(name) || !this.isFilledString(id)) { 58 | res.statusCode = 500; 59 | res.end('Error, please try again later!'); 60 | return; 61 | } 62 | 63 | var selectedResponses = getCurrentlySelectedResponses(options); 64 | var collection = { 65 | name, 66 | description, 67 | selectedResponses, 68 | }; 69 | var collectionFilePath = `${options.restPath}/_collections/${id}.json`; 70 | 71 | this.writeFile(collectionFilePath, JSON.stringify(collection, null, 2)); 72 | log(`Wrote file "${collectionFilePath}"`); 73 | 74 | res.redirect('/'); 75 | }, 76 | 77 | /** 78 | * @method handleActivateCollection 79 | * @param {object} req 80 | * @param {object} res 81 | */ 82 | handleActivateCollection: function (req, res) { 83 | var wasActivated = activateCollection(req.params.id, this.options); 84 | 85 | if (!wasActivated) { 86 | res.statusCode = 500; 87 | res.end(); 88 | return; 89 | } 90 | res.end(); 91 | }, 92 | 93 | /** 94 | * @method handleDeleteCollection 95 | * @param {object} req 96 | * @param {object} res 97 | */ 98 | handleDeleteCollection: function (req, res) { 99 | var wasDeleted = deleteCollection(req.params.id, this.options); 100 | 101 | if (!wasDeleted) { 102 | res.statusCode = 500; 103 | res.end(); 104 | return; 105 | } 106 | res.end(); 107 | }, 108 | 109 | }); 110 | 111 | module.exports = CollectionController; 112 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # node-mock-server 2 | 3 | > File based Node REST API mock server 4 | 5 | [![Build status](https://img.shields.io/travis/smollweide/node-mock-server/master.svg)](https://travis-ci.org/smollweide/node-mock-server) 6 | [![npm](https://badge.fury.io/js/node-mock-server.svg)](https://badge.fury.io/js/node-mock-server) 7 | [![npm](https://img.shields.io/npm/dt/node-mock-server.svg)](https://www.npmjs.com/package/node-mock-server) 8 | [![Codestyle](https://img.shields.io/badge/codestyle-namics-green.svg)](https://github.com/namics/eslint-config-namics) 9 | 10 | ![node-mock-server-ui.png](https://cloud.githubusercontent.com/assets/2912007/26034363/c509d2c2-38bb-11e7-9175-4a151f7a550f.jpg) 11 | 12 | ## Getting Started 13 | 14 | This application requires Node `6` or higher. 15 | For Node `<6` please use `node-mock-server@0.22.1` 16 | For Node `<4` please use `node-mock-server@0.11.0` 17 | 18 | ##### 1. Install npm package: 19 | 20 | ``` 21 | $ npm install node-mock-server --save-dev 22 | ``` 23 | 24 | ##### 2. Start init process: 25 | 26 | ``` 27 | $ node node_modules/node-mock-server/init 28 | ``` 29 | 30 | ### Options 31 | 32 | [node-mock-server options](/doc/readme-options.md) 33 | 34 | ### Usage examples 35 | 36 | [node-mock-server usage examples](/doc/readme-usage-examples.md) 37 | 38 | ## Features 39 | 40 | - Node.js and file based ([folder structure](/doc/readme-folder-structure.md)) 41 | - [Node Mock Server UI](/doc/readme-ui-documentation.md) 42 | - [Functions in mock data](/doc/readme-mock-functions.md) 43 | - [Faker included](/doc/readme-faker.md) 44 | - [Query params in mock data](/doc/readme-query-params.md) 45 | - [Dynamic path params in mock data](/doc/readme-path-params.md) 46 | - [Expected responses](/doc/readme-expected-response.md) 47 | - [Middleware responses](/doc/readme-middleware.md) 48 | - [Express Middleware](/doc/readme-express-middleware.md) 49 | - [Error cases](/doc/readme-expected-response.md) 50 | - [Swagger import](/doc/readme-swagger-import.md) 51 | - DTO import 52 | - DTO response function 53 | - [Response validation](/doc/readme-response-validation.md) 54 | - [Response header](/doc/readme-response-header.md) 55 | - [DTO to Class converter](/doc/readme-dto-2-class.md) 56 | - [Collections](/doc/readme-collections.md) 57 | - [Tunnel](/doc/readme-tunnel.md) 58 | 59 | ## CLI 60 | 61 | ``` 62 | $ node --help 63 | 64 | Usage 65 | $ node [--version] [--help] [] 66 | 67 | Options 68 | $ start mock server 69 | $ --version print node-mock-server version 70 | $ --help print help 71 | $ swagger-import run a swagger import 72 | $ validate run a validation for all mock data 73 | $ collections print all available collections 74 | $ collection activate collection 75 | 76 | Examples 77 | $ node demo/index.js --version 78 | $ node demo/index.js collections 79 | ``` 80 | 81 | ## Demo 82 | 83 | ```shell 84 | git clone https://github.com/smollweide/node-mock-server.git 85 | cd node-mock-server 86 | npm install 87 | node demo 88 | ``` 89 | 90 | ## License 91 | 92 | [MIT License](https://github.com/smollweide/node-mock-server/blob/master/LICENSE) 93 | 94 | ## Changelog 95 | 96 | Please see the [Releases](https://github.com/smollweide/node-mock-server/releases) 97 | -------------------------------------------------------------------------------- /lib/SwaggerLog.js: -------------------------------------------------------------------------------- 1 | /* eslint no-use-before-define: 0 */ 2 | /* eslint block-scoped-var: 0 */ 3 | 'use strict'; 4 | 5 | var log = require('chip')(); 6 | 7 | if (!_swaggerLog) { 8 | var _swaggerLog = []; 9 | } 10 | 11 | /** 12 | * @function _getDateTime 13 | * @returns {string} dateTime 14 | * @private 15 | */ 16 | function _getDateTime() { 17 | return new Date() 18 | .toISOString() 19 | .replace(/T/, ' ') 20 | .replace(/\..+/, '') + ' GMT+0000'; 21 | } 22 | 23 | /** 24 | * @function _getTime 25 | * @returns {string} time 26 | * @private 27 | */ 28 | function _getTime() { 29 | return new Date() 30 | .toISOString() 31 | .replace(/T/, ' ') 32 | .replace(/\..+/, '') 33 | .split(' ')[1]; 34 | } 35 | 36 | /** 37 | * @function _consoleLog 38 | * @param {object} msg 39 | * @param {string} msg.type ENUM(success|warn|error) 40 | * @param {string} msg.msg 41 | * @param {string} msg.time 42 | * @returns {void} 43 | * @private 44 | */ 45 | function _consoleLog(msg) { 46 | 47 | var type; 48 | 49 | if (process.env.NODE_ENV === 'test') { 50 | return; 51 | } 52 | 53 | type = msg.type; 54 | type = (type === 'neutral') ? 'log' : type; 55 | type = (type === 'success') ? 'log' : type; 56 | 57 | if (log && log[type]) { 58 | log[type](msg.time + ': ' + _getWithoutHtml(msg.msg)); 59 | } 60 | } 61 | 62 | /** 63 | * @method _getWithoutHtml 64 | * @param {string} value 65 | * @returns {string} 66 | * @private 67 | */ 68 | function _getWithoutHtml(value) { 69 | if (typeof value !== 'string') { 70 | return ''; 71 | } 72 | return value.replace(/<(.|\n)*?>/g, ''); 73 | } 74 | 75 | /** 76 | * @function _push 77 | * @param {object} msg 78 | * @param {string} msg.type ENUM(neutral|success|warn|error) 79 | * @param {string} msg.msg 80 | * @returns {void} 81 | * @private 82 | */ 83 | function _push(msg) { 84 | 85 | var obj = { 86 | type: msg.type || 'neutral', 87 | msg: msg.msg, 88 | dateTime: _getDateTime(), 89 | time: _getTime(), 90 | }; 91 | 92 | _swaggerLog.push(obj); 93 | _consoleLog(obj); 94 | } 95 | 96 | var swaggerLog = { 97 | 98 | /** 99 | * @function error 100 | * @param {string} msg 101 | * @returns {void} 102 | * @public 103 | */ 104 | error: function (msg) { 105 | _push({ 106 | msg: msg, 107 | type: 'error', 108 | }); 109 | }, 110 | 111 | /** 112 | * @function warn 113 | * @param {string} msg 114 | * @returns {void} 115 | * @public 116 | */ 117 | warn: function (msg) { 118 | _push({ 119 | msg: msg, 120 | type: 'warn', 121 | }); 122 | }, 123 | 124 | /** 125 | * @function success 126 | * @param {string} msg 127 | * @returns {void} 128 | * @public 129 | */ 130 | success: function (msg) { 131 | _push({ 132 | msg: msg, 133 | type: 'success', 134 | }); 135 | }, 136 | 137 | /** 138 | * @function neutral 139 | * @param {string} msg 140 | * @returns {void} 141 | * @public 142 | */ 143 | neutral: function (msg) { 144 | _push({ 145 | msg: msg, 146 | type: 'neutral', 147 | }); 148 | }, 149 | 150 | /** 151 | * @function get 152 | * @returns {Array} 153 | * @public 154 | */ 155 | get: function () { 156 | return _swaggerLog; 157 | }, 158 | 159 | /** 160 | * @function clear 161 | * @returns {Array} 162 | * @public 163 | */ 164 | clear: function () { 165 | _swaggerLog = []; 166 | }, 167 | 168 | }; 169 | 170 | module.exports = swaggerLog; 171 | -------------------------------------------------------------------------------- /lib/ValidatorLog.js: -------------------------------------------------------------------------------- 1 | /* eslint no-use-before-define: 0 */ 2 | /* eslint block-scoped-var: 0 */ 3 | 'use strict'; 4 | 5 | var log = require('chip')(); 6 | 7 | if (!_validatorLog) { 8 | var _validatorLog = []; 9 | } 10 | 11 | /** 12 | * @function _getDateTime 13 | * @returns {string} dateTime 14 | * @private 15 | */ 16 | function _getDateTime() { 17 | return new Date() 18 | .toISOString() 19 | .replace(/T/, ' ') 20 | .replace(/\..+/, '') + ' GMT+0000'; 21 | } 22 | 23 | /** 24 | * @function _getTime 25 | * @returns {string} time 26 | * @private 27 | */ 28 | function _getTime() { 29 | return new Date() 30 | .toISOString() 31 | .replace(/T/, ' ') 32 | .replace(/\..+/, '') 33 | .split(' ')[1]; 34 | } 35 | 36 | /** 37 | * @function _consoleLog 38 | * @param {object} msg 39 | * @param {string} msg.type ENUM(success|warn|error) 40 | * @param {string} msg.msg 41 | * @param {string} msg.time 42 | * @returns {void} 43 | * @private 44 | */ 45 | function _consoleLog(msg) { 46 | 47 | var type; 48 | 49 | if (process.env.NODE_ENV === 'test') { 50 | return; 51 | } 52 | 53 | type = msg.type; 54 | type = (type === 'neutral') ? 'log' : type; 55 | type = (type === 'success') ? 'info' : type; 56 | 57 | if (log && log[type]) { 58 | log[type](msg.time + ': ' + _getWithoutHtml(msg.msg)); 59 | } 60 | } 61 | 62 | /** 63 | * @method _getWithoutHtml 64 | * @param {string} value 65 | * @returns {string} 66 | * @private 67 | */ 68 | function _getWithoutHtml(value) { 69 | if (typeof value !== 'string') { 70 | return ''; 71 | } 72 | return value.replace(/<(.|\n)*?>/g, ''); 73 | } 74 | 75 | /** 76 | * @function _push 77 | * @param {object} msg 78 | * @param {string} msg.type ENUM(neutral|success|warn|error) 79 | * @param {string} msg.msg 80 | * @returns {void} 81 | * @private 82 | */ 83 | function _push(msg) { 84 | 85 | var obj = { 86 | type: msg.type || 'neutral', 87 | msg: msg.msg, 88 | dateTime: _getDateTime(), 89 | time: _getTime(), 90 | }; 91 | 92 | _validatorLog.push(obj); 93 | _consoleLog(obj); 94 | } 95 | 96 | var validatorLog = { 97 | 98 | /** 99 | * @function error 100 | * @param {string} msg 101 | * @returns {void} 102 | * @public 103 | */ 104 | error: function (msg) { 105 | _push({ 106 | msg: msg, 107 | type: 'error', 108 | }); 109 | }, 110 | 111 | /** 112 | * @function warn 113 | * @param {string} msg 114 | * @returns {void} 115 | * @public 116 | */ 117 | warn: function (msg) { 118 | _push({ 119 | msg: msg, 120 | type: 'warn', 121 | }); 122 | }, 123 | 124 | /** 125 | * @function success 126 | * @param {string} msg 127 | * @returns {void} 128 | * @public 129 | */ 130 | success: function (msg) { 131 | _push({ 132 | msg: msg, 133 | type: 'success', 134 | }); 135 | }, 136 | 137 | /** 138 | * @function neutral 139 | * @param {string} msg 140 | * @returns {void} 141 | * @public 142 | */ 143 | neutral: function (msg) { 144 | _push({ 145 | msg: msg, 146 | type: 'neutral', 147 | }); 148 | }, 149 | 150 | /** 151 | * @function get 152 | * @returns {Array} 153 | * @public 154 | */ 155 | get: function () { 156 | return _validatorLog; 157 | }, 158 | 159 | /** 160 | * @function clear 161 | * @returns {Array} 162 | * @public 163 | */ 164 | clear: function () { 165 | _validatorLog = []; 166 | }, 167 | 168 | }; 169 | 170 | module.exports = validatorLog; 171 | -------------------------------------------------------------------------------- /test/tests-get-response.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert'), 3 | GetResponse = require('../lib/GetResponse'); 4 | 5 | module.exports = function(serverOptions, _getFile) { 6 | 7 | var pathExpGetResponse = './test/expected/get-response'; 8 | 9 | // GET call 10 | it('response from GET /product/{productCode}', function() { 11 | var response, 12 | data, 13 | expected; 14 | 15 | response = new GetResponse({ 16 | path: serverOptions.restPath + '/products/{productCode}', 17 | method: 'get', 18 | expected: 'success-default' 19 | }, serverOptions); 20 | expected = _getFile(pathExpGetResponse + '/01.json'); 21 | data = response.get(); 22 | assert.equal(JSON.stringify(data), expected); 23 | }); 24 | 25 | // POST call 26 | it('response from POST /product/{productCode}', function() { 27 | var response, 28 | data, 29 | expected; 30 | 31 | response = new GetResponse({ 32 | path: serverOptions.restPath + '/products/{productCode}', 33 | method: 'post', 34 | expected: 'success' 35 | }, serverOptions); 36 | expected = JSON.parse(_getFile(pathExpGetResponse + '/02.json')); 37 | data = response.get(); 38 | assert.equal(data.success, expected.success); 39 | assert.equal(data.body, expected.body); 40 | assert.equal(data.query, expected.query); 41 | assert.equal(data.param, expected.param); 42 | assert.equal(typeof data.faker, typeof expected.faker); 43 | assert.equal(typeof data.price, typeof expected.price); 44 | }); 45 | 46 | // FAKER call 47 | it('response from GET /product/{productCode} - with faker', function() { 48 | var response, 49 | data; 50 | 51 | response = new GetResponse({ 52 | path: serverOptions.restPath + '/products/{productCode}', 53 | method: 'get', 54 | expected: 'faker' 55 | }, serverOptions); 56 | data = response.get(); 57 | 58 | assert.equal(typeof data.price, 'object'); 59 | assert.equal(data.cards instanceof Array, true); 60 | assert.equal(typeof data.email, 'string'); 61 | }); 62 | 63 | // FUNCTIONS call 64 | it('response from GET /product/{productCode} - with functions', function() { 65 | var response, 66 | data; 67 | 68 | response = new GetResponse({ 69 | path: serverOptions.restPath + '/products/{productCode}', 70 | method: 'get', 71 | expected: 'func' 72 | }, serverOptions); 73 | data = response.get(); 74 | 75 | assert.equal(typeof data.image, 'object'); 76 | assert.equal(typeof data.image.url, 'string'); 77 | assert.equal(typeof data.image.alt, 'string'); 78 | }); 79 | 80 | // REQUEST DATA call 81 | it('response from GET /product/{productCode} - with request data', function() { 82 | var response, 83 | data; 84 | 85 | response = new GetResponse({ 86 | path: serverOptions.restPath + '/products/{productCode}', 87 | method: 'get', 88 | expected: 'request-data', 89 | queryParams: { 90 | currentPage: 3 91 | } 92 | }, serverOptions); 93 | data = response.get(); 94 | 95 | assert.equal(data.currentPage, 3); 96 | }); 97 | 98 | // REQUEST DATA call 99 | it('response from GET /product/{productCode} - with request data from path', function() { 100 | var response, 101 | data; 102 | 103 | response = new GetResponse({ 104 | path: serverOptions.restPath + '/products/test', 105 | method: 'get', 106 | expected: 'request-data', 107 | queryParams: { 108 | currentPage: 12 109 | } 110 | }, serverOptions); 111 | data = response.get(); 112 | 113 | assert.equal(data.productCode, 'test'); 114 | }); 115 | 116 | }; 117 | -------------------------------------------------------------------------------- /src/templates/dto_es6.ejs: -------------------------------------------------------------------------------- 1 | <% 2 | 3 | for (var i = 0; i < refs.length; i += 1) { 4 | if (refs[i] !== className) { 5 | %> 6 | import <%=refs[i]%> from './<%=refs[i]%>.class';<% 7 | } 8 | } 9 | 10 | if (options.isValidator) { 11 | %> 12 | 13 | /** 14 | * @param {string} type - the typeof the value 15 | * @param {*} value - the value 16 | * @returns {boolean} returns true if type is correct 17 | * @private 18 | */ 19 | const _isValidType = (type, value) => { 20 | switch (type) { 21 | case 'Array': return (value instanceof Array); 22 | default: return (typeof value === type.toLowerCase()); 23 | } 24 | }; 25 | <% 26 | } 27 | %>const _array = (arr) => arr || []; 28 | 29 | /** 30 | * @constructor <%=classNameDecapitalize%> 31 | */ 32 | class <%=className%> { 33 | 34 | /** 35 | * @param {Object} <%=classNameDecapitalize%> - initial data 36 | * @returns {void} 37 | * @public 38 | */ 39 | constructor(<%=classNameDecapitalize%>) { 40 | 41 | <%=classNameDecapitalize%> = <%=classNameDecapitalize%> || {}; 42 | <% 43 | 44 | for (var i = 0; i < attributes.length; i += 1) { 45 | var attr = attributes[i]; 46 | if (attr.type === 'Object') { 47 | %> <%=classNameDecapitalize%>.<%=attr.name%> = <%=classNameDecapitalize%>.<%=attr.name%> || {}; 48 | <% 49 | } 50 | } 51 | 52 | for (var i = 0; i < attributes.length; i += 1) { var attr = attributes[i]; %><% if (attr.type === 'string' || attr.type === 'number' || attr.type === 'boolean') { %> 53 | this.<%=attr.nameCamel%> = <%=classNameDecapitalize%>.<%=attr.name%>;<% } %><% if (attr.type === 'ref') { %> 54 | this.<%=attr.nameCamel%> = new <%=attr.ref%>(<%=classNameDecapitalize%>.<%=attr.nameCamel%>);<% } %><% if (attr.type === 'Array' && attr.subType === 'ref') { %> 55 | this.<%=attr.nameCamel%> = _array(<%=classNameDecapitalize%>.<%=attr.nameCamel%>).map((<%=attr.subRefDecapitalize%>) => new <%=attr.subRef%>(<%=attr.subRefDecapitalize%>));<% } %><% if (attr.type === 'Array' && attr.subType !== 'ref') { %> 56 | this.<%=attr.nameCamel%> = <%=classNameDecapitalize%>.<%=attr.name%>;<% } %><% } %> 57 | } 58 | <% 59 | 60 | for (var i = 0; i < attributes.length; i += 1) { 61 | var attr = attributes[i]; 62 | var type; 63 | 64 | if (attr.type === 'Array') { 65 | if (attr.subType === 'ref') { 66 | type = `Array<${attr.subRef}>`; 67 | } else { 68 | type = `Array<${attr.subType}>`; 69 | } 70 | } else if (attr.type === 'ref') { 71 | type = attr.ref; 72 | } else { 73 | type = attr.type; 74 | } 75 | 76 | if (attr.type !== 'Object') { 77 | 78 | if (options.isGetter) { 79 | %> 80 | /** 81 | * @returns {<%-type%>} returns <%=attr.nameCamel%> 82 | * @public 83 | */ 84 | get <%=attr.nameCamel%>() { 85 | return this.<%=attr.nameCamel%>; 86 | } 87 | <% 88 | } 89 | 90 | if (options.isSetter) { 91 | %> 92 | /** 93 | * @param {<%-type%>} value - the set value 94 | * @returns {void} 95 | * @public 96 | */ 97 | set <%=attr.nameCamel%>(value) { 98 | this.<%=attr.nameCamel%> = value; 99 | } 100 | <% 101 | } 102 | 103 | if (options.isValidator) { 104 | %> 105 | /** 106 | * @returns {boolean} returns true if <%=attr.nameCapitalize%> is valid 107 | * @public 108 | */ 109 | valid<%=attr.nameCapitalize%>() { 110 | try { 111 | const v = this.<%=attr.name%>; 112 | return (v && _isValidType('<%=attr.type%>', v)); 113 | } catch (err) { 114 | return false; 115 | } 116 | } 117 | <% 118 | } 119 | } 120 | } 121 | 122 | %> 123 | } 124 | 125 | export default <%=className%>; 126 | -------------------------------------------------------------------------------- /lib/cli/ask/swagger-import.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | var inquirer = require('inquirer'); 3 | var Promise = require('es6-promise-polyfill').Promise; 4 | 5 | function askForSwaggerImport() { 6 | return new Promise((resolve) => { 7 | inquirer.prompt([ 8 | { 9 | type: 'list', 10 | choices: ['Yes', 'No'], 11 | name: 'useSwaggerImport', 12 | message: 'Do you want to use the swagger import?', 13 | default: 'No', 14 | }, 15 | ]).then(function (answers) { 16 | if (answers.useSwaggerImport.toUpperCase() === 'NO') { 17 | resolve({}); 18 | return; 19 | } 20 | console.log(''); 21 | console.log('/**'); 22 | console.log(' * To import schemas from swagger you need to provide the url to the swagger api json file.'); 23 | console.log(' * In case of you want to import the schemas from "http://petstore.swagger.io/".'); 24 | console.log(' * You need to provide this url: http://petstore.swagger.io/v2/swagger.json'); 25 | console.log('**/'); 26 | console.log(''); 27 | inquirer.prompt([ 28 | { 29 | type: 'input', 30 | name: 'protocol', 31 | message: 'Enter the swagger url protocol', 32 | default: 'http', 33 | }, 34 | { 35 | type: 'input', 36 | name: 'authUser', 37 | message: 'Enter the swagger url basic auth user name', 38 | }, 39 | { 40 | type: 'input', 41 | name: 'authPass', 42 | message: 'Enter the swagger url basic auth password', 43 | }, 44 | { 45 | type: 'input', 46 | name: 'host', 47 | message: 'Enter the swagger url host', 48 | default: 'petstore.swagger.io', 49 | }, 50 | { 51 | type: 'input', 52 | name: 'port', 53 | message: 'Enter the swagger url port', 54 | default: '80', 55 | }, 56 | { 57 | type: 'input', 58 | name: 'path', 59 | message: 'Enter the swagger url path', 60 | default: '/v2/swagger.json', 61 | }, 62 | { 63 | type: 'input', 64 | name: 'replacePathsStr', 65 | message: 'Enter the part of the endpoints which should be removed (if needed)', 66 | }, 67 | { 68 | type: 'confirm', 69 | name: 'createErrorFile', 70 | message: 'Write error response files while importing?', 71 | default: true, 72 | }, 73 | { 74 | type: 'confirm', 75 | name: 'createEmptyFile', 76 | message: 'Write empty response files while importing?', 77 | default: true, 78 | }, 79 | { 80 | type: 'confirm', 81 | name: 'overwriteExistingDescriptions', 82 | message: 'Overwrite existing descriptions?', 83 | default: true, 84 | }, 85 | { 86 | type: 'input', 87 | name: 'responseFuncPath', 88 | message: 'Enter the desired path (from the mock server directory) to store the imported mock functions', 89 | default: 'func-imported', 90 | }, 91 | { 92 | type: 'input', 93 | name: 'customDTOToClassTemplate', 94 | message: 'Enter the path to a custom "DTO to class" template', 95 | }, 96 | ]).then(function (answersSwaggerImport) { 97 | if (answersSwaggerImport.authUser === '') { 98 | delete answersSwaggerImport.authUser; 99 | } 100 | if (answersSwaggerImport.authPass === '') { 101 | delete answersSwaggerImport.authPass; 102 | } 103 | if (answersSwaggerImport.customDTOToClassTemplate === '') { 104 | delete answersSwaggerImport.customDTOToClassTemplate; 105 | } 106 | resolve({ 107 | swaggerImport: answersSwaggerImport, 108 | }); 109 | }); 110 | }); 111 | }); 112 | } 113 | 114 | module.exports = askForSwaggerImport; 115 | -------------------------------------------------------------------------------- /views/tab-pane-mock.ejs: -------------------------------------------------------------------------------- 1 |
2 | <% var urlMock = urlBase + urlPath + method.pathCleaned;%> 3 |

Url:

4 |

5 | 6 | <%=urlMock%> 7 | 8 |

9 |
10 | 13 |

14 | 18 | open directory 19 | 20 |

21 | <% for(var m=0; m 22 | <% var mock = method.availableMockResponses[m]; %> 23 | <% var checked = mock.isSelected ? 'checked' : ''; %> 24 | 25 | <% var isDocument = true; %> 26 | <% var isMiddleware = mock.name.search(/^middleware$/) >= 0; %> 27 | <% var isTunnel = mock.name.search(/^tunnel$/) >= 0; %> 28 | <% var isImage = (mock.mime || '').search(/^image\//) >= 0; %> 29 | <% var isJSON = (mock.mime || '').search(/json$/) >= 0; %> 30 | <% var isError = mock.name.search(/error/) >= 0; %> 31 | <% if (isImage || isJSON || isError || isMiddleware || isTunnel) { isDocument = false; } %> 32 | 33 | <% var noInteractionPossible = isMiddleware || isImage || isTunnel; %> 34 | 35 | <% var showValidationResult = isJSON && !isError; %> 36 | <% var showDeleteBtn = isJSON || isImage || isDocument; %> 37 | <% var showOpenBtn = isJSON || isDocument; %> 38 | <% var showValidateBtn = isJSON && !isError; %> 39 | 40 | <% 41 | var label = ''; 42 | if (mock.isValidated && showValidationResult) { 43 | if (mock.isValid) { 44 | label = 'Valid'; 45 | } else { 46 | label = 'Invalid (' + mock.inValidCounter + ')'; 47 | } 48 | } 49 | %> 50 |
51 | 63 | <% if (showDeleteBtn) { %> 64 | 70 | delete 71 | 72 | <% } if (showOpenBtn) { %> 73 | 77 | open 78 | 79 | <% } %> 80 | <% if (showValidateBtn) { %> 81 |  preview 82 | 91 | <% } %> 92 | 93 |
94 | <% } %> 95 | 103 |
104 |
105 | -------------------------------------------------------------------------------- /src/app/css/styles.css: -------------------------------------------------------------------------------- 1 | .hljs { 2 | background: transparent; 3 | } 4 | .parent { 5 | color: #999; 6 | } 7 | .list-group-item > .badge { 8 | float: none; 9 | margin-right: 6px; 10 | } 11 | .panel-title > .methods { 12 | float: right; 13 | } 14 | .badge { 15 | border-radius: 0; 16 | text-transform: uppercase; 17 | width: 75px; 18 | font-weight: normal; 19 | color: #f3f3f6; 20 | line-height: normal; 21 | } 22 | .badge_get { 23 | background-color: #63a8e2; 24 | } 25 | .badge_post { 26 | background-color: #6cbd7d; 27 | } 28 | .badge_put { 29 | background-color: #22bac4; 30 | } 31 | .badge_delete { 32 | background-color: #d26460; 33 | } 34 | .badge.is-deprecated { 35 | text-decoration: line-through; 36 | opacity: 0.5; 37 | } 38 | .list-group, .panel-group { 39 | margin-bottom: 0; 40 | } 41 | .panel-group .panel+.panel-white { 42 | margin-top: 0; 43 | } 44 | .panel-group .panel-white { 45 | border-bottom: 1px solid #F5F5F5; 46 | border-radius: 0; 47 | } 48 | .panel-white:last-child { 49 | border-bottom-color: white; 50 | -webkit-box-shadow: none; 51 | box-shadow: none; 52 | } 53 | .panel-white .panel-heading { 54 | background: white; 55 | } 56 | .panel-title a { 57 | position: relative; 58 | } 59 | .tab-pane ul { 60 | padding-left: 2em; 61 | } 62 | .tab-pane h2 { 63 | font-size: 1.2em; 64 | padding-bottom: 4px; 65 | border-bottom: 1px solid #ddd; 66 | } 67 | .tab-pane h3 { 68 | font-size: 1.1em; 69 | } 70 | .tab-content { 71 | border-left: 1px solid #ddd; 72 | border-right: 1px solid #ddd; 73 | border-bottom: 1px solid #ddd; 74 | padding: 10px; 75 | } 76 | #sidebar { 77 | margin-top: 30px; 78 | } 79 | .top-resource-description { 80 | border-bottom: 1px solid #ddd; 81 | background: #fcfcfc; 82 | padding: 15px 15px 0 15px; 83 | margin: -15px -15px 10px -15px; 84 | } 85 | .resource-description { 86 | border-bottom: 1px solid #fcfcfc; 87 | background: #fcfcfc; 88 | padding: 15px 15px 0 15px; 89 | margin: -15px -15px 10px -15px; 90 | } 91 | .list-group .badge { 92 | float: left; 93 | } 94 | .method_description { 95 | margin-left: 85px; 96 | } 97 | .method_description p:last-child { 98 | margin: 0; 99 | } 100 | .list-group-item { 101 | cursor: pointer; 102 | } 103 | .list-group-item:hover { 104 | background-color: #f5f5f5; 105 | } 106 | .security-info { 107 | font-size: 0.8em; 108 | padding-bottom: 20px; 109 | } 110 | 111 | .tab-content.no-style { 112 | border:0; 113 | padding:0; 114 | margin:0; 115 | } 116 | 117 | .tab-pane .nav-pills { 118 | padding:0; 119 | } 120 | 121 | .modal .form-horizontal { 122 | padding: 0 15px; 123 | } 124 | .form-control[data-status="not-saved"] { 125 | border: 1px solid dimgray; 126 | } 127 | .form-control[data-status="saving"] { 128 | border: 1px dashed green; 129 | } 130 | .form-control[data-status="saved"] { 131 | border: 1px solid green; 132 | } 133 | .form-control[data-status="error"] { 134 | border: 1px solid red; 135 | } 136 | .modal-content-with-label { 137 | padding-top: 23px; 138 | } 139 | .status-label { 140 | position: absolute; 141 | top: 0; 142 | right: 0; 143 | border-radius:5px 5px 0 0; 144 | font-size: 11px; 145 | left: 0; 146 | padding: 6px; 147 | } 148 | .nav-pills>li>a { 149 | padding: 2px 10px; 150 | } 151 | 152 | .format-json-outlet { 153 | background-color: rgb(245, 245, 245); 154 | padding: 10px 15px 15px 15px; 155 | border: 1px solid rgba(204, 204, 204, 0.57); 156 | border-radius:4px; 157 | } 158 | 159 | .align-center { 160 | text-align: center; 161 | } 162 | 163 | .spacer-left { 164 | margin-left: 5px; 165 | } 166 | 167 | .space-left-l { 168 | margin-left: 10px; 169 | } 170 | 171 | .cursor-pointer { 172 | cursor: pointer; 173 | } 174 | -------------------------------------------------------------------------------- /src/libraries/css/json-formatter-js.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * json-formatter-js 3 | * https://github.com/mohsen1/json-formatter-js#readme 4 | * Version: 0.2.0 - 2015-10-30T17:24:20.952Z 5 | * License: MIT 6 | */.json-formatter-dark.json-formatter-row,.json-formatter-row{font-family:monospace}.json-formatter-dark.json-formatter-row.open>.toggler-link .toggler:after,.json-formatter-row.open>.toggler-link .toggler:after{transform:rotate(90deg)}.json-formatter-row,.json-formatter-row a,.json-formatter-row a:hover{color:#000;text-decoration:none}.json-formatter-row .json-formatter-row{margin-left:1rem}.json-formatter-row .children.empty{opacity:.5;margin-left:1rem}.json-formatter-row .children.empty:after{display:none}.json-formatter-row .children.empty.object:after{content:"No properties"}.json-formatter-row .children.empty.array:after{content:"[]"}.json-formatter-row .string{color:green;white-space:pre;word-wrap:break-word}.json-formatter-row .number{color:#00f}.json-formatter-row .boolean{color:red}.json-formatter-row .null{color:#855a00}.json-formatter-row .undefined{color:#ca0b69}.json-formatter-row .function{color:#ff20ed}.json-formatter-row .date{background-color:rgba(0,0,0,.05)}.json-formatter-row .url{text-decoration:underline;color:#00f;cursor:pointer}.json-formatter-row .bracket{color:#00f}.json-formatter-row .key{color:#00008b;cursor:pointer}.json-formatter-row .constructor-name{cursor:pointer}.json-formatter-row .toggler{line-height:1.2rem;font-size:.8em;vertical-align:middle;opacity:.6;cursor:pointer}.json-formatter-row .toggler:after{display:inline-block;transition:transform .1s ease-in;content:"►"}.json-formatter-row>a>.preview-text{opacity:0;transition:opacity .15s ease-in;font-style:italic}.json-formatter-row:hover>a>.preview-text{opacity:.6}.json-formatter-row.open>.children:after{display:inline-block}.json-formatter-dark.json-formatter-row .children.empty:after,.json-formatter-row.open>a>.preview-text{display:none}.json-formatter-dark.json-formatter-row,.json-formatter-dark.json-formatter-row a,.json-formatter-dark.json-formatter-row a:hover{color:#fff;text-decoration:none}.json-formatter-dark.json-formatter-row .json-formatter-row{margin-left:1rem}.json-formatter-dark.json-formatter-row .children.empty{opacity:.5;margin-left:1rem}.json-formatter-dark.json-formatter-row .children.empty.object:after{content:"No properties"}.json-formatter-dark.json-formatter-row .children.empty.array:after{content:"[]"}.json-formatter-dark.json-formatter-row .string{color:#31f031;white-space:pre;word-wrap:break-word}.json-formatter-dark.json-formatter-row .number{color:#66c2ff}.json-formatter-dark.json-formatter-row .boolean{color:#ec4242}.json-formatter-dark.json-formatter-row .null{color:#eec97d}.json-formatter-dark.json-formatter-row .undefined{color:#ef8fbe}.json-formatter-dark.json-formatter-row .function{color:#fd48cb}.json-formatter-dark.json-formatter-row .date{background-color:rgba(255,255,255,.05)}.json-formatter-dark.json-formatter-row .url{text-decoration:underline;color:#027bff;cursor:pointer}.json-formatter-dark.json-formatter-row .bracket{color:#9494ff}.json-formatter-dark.json-formatter-row .key{color:#23a0db;cursor:pointer}.json-formatter-dark.json-formatter-row .constructor-name{cursor:pointer}.json-formatter-dark.json-formatter-row .toggler{line-height:1.2rem;font-size:.8em;vertical-align:middle;opacity:.6;cursor:pointer}.json-formatter-dark.json-formatter-row .toggler:after{display:inline-block;transition:transform .1s ease-in;content:"►"}.json-formatter-dark.json-formatter-row>a>.preview-text{opacity:0;transition:opacity .15s ease-in;font-style:italic}.json-formatter-dark.json-formatter-row:hover>a>.preview-text{opacity:.6}.json-formatter-dark.json-formatter-row.open>.children:after{display:inline-block}.json-formatter-dark.json-formatter-row.open>a>.preview-text{display:none} -------------------------------------------------------------------------------- /doc/readme-usage-examples.md: -------------------------------------------------------------------------------- 1 | 2 | # Usage examples 3 | 4 | ## Default Options 5 | ```js 6 | var mockServer = require('node-mock-server'); 7 | mockServer({}); 8 | ``` 9 | 10 | ## Custom Options 11 | 12 | ```js 13 | var mockServer = require('node-mock-server'); 14 | var path = require('path'); 15 | 16 | mockServer({ 17 | restPath: path.join(__dirname, '/mock/rest'), 18 | dirName: __dirname, 19 | title: 'Api mock server', 20 | version: 2, 21 | urlBase: 'http://localhost:3001', 22 | urlPath: '/rest/v2', 23 | port: 3001, 24 | uiPath: '/', 25 | funcPath: path.join(__dirname, '/func'), 26 | headers: { 27 | 'Global-Custom-Header': 'Global-Custom-Header' 28 | }, 29 | customDTOToClassTemplate: path.join(__dirname, '/templates/dto_es6flow.ejs'), 30 | middleware: { 31 | '/rest/products/#{productCode}/GET'(serverOptions, requestOptions) { 32 | var productCode = requestOptions.req.params[0].split('/')[3]; 33 | 34 | if (productCode === '1234') { 35 | requestOptions.res.statusCode = 201; 36 | requestOptions.res.end('product 1234'); 37 | return null; 38 | } 39 | 40 | return 'success'; 41 | } 42 | }, 43 | expressMiddleware: [ 44 | function (express) { 45 | return ['/public', express.static(__dirname + '/public')]; 46 | } 47 | ], 48 | swaggerImport: { 49 | protocol: 'http', 50 | authUser: undefined, 51 | authPass: undefined, 52 | host: 'petstore.swagger.io', 53 | port: 80, 54 | path: '/v2/swagger.json', 55 | dest: path.join(__dirname, '/mock/rest'), 56 | replacePathsStr: '/v2/{baseSiteId}', 57 | createErrorFile: true, 58 | createEmptyFile: true, 59 | overwriteExistingDescriptions: true, 60 | responseFuncPath: path.join(__dirname, '/func-imported') 61 | }, 62 | open: true 63 | }); 64 | ``` 65 | 66 | ## Custom Options behind a corporate proxy 67 | 68 | ```js 69 | var mockServer = require('node-mock-server'); 70 | var path = require('path'); 71 | 72 | var HttpProxyAgent = require( 'http-proxy-agent' ); 73 | var agent = new HttpProxyAgent( 'http://user:pass@IP.IP.IP.IP:PORT' ); 74 | 75 | mockServer({ 76 | restPath: path.join(__dirname, '/mock/rest'), 77 | dirName: __dirname, 78 | title: 'Api mock server', 79 | version: 2, 80 | urlBase: 'http://localhost:3001', 81 | urlPath: '/rest/v2', 82 | port: 3001, 83 | uiPath: '/', 84 | funcPath: path.join(__dirname, '/func'), 85 | headers: { 86 | 'Global-Custom-Header': 'Global-Custom-Header' 87 | }, 88 | customDTOToClassTemplate: path.join(__dirname, '/templates/dto_es6flow.ejs'), 89 | middleware: { 90 | '/rest/products/#{productCode}/GET'(serverOptions, requestOptions) { 91 | var productCode = requestOptions.req.params[0].split('/')[3]; 92 | 93 | if (productCode === '1234') { 94 | requestOptions.res.statusCode = 201; 95 | requestOptions.res.end('product 1234'); 96 | return null; 97 | } 98 | 99 | return 'success'; 100 | } 101 | }, 102 | expressMiddleware: [ 103 | function (express) { 104 | return ['/public', express.static(__dirname + '/public')]; 105 | } 106 | ], 107 | swaggerImport: { 108 | protocol: 'http', 109 | authUser: undefined, 110 | authPass: undefined, 111 | host: 'petstore.swagger.io', 112 | port: 80, 113 | path: '/v2/swagger.json', 114 | dest: path.join(__dirname, '/mock/rest'), 115 | replacePathsStr: '/v2/{baseSiteId}', 116 | createErrorFile: true, 117 | createEmptyFile: true, 118 | overwriteExistingDescriptions: true, 119 | responseFuncPath: path.join(__dirname, '/func-imported'), 120 | agent: agent 121 | }, 122 | open: true 123 | }); 124 | ``` 125 | -------------------------------------------------------------------------------- /lib/cli/ask/headers.js: -------------------------------------------------------------------------------- 1 | var inquirer = require('inquirer'); 2 | var jQueryExtend = require('extend'); 3 | var Promise = require('es6-promise-polyfill').Promise; 4 | var defaultOptions = require('../../defaults/options-defaults'); 5 | 6 | function askForHeader(headerCallback) { 7 | inquirer.prompt([ 8 | { 9 | type: 'input', 10 | name: 'name', 11 | message: 'Enter response header key', 12 | }, 13 | { 14 | type: 'input', 15 | name: 'value', 16 | message: 'Enter response header value', 17 | }, 18 | ]).then(function (headerAnswer) { 19 | var headers = {}; 20 | headers[headerAnswer.name] = headerAnswer.value; 21 | headerCallback(headers); 22 | }); 23 | } 24 | function askForAddHeader(headers, headerCallback) { 25 | inquirer.prompt([ 26 | { 27 | type: 'list', 28 | name: 'addHeader', 29 | choices: ['Yes', 'No'], 30 | message: 'Do you want to add another response header?', 31 | default: 'Yes', 32 | }, 33 | ]).then(function (answers) { 34 | if (answers.addHeader.toUpperCase() === 'YES') { 35 | askForHeader(function (headerResult) { 36 | headers = jQueryExtend(true, headers, headerResult); 37 | askForAddHeader(headers, headerCallback); 38 | }); 39 | return; 40 | } 41 | headerCallback({ headers: headers }); 42 | }); 43 | } 44 | 45 | function askForHeaders() { 46 | return new Promise((resolve) => { 47 | inquirer.prompt([ 48 | { 49 | type: 'list', 50 | choices: ['Yes', 'No'], 51 | name: 'useDefaultHeaders', 52 | message: 'Do you want to use the default response headers?', 53 | default: 'Yes', 54 | }, 55 | ]).then(function (answers) { 56 | if (answers.useDefaultHeaders.toUpperCase() === 'YES') { 57 | resolve({ 58 | contentType: defaultOptions.contentType, 59 | accessControlExposeHeaders: defaultOptions.accessControlExposeHeaders, 60 | accessControlAllowOrigin: defaultOptions.accessControlAllowOrigin, 61 | accessControlAllowMethods: defaultOptions.accessControlAllowMethods, 62 | accessControlAllowHeaders: defaultOptions.accessControlAllowHeaders, 63 | accessControlAllowCredentials: defaultOptions.accessControlAllowCredentials, 64 | }); 65 | return; 66 | } 67 | inquirer.prompt([ 68 | { 69 | type: 'input', 70 | name: 'contentType', 71 | message: 'Enter "Content-Type" response header', 72 | default: 'application/json', 73 | }, 74 | { 75 | type: 'input', 76 | name: 'accessControlExposeHeaders', 77 | message: 'Enter "Access-Control-Expose-Headers" response header', 78 | default: 'X-Total-Count', 79 | }, 80 | { 81 | type: 'input', 82 | name: 'accessControlAllowOrigin', 83 | message: 'Enter "Access-Control-Allow-Origin" response header', 84 | default: '*', 85 | }, 86 | { 87 | type: 'input', 88 | name: 'accessControlAllowMethods', 89 | message: 'Enter "Access-Control-Allow-Methods" response header', 90 | default: 'GET, POST, PUT, OPTIONS, DELETE, PATCH, HEAD', 91 | }, 92 | { 93 | type: 'input', 94 | name: 'accessControlAllowHeaders', 95 | message: 'Enter "Access-Control-Allow-Headers" response header', 96 | default: 'origin, x-requested-with, content-type', 97 | }, 98 | { 99 | type: 'list', 100 | choices: ['true', 'false'], 101 | name: 'accessControlAllowCredentials', 102 | message: 'Enter "Access-Control-Allow-Credentials" response header', 103 | default: 'true', 104 | }, 105 | ]).then(function (answersOtherHeaders) { 106 | askForAddHeader({}, function (answerHeaders) { 107 | resolve(jQueryExtend(true, answerHeaders, answersOtherHeaders)); 108 | }); 109 | }); 110 | }); 111 | }); 112 | } 113 | 114 | module.exports = askForHeaders; 115 | -------------------------------------------------------------------------------- /lib/ValidatorResponse.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | var extend = require('util')._extend; 5 | var GetResponse = require('./GetResponse'); 6 | var validatorLog = require('./ValidatorLog'); 7 | var ValidatorDataSchema = require('./ValidatorDataSchema'); 8 | var Utils = require('./Utils'); 9 | 10 | /** 11 | * 12 | * @class ValidatorResponse 13 | * @namespace ValidatorResponse 14 | * @param {object} options 15 | * @param {object} serverOptions 16 | * @constructor 17 | * 18 | * Swagger importer 19 | */ 20 | function ValidatorResponse(options, serverOptions) { 21 | this.init(options, serverOptions); 22 | } 23 | 24 | ValidatorResponse.prototype = extend(ValidatorResponse.prototype, Utils.prototype); 25 | ValidatorResponse.prototype = extend(ValidatorResponse.prototype, { 26 | 27 | constructor: ValidatorResponse, 28 | 29 | _defaults: { 30 | path: undefined, 31 | method: undefined, 32 | expected: undefined, 33 | }, 34 | 35 | /** 36 | * 37 | * @method init 38 | * called by constructor 39 | * @param {object} options 40 | * @param {object} serverOptions 41 | * @public 42 | */ 43 | init: function (options, serverOptions) { 44 | 45 | options = extend(this._defaults, options || {}); 46 | 47 | this._options = options; 48 | this._serverOptions = serverOptions; 49 | this._isValid = true; 50 | this._path = this._options.path; 51 | this._method = this._options.method.toUpperCase(); 52 | this._expected = this._options.expected; 53 | 54 | if (!this.isFilledString(this._path)) { 55 | this._isValid = false; 56 | validatorLog.error('options.path can\'t be empty!'); 57 | } 58 | 59 | if (!this.isFilledString(this._method)) { 60 | this._isValid = false; 61 | validatorLog.error('options.method can\'t be empty!'); 62 | } 63 | 64 | if (!this.isFilledString(this._expected)) { 65 | this._isValid = false; 66 | validatorLog.error('options.expected can\'t be empty!'); 67 | } 68 | 69 | if (this._isValid) { 70 | this._pathMethod = this._path + '/' + this._method; 71 | this._pathSchema = this._pathMethod + '/response_schema.json'; 72 | this._pathMockData = this._pathMethod + '/mock/' + this._expected; 73 | this._dataSchema = {}; 74 | this._dataExpected = {}; 75 | this._readFiles(); 76 | } 77 | 78 | if (this._isValid) { 79 | return new ValidatorDataSchema({ 80 | schema: this._dataSchema, 81 | data: this._dataExpected, 82 | path: this._pathMethod, 83 | expected: this._expected, 84 | restPath: this._serverOptions.restPath, 85 | filePathData: this._pathMockData, 86 | }); 87 | } 88 | 89 | }, 90 | 91 | /** 92 | * @method _readFiles 93 | * @returns {void} 94 | * @private 95 | */ 96 | _readFiles: function () { 97 | 98 | var response; 99 | var cleanedPath = this._cleanPath(this._serverOptions, this._pathMockData); 100 | 101 | if (this._isValid) { 102 | try { 103 | this._dataSchema = JSON.parse(this.readFile(this._pathSchema)); 104 | } catch (err) { 105 | validatorLog.success('Mock data in file ' + cleanedPath + ' are valid!'); 106 | this._isValid = false; 107 | } 108 | 109 | response = new GetResponse({ 110 | path: this._path, 111 | method: this._method, 112 | expected: this._expected, 113 | }, this._serverOptions); 114 | 115 | this._dataExpected = response.get(); 116 | } 117 | }, 118 | 119 | /** 120 | * 121 | * @method cleanPath 122 | * @param {Object} options 123 | * @param {String} path 124 | * @returns {String} 125 | * @private 126 | */ 127 | _cleanPath: function (options, path) { 128 | 129 | if (typeof path !== 'string') { 130 | return ''; 131 | } 132 | 133 | if (typeof options !== 'object' || !options.restPath) { 134 | return path; 135 | } 136 | 137 | return path.replace(options.restPath, ''); 138 | }, 139 | 140 | }); 141 | 142 | module.exports = ValidatorResponse; 143 | --------------------------------------------------------------------------------