├── .gitignore ├── .travis.yml ├── Procfile ├── README.md ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *~ 3 | .* 4 | example.js 5 | Procfile 6 | node_modules 7 | tmp 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | # init xvfb 3 | # http://docs.travis-ci.com/user/gui-and-headless-browsers/ 4 | - "export DISPLAY=:99.0 && sh -e /etc/init.d/xvfb start" 5 | # install mongodb 3.0 6 | # https://github.com/mongodb/mongo-ruby-driver/blob/master/.travis.yml 7 | - curl http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.5.tgz | tar xvz -C /tmp && mkdir -p /tmp/data && /tmp/mongodb-linux-x86_64-3.0.5/bin/mongod --dbpath /tmp/data &> /dev/null & 8 | branches: 9 | only: 10 | - alpha 11 | - beta 12 | - master 13 | env: 14 | global: 15 | # this value is auto-generated by shAesEncryptTravis 16 | - secure: ZaEFBVYOMJWn1/JgVC9qxkjfrr7G6/aX+N1v8dsToLd45mnSvHawTXKb3/rH9Y8phBH/uLFkQjjhL3YU1yblPSS/+lqR4jiDY36l9Es6pt2Bn1UP1Fk1KmCZu4elelssHSZac+S5o8j/yCVIn2oVWT447GFyfizQgMeYYBUTGZ+QRn0bu3uAbkLlQFSDGcgUxBzrxOMSlLpaP9ekszMQOn4UUYmIopgfeyqtUQn+nOwqdYYREV5AHB3tAH4QV8oxcZWj1sZ6JFkpa5D+p4waKg2xvgVi66iN/WNiUmydPzrZvWu3Z6jCUl9gq86x7FeUQ5AcKbcj8IkOQgV2BdBdP9u/ohPej/FLEzs0TtKezyRnHxl9/S9YsHJF35FnllCZfMMIgi+qF+LEpRJAdXsIH1FvQmSsHffTajTh9iO9UHAXU0xdUr9NQ1ZYoQR7rudgAOpcw9KNYqFVBuocuG5U9IcTgtTWr0T9NZg9IMu0iAcTGPS/OTBhRETwjTSvfk1KS0y4hy5eUJp0Uwh1K8swKmsHvpw0HzxSNjoad9X/iddPkrwXm6g35wUIrvSk5J/CkUKe+sfOkpuiadC/0UbafsS+etnoogdeINgR5t8Y0hHEXSDG5Hln56xAFIY4fwF3CCb8+Ix43RgRrGTVgT6yrVuW1bjFcQMKbqohcCFMhns= # AES_256_KEY 17 | # this value is auto-generated by shAesEncryptTravis 18 | # - AES_ENCRYPTED_SH: ZjU1ZThmOTRlNWVlMDFiZTY0MDgxMDg3YTNmZTVmZDggVJwx16+4xQGKhh2X7+oklO5U7Dxuo4Ds10hFjDz1F6Yuh8Qpd2aovzJ820uK5CoErpBRYKa4xBgvbuF2aBrP87jveyiJlCk5xiWmEmfTkzaqyA+OAPq07I4ag3V3/EOnOwaqnlaDcgSIMTyqHYJryqOGziuQet9/crF1C3yt7C1KH1ig5E041x5FO5GnILKerT1/Bir02blzuPMZyZQR2ZOi3Froq6cB3aZrjOsB4E7Zmb6mwQ/zPExUTdPUAbyXrcC+5/Xl6OPBMnnUPOKYSIMFzVcKFiW0PD4wSu4tJIcrDwAOvbO/Ca3OrrmqDsqh5q6ZMy9upxcWnZKPEwo3SviHh8TsEaV1hGJH61HYgG7X7m4sOKy7C5qR1XDG7vLOqcZLgjbB5648AGJZudJ1eM2G5riFyUrd3NbeJjbYwtNY84AnXSCQv6IVEUVQz84pIv2MJKN1IVK/v1/gDv0Ron8qZoD/l8bphR4rEqC247+skAtn4VtRGUh7oMf7RSYtWgzlAiFBV0m1rJyYEgITcapXfe1cnRO8/5RWMlTurHptm45c6Akr/ZMrxipvOTvn8nB7s29YaVJwl2SwyVgdG63G+AdgeO1k4QUKEyV0fjm7Ti6RyoOMUlqvzFrqs7zTK42BqTH2ANMFG6xpE1ivn7XONp5peE10K02rVajJ8F1ytYTtlhgbtZ9OVMO3ybJAZep0iTe1+BVqdl14V03eW1rat2MGgkCa6elLkb3ZT+mrexPtmLlXM9ar7qE98krm0boG3LLhuqKBjyTpgW2tX6w/vhsZL3Al3Jxew2ppucVwOViGNOsJIrSmJ9zlRtz/+qPesvn17+eMnfpwH7iOsbgRtzuzvcih0KhGyUQOeAbzsqJjAxmiD4VcibZzCi9TB7Uy7r0eF1WgOlpEA8u8/3zIQ/u80/ttPYf42imEzFlumpfwxyw5kmEglNE6y0uP0eQFdzS+VkDnN3V8ke1T3Pz/30aSpNeSUyoCWlWiYdLE4M8EDW/EuI+BsEEq6Wgienl7tbOQa77Z4LgC/EUwF2n984GsVYJJQqb4Zr6Wze9LFoWicNNK1OBytjAX7deYYcuuouGQpNtkUg3ATVoV/ftWIk83SsaLDTrP3j5UoyUUJkWNBP7hvW3CZ/8S/VAJYDybH9O9CNciZugMSDn3zbAd3yEN+FleP73wC74Kht0cZBm9K6fBgOXqv/v0GXGmz3yo5KA8+DUoh9nhaZmpeHpPopQGfqdZ369vlk0LbniADyiZRJ3ibi8SJrl3O5hlJDFnWotaWj65u1/CnNaGnwAcJoDN9v2S9ar+EZb6oh8b0trFzqLPjB+Tjcxqix8P05yWbGn2OWXCKEx89/El8zOp8VErCwH8dl9EpMiQsMwcyAk8/xjIgt8B5XbuG1xWRI0yS+PDbcCjFXk+lWnaf5f6eyUdVnmQsLfMioWKq8xogpzhY0fLAwYEhPifw+r6iisIqIxxwZ7KEYA/ru/XcJta5Ilzff3yVU9vvBu5sTUI42aYlRZFzi1TFRfItx4A+l/7Wmid1U/ISZaQO4G2hxYP4IeRp9kDrS3FnpJCpRWO4HvU9yt2ouD77W1qdpOPgs3GjWh3g7ayBb+XVBCYA70wKzG9iWzyEihKAsfymv0/aU8BR7gyepQuw+xLn48/WiTqSwEvTAE77k2Fv/Cg09hjkZp5LjsLexys4+4nFO92a0M52uejpdvEoRELbR3E9Je0MiyQLNy04JJPFTXp9eTtezb34lhiorUtIZ2z+XtRyWQc6VdkCU0ZxXY0ENEYgg+H9WOcyesbwiJ7plrdPwdPQInpd2gF55UQYYjr/y08Ca8mkvuar32jsVTw2E17K1jGyF8pP7+jGb4hqgJpQkilS3ztRzASftW5P7MvqcSydGjfsQcIuftHOWmm/LYVa9uGXmwPdXwssb4nDGxk9bzOuI8ktmRXsySQXRs0s8kt9LYNAq33mQDcend5kYDerrTD/i0QjufP4tge6RhwnpKKbSxkiYN+b2xj9D/snjIf4y0zJYbjrgHgxh/VVcFPSPC4n3nq/hHGV0dze8+qbugZkvLunmy99WKC47mApbSGAo43v8dw5WUnL/Rk/7aOSbYz/aORqCJEV62da6ee4yLpcf/c2GaOu5ByopkVlVgwF+ejb8Xs0WHxDL/7YBUSdMvBFpTg3XM/xOJq1YcWKJvOeFEbMYrShU6z0Q50hXeX4q5iWWzj3zjMdT0t7fXkSp04n0P/5wzxqSs3xb8wZ8Z1OE7yxsfnmMcugf5P/CaiVJs1KZJGDRHPfmLKKY+YBkSvspH8mcnWLz9myIbUiQHo8dXckAwwFObcVNvbTPW1Tvkmj+Rn56/HgNjT8DpcssBQfOChpNBx0KUqCh/tuexHxvIps8bo1goTyKXMEDBeFxdKjz5wVq+nZU8QsbzeQV6D2qu2GXTfenJiX8Ff3M1ExOp5W4szD+xrjJe+4ua6o+u48VsTDPbSLxaENxH6q49AuNFGPms5yVemKYxCZaFfVe7P0J+6zw/YVvw5qGkvsgt2dar3cOGFlqLBrQupncpxqByObLqiAfY7bMXQQTvAEDS9IlRiinxagXH/FNPDY5UmzUFmdTvI0BnxXRrzlcsjJZ5rrWc/dtz+3j9jv62oc935CTkIpI5FUmtbZ1tJjdwpeOGIe+yVVN1oVq585FILFPwSLlHC4TUM+Q3t3PdnRRFMYzURawKAXMu+2HrlO+g7LVB6EvKQasSUxksEvSms3tK9/5MklrawneB7ukdGGJgDWwvgbFmpSX6mxt/tMbmUw+iJFquphIW1ts0lmCxnVulrarQIwW4LimzAkSL8xme5czfx3n19aHicHVYO5MgKUq622ZH2BmrLW2cp/A4Wtqfx40oL/06JFElSdD5jO9R5ZO1C7hCJmkYAatg+QdWCB8UUW8vGzzMdki37nxm//a/ByC3TFhbkNNgjvpQ6wXaQ6GpEBmFN3NnSc+T7W2Sws1BvOXTDh0IIjwRhzm3vpwgFd9IVHED0UVbxy8v1cKbTrI5sI1vnrcphGT1p1kpulLWF7OBeH7dsh5mOOglxVr6AczBSmn73Arj/5xGix0gYPHMuCs87N4irCgFF7ofR6I0t90tlHLsvvRPpeiahn2z4y82A2tVWzLKzPvLDgV+QATkVma1nvylSAK1KgFNZEoQV1WTCVU7A2KEW8ydBz6cYwKGqrkZ6O7tgkoZEfQKKnYmZXcMRJifnhl0M/0ejLuxIKBVHZLs4si0dhO5WginLvdQf7DD7/JE9/5KyiDhqr4qWPdbTFMgs8JSXYVb9mmp9M8ZCFg5hOvrT # AES_ENCRYPTED_SH 19 | language: 20 | - node_js 21 | node_js: 22 | - "0.10" 23 | - "0.12" 24 | script: 25 | - npm run-script build-ci 26 | # http://docs.travis-ci.com/user/workers/container-based-infrastructure 27 | sudo: 28 | false 29 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm start --mongodb-url="$MONGOLAB_URI" --server-port=$PORT 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | swagger-mongodb 2 | =============== 3 | lightweight swagger-ui crud-middleware backed by mongodb 4 | 5 | [![NPM](https://img.shields.io/npm/v/swagger-mongodb.svg?style=flat-square)](https://www.npmjs.org/package/swagger-mongodb) 6 | 7 | 8 | 9 | # live test-server 10 | [![heroku.com test-server](https://kaizhu256.github.io/node-swagger-mongodb/build/screen-capture.herokuDeploy.slimerjs..png)](https://hrku01-swagger-mongodb-beta.herokuapp.com) 11 | 12 | 13 | 14 | # build-status [![travis-ci.org build-status](https://api.travis-ci.org/kaizhu256/node-swagger-mongodb.svg)](https://travis-ci.org/kaizhu256/node-swagger-mongodb) 15 | [![build commit status](https://kaizhu256.github.io/node-swagger-mongodb/build/build.badge.svg)](https://travis-ci.org/kaizhu256/node-swagger-mongodb) 16 | 17 | | git-branch : | [master](https://github.com/kaizhu256/node-swagger-mongodb/tree/master) | [beta](https://github.com/kaizhu256/node-swagger-mongodb/tree/beta) | [alpha](https://github.com/kaizhu256/node-swagger-mongodb/tree/alpha)| 18 | |--:|:--|:--|:--| 19 | | test-server : | [![heroku.com test-server](https://kaizhu256.github.io/node-swagger-mongodb/heroku-logo.75x25.png)](https://hrku01-swagger-mongodb-master.herokuapp.com) | [![heroku.com test-server](https://kaizhu256.github.io/node-swagger-mongodb/heroku-logo.75x25.png)](https://hrku01-swagger-mongodb-beta.herokuapp.com) | [![heroku.com test-server](https://kaizhu256.github.io/node-swagger-mongodb/heroku-logo.75x25.png)](https://hrku01-swagger-mongodb-alpha.herokuapp.com)| 20 | | test-report : | [![test-report](https://kaizhu256.github.io/node-swagger-mongodb/build..master..travis-ci.org/test-report.badge.svg)](https://kaizhu256.github.io/node-swagger-mongodb/build..master..travis-ci.org/test-report.html) | [![test-report](https://kaizhu256.github.io/node-swagger-mongodb/build..beta..travis-ci.org/test-report.badge.svg)](https://kaizhu256.github.io/node-swagger-mongodb/build..beta..travis-ci.org/test-report.html) | [![test-report](https://kaizhu256.github.io/node-swagger-mongodb/build..alpha..travis-ci.org/test-report.badge.svg)](https://kaizhu256.github.io/node-swagger-mongodb/build..alpha..travis-ci.org/test-report.html)| 21 | | coverage : | [![istanbul-lite coverage](https://kaizhu256.github.io/node-swagger-mongodb/build..master..travis-ci.org/coverage.badge.svg)](https://kaizhu256.github.io/node-swagger-mongodb/build..master..travis-ci.org/coverage.html/index.html) | [![istanbul-lite coverage](https://kaizhu256.github.io/node-swagger-mongodb/build..beta..travis-ci.org/coverage.badge.svg)](https://kaizhu256.github.io/node-swagger-mongodb/build..beta..travis-ci.org/coverage.html/index.html) | [![istanbul-lite coverage](https://kaizhu256.github.io/node-swagger-mongodb/build..alpha..travis-ci.org/coverage.badge.svg)](https://kaizhu256.github.io/node-swagger-mongodb/build..alpha..travis-ci.org/coverage.html/index.html)| 22 | | build-artifacts : | [![build-artifacts](https://kaizhu256.github.io/node-swagger-mongodb/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-swagger-mongodb/tree/gh-pages/build..master..travis-ci.org) | [![build-artifacts](https://kaizhu256.github.io/node-swagger-mongodb/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-swagger-mongodb/tree/gh-pages/build..beta..travis-ci.org) | [![build-artifacts](https://kaizhu256.github.io/node-swagger-mongodb/glyphicons_144_folder_open.png)](https://github.com/kaizhu256/node-swagger-mongodb/tree/gh-pages/build..alpha..travis-ci.org)| 23 | 24 | #### master branch 25 | - stable branch 26 | - HEAD should be tagged, npm-published package 27 | 28 | #### beta branch 29 | - semi-stable branch 30 | - HEAD should be latest, npm-published package 31 | 32 | #### alpha branch 33 | - unstable branch 34 | - HEAD is arbitrary 35 | - commit history may be rewritten 36 | 37 | 38 | 39 | # documentation 40 | #### this package requires 41 | - darwin or linux os 42 | - mongodb 2.6 or higher 43 | 44 | #### [api-doc](https://kaizhu256.github.io/node-swagger-mongodb/build/doc.api.html) 45 | [![api-doc](https://kaizhu256.github.io/node-swagger-mongodb/build/screen-capture.docApiCreate.slimerjs._2Fhome_2Ftravis_2Fbuild_2Fkaizhu256_2Fnode-swagger-mongodb_2Ftmp_2Fbuild_2Fdoc.api.html.png)](https://kaizhu256.github.io/node-swagger-mongodb/build/doc.api.html) 46 | 47 | 48 | 49 | # quickstart web example 50 | #### to run this example, follow the instruction in the script below 51 | - example.js 52 | 53 | ```javascript 54 | /* 55 | example.js 56 | 57 | this node script will serve a lightweight swagger-ui crud-api backed by mongodb 58 | 59 | instruction 60 | 1. save this script as example.js 61 | 2. run the shell command: 62 | $ npm install swagger-mongodb && npm_config_server_port=1337 node example.js 63 | 3. open a browser to http://localhost:1337 64 | 4. interact with the swagger-ui crud-api 65 | */ 66 | 67 | /*jslint 68 | browser: true, 69 | maxerr: 8, 70 | maxlen: 96, 71 | node: true, 72 | nomen: true, 73 | regexp: true, 74 | stupid: true 75 | */ 76 | 77 | (function (local) { 78 | 'use strict'; 79 | switch (local.modeJs) { 80 | 81 | 82 | 83 | // run node js-env code 84 | case 'node': 85 | // export local 86 | module.exports = local; 87 | // init assets 88 | local.utility2.cacheDict.assets['/'] = '\n' + 89 | /* jslint-ignore-begin */ 90 | '\n' + 91 | '\n' + 92 | ' \n' + 93 | ' \n' + 94 | ' {{envDict.npm_package_name}} [{{envDict.npm_package_version}}]\n' + 95 | ' \n' + 96 | ' \n' + 97 | ' \n' + 112 | ' {{envDict.npm_config_html_head_extra}}\n' + 113 | '\n' + 114 | '\n' + 115 | ' \n' + 118 | '

{{envDict.npm_package_name}} [{{envDict.npm_package_version}}]

\n' + 119 | '

{{envDict.npm_package_description}}

\n' + 120 | '
\n' + 121 | ' \n' + 122 | ' \n' + 123 | ' \n' + 124 | ' \n' + 125 | ' \n' + 126 | ' \n' + 127 | ' \n' + 128 | ' \n' + 147 | ' {{envDict.npm_config_html_body_extra}}\n' + 148 | '\n' + 149 | /* jslint-ignore-end */ 150 | '\n'; 151 | local.utility2.cacheDict.assets['/'] = local.utility2.stringFormat( 152 | local.utility2.cacheDict.assets['/'], 153 | { envDict: local.utility2.envDict }, 154 | '' 155 | ); 156 | local.utility2.cacheDict.assets['/assets/example.js'] = 157 | local.utility2.istanbul_lite.instrumentSync( 158 | local.fs.readFileSync(__dirname + '/example.js', 'utf8'), 159 | __dirname + '/example.js' 160 | ); 161 | local.utility2.cacheDict.assets['/test/test.js'] = 162 | local.utility2.istanbul_lite.instrumentInPackage( 163 | local.fs.readFileSync(local.swmg.__dirname + '/test.js', 'utf8'), 164 | local.swmg.__dirname + '/test.js', 165 | 'swagger-mongodb' 166 | ); 167 | // init mongodb-client 168 | local.utility2.onReady.counter += 1; 169 | local.utility2.taskRunOrSubscribe({ 170 | key: 'swagger-mongodb.mongodbConnect', 171 | onTask: function (onError) { 172 | local.mongodb.MongoClient.connect( 173 | local.utility2.envDict.npm_config_mongodb_url || 174 | 'mongodb://localhost:27017/test', 175 | function (error, db) { 176 | // validate no error occurred 177 | local.utility2.assert(!error, error); 178 | local.swmg.db = db; 179 | onError(); 180 | local.utility2.onReady(); 181 | } 182 | ); 183 | } 184 | }); 185 | // init middleware 186 | local.middleware = local.utility2.middlewareGroupCreate([ 187 | // init pre-middleware 188 | local.utility2.middlewareInit, 189 | // init cached-assets middleware 190 | local.utility2.middlewareAssetsCached, 191 | // init http-body-get middleware 192 | local.utility2.middlewareBodyGet, 193 | // init http-body-parse-upload middleware 194 | function (request, response, nextMiddleware) { 195 | var boundary, bodyText; 196 | // jslint-hack 197 | local.utility2.nop(response); 198 | local.utility2.testTryCatch(function () { 199 | if ((request.headers['content-type'] || '') 200 | .indexOf('multipart/form-data') !== 0) { 201 | nextMiddleware(); 202 | return; 203 | } 204 | boundary = 205 | '--' + (/boundary=(.*)/).exec(request.headers['content-type'])[1]; 206 | request.swmgBodyParsed = {}; 207 | bodyText = String(request.bodyRaw); 208 | bodyText.split(boundary).slice(1, -1).forEach(function (part) { 209 | request.swmgBodyParsed[ 210 | (/\bname="([^"]*)/).exec(part)[1] 211 | ] = part.split('\r\n\r\n').slice(1).join('\r\n\r\n').slice(0, -2); 212 | }); 213 | // set file 214 | bodyText.replace('\r\n\r\n', function (match0, ii) { 215 | // jslint-hack 216 | local.utility2.nop(match0); 217 | request.swmgBodyParsed.file = request.bodyRaw 218 | .slice(ii + 4, -(boundary.length + 6)) 219 | .toString('base64'); 220 | }); 221 | request.swmgBodyParsed.file = request.bodyRaw 222 | .slice(bodyText.lastIndexOf('\r\n\r\n') + 4, -(boundary.length + 6)) 223 | .toString('base64'); 224 | // set filename 225 | request.swmgBodyParsed.filename = (/\bfilename="([^"]+)/).exec(bodyText); 226 | request.swmgBodyParsed.filename = 227 | request.swmgBodyParsed.filename && 228 | request.swmgBodyParsed.filename[1]; 229 | nextMiddleware(); 230 | }, nextMiddleware); 231 | }, 232 | // init http-body-parse middleware 233 | local.swmg.middlewareBodyParse, 234 | // init swagger pre-middleware 235 | function (request, response, nextMiddleware) { 236 | // jslint-hack 237 | local.utility2.nop(request); 238 | // enable cors 239 | // http://en.wikipedia.org/wiki/Cross-origin_resource_sharing 240 | response.setHeader( 241 | 'Access-Control-Allow-Methods', 242 | 'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT' 243 | ); 244 | response.setHeader('Access-Control-Allow-Origin', '*'); 245 | // init content-type 246 | response.setHeader('Content-Type', 'application/json; charset=UTF-8'); 247 | nextMiddleware(); 248 | }, 249 | // init swagger middleware 250 | local.swmg.middlewareSwagger 251 | ]); 252 | // init error-middleware 253 | local.middlewareError = local.swmg.middlewareError; 254 | // init petstore-api 255 | (function () { 256 | var methodPath, options, schema; 257 | options = local.utility2.jsonCopy(require(local.swmg.local 258 | .swagger_ui_lite.__dirname + '/swagger.json')); 259 | options = { 260 | definitions: options.definitions, 261 | paths: options.paths, 262 | tags: options.tags 263 | }; 264 | // remove unused properties 265 | delete options.definitions.ApiResponse; 266 | // init schema 267 | Object.keys(options.definitions).forEach(function (schemaName) { 268 | schema = options.definitions[schemaName]; 269 | // init id 270 | schema.properties.id = { type: 'string' }; 271 | schema['x-inheritList'] = [{ $ref: '#/definitions/JsonapiResource' }]; 272 | }); 273 | local.utility2.objectSetOverride(options, { 274 | definitions: { 275 | // init Pet schema 276 | Pet: { 277 | // drop collection on init 278 | _collectionDrop: true, 279 | // upsert fixtures 280 | _collectionFixtureList: [{ 281 | id: 'pet0', 282 | name: 'birdie', 283 | photoUrls: [], 284 | status: 'available', 285 | tags: [{ name: 'bird'}] 286 | }, { 287 | id: 'pet1', 288 | name: 'kittie', 289 | status: 'pending', 290 | photoUrls: [], 291 | tags: [{ name: 'cat'}] 292 | }, { 293 | id: 'pet2', 294 | name: 'doggie', 295 | photoUrls: [], 296 | status: 'sold', 297 | tags: [{ name: 'dog'}] 298 | }], 299 | _collectionName: 'SwmgPet' 300 | }, 301 | // init Order schema 302 | Order: { 303 | // create index 304 | _collectionCreateIndexList: [{ 305 | key: { status: 1 }, 306 | name: 'status_1' 307 | }], 308 | // drop collection on init 309 | _collectionDrop: true, 310 | // upsert fixtures 311 | _collectionFixtureList: [{ 312 | id: 'order0', 313 | status: 'available' 314 | }, { 315 | id: 'order1', 316 | status: 'pending' 317 | }, { 318 | id: 'order2', 319 | status: 'sold' 320 | }], 321 | _collectionName: 'SwmgOrder', 322 | properties: { 323 | petId: { type: 'string' } 324 | } 325 | }, 326 | // init User schema 327 | User: { 328 | // create index 329 | _collectionCreateIndexList: [{ 330 | key: { username: 1 }, 331 | name: 'username_1', 332 | unique: true 333 | }], 334 | // drop collection on init 335 | _collectionDrop: true, 336 | // upsert fixtures 337 | _collectionFixtureList: [{ 338 | email: 'john@doe.com', 339 | firstName: 'john', 340 | id: 'user0', 341 | lastName: 'doe', 342 | password: 'hello', 343 | phone: '1234-5678', 344 | username: 'john.doe' 345 | }, { 346 | email: 'jane@doe.com', 347 | firstName: 'jane', 348 | id: 'user1', 349 | lastName: 'doe', 350 | password: 'bye', 351 | phone: '8765-4321', 352 | username: 'jane.doe' 353 | }], 354 | _collectionName: 'SwmgUser' 355 | } 356 | }, 357 | // init crud-api 358 | paths: { 359 | '/pet/crudGetByQueryMany': { get: { 360 | _collectionName: 'SwmgPet', 361 | _crudApi: 'pet', 362 | _schemaName: 'Pet', 363 | operationId: 'crudGetByQueryMany', 364 | tags: ['pet'] 365 | } }, 366 | '/store/crudGetByQueryMany': { get: { 367 | _collectionName: 'SwmgOrder', 368 | _crudApi: 'store', 369 | _schemaName: 'Order', 370 | operationId: 'crudGetByQueryMany', 371 | tags: ['store'] 372 | } }, 373 | '/user/crudGetByQueryMany': { get: { 374 | _collectionName: 'SwmgUser', 375 | _crudApi: 'user', 376 | _schemaName: 'User', 377 | operationId: 'crudGetByQueryMany', 378 | tags: ['user'] 379 | } } 380 | } 381 | }, 4); 382 | // transform petstore-api to swagger-mongodb's crud-api 383 | Object.keys(options.paths).forEach(function (path) { 384 | Object.keys(options.paths[path]).forEach(function (method) { 385 | methodPath = options.paths[path][method]; 386 | // init methodPath._schemaName 387 | switch (path.split('/')[1]) { 388 | case 'pet': 389 | methodPath._schemaName = 'Pet'; 390 | break; 391 | case 'store': 392 | methodPath._schemaName = 'Order'; 393 | break; 394 | case 'user': 395 | methodPath._schemaName = 'User'; 396 | break; 397 | } 398 | methodPath._collectionName = 'Swmg' + methodPath._schemaName; 399 | delete methodPath.produces; 400 | delete methodPath.responses; 401 | delete methodPath.security; 402 | // init jsonapi response 403 | local.utility2.objectSetDefault(methodPath, { responses: { 404 | 200: { 405 | description: '200 ok - http://jsonapi.org/format' + 406 | '/#document-structure-top-level', 407 | schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } 408 | } 409 | } }, 2); 410 | // init crudCreateMany / crudCreateOne / crudDeleteByIdOne / crudGetByIdOne 411 | switch (methodPath.operationId) { 412 | case 'addPet': 413 | case 'createUser': 414 | case 'placeOrder': 415 | methodPath.operationId = 'crudCreateOne'; 416 | break; 417 | case 'createUsersWithArrayInput': 418 | case 'createUsersWithListInput': 419 | methodPath.operationId = 'crudCreateMany'; 420 | break; 421 | case 'deleteOrder': 422 | case 'deletePet': 423 | case 'deleteUser': 424 | methodPath.operationId = 'crudDeleteByIdOne'; 425 | break; 426 | case 'getOrderById': 427 | case 'getPetById': 428 | case 'getUserByName': 429 | methodPath.operationId = 'crudGetByIdOne'; 430 | break; 431 | } 432 | // init id 433 | (methodPath.parameters || []).forEach(function (paramDef) { 434 | switch (paramDef.name) { 435 | case 'orderId': 436 | case 'petId': 437 | delete paramDef.format; 438 | paramDef.type = 'string'; 439 | break; 440 | } 441 | }); 442 | }); 443 | }); 444 | local.swmg.apiUpdate(options); 445 | }()); 446 | // init petstore-middleware 447 | local.middleware.middlewareList.push(function (request, response, nextMiddleware) { 448 | var modeNext, onNext, options; 449 | modeNext = 0; 450 | onNext = function (error, data) { 451 | local.utility2.testTryCatch(function () { 452 | modeNext = error 453 | ? Infinity 454 | : modeNext + 1; 455 | switch (modeNext) { 456 | case 1: 457 | // init id 458 | ((request.swmgMethodPath && request.swmgMethodPath.parameters) || [ 459 | ]).forEach(function (paramDef) { 460 | switch (paramDef.name) { 461 | case 'orderId': 462 | case 'petId': 463 | request.swmgParamDict.id = request.swmgParamDict[paramDef.name]; 464 | break; 465 | } 466 | }); 467 | // init options 468 | if (request.swmgMethodPath) { 469 | options = { 470 | collectionName: request.swmgMethodPath._collectionName, 471 | data: request.swmgParamDict, 472 | operationId: request.swmgMethodPath.operationId, 473 | paramDefList: request.swmgMethodPath.parameters, 474 | schemaName: request.swmgMethodPath._schemaName 475 | }; 476 | } 477 | switch (request.swmgPathname) { 478 | // handle pet request 479 | case 'DELETE /pet/': 480 | case 'GET /pet/': 481 | case 'POST /pet': 482 | local.swmg._crudApi(options, onNext); 483 | break; 484 | case 'GET /pet/findByStatus': 485 | options.operationId = 'crudGetByQueryMany'; 486 | options.data.fields = '{}'; 487 | options.data.hint = '{}'; 488 | options.data.limit = 100; 489 | options.data.query = '{"status":{"$in":' + 490 | JSON.stringify(options.data.status) + '}}'; 491 | options.data.skip = 0; 492 | options.data.sort = '{"_timeModified":-1}'; 493 | local.swmg._crudApi(options, onNext); 494 | break; 495 | case 'GET /pet/findByTags': 496 | options.operationId = 'crudGetByQueryMany'; 497 | options.data.fields = '{}'; 498 | options.data.hint = '{}'; 499 | options.data.limit = 100; 500 | options.data.query = '{"status":{"$in":' + 501 | JSON.stringify(options.data.tags) + '}}'; 502 | options.data.skip = 0; 503 | options.data.sort = '{"_timeModified":-1}'; 504 | options.paramDefList[0].default = 'bird,cat,dog'; 505 | local.swmg._crudApi(options, onNext); 506 | break; 507 | case 'POST /pet/': 508 | options.data.upsert = true; 509 | options.data.body = { 510 | id: options.data.id, 511 | name: options.data.name, 512 | status: options.data.status 513 | }; 514 | options.operationId = 'crudUpdateOne'; 515 | local.swmg._crudApi(options, onNext); 516 | break; 517 | case 'POST /pet//': 518 | options.data.body = { 519 | additionalMetadata: options.data.additionalMetadata, 520 | file: options.data.file, 521 | filename: 522 | request.swmgBodyParsed && request.swmgBodyParsed.filename, 523 | id: options.id 524 | }; 525 | options.data.upsert = true; 526 | options.operationId = 'crudUpdateOne'; 527 | local.swmg._crudApi(options, onNext); 528 | break; 529 | case 'PUT /pet': 530 | options.data.upsert = true; 531 | options.operationId = 'crudReplaceOne'; 532 | local.swmg._crudApi(options, onNext); 533 | break; 534 | // handle store request 535 | case 'DELETE /store/order/': 536 | case 'GET /store/order/': 537 | case 'POST /store/order': 538 | local.swmg._crudApi(options, onNext); 539 | break; 540 | case 'GET /store/inventory': 541 | options.data = { body: [{ 542 | $group: { _id: '$status', total: { $sum: 1} } 543 | }, { 544 | $project: { _id: 0, status: '$_id', total: '$total' } 545 | }]}; 546 | options.operationId = 'crudAggregateMany'; 547 | local.swmg._crudApi(options, onNext); 548 | break; 549 | // handle user request 550 | case 'DELETE /user/': 551 | case 'GET /user/': 552 | case 'POST /user/createWithArray': 553 | case 'POST /user/createWithList': 554 | options.optionsId = { username: request.swmgParamDict.username}; 555 | local.swmg._crudApi(options, onNext); 556 | break; 557 | case 'POST /user': 558 | options.data.username = options.data.body.username; 559 | options.optionsId = { username: request.swmgParamDict.username}; 560 | local.swmg._crudApi(options, onNext); 561 | break; 562 | case 'PUT /user/': 563 | options.data.body.username = options.data.username; 564 | options.data.upsert = true; 565 | options.operationId = 'crudReplaceOne'; 566 | options.optionsId = { username: request.swmgParamDict.username}; 567 | local.swmg._crudApi(options, onNext); 568 | break; 569 | default: 570 | nextMiddleware(); 571 | } 572 | break; 573 | default: 574 | // validate no error occurred 575 | local.utility2.assert(!error, error); 576 | // respond with json-object 577 | response.end(JSON.stringify(data)); 578 | } 579 | }, nextMiddleware); 580 | }; 581 | onNext(); 582 | }); 583 | // run server-test 584 | local.utility2.testRunServer(local); 585 | break; 586 | } 587 | }((function () { 588 | 'use strict'; 589 | var local; 590 | 591 | 592 | 593 | // run shared js-env code 594 | (function () { 595 | // init local 596 | local = {}; 597 | // init js-env 598 | local.modeJs = (function () { 599 | try { 600 | return module.exports && 601 | typeof process.versions.node === 'string' && 602 | typeof require('http').createServer === 'function' && 603 | 'node'; 604 | } catch (errorCaughtNode) { 605 | return typeof navigator.userAgent === 'string' && 606 | typeof document.querySelector('body') === 'object' && 607 | 'browser'; 608 | } 609 | }()); 610 | // init global 611 | local.global = local.modeJs === 'browser' 612 | ? window 613 | : global; 614 | // export local 615 | local.global.local = local; 616 | // init swagger-mongodb 617 | local.swmg = local.modeJs === 'browser' 618 | ? window.swmg 619 | : require('swagger-mongodb'); 620 | // import swmg.local 621 | Object.keys(local.swmg.local).forEach(function (key) { 622 | local[key] = local[key] || local.swmg.local[key]; 623 | }); 624 | // init utility2 625 | local.utility2 = local.swmg.local.utility2; 626 | // init onReady 627 | local.utility2.onReadyInit(); 628 | }()); 629 | return local; 630 | }()))); 631 | ``` 632 | 633 | #### output from shell 634 | [![screen-capture](https://kaizhu256.github.io/node-swagger-mongodb/build/screen-capture.testExampleJs.svg)](https://travis-ci.org/kaizhu256/node-swagger-mongodb) 635 | 636 | #### output from phantomjs-lite 637 | [![screen-capture](https://kaizhu256.github.io/node-swagger-mongodb/build/screen-capture.testExampleJs.slimerjs..png)](https://hrku01-swagger-mongodb-beta.herokuapp.com) 638 | 639 | 640 | 641 | # npm-dependencies 642 | - [mongodb-minimal](https://www.npmjs.com/package/mongodb-minimal) 643 | - [swagger-ui-lite](https://www.npmjs.com/package/swagger-ui-lite) 644 | - [utility2](https://www.npmjs.com/package/utility2) 645 | 646 | 647 | 648 | # package-listing 649 | [![screen-capture](https://kaizhu256.github.io/node-swagger-mongodb/build/screen-capture.gitLsTree.svg)](https://github.com/kaizhu256/node-swagger-mongodb) 650 | 651 | 652 | 653 | # package.json 654 | ```json 655 | { 656 | "author": "kai zhu ", 657 | "bin": { "swagger-mongodb": "index.js" }, 658 | "dependencies": { 659 | "mongodb-minimal": "2015.8.1", 660 | "swagger-ui-lite": "2015.6.2", 661 | "utility2": "~2015.8.5" 662 | }, 663 | "description": "lightweight swagger-ui crud-middleware backed by mongodb", 664 | "devDependencies": { 665 | "phantomjs-lite": "2015.7.1" 666 | }, 667 | "engines": { "node": ">=0.10 <=0.12" }, 668 | "keywords": [ 669 | "api", 670 | "browser", 671 | "cms", "crud", 672 | "mongo", "mongodb", 673 | "swagger", "swagger-ui", 674 | "web" 675 | ], 676 | "license": "MIT", 677 | "name": "swagger-mongodb", 678 | "os": ["darwin", "linux"], 679 | "repository" : { 680 | "type" : "git", 681 | "url" : "https://github.com/kaizhu256/node-swagger-mongodb.git" 682 | }, 683 | "scripts": { 684 | "build-ci": "node_modules/.bin/utility2 shRun shReadmeBuild", 685 | "build-doc": "node_modules/.bin/utility2 shRun shReadmeExportPackageJson && \ 686 | node_modules/.bin/utility2 shRun shDocApiCreate \"{ \ 687 | exampleFileList:['example.js','test.js','index.js'], \ 688 | moduleDict:{'swagger-mongodb':{aliasList:['swmg'],exports:require('./index.js')}} \ 689 | }\"", 690 | "start": "npm_config_mode_auto_restart=1 node_modules/.bin/utility2 shRun node test.js", 691 | "test": "node_modules/.bin/utility2 shRun shReadmeExportPackageJson && \ 692 | node_modules/.bin/utility2 test test.js" 693 | }, 694 | "version": "2015.8.3" 695 | } 696 | ``` 697 | 698 | 699 | 700 | # todo 701 | - add logging feature 702 | - rename delete to remove for naming consistency 703 | - migrate to travis-ci docker container build 704 | - add cached param for crudGetByQueryMany 705 | - add SwmgUserLoginTokenCapped 706 | - re-enable user login/logout 707 | - test /user/login and /user/logout 708 | - add max / min validation 709 | - none 710 | 711 | 712 | 713 | # change since af87c5b9 714 | - npm publish 2015.8.3 715 | - lockdown npm dependencies 716 | - none 717 | 718 | 719 | 720 | # changelog of last 50 commits 721 | [![screen-capture](https://kaizhu256.github.io/node-swagger-mongodb/build/screen-capture.gitLog.svg)](https://github.com/kaizhu256/node-swagger-mongodb/commits) 722 | 723 | 724 | 725 | # internal build-script 726 | - build.sh 727 | 728 | ```shell 729 | # build.sh 730 | 731 | # this shell script will run the build for this package 732 | 733 | shBuild() { 734 | # this function will run the main build 735 | local TEST_URL || return $? 736 | 737 | # init env 738 | export npm_config_mode_slimerjs=1 || return $? 739 | . node_modules/.bin/utility2 && shInit || return $? 740 | 741 | # run npm-test on published package 742 | shRun shNpmTestPublished || return $? 743 | 744 | # test example js script 745 | export npm_config_timeout_exit=10000 || return $? 746 | MODE_BUILD=testExampleJs shRunScreenCapture shReadmeTestJs example.js || return $? 747 | unset npm_config_timeout_exit || return $? 748 | 749 | # run npm-test 750 | MODE_BUILD=npmTest shRunScreenCapture npm test || return $? 751 | 752 | # create api-doc 753 | npm run-script build-doc || return $? 754 | 755 | # if running legacy-node, then do not continue 756 | [ "$(node --version)" \< "v0.12" ] && return 757 | 758 | # deploy app to heroku 759 | shRun shHerokuDeploy hrku01-$npm_package_name-$CI_BRANCH || return $? 760 | 761 | # test deployed app to heroku 762 | if [ "$CI_BRANCH" = alpha ] || 763 | [ "$CI_BRANCH" = beta ] || 764 | [ "$CI_BRANCH" = master ] 765 | then 766 | TEST_URL="https://hrku01-$npm_package_name-$CI_BRANCH.herokuapp.com" || return $? 767 | TEST_URL="$TEST_URL?modeTest=phantom&timeExit={{timeExit}}" || return $? 768 | MODE_BUILD=herokuTest shPhantomTest "$TEST_URL" || return $? 769 | fi 770 | } 771 | shBuild 772 | 773 | # save exit-code 774 | EXIT_CODE=$? 775 | # create package-listing 776 | MODE_BUILD=gitLsTree shRunScreenCapture shGitLsTree || exit $? 777 | # create recent changelog of last 50 commits 778 | MODE_BUILD=gitLog shRunScreenCapture git log -50 --pretty="%ai\u000a%B" || exit $? 779 | # if running legacy-node, then do not continue 780 | [ "$(node --version)" \< "v0.12" ] && exit $EXIT_CODE 781 | # upload build-artifacts to github, and if number of commits > 16, then squash older commits 782 | COMMIT_LIMIT=16 shBuildGithubUpload || exit $? 783 | exit $EXIT_CODE 784 | ``` 785 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*jslint 2 | bitwise: true, 3 | browser: true, 4 | maxerr: 8, 5 | maxlen: 96, 6 | node: true, 7 | nomen: true, 8 | regexp: true, 9 | stupid: true 10 | */ 11 | (function (local) { 12 | 'use strict'; 13 | 14 | 15 | 16 | // run shared js-env code 17 | (function () { 18 | local.swmg.normalizeIdMongodb = function (data) { 19 | /* 20 | * this function will recursively convert the property id to _id 21 | */ 22 | local.utility2.objectTraverse(data, function (element) { 23 | if (element && element.id) { 24 | element._id = element._id || element.id; 25 | delete element.id; 26 | } 27 | }); 28 | return data; 29 | }; 30 | 31 | local.swmg.normalizeIdSwagger = function (data) { 32 | /* 33 | * this function will recursively convert the property _id to id 34 | */ 35 | local.utility2.objectTraverse(data, function (element) { 36 | if (element && element._id) { 37 | element.id = element.id || element._id; 38 | delete element._id; 39 | } 40 | }); 41 | return data; 42 | }; 43 | 44 | local.swmg.normalizeParamDictSwagger = function (data, methodPath) { 45 | /* 46 | * this function will parse the data according to methodPath.parameters 47 | */ 48 | var tmp; 49 | methodPath.parameters.forEach(function (paramDef) { 50 | tmp = data[paramDef.name]; 51 | // init default value 52 | if (tmp === undefined) { 53 | // jsonCopy object to prevent side-effects 54 | data[paramDef.name] = local.utility2.jsonCopy(paramDef.default); 55 | } 56 | // parse csv array 57 | if (paramDef.type === 'array' && 58 | paramDef.collectionFormat && 59 | typeof tmp === 'string') { 60 | switch (paramDef.collectionFormat) { 61 | case 'csv': 62 | tmp = tmp.split(','); 63 | break; 64 | case 'pipes': 65 | tmp = tmp.split('|'); 66 | break; 67 | case 'ssv': 68 | tmp = tmp.split(' '); 69 | break; 70 | case 'tsv': 71 | tmp = tmp.split('\t'); 72 | break; 73 | } 74 | } 75 | // JSON.parse swmgParamDict 76 | if (paramDef.type !== 'string' && 77 | (typeof tmp === 'string' || 78 | (local.modeJs === 'node' && Buffer.isBuffer(tmp)))) { 79 | try { 80 | tmp = JSON.parse(tmp); 81 | } catch (ignore) { 82 | } 83 | } 84 | data[paramDef.name] = tmp; 85 | }); 86 | return data; 87 | }; 88 | 89 | local.swmg.onErrorJsonapi = function (options, onError) { 90 | /* 91 | * this function will convert the error and data to jsonapi format, 92 | * http://jsonapi.org/format/#errors 93 | * and pass them to onError 94 | */ 95 | return function (error, data) { 96 | if (typeof options === 'function') { 97 | options = options(); 98 | } 99 | options = options || {}; 100 | options.id = options.id || local.utility2.uuidTime(); 101 | // handle error 102 | if (error) { 103 | if (error.errors && Array.isArray(error.errors) && error.errors[0]) { 104 | onError(error); 105 | return; 106 | } 107 | // prepend mongodb-errmsg 108 | if (error.errmsg) { 109 | local.utility2.errorMessagePrepend(error, error.errmsg + '\n'); 110 | } 111 | options.message = error.message; 112 | options.stack = error.stack; 113 | options.statusCode = Number(error.statusCode) || 500; 114 | options.errors = local.utility2 115 | .jsonCopy(local.swmg.normalizeIdSwagger(error)); 116 | local.utility2.objectSetDefault(options.errors, { 117 | code: options.statusCode, 118 | detail: options.stack, 119 | id: options.id, 120 | message: options.message 121 | }); 122 | options.errors.code = String(options.errors.code); 123 | options.errors.detail = String(options.errors.detail); 124 | options.errors.id = String(options.errors.id); 125 | options.errors.message = String(options.errors.message); 126 | options.errors = [options.errors]; 127 | onError(options); 128 | return; 129 | } 130 | // handle data 131 | options.data = data; 132 | local.swmg.normalizeIdSwagger(options); 133 | if (!Array.isArray(options.data)) { 134 | options.data = [options.data]; 135 | } 136 | onError(null, options); 137 | }; 138 | }; 139 | 140 | local.swmg.schemaDereference = function ($ref) { 141 | /* 142 | * this function will try to dereference the schema from $ref 143 | */ 144 | try { 145 | return ((local.global.swaggerUi && 146 | local.global.swaggerUi.api && 147 | local.global.swaggerUi.api.swaggerJson) || 148 | local.swmg.swaggerJson) 149 | .definitions[(/^\#\/definitions\/(\w+)$/).exec($ref)[1]]; 150 | } catch (ignore) { 151 | } 152 | }; 153 | 154 | local.swmg.validateByParamDefList = function (options) { 155 | /* 156 | * this function will validate options.data against options.paramDefList 157 | */ 158 | var data, key; 159 | try { 160 | data = options.data; 161 | // validate data 162 | local.utility2.assert(data && typeof data === 'object', data); 163 | (options.paramDefList || []).forEach(function (paramDef) { 164 | key = paramDef.name; 165 | local.swmg.validateByPropertyDef({ 166 | data: data[key], 167 | key: key, 168 | propertyDef: paramDef, 169 | required: paramDef.required 170 | }); 171 | }); 172 | } catch (errorCaught) { 173 | local.utility2.errorMessagePrepend(errorCaught, '"' + options.key + '.' + key + 174 | '" - '); 175 | throw errorCaught; 176 | } 177 | }; 178 | 179 | local.swmg.validateByPropertyDef = function (options) { 180 | /* 181 | * this function will validate options.data against options.propertyDef 182 | */ 183 | var assert, data, propertyDef, tmp; 184 | assert = function (valid) { 185 | if (!valid) { 186 | throw new Error('invalid "' + options.key + ':' + (propertyDef.format || 187 | propertyDef.type) + '" property - ' + JSON.stringify(data)); 188 | } 189 | }; 190 | data = options.data; 191 | propertyDef = options.propertyDef; 192 | // validate undefined data 193 | if (data === null || data === undefined) { 194 | if (options.required) { 195 | throw new Error('required "' + options.key + ':' + (propertyDef.format || 196 | propertyDef.type) + '" property cannot be null or undefined'); 197 | } 198 | return; 199 | } 200 | // validate schema 201 | tmp = propertyDef.$ref || (propertyDef.schema && propertyDef.schema.$ref); 202 | if (tmp) { 203 | local.swmg.validateBySchema({ 204 | circularList: options.circularList, 205 | data: data, 206 | key: tmp, 207 | schema: local.swmg.schemaDereference(tmp) 208 | }); 209 | return; 210 | } 211 | // init circularList 212 | if (data && typeof data === 'object') { 213 | options.circularList = options.circularList || []; 214 | if (options.circularList.indexOf(data) >= 0) { 215 | return; 216 | } 217 | options.circularList.push(data); 218 | } 219 | // validate propertyDef embedded in propertyDef.schema.type 220 | if (!propertyDef.type && propertyDef.schema && propertyDef.schema.type) { 221 | propertyDef = propertyDef.schema; 222 | } 223 | // validate propertyDef.type 224 | // https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md 225 | // #data-types 226 | switch (propertyDef.type) { 227 | case 'array': 228 | assert(Array.isArray(data) && propertyDef.items); 229 | // recurse - validate elements in list 230 | data.forEach(function (element) { 231 | local.swmg.validateByPropertyDef({ 232 | circularList: options.circularList, 233 | data: element, 234 | key: options.key, 235 | propertyDef: propertyDef.items 236 | }); 237 | }); 238 | break; 239 | case 'boolean': 240 | assert(typeof data === 'boolean'); 241 | break; 242 | case 'integer': 243 | assert(typeof data === 'number' && isFinite(data) && (data | 0) === data); 244 | switch (propertyDef.format) { 245 | case 'int32': 246 | case 'int64': 247 | break; 248 | } 249 | break; 250 | case 'number': 251 | assert(typeof data === 'number' && isFinite(data)); 252 | switch (propertyDef.format) { 253 | case 'double': 254 | case 'float': 255 | break; 256 | } 257 | break; 258 | case 'object': 259 | assert(typeof data === 'object'); 260 | break; 261 | case 'string': 262 | assert(typeof data === 'string'); 263 | switch (propertyDef.format) { 264 | // https://github.com/swagger-api/swagger-spec/issues/50 265 | case 'byte': 266 | assert(!(/[^\n\r\+\/0-9\=A-Za-z]/).test(data)); 267 | break; 268 | case 'date': 269 | tmp = new Date(data); 270 | assert(tmp.getTime() && data === tmp.toISOString().slice(0, 10)); 271 | break; 272 | case 'date-time': 273 | tmp = new Date(data); 274 | assert(tmp.getTime() && 275 | data.slice(0, 19) === tmp.toISOString().slice(0, 19)); 276 | break; 277 | case 'email': 278 | assert(local.utility2.regexpEmailValidate.test(data)); 279 | break; 280 | case 'json': 281 | try { 282 | JSON.parse(data); 283 | } catch (errorCaught) { 284 | assert(null); 285 | } 286 | break; 287 | } 288 | break; 289 | } 290 | }; 291 | 292 | local.swmg.validateBySchema = function (options) { 293 | /* 294 | * this function will validate options.data against options.schema 295 | */ 296 | var data, key, schema; 297 | try { 298 | data = options.data; 299 | // init circularList 300 | if (data && typeof data === 'object') { 301 | options.circularList = options.circularList || []; 302 | if (options.circularList.indexOf(data) >= 0) { 303 | return; 304 | } 305 | options.circularList.push(data); 306 | } 307 | // validate data 308 | switch (options.key) { 309 | // ignore undefined schema 310 | case '#/definitions/Undefined': 311 | return; 312 | } 313 | local.utility2.assert(data && typeof data === 'object', 'invalid data ' + data); 314 | schema = options.schema; 315 | // validate schema 316 | local.utility2.assert( 317 | schema && typeof schema === 'object', 318 | 'invalid schema ' + schema 319 | ); 320 | Object.keys(schema.properties || {}).forEach(function (_) { 321 | key = _; 322 | local.swmg.validateByPropertyDef({ 323 | circularList: options.circularList, 324 | data: data[key], 325 | depth: options.depth - 1, 326 | key: key, 327 | propertyDef: schema.properties[key], 328 | required: schema.required && schema.required.indexOf(key) >= 0 329 | }); 330 | }); 331 | } catch (errorCaught) { 332 | local.utility2.errorMessagePrepend(errorCaught, '"' + options.key + '.' + key + 333 | '" - '); 334 | throw errorCaught; 335 | } 336 | }; 337 | 338 | local.swmg.validateBySwagger = function (options) { 339 | /* 340 | * this function will validate the entire swagger json object 341 | */ 342 | local.swagger_tools.v2.validate( 343 | // jsonCopy object to prevent side-effects 344 | local.utility2.jsonCopy(options), 345 | function (error, result) { 346 | // validate no error occurred 347 | local.utility2.assert(!error, error); 348 | ['errors', 'undefined', 'warnings'].forEach(function (errorType) { 349 | ((result && result[errorType]) || [ 350 | ]).slice(0, 8).forEach(function (element) { 351 | console.error('swagger schema - ' + errorType.slice(0, -1) + ' - ' + 352 | element.code + ' - ' + element.message + ' - ' + 353 | JSON.stringify(element.path)); 354 | }); 355 | }); 356 | error = result && result.errors && result.errors[0]; 357 | // validate no error occurred 358 | local.utility2.assert(!error, new Error(error && error.message)); 359 | } 360 | ); 361 | }; 362 | }()); 363 | switch (local.modeJs) { 364 | 365 | 366 | 367 | // run node js-env code 368 | case 'node': 369 | local.swmg._crudApi = function (options, onError) { 370 | /* 371 | * this function will run the low-level crud-api on the given options.data 372 | */ 373 | var modeNext, onNext; 374 | options.onError2 = local.swmg.onErrorJsonapi(function () { 375 | return options.response; 376 | }, onError); 377 | modeNext = 0; 378 | onNext = local.utility2.onErrorWithStack(function (error, data) { 379 | local.utility2.testTryCatch(function () { 380 | modeNext = error 381 | ? Infinity 382 | : modeNext + 1; 383 | switch (modeNext) { 384 | case 1: 385 | // jsonCopy object to prevent side-effects 386 | options.data = local.utility2.jsonCopy(options.data); 387 | // validate params 388 | local.swmg.validateByParamDefList({ 389 | data: options.data, 390 | key: options.schemaName + '.' + options.operationId, 391 | paramDefList: options.paramDefList 392 | }); 393 | // convert id to mongodb format 394 | local.swmg.normalizeIdMongodb(options); 395 | // init body 396 | options.data.body = options.data.body || {}; 397 | // init id 398 | options.data._id = options.data.body._id = 399 | String(options.data.body._id || 400 | options.data._id || 401 | local.utility2.uuidTime()); 402 | options.optionsId = options.optionsId || { _id: options.data._id }; 403 | options.optionsIdKey = Object.keys(options.optionsId)[0]; 404 | // init collection 405 | options.collection = 406 | local.swmg.cacheDict.collection[options.collectionName]; 407 | // init response 408 | options.response = { _id: options.data._id }; 409 | // init _timeCreated 410 | switch (options.operationId) { 411 | case 'crudUpdateOne': 412 | options.collection 413 | .findOne(options.optionsId, { _timeCreated: 1 }, onNext); 414 | return; 415 | } 416 | onNext(); 417 | break; 418 | case 2: 419 | // init _timeCreated and _timeModified 420 | options.tmp = data && data._timeCreated; 421 | switch (options.operationId) { 422 | case 'crudCreateOne': 423 | case 'crudReplaceOne': 424 | options.data.body._timeCreated = 425 | options.data.body._timeModified = new Date().toISOString(); 426 | break; 427 | case 'crudUpdateOne': 428 | options.data.body._timeCreated = 429 | options.data.body._timeModified = new Date().toISOString(); 430 | if (options.tmp < options.data.body._timeCreated && 431 | new Date(options.tmp).getTime()) { 432 | options.data.body._timeCreated = options.tmp; 433 | } 434 | break; 435 | } 436 | switch (options.operationId) { 437 | case 'crudAggregateMany': 438 | // aggregate data 439 | options.collection.aggregate(local.swmg 440 | .normalizeIdMongodb(options.data.body), onNext); 441 | break; 442 | case 'crudCountByQueryOne': 443 | // count data 444 | options.collection.count(local.swmg 445 | .normalizeIdMongodb(JSON.parse(options.data.query)), onNext); 446 | break; 447 | case 'crudCreateOne': 448 | // insert data 449 | options.collection.insert(options.data.body, onNext); 450 | break; 451 | case 'crudDeleteByIdOne': 452 | // delete data 453 | options.collection.removeOne(options.optionsId, onNext); 454 | break; 455 | case 'crudDeleteByQueryMany': 456 | // delete data 457 | options.collection.remove(local.swmg 458 | .normalizeIdMongodb(JSON.parse(options.data.query)), onNext); 459 | break; 460 | case 'crudExistsByIdOne': 461 | // find data 462 | options.collection.findOne(options.optionsId, { _id: 1 }, onNext); 463 | break; 464 | case 'crudGetByIdOne': 465 | // find data 466 | options.collection.findOne(options.optionsId, onNext); 467 | break; 468 | case 'crudGetByQueryMany': 469 | data = local.swmg.normalizeIdMongodb([ 470 | JSON.parse(options.data.query), 471 | JSON.parse(options.data.fields), 472 | { 473 | hint: JSON.parse(options.data.hint), 474 | limit: options.data.limit, 475 | skip: options.data.skip, 476 | sort: JSON.parse(options.data.sort) 477 | } 478 | ]); 479 | // find data 480 | options.cursor = options.collection.find(data[0], data[1], data[2]); 481 | options.cursor.toArray(onNext); 482 | break; 483 | case 'crudGetDistinctValueByPropertyMany': 484 | // find data 485 | options.collection.distinct( 486 | options.data.field.replace((/^id$/), '_id'), 487 | local.swmg.normalizeIdMongodb(JSON.parse(options.data.query)), 488 | onNext 489 | ); 490 | break; 491 | case 'crudCreateMany': 492 | case 'crudReplaceMany': 493 | // insert / replace data 494 | if (!options.data.body.length) { 495 | options.response.data = []; 496 | modeNext = Infinity; 497 | onNext(); 498 | break; 499 | } 500 | options.bulk = options.collection.initializeOrderedBulkOp(); 501 | options.data.body.forEach(function (element) { 502 | // init id 503 | element._id = element._id || local.utility2.uuidTime(); 504 | element[options.optionsIdKey] = element[options.optionsIdKey] || 505 | element._id; 506 | // init _timeCreated and _timeModified 507 | element._timeCreated = 508 | element._timeModified = new Date().toISOString(); 509 | switch (options.operationId) { 510 | case 'crudCreateMany': 511 | // insert data 512 | options.bulk.insert(element); 513 | break; 514 | case 'crudReplaceMany': 515 | options.bulkFindQuery = {}; 516 | options.bulkFindQuery[options.optionsIdKey] = 517 | element[options.optionsIdKey]; 518 | options.bulkFind = options.bulk.find(options.bulkFindQuery); 519 | // upsert data 520 | if (options.data.upsert) { 521 | options.bulkFind = options.bulkFind.upsert(); 522 | } 523 | // replace data 524 | options.bulkFind.replaceOne(element); 525 | break; 526 | } 527 | }); 528 | options.bulk.execute(onNext); 529 | break; 530 | case 'crudReplaceOne': 531 | // replace data 532 | options.collection.update(options.optionsId, options.data.body, { 533 | upsert: options.data.upsert 534 | }, onNext); 535 | break; 536 | case 'crudUpdateOne': 537 | // update data 538 | options.collection.update(options 539 | .optionsId, { $set: options.data.body }, { 540 | upsert: options.data.upsert 541 | }, onNext); 542 | break; 543 | default: 544 | onNext(new Error('undefined crud operation - ' + 545 | options.schemaName + '.' + options.operationId)); 546 | } 547 | break; 548 | case 3: 549 | // jsonCopy object to prevent side-effects 550 | data = local.utility2.jsonCopy(data); 551 | switch (options.operationId) { 552 | case 'crudAggregateMany': 553 | case 'crudCountByQueryOne': 554 | case 'crudGetByIdOne': 555 | case 'crudGetDistinctValueByPropertyMany': 556 | case 'crudGetByQueryMany': 557 | options.response.data = data; 558 | break; 559 | case 'crudCreateMany': 560 | case 'crudReplaceMany': 561 | options.response.meta = data; 562 | options.collection.find({ 563 | _id: { $in: options.data.body.map(function (element) { 564 | return element._id; 565 | }) } 566 | }).toArray(onNext); 567 | return; 568 | case 'crudCreateOne': 569 | case 'crudReplaceOne': 570 | case 'crudUpdateOne': 571 | options.response.meta = data; 572 | if ((data.n || (data.result && data.result.n)) !== 1) { 573 | onNext(new Error(options.operationId + ' failed')); 574 | return; 575 | } 576 | options.collection.findOne(options.optionsId, onNext); 577 | return; 578 | case 'crudDeleteByIdOne': 579 | case 'crudDeleteByQueryMany': 580 | options.response.meta = data; 581 | break; 582 | case 'crudExistsByIdOne': 583 | options.response.data = !!data; 584 | break; 585 | } 586 | modeNext += 1; 587 | onNext(error); 588 | break; 589 | case 4: 590 | switch (options.operationId) { 591 | case 'crudCreateMany': 592 | case 'crudReplaceMany': 593 | options.tmp = {}; 594 | data.forEach(function (element) { 595 | options.tmp[element._id] = element; 596 | }); 597 | options.response.data = options.data.body.map(function (element) { 598 | return options.tmp[element._id]; 599 | }); 600 | break; 601 | default: 602 | // jsonCopy object to prevent side-effects 603 | options.response.data = local.utility2.jsonCopy(data); 604 | } 605 | onNext(); 606 | break; 607 | default: 608 | options.onError2(error, options.response.data); 609 | } 610 | }, options.onError2); 611 | }); 612 | onNext(); 613 | }; 614 | 615 | local.swmg.apiUpdate = function (options) { 616 | /* 617 | * this function will update the api 618 | */ 619 | var methodPath, tmp; 620 | options.definitions = options.definitions || {}; 621 | options.paths = options.paths || {}; 622 | Object.keys(options.definitions).forEach(function (schemaName) { 623 | var schema; 624 | schema = options.definitions[schemaName]; 625 | schema._schemaName = schemaName; 626 | if (!schema._collectionName) { 627 | return; 628 | } 629 | local.utility2.objectSetDefault(options, JSON.parse(JSON.stringify({ 630 | definitions: { 631 | // init JsonapiResponse{{_schemaName}} 632 | 'JsonapiResponse{{_schemaName}}': { 633 | properties: { data: { 634 | items: { $ref: '#/definitions/{{_schemaName}}' }, 635 | type: 'array' 636 | } }, 637 | 'x-inheritList': [{ $ref: '#/definitions/JsonapiResponse' }] 638 | } 639 | } 640 | }).replace((/\{\{_schemaName\}\}/g), schemaName)), 2); 641 | // hack - init swaggerJson$$Dummy, 642 | // to pass validation warnings for auto-created schemas 643 | tmp = local.swmg.swaggerJson$$Dummy; 644 | local.utility2.objectSetOverride(tmp, JSON.parse(JSON.stringify({ 645 | paths: { '/$$Dummy/{{_schemaName}}': { get: { 646 | responses: { 647 | 200: { 648 | description: '', 649 | schema: { $ref: 650 | '#/definitions/JsonapiResponse{{_schemaName}}' } 651 | } 652 | } 653 | } } } 654 | }).replace((/\{\{_schemaName\}\}/g), schemaName)), 2); 655 | // init crud-api 656 | (schema._crudApiList || []).forEach(function (methodPath) { 657 | methodPath = JSON.parse(local.swmg.cacheDict.methodPathCrudDefault[ 658 | methodPath 659 | ] 660 | .replace((/\{\{_collectionName\}\}/g), schema._collectionName) 661 | .replace((/\{\{_crudApi\}\}/g), schema._crudApi) 662 | .replace((/\{\{_schemaName\}\}/g), schema._schemaName)); 663 | options.paths[methodPath._path] = options.paths[methodPath._path] || {}; 664 | options.paths[methodPath._path][methodPath._method] = methodPath; 665 | }); 666 | // init collectionName / crudApi / schemaName 667 | schema = options.definitions[schemaName] = JSON.parse( 668 | JSON.stringify(schema) 669 | .replace((/\{\{_collectionName\}\}/g), schema._collectionName) 670 | .replace((/\{\{_crudApi\}\}/g), schema._crudApi) 671 | .replace((/\{\{_schemaName\}\}/g), schema._schemaName) 672 | ); 673 | // update cacheDict.collection 674 | local.utility2.onReady.counter += 1; 675 | local.utility2.taskRunOrSubscribe({ 676 | key: 'swagger-mongodb.mongodbConnect' 677 | }, function () { 678 | local.swmg.collectionCreate(schema, local.utility2.onReady); 679 | }); 680 | }); 681 | // update paths 682 | Object.keys(options.paths).forEach(function (path) { 683 | Object.keys(options.paths[path]).forEach(function (method) { 684 | methodPath = options.paths[path][method]; 685 | methodPath._method = method; 686 | methodPath._path = path; 687 | // init crud-api 688 | tmp = methodPath._crudApi && 689 | local.swmg.cacheDict.methodPathCrudDefault[methodPath.operationId]; 690 | if (tmp) { 691 | local.utility2.objectSetDefault(methodPath, JSON.parse(tmp), 2); 692 | } 693 | // init methodPath 694 | local.utility2.objectSetDefault(methodPath, { 695 | parameters: [], 696 | responses: { 697 | 200: { 698 | description: 'ok - ' + 699 | 'http://jsonapi.org/format/#document-top-level', 700 | schema: { $ref: '#/definitions/JsonapiResponse' } 701 | } 702 | }, 703 | tags: [] 704 | }, 2); 705 | // init collectionName / crudApi / schemaName 706 | local.utility2.objectSetOverride(methodPath, JSON.parse( 707 | JSON.stringify(methodPath) 708 | .replace((/\{\{_collectionName\}\}/g), methodPath._collectionName) 709 | .replace((/\{\{_crudApi\}\}/g), methodPath._crudApi) 710 | .replace((/\{\{_schemaName\}\}/g), methodPath._schemaName) 711 | ), 1); 712 | // update cacheDict.methodPath 713 | local.swmg.cacheDict.methodPath[method.toUpperCase() + ' ' + path.replace( 714 | (/\{.*/), 715 | function (match0) { 716 | return match0.replace((/[^\/]/g), ''); 717 | } 718 | )] = JSON.stringify(methodPath); 719 | }); 720 | }); 721 | // merge tags 722 | tmp = {}; 723 | // update tags from options._tagDict 724 | Object.keys(options._tagDict || {}).forEach(function (key) { 725 | tmp[key] = options._tagDict[key]; 726 | tmp[key].name = key; 727 | }); 728 | // update tags from options.tags 729 | [local.swmg.swaggerJson.tags, options.tags].forEach(function (tags) { 730 | (tags || []).forEach(function (element) { 731 | tmp[element.name] = element; 732 | }); 733 | }); 734 | tmp = local.swmg.swaggerJson.tags = Object.keys(tmp).sort().map(function (key) { 735 | return tmp[key]; 736 | }); 737 | // update swaggerJson with options, with underscore keys removed 738 | local.utility2.objectSetOverride( 739 | local.swmg.swaggerJson, 740 | local.utility2.objectTraverse( 741 | // jsonCopy object to prevent side-effects 742 | local.utility2.jsonCopy(options), 743 | function (element) { 744 | if (element && typeof element === 'object') { 745 | Object.keys(element).forEach(function (key) { 746 | // security - remove underscore key 747 | if (key[0] === '_') { 748 | delete element[key]; 749 | } 750 | }); 751 | } 752 | } 753 | ), 754 | 2 755 | ); 756 | // restore tags 757 | local.swmg.swaggerJson.tags = tmp; 758 | // init properties from x-inheritList 759 | [0, 1, 2, 3].forEach(function () { 760 | Object.keys(local.swmg.swaggerJson.definitions).forEach(function (schema) { 761 | schema = local.swmg.swaggerJson.definitions[schema]; 762 | // jsonCopy object to prevent side-effects 763 | local.utility2.jsonCopy(schema['x-inheritList'] || []) 764 | .reverse() 765 | .forEach(function (element) { 766 | local.utility2.objectSetDefault(schema, { 767 | properties: 768 | local.swmg.schemaDereference(element.$ref).properties 769 | }, 2); 770 | }); 771 | }); 772 | }); 773 | // jsonCopy object to prevent side-effects 774 | local.swmg.swaggerJson = JSON.parse(local.utility2 775 | .jsonStringifyOrdered(local.utility2.jsonCopy(local.swmg.swaggerJson))); 776 | // validate swaggerJson 777 | local.swmg.validateBySwagger(local.utility2.objectSetDefault( 778 | local.utility2.jsonCopy(local.swmg.swaggerJson), 779 | local.swmg.swaggerJson$$Dummy, 780 | 2 781 | )); 782 | // init crud-api 783 | local.swmg.api = new local.swmg.SwaggerClient({ 784 | url: 'http://localhost:' + local.utility2.serverPortInit() 785 | }); 786 | local.swmg.api.buildFromSpec(local.utility2.jsonCopy(local.swmg.swaggerJson)); 787 | }; 788 | 789 | local.swmg.collectionCreate = function (schema, onError) { 790 | /* 791 | * this function will create a mongodb collection 792 | */ 793 | var collection, modeNext, onNext; 794 | modeNext = 0; 795 | onNext = function (error) { 796 | // validate no error occurred 797 | local.utility2.assert(!error, error); 798 | modeNext += 1; 799 | switch (modeNext) { 800 | case 1: 801 | collection = local.swmg.cacheDict.collection[schema._collectionName] = 802 | local.swmg.db.collection(schema._collectionName); 803 | // if $npm_config_mode_mongodb_readonly, then return this function 804 | if (local.utility2.envDict.npm_config_mode_mongodb_readonly || 805 | schema._collectionReadonly) { 806 | onError(); 807 | return; 808 | } 809 | // drop collection on init 810 | if (schema._collectionDrop) { 811 | console.warn('dropping collection ' + schema._collectionName + ' ...'); 812 | local.swmg.db.command({ drop: schema._collectionName }, function () { 813 | onNext(); 814 | }); 815 | return; 816 | } 817 | onNext(); 818 | return; 819 | case 2: 820 | // create collection 821 | if (schema._collectionCreate) { 822 | local.swmg.db.createCollection( 823 | schema._collectionName, 824 | schema._collectionCreate, 825 | function () { 826 | // convert existing collection to capped collection 827 | collection.isCapped(function (error, data) { 828 | if (!error && !data && schema._collectionCreate.capped) { 829 | local.swmg.db.command({ 830 | convertToCapped: schema._collectionName, 831 | size: schema._collectionCreate.size 832 | }, onNext); 833 | return; 834 | } 835 | onNext(); 836 | }); 837 | } 838 | ); 839 | return; 840 | } 841 | onNext(); 842 | return; 843 | case 3: 844 | // create index 845 | if (schema._collectionCreateIndexList) { 846 | local.swmg.db.command({ 847 | createIndexes: schema._collectionName, 848 | indexes: schema._collectionCreateIndexList 849 | }, onNext); 850 | return; 851 | } 852 | onNext(); 853 | return; 854 | case 4: 855 | // upsert fixtures 856 | local.swmg._crudApi({ 857 | collectionName: schema._collectionName, 858 | data: { 859 | body: local.utility2.jsonCopy(schema._collectionFixtureList || []), 860 | upsert: true 861 | }, 862 | operationId: 'crudReplaceMany', 863 | schemaName: schema._schemaName 864 | }, onNext); 865 | return; 866 | default: 867 | onError(); 868 | } 869 | }; 870 | onNext(); 871 | }; 872 | 873 | local.swmg.middlewareBodyParse = function (request, response, nextMiddleware) { 874 | /* 875 | * this function will parse the request-body 876 | */ 877 | // jslint-hack 878 | local.utility2.nop(response); 879 | local.utility2.testTryCatch(function () { 880 | if (request.swmgBodyParsed) { 881 | nextMiddleware(); 882 | return; 883 | } 884 | request.swmgBodyParsed = String(request.bodyRaw); 885 | switch ((/[^;]*/).exec(request.headers['content-type'] || '')[0]) { 886 | case 'application/x-www-form-urlencoded': 887 | request.swmgBodyParsed = 888 | local.url.parse('?' + request.swmgBodyParsed, true).query; 889 | break; 890 | default: 891 | try { 892 | request.swmgBodyParsed = JSON.parse(request.swmgBodyParsed); 893 | } catch (ignore) { 894 | } 895 | } 896 | nextMiddleware(); 897 | }, nextMiddleware); 898 | }; 899 | 900 | local.swmg.middlewareError = function (error, request, response) { 901 | /* 902 | * this function will handle errors according to http://jsonapi.org/format/#errors 903 | */ 904 | if (!error) { 905 | error = new Error('404 Not Found'); 906 | error.statusCode = 404; 907 | } 908 | local.swmg.onErrorJsonapi(null, function (error) { 909 | local.utility2.serverRespondHeadSet(request, response, error.statusCode, {}); 910 | // debug statusCode / method / url 911 | local.utility2.errorMessagePrepend(error, response.statusCode + ' ' + 912 | request.method + ' ' + request.url + '\n'); 913 | // print error.stack to stderr 914 | local.utility2.onErrorDefault(error); 915 | response.end(JSON.stringify(error)); 916 | })(error); 917 | }; 918 | 919 | local.swmg.middlewareSwagger = function (request, response, nextMiddleware) { 920 | /* 921 | * this function will run the main swagger-mongodb middleware 922 | */ 923 | var modeNext, onNext, tmp; 924 | modeNext = 0; 925 | onNext = function (error) { 926 | local.utility2.testTryCatch(function () { 927 | modeNext = error 928 | ? Infinity 929 | : modeNext + 1; 930 | switch (modeNext) { 931 | case 1: 932 | // if request.url is not prefixed with swaggerJson.basePath, 933 | // then default to nextMiddleware 934 | if (request.urlParsed.pathnameNormalized 935 | .indexOf(local.swmg.swaggerJson.basePath) !== 0) { 936 | modeNext = Infinity; 937 | onNext(); 938 | return; 939 | } 940 | // init swmgPathname 941 | request.swmgPathname = request.method + ' ' + 942 | request.urlParsed.pathnameNormalized 943 | .replace(local.swmg.swaggerJson.basePath, ''); 944 | switch (request.swmgPathname) { 945 | // serve swagger.json 946 | case 'GET /swagger.json': 947 | response.end(JSON.stringify(local.swmg.swaggerJson)); 948 | return; 949 | } 950 | // init swmgMethodPath 951 | while (true) { 952 | request.swmgMethodPath = 953 | local.swmg.cacheDict.methodPath[request.swmgPathname]; 954 | // if swmgMethodPath exists, then break and continue 955 | if (request.swmgMethodPath) { 956 | request.swmgMethodPath = JSON.parse(request.swmgMethodPath); 957 | onNext(); 958 | break; 959 | } 960 | // if cannot init swmgMethodPath, then default to nextMiddleware 961 | if (request.swmgPathname === request.swmgPathnameOld) { 962 | modeNext = Infinity; 963 | onNext(); 964 | break; 965 | } 966 | request.swmgPathnameOld = request.swmgPathname; 967 | request.swmgPathname = 968 | request.swmgPathname.replace((/\/[^\/]+?(\/*?)$/), '/$1'); 969 | } 970 | break; 971 | case 2: 972 | // init swmgParamDict 973 | request.swmgParamDict = {}; 974 | // parse path param 975 | tmp = request.urlParsed.pathname 976 | .replace(local.swmg.swaggerJson.basePath, '').split('/'); 977 | request.swmgMethodPath._path.split('/').forEach(function (key, ii) { 978 | if ((/^\{\S*?\}$/).test(key)) { 979 | request.swmgParamDict[key.slice(1, -1)] = 980 | decodeURIComponent(tmp[ii]); 981 | } 982 | }); 983 | request.swmgMethodPath.parameters.forEach(function (paramDef) { 984 | switch (paramDef.in) { 985 | // parse body param 986 | case 'body': 987 | request.swmgParamDict[paramDef.name] = 988 | request.swmgParamDict[paramDef.name] || 989 | request.swmgBodyParsed; 990 | break; 991 | // parse formData param 992 | case 'formData': 993 | request.swmgParamDict[paramDef.name] = 994 | request.swmgParamDict[paramDef.name] || 995 | request.swmgBodyParsed[paramDef.name]; 996 | break; 997 | // parse header param 998 | case 'header': 999 | request.swmgParamDict[paramDef.name] = 1000 | request.headers[paramDef.name.toLowerCase()]; 1001 | break; 1002 | // parse query param 1003 | case 'query': 1004 | request.swmgParamDict[paramDef.name] = 1005 | request.urlParsed.query[paramDef.name]; 1006 | break; 1007 | } 1008 | // init default param 1009 | request.swmgParamDict[paramDef.name] = 1010 | request.swmgParamDict[paramDef.name] || paramDef.default; 1011 | }); 1012 | onNext(); 1013 | break; 1014 | case 3: 1015 | // normalize params 1016 | local.swmg.normalizeParamDictSwagger(request 1017 | .swmgParamDict, request.swmgMethodPath); 1018 | // validate params 1019 | local.swmg.validateByParamDefList({ 1020 | data: request.swmgParamDict, 1021 | key: request.swmgPathname, 1022 | paramDefList: request.swmgMethodPath.parameters 1023 | }); 1024 | // run default crud-api 1025 | if (request.swmgMethodPath._crudApi) { 1026 | local.swmg._crudApi({ 1027 | collectionName: request.swmgMethodPath._collectionName, 1028 | data: request.swmgParamDict, 1029 | operationId: request.swmgMethodPath.operationId, 1030 | paramDefList: request.swmgMethodPath.parameters, 1031 | schemaName: request.swmgMethodPath._schemaName 1032 | }, function (error, data) { 1033 | if (!error && data) { 1034 | response.end(JSON.stringify(data)); 1035 | return; 1036 | } 1037 | onNext(error); 1038 | }); 1039 | return; 1040 | } 1041 | onNext(); 1042 | break; 1043 | default: 1044 | nextMiddleware(error); 1045 | } 1046 | }, nextMiddleware); 1047 | }; 1048 | onNext(); 1049 | }; 1050 | break; 1051 | } 1052 | switch (local.modeJs) { 1053 | 1054 | 1055 | 1056 | // run browser js-env code 1057 | case 'browser': 1058 | // export swagger-mongodb 1059 | window.swmg = local.swmg; 1060 | // require modules 1061 | local.utility2 = window.utility2; 1062 | break; 1063 | 1064 | 1065 | 1066 | // run node js-env code 1067 | case 'node': 1068 | // export swagger-mongodb 1069 | module.exports = local.swmg; 1070 | module.exports.__dirname = __dirname; 1071 | // require modules 1072 | local.fs = require('fs'); 1073 | local.mongodb = require('mongodb-minimal'); 1074 | local.path = require('path'); 1075 | local.swagger_tools = require('swagger-ui-lite/swagger-tools-standalone-min.js'); 1076 | local.swagger_ui_lite = require('swagger-ui-lite'); 1077 | local.url = require('url'); 1078 | local.utility2 = require('utility2'); 1079 | local.vm = require('vm'); 1080 | // init swaggerJson 1081 | local.swmg.swaggerJson = { 1082 | basePath: local.utility2.envDict.npm_config_mode_api_prefix || '/api/v0', 1083 | definitions: { 1084 | Array: { items: {}, type: 'array' }, 1085 | // http://jsonapi.org/format/#errors 1086 | JsonapiError: { 1087 | properties: { 1088 | code: { type: 'string' }, 1089 | detail: { type: 'string' }, 1090 | id: { type: 'string' }, 1091 | message: { type: 'string' } 1092 | } 1093 | }, 1094 | // http://jsonapi.org/format/#document-structure-resource-objects 1095 | JsonapiResource: { 1096 | properties: { 1097 | id: { type: 'string' } 1098 | } 1099 | }, 1100 | // http://jsonapi.org/format/#document-structure-top-level 1101 | JsonapiResponse: { 1102 | properties: { 1103 | data: { 1104 | items: { $ref: '#/definitions/JsonapiResource' }, 1105 | type: 'array' 1106 | }, 1107 | errors: { 1108 | items: { $ref: '#/definitions/JsonapiError' }, 1109 | type: 'array' 1110 | }, 1111 | meta: { $ref: '#/definitions/Object' }, 1112 | statusCode: { type: 'integer' } 1113 | } 1114 | }, 1115 | // http://docs.mongodb.org/manual/reference/operator/aggregation/ 1116 | MongodbAggregationPipeline: { 1117 | properties: { 1118 | $group: { default: { "_id": "all", "count": { "$sum": 1 } } } 1119 | } 1120 | }, 1121 | Object: { type: 'object' }, 1122 | Undefined: {} 1123 | }, 1124 | info: { 1125 | description: 'demo of swagger-mongodb crud-api', 1126 | title: 'swagger-mongodb api', 1127 | version: '0' 1128 | }, 1129 | paths: {}, 1130 | swagger: '2.0', 1131 | tags: [] 1132 | }; 1133 | // hack - init swaggerJson$$Dummy to pass validation warnings for auto-created schemas 1134 | local.swmg.swaggerJson$$Dummy = { 1135 | definitions: { 1136 | $$Dummy: { 1137 | properties: { 1138 | propArray: { 1139 | items: { $ref: '#/definitions/Array' }, 1140 | type: 'array' 1141 | }, 1142 | propMongodbAggregationPipeline: { 1143 | items: { $ref: '#/definitions/MongodbAggregationPipeline' }, 1144 | type: 'array' 1145 | }, 1146 | propObject: { items: { $ref: '#/definitions/Object' } }, 1147 | propUndefined: { items: { $ref: '#/definitions/Undefined' } } 1148 | } 1149 | }, 1150 | JsonapiResponse$$Dummy: { 1151 | properties: { data: { 1152 | items: { $ref: '#/definitions/$$Dummy' }, 1153 | type: 'array' 1154 | } }, 1155 | 'x-inheritList': [{ $ref: '#/definitions/JsonapiResponse' }] 1156 | } 1157 | }, 1158 | paths: { '/$$Dummy': { get: { 1159 | responses: { 1160 | 200: { 1161 | description: '', 1162 | schema: { $ref: '#/definitions/JsonapiResponse$$Dummy' } 1163 | } 1164 | } 1165 | } } } 1166 | }; 1167 | // init assets 1168 | local.utility2.cacheDict.assets['/assets/swagger-mongodb.js'] = 1169 | local.utility2.istanbul_lite.instrumentInPackage( 1170 | local.fs.readFileSync(__filename, 'utf8'), 1171 | __filename, 1172 | 'swagger-mongodb' 1173 | ); 1174 | local.utility2.cacheDict.assets['/assets/swagger-ui.html'] = local.fs 1175 | .readFileSync( 1176 | local.swagger_ui_lite.__dirname + '/swagger-ui.html', 1177 | 'utf8' 1178 | ) 1179 | // swagger-hack - add extra assets 1180 | .replace( 1181 | "", 1182 | "" + 1183 | '' + 1184 | '' 1185 | ) 1186 | // swagger-hack - update swagger.json url 1187 | .replace( 1188 | 'http://petstore.swagger.io/v2/swagger.json', 1189 | local.swmg.swaggerJson.basePath + '/swagger.json' 1190 | ); 1191 | local.utility2.cacheDict.assets['/assets/swagger-ui.rollup.css'] = local.fs 1192 | .readFileSync( 1193 | local.swagger_ui_lite.__dirname + '/swagger-ui.rollup.css', 1194 | 'utf8' 1195 | ); 1196 | local.utility2.cacheDict.assets['/assets/swagger-ui.rollup.js'] = local.fs 1197 | .readFileSync( 1198 | local.swagger_ui_lite.__dirname + '/swagger-ui.rollup.js', 1199 | 'utf8' 1200 | ) 1201 | // swagger-hack - disable underscore-min.map 1202 | .replace('//# sourceMappingURL=underscore-min.map', '') 1203 | // swagger-hack - save swaggerJson 1204 | .replace( 1205 | 'this.apis = {};', 1206 | 'this.apis = {}; this.swaggerJson = JSON.parse(JSON.stringify(response));' 1207 | ) 1208 | // swagger-hack - disable missingParams validation handling 1209 | .replace( 1210 | 'var missingParams = this.getMissingParams(args);', 1211 | 'var missingParams = [];' 1212 | ) 1213 | // swagger-hack - add modeErroData and validation handling 1214 | .replace('new SwaggerHttp().execute(obj, opts);', 'if (opts.modeErrorData) {' + 1215 | 'var onError = success;' + 1216 | 'error = function (error) {' + 1217 | 'error = error.obj || error;' + 1218 | 'error = error.data || error;' + 1219 | 'try { error = JSON.parse(error) } catch (ignore) {}' + 1220 | 'if (typeof error === "string") {' + 1221 | 'error = { message: error };' + 1222 | '}' + 1223 | 'onError(error);' + 1224 | '};' + 1225 | 'success = function (data) { onError(null, data); };' + 1226 | '}' + 1227 | 'try {' + 1228 | 'if (window.swmg) {' + 1229 | 'window.swmg.validateByParamDefList({' + 1230 | 'data: window.swmg.normalizeParamDictSwagger(' + 1231 | 'JSON.parse(JSON.stringify(args)),' + 1232 | 'this' + 1233 | '),' + 1234 | 'key: this.operation.operationId,' + 1235 | 'paramDefList: this.parameters' + 1236 | '});' + 1237 | '}' + 1238 | '} catch (errorCaught) {' + 1239 | 'window.swmg.onErrorJsonapi(null, function (error) {' + 1240 | 'console.error(error);' + 1241 | 'obj.on.error({' + 1242 | 'data: JSON.stringify(error),' + 1243 | 'headers: { "Content-Type": "application/json" }' + 1244 | '});' + 1245 | '})(errorCaught);' + 1246 | 'return;' + 1247 | '}' + 1248 | 'new SwaggerHttp().execute(obj, opts);') 1249 | // swagger-hack - handle json error 1250 | .replace('new_err.status = res.status;', 'new_err.status = res.status;' + 1251 | 'if (res.body && res.body.message && res.body.stack) { err = res.body; }') 1252 | // swagger-hack - disable online validation 1253 | .replace("if ('validatorUrl' in opts.swaggerOptions) {", "if (true) {"); 1254 | local.utility2.cacheDict.assets['/assets/swagger-ui.explorer_icons.png'] = local.fs 1255 | .readFileSync(local.swagger_ui_lite.__dirname + 1256 | '/swagger-ui.explorer_icons.png'); 1257 | local.utility2.cacheDict.assets['/assets/swagger-ui.logo_small.png'] = local.fs 1258 | .readFileSync(local.swagger_ui_lite.__dirname + 1259 | '/swagger-ui.logo_small.png'); 1260 | local.utility2.cacheDict.assets['/assets/swagger-ui.throbber.gif'] = local.fs 1261 | .readFileSync(local.swagger_ui_lite.__dirname + 1262 | '/swagger-ui.throbber.gif'); 1263 | // init SwaggerClient 1264 | (function () { 1265 | local.Handlebars = { 1266 | registerHelper: local.utility2.nop, 1267 | template: local.utility2.nop 1268 | }; 1269 | local.XMLHttpRequest = function () { 1270 | var self; 1271 | self = this; 1272 | self.headers = {}; 1273 | }; 1274 | local.XMLHttpRequest.prototype.onreadystatechange = local.utility2.nop; 1275 | local.XMLHttpRequest.prototype.open = function (method, url) { 1276 | this.method = method; 1277 | this.url = url; 1278 | }; 1279 | local.XMLHttpRequest.prototype.send = function (data) { 1280 | var self; 1281 | self = this; 1282 | self.data = data; 1283 | self.xhr = self; 1284 | local.utility2.ajax(self, local.utility2.onErrorDefault); 1285 | }; 1286 | local.XMLHttpRequest.prototype.setRequestHeader = function (key, value) { 1287 | this.headers[key.toLowerCase()] = value; 1288 | }; 1289 | local.$ = local.utility2.nop; 1290 | local.console = console; 1291 | local.clearTimeout = clearTimeout; 1292 | local.location = {}; 1293 | local.setTimeout = setTimeout; 1294 | local.window = local; 1295 | local.vm.runInNewContext( 1296 | local.utility2.cacheDict.assets['/assets/swagger-ui.rollup.js'] 1297 | // swagger-hack - remove browser js-env code 1298 | .replace((/[\S\s]+?\/underscore-min\.js \*\//), function (match0) { 1299 | return match0.replace((/\S+/g), ''); 1300 | }), 1301 | local, 1302 | __dirname + '/swagger-ui.rollup.js' 1303 | ); 1304 | local.swmg.SwaggerClient = local.SwaggerClient; 1305 | local.swmg.SwaggerUi = local.SwaggerUi; 1306 | }()); 1307 | // init api 1308 | local.swmg.apiUpdate({}); 1309 | break; 1310 | } 1311 | }((function () { 1312 | 'use strict'; 1313 | var local; 1314 | 1315 | 1316 | 1317 | // run shared js-env code 1318 | (function () { 1319 | // init local 1320 | local = {}; 1321 | // init js-env 1322 | local.modeJs = (function () { 1323 | try { 1324 | return module.exports && 1325 | typeof process.versions.node === 'string' && 1326 | typeof require('http').createServer === 'function' && 1327 | 'node'; 1328 | } catch (errorCaughtNode) { 1329 | return typeof navigator.userAgent === 'string' && 1330 | typeof document.querySelector('body') === 'object' && 1331 | 'browser'; 1332 | } 1333 | }()); 1334 | // init global 1335 | local.global = local.modeJs === 'browser' 1336 | ? window 1337 | : global; 1338 | // init utility2 1339 | local.utility2 = local.modeJs === 'browser' 1340 | ? window.utility2 1341 | : require('utility2'); 1342 | // init swagger-mongodb 1343 | local.swmg = { cacheDict: { collection: {}, methodPath: {} }, local: local }; 1344 | /* jslint-indent-begin 8 */ 1345 | /*jslint maxlen: 104*/ 1346 | // init methodPathCrudDefault 1347 | local.swmg.cacheDict.methodPathCrudDefault = { 1348 | crudAggregateMany: { 1349 | _collectionName: '{{_collectionName}}', 1350 | _crudApi: '{{_crudApi}}', 1351 | _method: 'post', 1352 | _path: '/{{_crudApi}}/crudAggregateMany', 1353 | operationId: 'crudAggregateMany', 1354 | parameters: [{ 1355 | description: 'mongodb aggregation array', 1356 | in: 'body', 1357 | name: 'body', 1358 | required: true, 1359 | schema: { 1360 | items: { $ref: '#/definitions/MongodbAggregationPipeline' }, 1361 | type: 'array' 1362 | } 1363 | }], 1364 | summary: 'aggregate many {{_schemaName}} objects', 1365 | tags: ['{{_crudApi}}'] 1366 | }, 1367 | crudCountByQueryOne: { 1368 | _collectionName: '{{_collectionName}}', 1369 | _crudApi: '{{_crudApi}}', 1370 | _method: 'get', 1371 | _path: '/{{_crudApi}}/crudCountByQueryOne', 1372 | operationId: 'crudCountByQueryOne', 1373 | parameters: [{ 1374 | default: '{}', 1375 | description: 'mongodb query param', 1376 | format: 'json', 1377 | in: 'query', 1378 | name: 'query', 1379 | type: 'string' 1380 | }], 1381 | summary: 'count many {{_schemaName}} objects by query', 1382 | tags: ['{{_crudApi}}'] 1383 | }, 1384 | crudCreateMany: { 1385 | _collectionName: '{{_collectionName}}', 1386 | _crudApi: '{{_crudApi}}', 1387 | _method: 'put', 1388 | _path: '/{{_crudApi}}/crudCreateMany', 1389 | operationId: 'crudCreateMany', 1390 | parameters: [{ 1391 | description: '{{_schemaName}} object list', 1392 | in: 'body', 1393 | name: 'body', 1394 | required: true, 1395 | schema: { items: { $ref: '#/definitions/{{_schemaName}}' }, type: 'array' } 1396 | }], 1397 | summary: 'create many {{_schemaName}} objects in ordered list', 1398 | tags: ['{{_crudApi}}'] 1399 | }, 1400 | crudCreateOne: { 1401 | _collectionName: '{{_collectionName}}', 1402 | _crudApi: '{{_crudApi}}', 1403 | _method: 'post', 1404 | _path: '/{{_crudApi}}/crudCreateOne', 1405 | operationId: 'crudCreateOne', 1406 | parameters: [{ 1407 | description: '{{_schemaName}} object', 1408 | in: 'body', 1409 | name: 'body', 1410 | required: true, 1411 | schema: { $ref: '#/definitions/{{_schemaName}}' } 1412 | }], 1413 | responses: { 1414 | 200: { 1415 | description: '200 ok - http://jsonapi.org/format/#document-structure-top-level', 1416 | schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } 1417 | } 1418 | }, 1419 | summary: 'create one {{_schemaName}} object', 1420 | tags: ['{{_crudApi}}'] 1421 | }, 1422 | crudDeleteByIdOne: { 1423 | _collectionName: '{{_collectionName}}', 1424 | _crudApi: '{{_crudApi}}', 1425 | _method: 'delete', 1426 | _path: '/{{_crudApi}}/crudDeleteByIdOne/{id}', 1427 | operationId: 'crudDeleteByIdOne', 1428 | parameters: [{ 1429 | description: '{{_schemaName}} id', 1430 | in: 'path', 1431 | name: 'id', 1432 | required: true, 1433 | type: 'string' 1434 | }], 1435 | summary: 'delete one {{_schemaName}} object by id', 1436 | tags: ['{{_crudApi}}'] 1437 | }, 1438 | crudDeleteByQueryMany: { 1439 | _collectionName: '{{_collectionName}}', 1440 | _crudApi: '{{_crudApi}}', 1441 | _method: 'delete', 1442 | _path: '/{{_crudApi}}/crudDeleteByQueryMany', 1443 | operationId: 'crudDeleteByQueryMany', 1444 | parameters: [{ 1445 | default: '{"_id":{"$in":["id0","id1"]}}', 1446 | description: 'mongodb query param', 1447 | format: 'json', 1448 | in: 'query', 1449 | name: 'query', 1450 | required: true, 1451 | type: 'string' 1452 | }], 1453 | responses: { 1454 | 200: { 1455 | description: '200 ok - http://jsonapi.org/format/#document-structure-top-level', 1456 | schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } 1457 | } 1458 | }, 1459 | summary: 'get many {{_schemaName}} objects by query', 1460 | tags: ['{{_crudApi}}'] 1461 | }, 1462 | crudExistsByIdOne: { 1463 | _collectionName: '{{_collectionName}}', 1464 | _crudApi: '{{_crudApi}}', 1465 | _method: 'get', 1466 | _path: '/{{_crudApi}}/crudExistsByIdOne/{id}', 1467 | operationId: 'crudExistsByIdOne', 1468 | parameters: [{ 1469 | description: '{{_schemaName}} id', 1470 | in: 'path', 1471 | name: 'id', 1472 | required: true, 1473 | type: 'string' 1474 | }], 1475 | summary: 'check if one {{_schemaName}} object exists by id', 1476 | tags: ['{{_crudApi}}'] 1477 | }, 1478 | crudGetByIdOne: { 1479 | _collectionName: '{{_collectionName}}', 1480 | _crudApi: '{{_crudApi}}', 1481 | _method: 'get', 1482 | _path: '/{{_crudApi}}/crudGetByIdOne/{id}', 1483 | operationId: 'crudGetByIdOne', 1484 | parameters: [{ 1485 | description: '{{_schemaName}} id', 1486 | in: 'path', 1487 | name: 'id', 1488 | required: true, 1489 | type: 'string' 1490 | }], 1491 | responses: { 1492 | 200: { 1493 | description: '200 ok - http://jsonapi.org/format/#document-structure-top-level', 1494 | schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } 1495 | } 1496 | }, 1497 | summary: 'get one {{_schemaName}} object by id', 1498 | tags: ['{{_crudApi}}'] 1499 | }, 1500 | crudGetByQueryMany: { 1501 | _collectionName: '{{_collectionName}}', 1502 | _crudApi: '{{_crudApi}}', 1503 | _method: 'get', 1504 | _path: '/{{_crudApi}}/crudGetByQueryMany', 1505 | operationId: 'crudGetByQueryMany', 1506 | parameters: [{ 1507 | default: '{}', 1508 | description: 'mongodb query param', 1509 | format: 'json', 1510 | in: 'query', 1511 | name: 'query', 1512 | required: true, 1513 | type: 'string' 1514 | }, { 1515 | default: '{}', 1516 | description: 'mongodb fields param', 1517 | format: 'json', 1518 | in: 'query', 1519 | name: 'fields', 1520 | type: 'string' 1521 | }, { 1522 | default: '{}', 1523 | description: 'mongodb cursor hint param', 1524 | format: 'json', 1525 | in: 'query', 1526 | name: 'hint', 1527 | type: 'string' 1528 | }, { 1529 | default: 10, 1530 | description: 'mongodb cursor limit param', 1531 | in: 'query', 1532 | name: 'limit', 1533 | required: true, 1534 | type: 'integer' 1535 | }, { 1536 | default: 0, 1537 | description: 'mongodb cursor skip param', 1538 | in: 'query', 1539 | name: 'skip', 1540 | type: 'integer' 1541 | }, { 1542 | default: '{"_timeModified":-1}', 1543 | description: 'mongodb cursor sort param', 1544 | format: 'json', 1545 | in: 'query', 1546 | name: 'sort', 1547 | type: 'string' 1548 | }], 1549 | responses: { 1550 | 200: { 1551 | description: '200 ok - http://jsonapi.org/format/#document-structure-top-level', 1552 | schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } 1553 | } 1554 | }, 1555 | summary: 'get many {{_schemaName}} objects by query', 1556 | tags: ['{{_crudApi}}'] 1557 | }, 1558 | crudGetDistinctValueByPropertyMany: { 1559 | _collectionName: '{{_collectionName}}', 1560 | _crudApi: '{{_crudApi}}', 1561 | _method: 'get', 1562 | _path: '/{{_crudApi}}/crudGetDistinctValueByPropertyMany', 1563 | operationId: 'crudGetDistinctValueByPropertyMany', 1564 | parameters: [{ 1565 | default: 'id', 1566 | description: 'mongodb query param', 1567 | in: 'query', 1568 | name: 'field', 1569 | required: true, 1570 | type: 'string' 1571 | }, { 1572 | default: '{}', 1573 | description: 'mongodb query param', 1574 | format: 'json', 1575 | in: 'query', 1576 | name: 'query', 1577 | type: 'string' 1578 | }], 1579 | summary: 'get many distinct {{_schemaName}} values by field', 1580 | tags: ['{{_crudApi}}'] 1581 | }, 1582 | crudReplaceMany: { 1583 | _collectionName: '{{_collectionName}}', 1584 | _crudApi: '{{_crudApi}}', 1585 | _method: 'put', 1586 | _path: '/{{_crudApi}}/crudReplaceMany', 1587 | operationId: 'crudReplaceMany', 1588 | parameters: [{ 1589 | description: '{{_schemaName}} object list', 1590 | in: 'body', 1591 | name: 'body', 1592 | required: true, 1593 | schema: { items: { $ref: '#/definitions/{{_schemaName}}' }, type: 'array' } 1594 | }, { 1595 | default: false, 1596 | description: 'upsert {{_schemaName}} object if it does not exist', 1597 | in: 'query', 1598 | name: 'upsert', 1599 | type: 'boolean' 1600 | }], 1601 | summary: 'replace many {{_schemaName}} objects in ordered list', 1602 | tags: ['{{_crudApi}}'] 1603 | }, 1604 | crudReplaceOne: { 1605 | _collectionName: '{{_collectionName}}', 1606 | _crudApi: '{{_crudApi}}', 1607 | _method: 'put', 1608 | _path: '/{{_crudApi}}/crudReplaceOne', 1609 | operationId: 'crudReplaceOne', 1610 | parameters: [{ 1611 | description: '{{_schemaName}} object', 1612 | in: 'body', 1613 | name: 'body', 1614 | required: true, 1615 | schema: { $ref: '#/definitions/{{_schemaName}}' } 1616 | }, { 1617 | default: false, 1618 | description: 'upsert {{_schemaName}} object if it does not exist', 1619 | in: 'query', 1620 | name: 'upsert', 1621 | type: 'boolean' 1622 | }], 1623 | responses: { 1624 | 200: { 1625 | description: '200 ok - http://jsonapi.org/format/#document-structure-top-level', 1626 | schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } 1627 | } 1628 | }, 1629 | summary: 'replace one {{_schemaName}} object', 1630 | tags: ['{{_crudApi}}'] 1631 | }, 1632 | crudUpdateOne: { 1633 | _collectionName: '{{_collectionName}}', 1634 | _crudApi: '{{_crudApi}}', 1635 | _method: 'put', 1636 | _path: '/{{_crudApi}}/crudUpdateOne', 1637 | operationId: 'crudUpdateOne', 1638 | parameters: [{ 1639 | description: '{{_schemaName}} object with no validation check', 1640 | in: 'body', 1641 | name: 'body', 1642 | required: true, 1643 | schema: { $ref: '#/definitions/Object' } 1644 | }, { 1645 | default: false, 1646 | description: 'upsert {{_schemaName}} object if it does not exist', 1647 | in: 'query', 1648 | name: 'upsert', 1649 | type: 'boolean' 1650 | }], 1651 | responses: { 1652 | 200: { 1653 | description: '200 ok - http://jsonapi.org/format/#document-structure-top-level', 1654 | schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } 1655 | } 1656 | }, 1657 | summary: 'update one {{_schemaName}} object', 1658 | tags: ['{{_crudApi}}'] 1659 | } 1660 | }; 1661 | /* jslint-indent-end */ 1662 | Object.keys(local.swmg.cacheDict.methodPathCrudDefault).forEach(function (key) { 1663 | local.swmg.cacheDict.methodPathCrudDefault[key] = 1664 | JSON.stringify(local.swmg.cacheDict.methodPathCrudDefault[key]); 1665 | }); 1666 | }()); 1667 | return local; 1668 | }()))); 1669 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "kai zhu ", 3 | "bin": { "swagger-mongodb": "index.js" }, 4 | "dependencies": { 5 | "mongodb-minimal": "2015.8.1", 6 | "swagger-ui-lite": "2015.6.2", 7 | "utility2": "~2015.8.5" 8 | }, 9 | "description": "lightweight swagger-ui crud-middleware backed by mongodb", 10 | "devDependencies": { 11 | "phantomjs-lite": "2015.7.1" 12 | }, 13 | "engines": { "node": ">=0.10 <=0.12" }, 14 | "keywords": [ 15 | "api", 16 | "browser", 17 | "cms", "crud", 18 | "mongo", "mongodb", 19 | "swagger", "swagger-ui", 20 | "web" 21 | ], 22 | "license": "MIT", 23 | "name": "swagger-mongodb", 24 | "os": ["darwin", "linux"], 25 | "repository" : { 26 | "type" : "git", 27 | "url" : "https://github.com/kaizhu256/node-swagger-mongodb.git" 28 | }, 29 | "scripts": { 30 | "build-ci": "node_modules/.bin/utility2 shRun shReadmeBuild", 31 | "build-doc": "node_modules/.bin/utility2 shRun shReadmeExportPackageJson && node_modules/.bin/utility2 shRun shDocApiCreate \"{ exampleFileList:['example.js','test.js','index.js'], moduleDict:{'swagger-mongodb':{aliasList:['swmg'],exports:require('./index.js')}} }\"", 32 | "start": "npm_config_mode_auto_restart=1 node_modules/.bin/utility2 shRun node test.js", 33 | "test": "node_modules/.bin/utility2 shRun shReadmeExportPackageJson && node_modules/.bin/utility2 test test.js" 34 | }, 35 | "version": "2015.8.3" 36 | } -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /*jslint 2 | browser: true, 3 | maxerr: 8, 4 | maxlen: 96, 5 | node: true, 6 | nomen: true, 7 | regexp: true, 8 | stupid: true 9 | */ 10 | (function (local) { 11 | 'use strict'; 12 | 13 | 14 | 15 | // run shared js-env code 16 | (function () { 17 | local.optionsId = function (options) { 18 | /* 19 | * this function will init petstore id's 20 | */ 21 | return local.utility2.objectSetDefault(options, { 22 | name: options.id, 23 | orderId: options.id, 24 | petId: options.id, 25 | photoUrls: [options.id], 26 | status: options.id, 27 | tags: [{ id: options.id, name: options.id }], 28 | username: options.id 29 | }); 30 | }; 31 | 32 | // init tests 33 | local.testCase_ajax_404 = function (options, onError) { 34 | /* 35 | * this function will test ajax's "404 not found" handling-behavior 36 | */ 37 | // jslint-hack 38 | local.utility2.nop(options); 39 | // test '/_test/undefined' 40 | local.utility2.ajax({ url: '/_test/undefined' }, function (error) { 41 | local.utility2.testTryCatch(function () { 42 | // validate error occurred 43 | local.utility2.assert(error, error); 44 | // validate 404 http statusCode 45 | local.utility2.assert(error.statusCode === 404, error.statusCode); 46 | onError(); 47 | }, onError); 48 | }); 49 | }; 50 | 51 | local.testCase_crudCreateMany_default = function (options, onError) { 52 | /* 53 | * this function will test crudCreateMany's default handling-behavior 54 | */ 55 | var api, modeNext, onError2, onNext, onParallel; 56 | if (!options) { 57 | onParallel = local.utility2.onParallel(onError); 58 | onParallel.counter += 1; 59 | [ 60 | '', 61 | 'pet', 62 | 'store', 63 | 'user' 64 | ].forEach(function (api) { 65 | // test create handling-behavior 66 | [ 67 | 'crudCreateMany', 68 | 'crudReplaceMany', 69 | 'createUsersWithArrayInput', 70 | 'createUsersWithListInput' 71 | ].forEach(function (operationId) { 72 | onParallel.counter += 1; 73 | local.testCase_crudCreateMany_default({ 74 | api: api, 75 | id: 'test_' + local.utility2.uuidTime(), 76 | operationId: operationId 77 | }, onParallel); 78 | }); 79 | // test upsert handling-behavior 80 | [ 81 | 'crudReplaceMany' 82 | ].forEach(function (operationId) { 83 | onParallel.counter += 1; 84 | local.testCase_crudCreateMany_default({ 85 | api: api, 86 | id: 'test_' + local.utility2.uuidTime(), 87 | operationId: operationId, 88 | upsert: true 89 | }, onParallel); 90 | }); 91 | }); 92 | onParallel(); 93 | return; 94 | } 95 | onError2 = function (error, data) { 96 | if (error) { 97 | // update error.message 98 | local.utility2.errorMessagePrepend(error, options.api + '.' + 99 | options.operationId + ' - '); 100 | } 101 | onError(error, data); 102 | }; 103 | modeNext = 0; 104 | onNext = function (error, data) { 105 | local.utility2.testTryCatch(function () { 106 | modeNext = error 107 | ? Infinity 108 | : modeNext + 1; 109 | switch (modeNext) { 110 | case 1: 111 | // init options 112 | options.body = [ 113 | local.optionsId({ id: options.id + '0', propRequired: true }), 114 | local.optionsId({ id: options.id + '1', propRequired: true }), 115 | // test default id handling-behavior 116 | local.optionsId({ propRequired: true }) 117 | ]; 118 | options.modeErrorData = true; 119 | // init api 120 | options.api = options.api || '_test'; 121 | api = local.swmg.api[options.api]; 122 | // if api does not have the operation, then skip testCase 123 | if (!api[options.operationId]) { 124 | onError2(); 125 | return; 126 | } 127 | // test crudCreateMany's default handling-behavior 128 | data = local.utility2.jsonCopy(options); 129 | api[options.operationId](data, options, onNext); 130 | break; 131 | case 2: 132 | // validate object 133 | data = data.obj.data; 134 | local.utility2.assert(data.length === options.body.length, data.length); 135 | switch (options.operationId) { 136 | case 'crudReplaceMany': 137 | if (!options.upsert) { 138 | local.utility2.assert(data.every(function (element) { 139 | return !element; 140 | }), data); 141 | modeNext = Infinity; 142 | onNext(); 143 | return; 144 | } 145 | break; 146 | } 147 | local.utility2.assert(data.every(function (element) { 148 | return element; 149 | }), data); 150 | options.body = data; 151 | // init query 152 | options.query = JSON.stringify({ id: { $in: options.body.map( 153 | function (element) { 154 | return element.id; 155 | } 156 | ) } }); 157 | // validate object 158 | api.crudGetByQueryMany({ 159 | limit: 8, 160 | query: options.query 161 | }, options, onNext); 162 | break; 163 | case 3: 164 | // validate object 165 | data = data.obj.data; 166 | local.utility2.assert(data.length === options.body.length, data.length); 167 | local.utility2.assert(data.every(function (element) { 168 | return element; 169 | }), data); 170 | onNext(); 171 | break; 172 | default: 173 | // validate no error occurred 174 | local.utility2.assert(!error, error); 175 | // remove object by query 176 | local.testCase_crudDeleteByQueryMany_default(options, onError2); 177 | } 178 | }, onError2); 179 | }; 180 | onNext(options && options.error); 181 | }; 182 | 183 | local.testCase_crudCreateXxx_default = function (options, onError) { 184 | /* 185 | * this function will test crudCreateXxx's default handling-behavior 186 | */ 187 | var api, modeNext, onError2, onNext, onParallel; 188 | if (!options) { 189 | onParallel = local.utility2.onParallel(onError); 190 | onParallel.counter += 1; 191 | [ 192 | '', 193 | 'pet', 194 | 'store', 195 | 'user' 196 | ].forEach(function (api) { 197 | // test create handling-behavior 198 | [ 199 | 'crudCreateOne', 200 | 'crudReplaceOne', 201 | 'crudUpdateOne', 202 | 'updatePet', 203 | 'updatePetWithForm', 204 | 'updateUser' 205 | ].forEach(function (operationId) { 206 | onParallel.counter += 1; 207 | local.testCase_crudCreateXxx_default({ 208 | api: api, 209 | id: 'test_' + local.utility2.uuidTime(), 210 | operationId: operationId 211 | }, onParallel); 212 | }); 213 | // test upsert handling-behavior 214 | [ 215 | 'crudReplaceOne', 216 | 'crudUpdateOne' 217 | ].forEach(function (operationId) { 218 | onParallel.counter += 1; 219 | local.testCase_crudCreateXxx_default({ 220 | api: api, 221 | id: 'test_' + local.utility2.uuidTime(), 222 | operationId: operationId, 223 | upsert: true 224 | }, onParallel); 225 | }); 226 | }); 227 | onParallel(); 228 | return; 229 | } 230 | onError2 = function (error, data) { 231 | if (error) { 232 | // update error.message 233 | local.utility2.errorMessagePrepend(error, options.api + '.' + 234 | options.operationId + ' - '); 235 | } 236 | onError(error, data); 237 | }; 238 | modeNext = 0; 239 | onNext = function (error, data) { 240 | local.utility2.testTryCatch(function () { 241 | modeNext += 1; 242 | switch (modeNext) { 243 | case 1: 244 | // validate no error occurred 245 | local.utility2.assert(!error, error); 246 | // init options 247 | options.body = local.optionsId({ 248 | id: options.id, 249 | propExtra: 'hello', 250 | propRequired: true 251 | }); 252 | options.modeErrorData = true; 253 | // init api 254 | options.api = options.api || '_test'; 255 | api = local.swmg.api[options.api]; 256 | // if api does not have the operation, then skip testCase 257 | if (!api[options.operationId]) { 258 | onError2(); 259 | return; 260 | } 261 | // get object 262 | api.crudGetByIdOne(local.optionsId({ 263 | id: options.id 264 | }), options, onNext); 265 | break; 266 | case 2: 267 | // validate no error occurred 268 | local.utility2.assert(!error, error); 269 | // validate object does not exist 270 | local.utility2.assert(data.obj.data[0] === null, data.obj.data[0]); 271 | // test createXxx's default handling-behavior 272 | data = local.utility2.jsonCopy(options); 273 | api[options.operationId](local.optionsId(data), options, onNext); 274 | break; 275 | case 3: 276 | switch (options.operationId) { 277 | case 'crudReplaceOne': 278 | case 'crudUpdateOne': 279 | if (!options.upsert) { 280 | // validate error occurred 281 | local.utility2.assert(error, error); 282 | modeNext = Infinity; 283 | onNext(); 284 | return; 285 | } 286 | break; 287 | default: 288 | // validate no error occurred 289 | local.utility2.assert(!error, error); 290 | } 291 | // validate object 292 | data = data.obj.data[0]; 293 | options._timeCreated = data._timeCreated; 294 | options._timeModified = data._timeModified; 295 | local.utility2.assert(data.name === options.id, data.name); 296 | local.utility2.assert(data.status === options.id, data.status); 297 | switch (options.operationId) { 298 | case 'updatePetWithForm': 299 | local.utility2 300 | .assert(data.propExtra === undefined, data.propExtra); 301 | local.utility2 302 | .assert(data.propRequired === undefined, data.propRequired); 303 | break; 304 | default: 305 | local.utility2.assert(data.propExtra === 'hello', data.propExtra); 306 | local.utility2 307 | .assert(data.propRequired === true, data.propRequired); 308 | } 309 | // get object 310 | api.crudGetByIdOne(local.optionsId({ 311 | id: options.id 312 | }), options, onNext); 313 | break; 314 | case 4: 315 | // validate no error occurred 316 | local.utility2.assert(!error, error); 317 | // validate object 318 | data = data.obj.data[0]; 319 | local.utility2.assert( 320 | data._timeCreated === options._timeCreated, 321 | [data._timeCreated, options._timeCreated] 322 | ); 323 | local.utility2.assert( 324 | data._timeModified === options._timeModified, 325 | [data._timeModified, options._timeModified] 326 | ); 327 | local.utility2.assert(data.name === options.id, data.name); 328 | local.utility2.assert(data.status === options.id, data.status); 329 | switch (options.operationId) { 330 | case 'updatePetWithForm': 331 | local.utility2 332 | .assert(data.propExtra === undefined, data.propExtra); 333 | local.utility2 334 | .assert(data.propRequired === undefined, data.propRequired); 335 | break; 336 | default: 337 | local.utility2.assert(data.propExtra === 'hello', data.propExtra); 338 | local.utility2 339 | .assert(data.propRequired === true, data.propRequired); 340 | } 341 | switch (options.operationId) { 342 | case 'crudCreateOne': 343 | // test duplicate createXxx's error handling-behavior 344 | data = local.utility2.jsonCopy(options); 345 | api[options.operationId](local.optionsId(data), options, onNext); 346 | break; 347 | default: 348 | onNext(); 349 | } 350 | break; 351 | case 5: 352 | switch (options.operationId) { 353 | case 'crudCreateOne': 354 | // validate error occurred 355 | local.utility2.assert(error, error); 356 | modeNext = Infinity; 357 | onNext(); 358 | return; 359 | default: 360 | // validate no error occurred 361 | local.utility2.assert(!error, error); 362 | } 363 | onNext(); 364 | break; 365 | default: 366 | if (options.modeNoDelete) { 367 | onError2(error, options); 368 | return; 369 | } 370 | // validate no error occurred 371 | local.utility2.assert(!error, error); 372 | // remove object by id 373 | local.testCase_crudDeleteByIdOne_default(options, onError2); 374 | } 375 | }, onError2); 376 | }; 377 | onNext(options && options.error); 378 | }; 379 | 380 | local.testCase_crudEcho_default = function (options, onError) { 381 | /* 382 | * this function will test crudEcho's default handling-behavior 383 | */ 384 | // jslint-hack 385 | local.utility2.nop(options); 386 | local.swmg.api._test.echo({ 387 | id: 'test_' + local.utility2.uuidTime(), 388 | // test array-csv-param handling-behavior 389 | paramArrayCsv: 'aa,bb', 390 | // test array-pipes-param handling-behavior 391 | paramArrayPipes: 'aa|bb', 392 | // test array-ssv-param handling-behavior 393 | paramArraySsv: 'aa bb', 394 | // test array-tsv-param handling-behavior 395 | paramArrayTsv: 'aa\tbb', 396 | // test body-param handling-behavior 397 | paramBody: 'hello!', 398 | // test header-param handling-behavior 399 | paramHeader: 'hello' 400 | }, { modeErrorData: true }, function (error, data) { 401 | local.utility2.testTryCatch(function () { 402 | // validate no error occurred 403 | local.utility2.assert(!error, error); 404 | // validate object 405 | data = local.utility2.jsonStringifyOrdered(data.obj); 406 | local.utility2.assert(data === JSON.stringify({ 407 | paramArrayCsv: ['aa', 'bb'], 408 | paramArrayPipes: ['aa', 'bb'], 409 | paramArraySsv: ['aa', 'bb'], 410 | paramArrayTsv: ['aa', 'bb'], 411 | paramBody: 'hello!', 412 | paramHeader: 'hello' 413 | }), data); 414 | onError(); 415 | }, onError); 416 | }); 417 | }; 418 | 419 | local.testCase_crudGetXxx_default = function (options, onError) { 420 | /* 421 | * this function will test crudGetXxx's default handling-behavior 422 | */ 423 | var api, modeNext, onError2, onNext, onParallel; 424 | if (!options) { 425 | onParallel = local.utility2.onParallel(onError); 426 | onParallel.counter += 1; 427 | [ 428 | '', 429 | 'pet', 430 | 'store', 431 | 'user' 432 | ].forEach(function (api) { 433 | [ 434 | 'crudAggregateMany', 435 | 'crudCountByQueryOne', 436 | 'crudGetByIdOne', 437 | 'crudGetByQueryMany', 438 | 'crudGetDistinctValueByPropertyMany', 439 | 'crudExistsByIdOne', 440 | 'findPetsByStatus', 441 | 'findPetsByTags', 442 | 'getInventory' 443 | ].forEach(function (operationId) { 444 | onParallel.counter += 1; 445 | local.testCase_crudGetXxx_default({ 446 | api: api, 447 | id: 'test_' + local.utility2.uuidTime(), 448 | operationId: operationId 449 | }, onParallel); 450 | }); 451 | }); 452 | onParallel(); 453 | return; 454 | } 455 | onError2 = function (error, data) { 456 | if (error) { 457 | // update error.message 458 | local.utility2.errorMessagePrepend(error, options.api + '.' + 459 | options.operationId + ' - '); 460 | } 461 | onError(error, data); 462 | }; 463 | modeNext = 0; 464 | onNext = function (error, data) { 465 | local.utility2.testTryCatch(function () { 466 | modeNext = error 467 | ? Infinity 468 | : modeNext + 1; 469 | switch (modeNext) { 470 | case 1: 471 | // init options 472 | options.field = 'id'; 473 | options.limit = 2; 474 | options.modeErrorData = true; 475 | options.query = JSON.stringify({ id: options.id }); 476 | // init api 477 | options.api = options.api || '_test'; 478 | api = local.swmg.api[options.api]; 479 | // if api does not have the operation, then skip testCase 480 | if (!api[options.operationId]) { 481 | onError2(); 482 | return; 483 | } 484 | // create object 485 | local.testCase_crudCreateXxx_default({ 486 | api: options.api, 487 | id: options.id, 488 | modeNoDelete: true, 489 | operationId: 'crudCreateOne' 490 | }, onNext); 491 | break; 492 | case 2: 493 | // validate object exists 494 | data = local.utility2.jsonCopy(options); 495 | data.body = [{ $group: { "_id": "all", "count": { "$sum": 1 } } }]; 496 | data.tags = options.id; 497 | api[options.operationId](local.optionsId(data), options, onNext); 498 | break; 499 | case 3: 500 | // validate object exists 501 | data = data.obj.data; 502 | switch (options.operationId) { 503 | case 'crudAggregateMany': 504 | case 'crudGetDistinctValueByPropertyMany': 505 | case 'getInventory': 506 | local.utility2.assert(data.length >= 1, data.length); 507 | break; 508 | default: 509 | local.utility2.assert(data.length === 1, data.length); 510 | } 511 | local.utility2.assert(data[0], data[0]); 512 | onNext(); 513 | break; 514 | default: 515 | // validate no error occurred 516 | local.utility2.assert(!error, error); 517 | // remove object by id 518 | local.testCase_crudDeleteByIdOne_default(options, onError2); 519 | } 520 | }, onError2); 521 | }; 522 | onNext(options && options.error); 523 | }; 524 | 525 | local.testCase_crudDeleteByQueryMany_default = function (options, onError) { 526 | /* 527 | * this function will test crudDeleteByQueryMany's default handling-behavior 528 | */ 529 | var api, modeNext, onNext, onParallel; 530 | modeNext = 0; 531 | onNext = function (error, data) { 532 | local.utility2.testTryCatch(function () { 533 | modeNext = error 534 | ? Infinity 535 | : modeNext + 1; 536 | switch (modeNext) { 537 | case 1: 538 | // init options 539 | options = options || {}; 540 | options.id = options.id || local.utility2.uuidTime(); 541 | options.modeErrorData = true; 542 | // init api 543 | options.api = options.api || '_test'; 544 | api = local.swmg.api[options.api]; 545 | // remove object by query 546 | options.query = options.query || '{"id":{"$in":["id0","id1"]}}'; 547 | if (!api.crudDeleteByQueryMany) { 548 | onParallel = local.utility2.onParallel(onNext); 549 | onParallel.counter += 1; 550 | JSON.parse(options.query).id.$in.forEach(function (id) { 551 | onParallel.counter += 1; 552 | api.crudDeleteByIdOne(local 553 | .optionsId({ id: id }), options, onParallel); 554 | }); 555 | onParallel(); 556 | return; 557 | } 558 | api.crudDeleteByQueryMany({ query: options.query }, options, onNext); 559 | break; 560 | case 2: 561 | // validate object does not exist 562 | api.crudGetByQueryMany({ 563 | limit: 8, 564 | query: options.query 565 | }, options, onNext); 566 | break; 567 | case 3: 568 | // validate object does not exist 569 | data = data.obj.data; 570 | local.utility2.assert(data.length === 0, data.length); 571 | onNext(); 572 | break; 573 | default: 574 | onError(error); 575 | } 576 | }, onError); 577 | }; 578 | onNext(options && options.error); 579 | }; 580 | 581 | local.testCase_crudDeleteByIdOne_default = function (options, onError) { 582 | /* 583 | * this function will test crudDeleteByIdOne's default handling-behavior 584 | */ 585 | var api, modeNext, onNext; 586 | modeNext = 0; 587 | onNext = function (error, data) { 588 | local.utility2.testTryCatch(function () { 589 | modeNext = error 590 | ? Infinity 591 | : modeNext + 1; 592 | switch (modeNext) { 593 | case 1: 594 | // init options 595 | options = options || {}; 596 | options.id = options.id || local.utility2.uuidTime(); 597 | options.modeErrorData = true; 598 | // init api 599 | options.api = options.api || '_test'; 600 | api = local.swmg.api[options.api]; 601 | // remove object by id 602 | api.crudDeleteByIdOne(local.optionsId({ 603 | id: options.id 604 | }), options, onNext); 605 | break; 606 | case 2: 607 | // validate object does not exist 608 | api.crudGetByIdOne(local.optionsId({ 609 | id: options.id 610 | }), options, onNext); 611 | break; 612 | case 3: 613 | // validate object does not exist 614 | local.utility2.assert(data.obj.data[0] === null, data.obj.data[0]); 615 | onNext(); 616 | break; 617 | default: 618 | onError(error); 619 | } 620 | }, onError); 621 | }; 622 | onNext(options && options.error); 623 | }; 624 | 625 | local.testCase_crudReplaceXxx_default = function (options, onError) { 626 | /* 627 | * this function will test crudReplaceXxx's default handling-behavior 628 | */ 629 | var api, modeNext, onError2, onNext, onParallel; 630 | if (!options) { 631 | onParallel = local.utility2.onParallel(onError); 632 | onParallel.counter += 1; 633 | [ 634 | '', 635 | 'pet', 636 | 'store', 637 | 'user' 638 | ].forEach(function (api) { 639 | // test update handling-behavior 640 | [ 641 | 'crudReplaceOne', 642 | 'crudUpdateOne', 643 | 'updatePetWithForm' 644 | ].forEach(function (operationId) { 645 | onParallel.counter += 1; 646 | local.testCase_crudReplaceXxx_default({ 647 | api: api, 648 | id: 'test_' + local.utility2.uuidTime(), 649 | operationId: operationId 650 | }, onParallel); 651 | }); 652 | // test upsert handling-behavior 653 | [ 654 | 'crudReplaceOne', 655 | 'crudUpdateOne' 656 | ].forEach(function (operationId) { 657 | onParallel.counter += 1; 658 | local.testCase_crudReplaceXxx_default({ 659 | api: api, 660 | id: 'test_' + local.utility2.uuidTime(), 661 | operationId: operationId, 662 | upsert: true 663 | }, onParallel); 664 | }); 665 | }); 666 | onParallel(); 667 | return; 668 | } 669 | onError2 = function (error, data) { 670 | if (error) { 671 | // update error.message 672 | local.utility2.errorMessagePrepend(error, options.api + '.' + 673 | options.operationId + ' - '); 674 | } 675 | onError(error, data); 676 | }; 677 | modeNext = 0; 678 | onNext = function (error, data) { 679 | local.utility2.testTryCatch(function () { 680 | modeNext = error 681 | ? Infinity 682 | : modeNext + 1; 683 | switch (modeNext) { 684 | case 1: 685 | // init options 686 | options.body = local.optionsId({ 687 | id: options.id, 688 | propRequired: false 689 | }); 690 | options.modeErrorData = true; 691 | // init api 692 | options.api = options.api || '_test'; 693 | api = local.swmg.api[options.api]; 694 | // if api does not have the operation, then skip testCase 695 | if (!api[options.operationId]) { 696 | onError2(); 697 | return; 698 | } 699 | // create object 700 | local.testCase_crudCreateXxx_default({ 701 | api: options.api, 702 | id: options.id, 703 | modeNoDelete: true, 704 | operationId: 'crudCreateOne' 705 | }, onNext); 706 | break; 707 | case 2: 708 | options._timeCreated = data._timeCreated; 709 | options._timeModified = data._timeModified; 710 | // test updateXxx's default handling-behavior 711 | data = local.utility2.jsonCopy(options); 712 | api[options.operationId](local.optionsId(data), options, onNext); 713 | break; 714 | case 3: 715 | // validate object 716 | data = data.obj.data[0]; 717 | local.utility2.assert( 718 | data._timeModified > options._timeModified, 719 | [data._timeModified, options._timeModified] 720 | ); 721 | local.utility2.assert(data.name === options.id, data.name); 722 | local.utility2.assert(data.status === options.id, data.status); 723 | switch (options.operationId) { 724 | case 'crudReplaceOne': 725 | local.utility2.assert( 726 | data._timeCreated > options._timeCreated, 727 | [data._timeCreated, options._timeCreated] 728 | ); 729 | local.utility2 730 | .assert(data.propExtra === undefined, data.propExtra); 731 | local.utility2 732 | .assert(data.propRequired === false, data.propRequired); 733 | break; 734 | case 'updatePetWithForm': 735 | local.utility2.assert( 736 | data._timeCreated === options._timeCreated, 737 | [data._timeCreated, options._timeCreated] 738 | ); 739 | local.utility2.assert(data.propExtra === 'hello', data.propExtra); 740 | local.utility2 741 | .assert(data.propRequired === true, data.propRequired); 742 | break; 743 | default: 744 | local.utility2.assert( 745 | data._timeCreated === options._timeCreated, 746 | [data._timeCreated, options._timeCreated] 747 | ); 748 | local.utility2.assert(data.propExtra === 'hello', data.propExtra); 749 | local.utility2 750 | .assert(data.propRequired === false, data.propRequired); 751 | } 752 | // get object 753 | api.crudGetByIdOne(local.optionsId({ 754 | id: options.id 755 | }), options, onNext); 756 | break; 757 | case 4: 758 | // validate object 759 | data = data.obj.data[0]; 760 | local.utility2.assert( 761 | data._timeModified > options._timeModified, 762 | [data._timeModified, options._timeModified] 763 | ); 764 | local.utility2.assert(data.name === options.id, data.name); 765 | local.utility2.assert(data.status === options.id, data.status); 766 | switch (options.operationId) { 767 | case 'crudReplaceOne': 768 | local.utility2.assert( 769 | data._timeCreated > options._timeCreated, 770 | [data._timeCreated, options._timeCreated] 771 | ); 772 | local.utility2 773 | .assert(data.propExtra === undefined, data.propExtra); 774 | local.utility2 775 | .assert(data.propRequired === false, data.propRequired); 776 | break; 777 | case 'updatePetWithForm': 778 | local.utility2.assert( 779 | data._timeCreated === options._timeCreated, 780 | [data._timeCreated, options._timeCreated] 781 | ); 782 | local.utility2.assert(data.propExtra === 'hello', data.propExtra); 783 | local.utility2 784 | .assert(data.propRequired === true, data.propRequired); 785 | break; 786 | default: 787 | local.utility2.assert( 788 | data._timeCreated === options._timeCreated, 789 | [data._timeCreated, options._timeCreated] 790 | ); 791 | local.utility2.assert(data.propExtra === 'hello', data.propExtra); 792 | local.utility2 793 | .assert(data.propRequired === false, data.propRequired); 794 | } 795 | onNext(); 796 | break; 797 | default: 798 | // validate no error occurred 799 | local.utility2.assert(!error, error); 800 | // remove object by id 801 | local.testCase_crudDeleteByIdOne_default(options, onError2); 802 | } 803 | }, onError2); 804 | }; 805 | onNext(options && options.error); 806 | }; 807 | 808 | local.testCase_crudUploadFile_default = function (options, onError) { 809 | /* 810 | * this function will test crudUploadFile's default handling-behavior 811 | */ 812 | var api, modeNext, onNext; 813 | modeNext = 0; 814 | onNext = function (error, data) { 815 | local.utility2.testTryCatch(function () { 816 | modeNext = error 817 | ? Infinity 818 | : modeNext + 1; 819 | switch (modeNext) { 820 | case 1: 821 | // init options 822 | options = options || {}; 823 | options.id = options.id || local.utility2.uuidTime(); 824 | options.modeErrorData = true; 825 | // init api 826 | options.api = 'pet'; 827 | api = local.swmg.api[options.api]; 828 | // create object 829 | local.testCase_crudCreateXxx_default({ 830 | api: options.api, 831 | id: options.id, 832 | modeNoDelete: true, 833 | operationId: 'crudCreateOne' 834 | }, onNext); 835 | break; 836 | case 2: 837 | options._timeCreated = data._timeCreated; 838 | options._timeModified = data._timeModified; 839 | // test file-upload handling-behavior 840 | local.utility2.ajax({ 841 | /* jslint-ignore-begin */ 842 | data: '------FormBoundaryXxx\r\nContent-Disposition: form-data; name="additionalMetadata"\r\n\r\nhello\r\n------FormBoundaryXxx\r\nContent-Disposition: form-data; name="file"; filename="hello.png"\r\nContent-Type: image/png\r\n\r\ndata\r\n------FormBoundaryXxx--\r\n', 843 | /* jslint-ignore-end */ 844 | headers: { 845 | 'Content-Type': 846 | 'multipart/form-data; boundary=----FormBoundaryXxx' 847 | }, 848 | method: 'POST', 849 | url: '/api/v0/pet/' + options.id + '/uploadImage' 850 | }, onNext); 851 | break; 852 | case 3: 853 | // validate object 854 | data = JSON.parse(data.responseText).data[0]; 855 | local.utility2.assert( 856 | data._timeCreated === options._timeCreated, 857 | [data._timeCreated, options._timeCreated] 858 | ); 859 | local.utility2.assert( 860 | data._timeModified > options._timeModified, 861 | [data._timeModified, options._timeModified] 862 | ); 863 | local.utility2.assert( 864 | data.additionalMetadata === 'hello', 865 | data.additionalMetadata 866 | ); 867 | local.utility2.assert(data.file === 'ZGF0YQ==', data.file); 868 | local.utility2.assert(data.filename === 'hello.png', data.filename); 869 | local.utility2.assert(data.petId === options.id, data.petId); 870 | local.utility2.assert(data.propExtra === 'hello', data.propExtra); 871 | local.utility2 872 | .assert(data.propRequired === true, data.propRequired); 873 | // get object 874 | api.crudGetByIdOne(local.optionsId({ 875 | id: options.id 876 | }), options, onNext); 877 | break; 878 | case 4: 879 | // validate object 880 | data = data.obj.data[0]; 881 | local.utility2.assert( 882 | data._timeCreated === options._timeCreated, 883 | [data._timeCreated, options._timeCreated] 884 | ); 885 | local.utility2.assert( 886 | data._timeModified > options._timeModified, 887 | [data._timeModified, options._timeModified] 888 | ); 889 | onNext(); 890 | break; 891 | default: 892 | // validate no error occurred 893 | local.utility2.assert(!error, error); 894 | // remove object by id 895 | local.testCase_crudDeleteByIdOne_default(options, onError); 896 | } 897 | }, onError); 898 | }; 899 | onNext(options && options.error); 900 | }; 901 | 902 | local.testCase_crudXxx_error = function (options, onError) { 903 | /* 904 | * this function will test crudXxx's error handling-behavior 905 | */ 906 | var api, onParallel, optionsCopy; 907 | onParallel = local.utility2.onParallel(onError); 908 | onParallel.counter += 1; 909 | // init options 910 | options = {}; 911 | options.modeErrorData = true; 912 | options.paramHeader = '1'; 913 | // init api 914 | api = local.swmg.api._test; 915 | // test api-error handling-behavior 916 | [ 917 | 'errorMiddleware', 918 | 'errorUndefinedApi', 919 | 'errorUndefinedCrud' 920 | ].forEach(function (operationId) { 921 | optionsCopy = local.utility2.jsonCopy(options); 922 | optionsCopy.id = 'test_' + local.utility2.uuidTime(); 923 | optionsCopy.operationId = operationId; 924 | onParallel.counter += 1; 925 | api[optionsCopy.operationId](optionsCopy, optionsCopy, function (error) { 926 | local.utility2.testTryCatch(function () { 927 | // validate error occurred 928 | local.utility2.assert(error, error); 929 | onParallel(); 930 | }, onParallel); 931 | }); 932 | }); 933 | // test low-level ajax-error handling-behavior 934 | [{ 935 | url: '/api/v0/_test/errorUndefined' 936 | }].forEach(function (options) { 937 | onParallel.counter += 1; 938 | local.utility2.ajax(options, function (error) { 939 | local.utility2.testTryCatch(function () { 940 | // validate error occurred 941 | local.utility2.assert(error, error); 942 | onParallel(); 943 | }, onParallel); 944 | }); 945 | }); 946 | // test testCase-error handling-behavior 947 | [ 948 | 'testCase_collectionCreate_misc', 949 | 'testCase_crudAggregateMany_default', 950 | 'testCase_crudCreateMany_default', 951 | 'testCase_crudCreateXxx_default', 952 | 'testCase_crudGetXxx_default', 953 | 'testCase_crudDeleteByIdOne_default', 954 | 'testCase_crudDeleteByQueryMany_default', 955 | 'testCase_crudReplaceXxx_default', 956 | 'testCase_crudUploadFile_default' 957 | ].forEach(function (testCase) { 958 | if (local[testCase]) { 959 | onParallel.counter += 1; 960 | local[testCase]({ 961 | error: local.utility2.errorDefault 962 | }, function (error) { 963 | local.utility2.testTryCatch(function () { 964 | // validate error occurred 965 | local.utility2.assert(error, error); 966 | onParallel(); 967 | }, onParallel); 968 | }); 969 | } 970 | }); 971 | onParallel(); 972 | }; 973 | 974 | local.testCase_validateByParamDefList_default = function (options, onError) { 975 | /* 976 | * this function will test validateByParamDefList's default handling-behavior 977 | */ 978 | var error; 979 | // jslint-hack 980 | local.utility2.nop(options); 981 | [{ 982 | data: { body: { propRequired: true } }, 983 | key: 'crudCreateOne', 984 | method: 'post' 985 | }, { 986 | data: { query: '{}' }, 987 | key: 'crudCountByQueryOne', 988 | method: 'get' 989 | }].forEach(function (options) { 990 | options.paramDefList = local.swmg.swaggerJson 991 | .paths['/_test/' + options.key][options.method] 992 | .parameters; 993 | local.swmg.validateByParamDefList(options); 994 | }); 995 | // test validateByParamDefList's error handling-behavior 996 | [{ 997 | data: { body: { propRequired: null } }, 998 | key: 'crudCreateOne', 999 | method: 'post' 1000 | }, { 1001 | data: { query: 'syntax error' }, 1002 | key: 'crudCountByQueryOne', 1003 | method: 'get' 1004 | }].forEach(function (options) { 1005 | try { 1006 | error = null; 1007 | options.paramDefList = local.swmg.swaggerJson 1008 | .paths['/_test/' + options.key][options.method] 1009 | .parameters; 1010 | local.swmg.validateByParamDefList(options); 1011 | } catch (errorCaught) { 1012 | error = errorCaught; 1013 | } 1014 | // validate error occurred 1015 | local.utility2.assert(error, error); 1016 | }); 1017 | // test validateByPropertyDef's circular-reference handling-behavior 1018 | local.swmg.validateByPropertyDef({ 1019 | data: { propObject: {} }, 1020 | propertyDef: { propObject: { type: 'object' } } 1021 | }); 1022 | onError(); 1023 | }; 1024 | 1025 | local.testCase_validateBySchema_default = function (options, onError) { 1026 | /* 1027 | * this function will test validateBySchema's default handling-behavior 1028 | */ 1029 | var optionsCopy; 1030 | options = { 1031 | data: { propRequired: true }, 1032 | schema: local.swmg.swaggerJson.definitions.TestCrudModel 1033 | }; 1034 | [ 1035 | { key: 'propArray', value: [null] }, 1036 | { key: 'propArraySubdoc', value: [{ propRequired: true }] }, 1037 | { key: 'propBoolean', value: true }, 1038 | { key: 'propInteger', value: 0 }, 1039 | { key: 'propIntegerInt32', value: 0 }, 1040 | { key: 'propIntegerInt64', value: 0 }, 1041 | { key: 'propNumberFloat', value: 0.5 }, 1042 | { key: 'propNumberDouble', value: 0.5 }, 1043 | { key: 'propObject', value: {} }, 1044 | { key: 'propObjectSubdoc', value: {} }, 1045 | { key: 'propString', value: '' }, 1046 | { key: 'propStringByte', value: local.modeJs === 'browser' 1047 | ? local.global.btoa(local.utility2.stringAsciiCharset) 1048 | : new Buffer(local.utility2.stringAsciiCharset).toString('base64') }, 1049 | { key: 'propStringDate', value: '1971-01-01' }, 1050 | { key: 'propStringDatetime', value: '1971-01-01T00:00:00Z' }, 1051 | { key: 'propStringEmail', value: 'q@q.com' }, 1052 | { key: 'propStringJson', value: 'null' }, 1053 | { key: 'propUndefined', value: null }, 1054 | { key: 'propUndefined', value: undefined }, 1055 | { key: 'propUndefined', value: true } 1056 | ].forEach(function (element) { 1057 | optionsCopy = local.utility2.jsonCopy(options.data); 1058 | optionsCopy[element.key] = element.value; 1059 | // test circular-reference handling-behavior 1060 | optionsCopy.propArraySubdoc = optionsCopy.propArraySubdoc || [optionsCopy]; 1061 | optionsCopy.propObject = optionsCopy.propObject || optionsCopy; 1062 | optionsCopy.propObjectSubdoc = optionsCopy.propObjectSubdoc || optionsCopy; 1063 | local.swmg.validateBySchema({ data: optionsCopy, schema: options.schema }); 1064 | }); 1065 | onError(); 1066 | }; 1067 | 1068 | local.testCase_validateBySchema_error = function (options, onError) { 1069 | /* 1070 | * this function will test validateBySchema's error handling-behavior 1071 | */ 1072 | var error, optionsCopy; 1073 | options = { 1074 | data: { propRequired: true }, 1075 | schema: local.swmg.swaggerJson.definitions.TestCrudModel 1076 | }; 1077 | [ 1078 | { data: null }, 1079 | { key: 'propArray', value: true }, 1080 | { key: 'propArraySubdoc', value: [{ propRequired: null }] }, 1081 | { key: 'propBoolean', value: 0 }, 1082 | { key: 'propInteger', value: true }, 1083 | { key: 'propInteger', value: Infinity }, 1084 | { key: 'propInteger', value: NaN }, 1085 | { key: 'propIntegerInt32', value: 0.5 }, 1086 | { key: 'propIntegerInt64', value: 0.5 }, 1087 | { key: 'propNumber', value: true }, 1088 | { key: 'propNumber', value: Infinity }, 1089 | { key: 'propNumber', value: NaN }, 1090 | { key: 'propNumberFloat', value: true }, 1091 | { key: 'propNumberDouble', value: true }, 1092 | { key: 'propObject', value: true }, 1093 | { key: 'propObjectSubdoc', value: 'non-object' }, 1094 | { key: 'propRequired', value: null }, 1095 | { key: 'propRequired', value: undefined }, 1096 | { key: 'propString', value: true }, 1097 | { key: 'propStringByte', value: local.utility2.stringAsciiCharset }, 1098 | { key: 'propStringDate', value: 'null' }, 1099 | { key: 'propStringDatetime', value: 'null' }, 1100 | { key: 'propStringEmail', value: 'null' }, 1101 | { key: 'propStringJson', value: 'syntax error' } 1102 | ].forEach(function (element) { 1103 | try { 1104 | error = null; 1105 | optionsCopy = local.utility2.jsonCopy(options.data); 1106 | optionsCopy[element.key] = element.value; 1107 | local.swmg.validateBySchema({ 1108 | data: element.data === null 1109 | ? null 1110 | : optionsCopy, 1111 | schema: options.schema 1112 | }); 1113 | } catch (errorCaught) { 1114 | error = errorCaught; 1115 | } 1116 | // validate error occurred 1117 | local.utility2.assert(error, error); 1118 | }); 1119 | onError(); 1120 | }; 1121 | 1122 | local.testCase_validateBySwagger_default = function (options, onError) { 1123 | /* 1124 | * this function will test validateBySwagger's default handling-behavior 1125 | */ 1126 | var error; 1127 | // jslint-hack 1128 | local.utility2.nop(options); 1129 | local.utility2.testMock([ 1130 | // suppress console.error 1131 | [console, { error: local.utility2.nop }] 1132 | ], function (onError) { 1133 | [null, {}].forEach(function (element) { 1134 | try { 1135 | local.swmg.validateBySwagger(element); 1136 | } catch (errorCaught) { 1137 | error = errorCaught; 1138 | } 1139 | // validate error occurred 1140 | local.utility2.assert(error, error); 1141 | }); 1142 | onError(); 1143 | }, onError); 1144 | }; 1145 | }()); 1146 | switch (local.modeJs) { 1147 | 1148 | 1149 | 1150 | // run node js-env code 1151 | case 'node': 1152 | // init tests 1153 | local.testCase_collectionCreate_misc = function (options, onError) { 1154 | /* 1155 | * this function will test the collectionCreate's misc handling-behavior 1156 | */ 1157 | var modeNext, onNext; 1158 | modeNext = 0; 1159 | onNext = function (error) { 1160 | modeNext = error 1161 | ? Infinity 1162 | : modeNext + 1; 1163 | switch (modeNext) { 1164 | case 1: 1165 | // test null schema handling-behavior 1166 | local.swmg.collectionCreate({ 1167 | _collectionName: 'SwmgTestMisc', 1168 | // test _collectionReadonly handling-behavior 1169 | _collectionReadonly: true 1170 | }, onNext); 1171 | break; 1172 | case 2: 1173 | local.swmg.collectionCreate({ 1174 | // test _collectionCreate handling-behavior 1175 | _collectionCreate: {}, 1176 | // test _collectionDrop handling-behavior 1177 | _collectionDrop: true, 1178 | _collectionName: 'SwmgTestMisc' 1179 | }, onNext); 1180 | break; 1181 | case 3: 1182 | local.swmg.collectionCreate({ 1183 | // test capped-collection handling-behavior 1184 | _collectionCreate: { capped: true, size: 1 }, 1185 | // test _collectionCreateIndexList handling-behavior 1186 | _collectionCreateIndexList: [{ 1187 | key: { propIndexed: 1 }, 1188 | name: 'propIndexed_1' 1189 | }], 1190 | _collectionName: 'SwmgTestMisc' 1191 | }, onNext); 1192 | break; 1193 | default: 1194 | onError(error); 1195 | } 1196 | }; 1197 | onNext(options && options.error); 1198 | }; 1199 | 1200 | local.testCase_middlewareBodyParse_misc = function (options, onError) { 1201 | /* 1202 | * this function will test middlewareBodyParse's misc handling-behavior 1203 | */ 1204 | var onParallel; 1205 | // jslint-hack 1206 | local.utility2.nop(options); 1207 | onParallel = local.utility2.onParallel(onError); 1208 | onParallel.counter += 1; 1209 | // test swmgBodyParsed exists handling-behavior 1210 | onParallel.counter += 1; 1211 | local.swmg.middlewareBodyParse({ 1212 | swmgBodyParsed: {}, 1213 | swmgMethodPath: { parameters: [] } 1214 | }, {}, onParallel); 1215 | onParallel(); 1216 | }; 1217 | 1218 | local.testCase_testPage_default = function (options, onError) { 1219 | /* 1220 | * this function will test the test-page's default handling-behavior 1221 | */ 1222 | // jslint-hack 1223 | local.utility2.nop(options); 1224 | local.utility2.phantomTest({ 1225 | url: 'http://localhost:' + 1226 | local.utility2.envDict.npm_config_server_port + 1227 | '?modeTest=phantom&timeExit={{timeExit}}' 1228 | }, onError); 1229 | }; 1230 | break; 1231 | } 1232 | switch (local.modeJs) { 1233 | 1234 | 1235 | 1236 | // run browser js-env code 1237 | case 'browser': 1238 | // init swaggerUi 1239 | local.utility2.onReady.counter += 1; 1240 | window.swaggerUi = new window.SwaggerUi({ 1241 | dom_id: "swagger-ui-container", 1242 | onComplete: function () { 1243 | local.swmg.swaggerJson = local.swmg.api.swaggerJson; 1244 | local.utility2.onReady(); 1245 | }, 1246 | supportedSubmitMethods: ['delete', 'get', 'patch', 'post', 'put'], 1247 | url: '/api/v0/swagger.json' 1248 | }); 1249 | // init api 1250 | window.swaggerUi.load(); 1251 | local.swmg.api = window.swaggerUi.api; 1252 | // run test 1253 | local.utility2.testRun(local); 1254 | break; 1255 | 1256 | 1257 | 1258 | // run node js-env code 1259 | case 'node': 1260 | // test null apiUpdate handling-behavior 1261 | local.swmg.apiUpdate({}); 1262 | // init test api 1263 | local.swmg.apiUpdate({ 1264 | definitions: { 1265 | // init TestCrudModel schema 1266 | TestCrudModel: { 1267 | // drop collection on init 1268 | _collectionDrop: true, 1269 | _collectionName: 'SwmgTestCrud', 1270 | // init default crud-api 1271 | _crudApi: '_test', 1272 | _crudApiList: [ 1273 | 'crudAggregateMany', 1274 | 'crudCountByQueryOne', 1275 | 'crudCreateMany', 1276 | 'crudCreateOne', 1277 | 'crudDeleteByQueryMany', 1278 | 'crudDeleteByIdOne', 1279 | 'crudExistsByIdOne', 1280 | 'crudGetByIdOne', 1281 | 'crudGetByQueryMany', 1282 | 'crudGetDistinctValueByPropertyMany', 1283 | 'crudReplaceMany', 1284 | 'crudReplaceOne', 1285 | 'crudUpdateOne' 1286 | ], 1287 | properties: { 1288 | propArray: { items: {}, type: 'array' }, 1289 | propArraySubdoc: { 1290 | default: [{ propRequired: true }], 1291 | items: { $ref: '#/definitions/TestCrudModel' }, 1292 | type: 'array' 1293 | }, 1294 | propBoolean: { type: 'boolean' }, 1295 | propInteger: { type: 'integer' }, 1296 | propIntegerInt32: { format: 'int32', type: 'integer' }, 1297 | propIntegerInt64: { format: 'int64', type: 'integer' }, 1298 | propNumber: { type: 'number' }, 1299 | propNumberDouble: { format: 'double', type: 'number' }, 1300 | propNumberFloat: { format: 'float', type: 'number' }, 1301 | propObject: { type: 'object' }, 1302 | propObjectSubdoc: { $ref: '#/definitions/TestNullModel' }, 1303 | propRequired: { default: true }, 1304 | propString: { type: 'string' }, 1305 | propStringByte: { format: 'byte', type: 'string' }, 1306 | propStringDate: { format: 'date', type: 'string' }, 1307 | propStringDatetime: { format: 'date-time', type: 'string' }, 1308 | propStringEmail: 1309 | { default: 'a@a.com', format: 'email', type: 'string' }, 1310 | propStringJson: 1311 | { default: 'null', format: 'json', type: 'string' }, 1312 | propUndefined: {} 1313 | }, 1314 | required: ['propRequired'], 1315 | 'x-inheritList': [{ $ref: '#/definitions/JsonapiResource' }] 1316 | }, 1317 | // init TestNullModel schema 1318 | TestNullModel: {} 1319 | }, 1320 | paths: { 1321 | // test custom api handling-behavior 1322 | '/_test/echo': { post: { 1323 | _collectionName: 'SwmgTestCrud', 1324 | operationId: 'echo', 1325 | parameters: [{ 1326 | // test array-csv-param handling-behavior 1327 | collectionFormat: 'csv', 1328 | description: 'csv-array param', 1329 | in: 'query', 1330 | items: { type: 'string' }, 1331 | name: 'paramArrayCsv', 1332 | type: 'array' 1333 | }, { 1334 | // test array-pipes-param handling-behavior 1335 | collectionFormat: 'pipes', 1336 | description: 'pipes-array param', 1337 | in: 'query', 1338 | items: { type: 'string' }, 1339 | name: 'paramArrayPipes', 1340 | type: 'array' 1341 | }, { 1342 | // test array-ssv-param handling-behavior 1343 | collectionFormat: 'ssv', 1344 | description: 'ssv-array param', 1345 | in: 'query', 1346 | items: { type: 'string' }, 1347 | name: 'paramArraySsv', 1348 | type: 'array' 1349 | }, { 1350 | // test array-tsv-param handling-behavior 1351 | collectionFormat: 'tsv', 1352 | description: 'tsv-array param', 1353 | in: 'query', 1354 | items: { type: 'string' }, 1355 | name: 'paramArrayTsv', 1356 | type: 'array' 1357 | }, { 1358 | description: 'body', 1359 | // test body-param handling-behavior 1360 | in: 'body', 1361 | name: 'paramBody', 1362 | schema: { $ref: '#/definitions/Undefined' } 1363 | }, { 1364 | description: 'header param', 1365 | // test header-param handling-behavior 1366 | in: 'header', 1367 | name: 'paramHeader', 1368 | type: 'string' 1369 | }, { 1370 | description: 'optional param', 1371 | in: 'query', 1372 | // test optional-param handling-behavior 1373 | name: 'paramOptional', 1374 | type: 'string' 1375 | }], 1376 | summary: 'echo request params back to client', 1377 | tags: ['_test'] 1378 | } }, 1379 | // test midddleware-error handling-behavior 1380 | '/_test/errorMiddleware': { get: { 1381 | operationId: 'errorMiddleware', 1382 | tags: ['_test'] 1383 | } }, 1384 | // test undefined api handling-behavior 1385 | '/_test/errorUndefinedApi': { get: { 1386 | operationId: 'errorUndefinedApi', 1387 | tags: ['_test'] 1388 | } }, 1389 | // test undefined crud-api handling-behavior 1390 | '/_test/errorUndefinedCrud': { get: { 1391 | _collectionName: 'SwmgTestCrud', 1392 | _crudApi: true, 1393 | operationId: 'errorUndefinedCrud', 1394 | tags: ['_test'] 1395 | } } 1396 | }, 1397 | _tagDict: { _test: { description: 'internal test-api' } } 1398 | }); 1399 | // init test-middleware 1400 | local.middleware.middlewareList.push(function (request, response, nextMiddleware) { 1401 | switch (request.swmgPathname) { 1402 | case 'POST /_test/echo': 1403 | response.end(JSON.stringify(request.swmgParamDict)); 1404 | break; 1405 | case 'GET /_test/errorMiddleware': 1406 | nextMiddleware(new Error('dummy error')); 1407 | break; 1408 | default: 1409 | nextMiddleware(); 1410 | } 1411 | }); 1412 | // run validation test 1413 | local.testCase_validateByParamDefList_default(null, local.utility2.onErrorDefault); 1414 | local.testCase_validateBySchema_default(null, local.utility2.onErrorDefault); 1415 | local.testCase_validateBySwagger_default(null, local.utility2.onErrorDefault); 1416 | // jslint dir 1417 | [ 1418 | __dirname 1419 | ].forEach(function (dir) { 1420 | local.fs.readdirSync(dir).forEach(function (file) { 1421 | file = dir + '/' + file; 1422 | // if the file is modified, then restart the process 1423 | local.utility2.onFileModifiedRestart(file); 1424 | switch (local.path.extname(file)) { 1425 | case '.js': 1426 | case '.json': 1427 | // jslint file 1428 | local.utility2.jslint_lite 1429 | .jslintAndPrint(local.fs.readFileSync(file, 'utf8'), file); 1430 | break; 1431 | } 1432 | }); 1433 | }); 1434 | // init repl debugger 1435 | local.utility2.replStart(); 1436 | break; 1437 | } 1438 | }((function () { 1439 | 'use strict'; 1440 | var local; 1441 | 1442 | 1443 | 1444 | // run shared js-env code 1445 | (function () { 1446 | // init local 1447 | local = {}; 1448 | // init js-env 1449 | local.modeJs = (function () { 1450 | try { 1451 | return module.exports && 1452 | typeof process.versions.node === 'string' && 1453 | typeof require('http').createServer === 'function' && 1454 | 'node'; 1455 | } catch (errorCaughtNode) { 1456 | return typeof navigator.userAgent === 'string' && 1457 | typeof document.querySelector('body') === 'object' && 1458 | 'browser'; 1459 | } 1460 | }()); 1461 | switch (local.modeJs) { 1462 | // re-init local from window.local 1463 | case 'browser': 1464 | local = window.local; 1465 | break; 1466 | // re-init local from example.js 1467 | case 'node': 1468 | [ 1469 | process.cwd(), 1470 | // test dir !== __dirname handling-behavior 1471 | '' 1472 | ].forEach(function (dir) { 1473 | if (dir !== __dirname) { 1474 | local = require(__dirname + '/example.js'); 1475 | return; 1476 | } 1477 | require('fs').writeFileSync( 1478 | __dirname + '/example.js', 1479 | require('fs').readFileSync(__dirname + '/README.md', 'utf8') 1480 | // support syntax-highlighting 1481 | .replace((/[\S\s]+?\n.*?example.js\s*?```\w*?\n/), function (match0) { 1482 | // preserve lineno 1483 | return match0.replace((/.+/g), ''); 1484 | }) 1485 | .replace((/\n```[\S\s]+/), '') 1486 | // disable mock package.json env 1487 | .replace(/(process.env.npm_package_\w+ = )/g, '// $1') 1488 | // alias require('$npm_package_name') to require('index.js'); 1489 | .replace( 1490 | "require('" + process.env.npm_package_name + "')", 1491 | "require(__dirname + '/index.js')" 1492 | ) 1493 | ); 1494 | local = require(__dirname + '/example.js'); 1495 | }); 1496 | break; 1497 | } 1498 | }()); 1499 | return local; 1500 | }()))); 1501 | --------------------------------------------------------------------------------