swagger-mongodb
lightweight swagger-ui crud-middleware backed by mongodb
live test-server
build-status
git-branch : | master | beta | alpha |
---|---|---|---|
test-server : | |||
test-report : | |||
coverage : | |||
build-artifacts : |
master branch
- stable branch
- HEAD should be tagged, npm-published package
beta branch
- semi-stable branch
- HEAD should be latest, npm-published package
alpha branch
- unstable branch
- HEAD is arbitrary
- commit history may be rewritten
documentation
this package requires
- darwin or linux os
- mongodb 2.6 or higher
api-doc
quickstart web example
to run this example, follow the instruction in the script below
- example.js
/*example.js this node script will serve a lightweight swagger-ui crud-api backed by mongodb instruction 1. save this script as example.js 2. run the shell command: $ npm install swagger-mongodb && npm_config_server_port=1337 node example.js 3. open a browser to http://localhost:1337 4. interact with the swagger-ui crud-api*/ /*jslint browser: true, maxerr: 8, maxlen: 96, node: true, nomen: true, regexp: true, stupid: true*/ (function (local) { 'use strict'; switch (local.modeJs) { // run node js-env code case 'node': // export local module.exports = local; // init assets local.utility2.cacheDict.assets['/'] = '<!DOCTYPE html>\n' +/* jslint-ignore-begin */'<html>\n' +'<head>\n' +' <meta charset="UTF-8">\n' +' <title>\n' +' {{envDict.npm_package_name}} [{{envDict.npm_package_version}}]\n' +' </title>\n' +' <link rel="stylesheet" href="/assets/utility2.css">\n' +' <style>\n' +' * {\n' +' box-sizing: border-box;\n' +' }\n' +' body {\n' +' background-color: #fff;\n' +' font-family: Helvetical Neue, Helvetica, Arial, sans-serif;\n' +' }\n' +' body > div {\n' +' margin: 20px 0 20px 0;\n' +' }\n' +' .testReportDiv {\n' +' display: none;\n' +' }\n' +' </style>\n' +' {{envDict.npm_config_html_head_extra}}\n' +'</head>\n' +'<body>\n' +' <div class="ajaxProgressDiv" style="display: none;">\n' +' <div class="ajaxProgressBarDiv ajaxProgressBarDivLoading">loading</div>\n' +' </div>\n' +' <h1>{{envDict.npm_package_name}} [{{envDict.npm_package_version}}]</h1>\n' +' <h3>{{envDict.npm_package_description}}</h3>\n' +' <div class="testReportDiv"></div>\n' +' <div id="swagger-ui-container" style="display: none;"></div>\n' +' <iframe height="512" src="/assets/swagger-ui.html" width="100%"></iframe>\n' +' <script src="/assets/utility2.js"></script>\n' +' <script src="/assets/swagger-ui.rollup.js"></script>\n' +' <script src="/assets/swagger-mongodb.js"></script>\n' +' <script src="/assets/example.js"></script>\n' +' <script src="/test/test.js"></script>\n' +' <script>\n' +' window.utility2 = window.utility2 || {};\n' +' window.utility2.envDict = {\n' +' npm_package_description: "{{envDict.npm_package_description}}",\n' +' npm_package_name: "{{envDict.npm_package_name}}",\n' +' npm_package_version: "{{envDict.npm_package_version}}"\n' +' };\n' +' document.querySelector("iframe").onload = function () {\n' +' var self;\n' +' self = this;\n' +' self.height = innerHeight - self.offsetTop - 20;\n' +' self.contentWindow.location.hash = location.hash;\n' +' self.contentWindow.onclick = function () {\n' +' setTimeout(function () {\n' +' location.hash = self.contentWindow.location.hash;\n' +' });\n' +' };\n' +' };\n' +' </script>\n' +' {{envDict.npm_config_html_body_extra}}\n' +'</body>\n' +/* jslint-ignore-end */ '</html>\n'; local.utility2.cacheDict.assets['/'] = local.utility2.stringFormat( local.utility2.cacheDict.assets['/'], { envDict: local.utility2.envDict }, '' ); local.utility2.cacheDict.assets['/assets/example.js'] = local.utility2.istanbul_lite.instrumentSync( local.fs.readFileSync(__dirname + '/example.js', 'utf8'), __dirname + '/example.js' ); local.utility2.cacheDict.assets['/test/test.js'] = local.utility2.istanbul_lite.instrumentInPackage( local.fs.readFileSync(local.swmg.__dirname + '/test.js', 'utf8'), local.swmg.__dirname + '/test.js', 'swagger-mongodb' ); // init mongodb-client local.utility2.onReady.counter += 1; local.utility2.taskRunOrSubscribe({ key: 'swagger-mongodb.mongodbConnect', onTask: function (onError) { local.mongodb.MongoClient.connect( local.utility2.envDict.npm_config_mongodb_url || 'mongodb://localhost:27017/test', function (error, db) { // validate no error occurred local.utility2.assert(!error, error); local.swmg.db = db; onError(); local.utility2.onReady(); } ); } }); // init middleware local.middleware = local.utility2.middlewareGroupCreate([ // init pre-middleware local.utility2.middlewareInit, // init cached-assets middleware local.utility2.middlewareAssetsCached, // init http-body-get middleware local.utility2.middlewareBodyGet, // init http-body-parse-upload middleware function (request, response, nextMiddleware) { var boundary, bodyText; // jslint-hack local.utility2.nop(response); local.utility2.testTryCatch(function () { if ((request.headers['content-type'] || '') .indexOf('multipart/form-data') !== 0) { nextMiddleware(); return; } boundary = '--' + (/boundary=(.*)/).exec(request.headers['content-type'])[1]; request.swmgBodyParsed = {}; bodyText = String(request.bodyRaw); bodyText.split(boundary).slice(1, -1).forEach(function (part) { request.swmgBodyParsed[ (/\bname="([^"]*)/).exec(part)[1] ] = part.split('\r\n\r\n').slice(1).join('\r\n\r\n').slice(0, -2); }); // set file bodyText.replace('\r\n\r\n', function (match0, ii) { // jslint-hack local.utility2.nop(match0); request.swmgBodyParsed.file = request.bodyRaw .slice(ii + 4, -(boundary.length + 6)) .toString('base64'); }); request.swmgBodyParsed.file = request.bodyRaw .slice(bodyText.lastIndexOf('\r\n\r\n') + 4, -(boundary.length + 6)) .toString('base64'); // set filename request.swmgBodyParsed.filename = (/\bfilename="([^"]+)/).exec(bodyText); request.swmgBodyParsed.filename = request.swmgBodyParsed.filename && request.swmgBodyParsed.filename[1]; nextMiddleware(); }, nextMiddleware); }, // init http-body-parse middleware local.swmg.middlewareBodyParse, // init swagger pre-middleware function (request, response, nextMiddleware) { // jslint-hack local.utility2.nop(request); // enable cors // http://en.wikipedia.org/wiki/Cross-origin_resource_sharing response.setHeader( 'Access-Control-Allow-Methods', 'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT' ); response.setHeader('Access-Control-Allow-Origin', '*'); // init content-type response.setHeader('Content-Type', 'application/json; charset=UTF-8'); nextMiddleware(); }, // init swagger middleware local.swmg.middlewareSwagger ]); // init error-middleware local.middlewareError = local.swmg.middlewareError; // init petstore-api (function () { var methodPath, options, schema; options = local.utility2.jsonCopy(require(local.swmg.local .swagger_ui_lite.__dirname + '/swagger.json')); options = { definitions: options.definitions, paths: options.paths, tags: options.tags }; // remove unused properties delete options.definitions.ApiResponse; // init schema Object.keys(options.definitions).forEach(function (schemaName) { schema = options.definitions[schemaName]; // init id schema.properties.id = { type: 'string' }; schema['x-inheritList'] = [{ $ref: '#/definitions/JsonapiResource' }]; }); local.utility2.objectSetOverride(options, { definitions: { // init Pet schema Pet: { // drop collection on init _collectionDrop: true, // upsert fixtures _collectionFixtureList: [{ id: 'pet0', name: 'birdie', photoUrls: [], status: 'available', tags: [{ name: 'bird'}] }, { id: 'pet1', name: 'kittie', status: 'pending', photoUrls: [], tags: [{ name: 'cat'}] }, { id: 'pet2', name: 'doggie', photoUrls: [], status: 'sold', tags: [{ name: 'dog'}] }], _collectionName: 'SwmgPet' }, // init Order schema Order: { // create index _collectionCreateIndexList: [{ key: { status: 1 }, name: 'status_1' }], // drop collection on init _collectionDrop: true, // upsert fixtures _collectionFixtureList: [{ id: 'order0', status: 'available' }, { id: 'order1', status: 'pending' }, { id: 'order2', status: 'sold' }], _collectionName: 'SwmgOrder', properties: { petId: { type: 'string' } } }, // init User schema User: { // create index _collectionCreateIndexList: [{ key: { username: 1 }, name: 'username_1', unique: true }], // drop collection on init _collectionDrop: true, // upsert fixtures _collectionFixtureList: [{ email: 'john@doe.com', firstName: 'john', id: 'user0', lastName: 'doe', password: 'hello', phone: '1234-5678', username: 'john.doe' }, { email: 'jane@doe.com', firstName: 'jane', id: 'user1', lastName: 'doe', password: 'bye', phone: '8765-4321', username: 'jane.doe' }], _collectionName: 'SwmgUser' } }, // init crud-api paths: { '/pet/crudGetByQueryMany': { get: { _collectionName: 'SwmgPet', _crudApi: 'pet', _schemaName: 'Pet', operationId: 'crudGetByQueryMany', tags: ['pet'] } }, '/store/crudGetByQueryMany': { get: { _collectionName: 'SwmgOrder', _crudApi: 'store', _schemaName: 'Order', operationId: 'crudGetByQueryMany', tags: ['store'] } }, '/user/crudGetByQueryMany': { get: { _collectionName: 'SwmgUser', _crudApi: 'user', _schemaName: 'User', operationId: 'crudGetByQueryMany', tags: ['user'] } } } }, 4); // transform petstore-api to swagger-mongodb's crud-api Object.keys(options.paths).forEach(function (path) { Object.keys(options.paths[path]).forEach(function (method) { methodPath = options.paths[path][method]; // init methodPath._schemaName switch (path.split('/')[1]) { case 'pet': methodPath._schemaName = 'Pet'; break; case 'store': methodPath._schemaName = 'Order'; break; case 'user': methodPath._schemaName = 'User'; break; } methodPath._collectionName = 'Swmg' + methodPath._schemaName; delete methodPath.produces; delete methodPath.responses; delete methodPath.security; // init jsonapi response local.utility2.objectSetDefault(methodPath, { responses: { 200: { description: '200 ok - http://jsonapi.org/format' + '/#document-structure-top-level', schema: { $ref: '#/definitions/JsonapiResponse{{_schemaName}}' } } } }, 2); // init crudCreateMany / crudCreateOne / crudDeleteByIdOne / crudGetByIdOne switch (methodPath.operationId) { case 'addPet': case 'createUser': case 'placeOrder': methodPath.operationId = 'crudCreateOne'; break; case 'createUsersWithArrayInput': case 'createUsersWithListInput': methodPath.operationId = 'crudCreateMany'; break; case 'deleteOrder': case 'deletePet': case 'deleteUser': methodPath.operationId = 'crudDeleteByIdOne'; break; case 'getOrderById': case 'getPetById': case 'getUserByName': methodPath.operationId = 'crudGetByIdOne'; break; } // init id (methodPath.parameters || []).forEach(function (paramDef) { switch (paramDef.name) { case 'orderId': case 'petId': delete paramDef.format; paramDef.type = 'string'; break; } }); }); }); local.swmg.apiUpdate(options); }()); // init petstore-middleware local.middleware.middlewareList.push(function (request, response, nextMiddleware) { var modeNext, onNext, options; modeNext = 0; onNext = function (error, data) { local.utility2.testTryCatch(function () { modeNext = error ? Infinity : modeNext + 1; switch (modeNext) { case 1: // init id ((request.swmgMethodPath && request.swmgMethodPath.parameters) || [ ]).forEach(function (paramDef) { switch (paramDef.name) { case 'orderId': case 'petId': request.swmgParamDict.id = request.swmgParamDict[paramDef.name]; break; } }); // init options if (request.swmgMethodPath) { options = { collectionName: request.swmgMethodPath._collectionName, data: request.swmgParamDict, operationId: request.swmgMethodPath.operationId, paramDefList: request.swmgMethodPath.parameters, schemaName: request.swmgMethodPath._schemaName }; } switch (request.swmgPathname) { // handle pet request case 'DELETE /pet/': case 'GET /pet/': case 'POST /pet': local.swmg._crudApi(options, onNext); break; case 'GET /pet/findByStatus': options.operationId = 'crudGetByQueryMany'; options.data.fields = '{}'; options.data.hint = '{}'; options.data.limit = 100; options.data.query = '{"status":{"$in":' + JSON.stringify(options.data.status) + '}}'; options.data.skip = 0; options.data.sort = '{"_timeModified":-1}'; local.swmg._crudApi(options, onNext); break; case 'GET /pet/findByTags': options.operationId = 'crudGetByQueryMany'; options.data.fields = '{}'; options.data.hint = '{}'; options.data.limit = 100; options.data.query = '{"status":{"$in":' + JSON.stringify(options.data.tags) + '}}'; options.data.skip = 0; options.data.sort = '{"_timeModified":-1}'; options.paramDefList[0].default = 'bird,cat,dog'; local.swmg._crudApi(options, onNext); break; case 'POST /pet/': options.data.upsert = true; options.data.body = { id: options.data.id, name: options.data.name, status: options.data.status }; options.operationId = 'crudUpdateOne'; local.swmg._crudApi(options, onNext); break; case 'POST /pet//': options.data.body = { additionalMetadata: options.data.additionalMetadata, file: options.data.file, filename: request.swmgBodyParsed && request.swmgBodyParsed.filename, id: options.id }; options.data.upsert = true; options.operationId = 'crudUpdateOne'; local.swmg._crudApi(options, onNext); break; case 'PUT /pet': options.data.upsert = true; options.operationId = 'crudReplaceOne'; local.swmg._crudApi(options, onNext); break; // handle store request case 'DELETE /store/order/': case 'GET /store/order/': case 'POST /store/order': local.swmg._crudApi(options, onNext); break; case 'GET /store/inventory': options.data = { body: [{ $group: { _id: '$status', total: { $sum: 1} } }, { $project: { _id: 0, status: '$_id', total: '$total' } }]}; options.operationId = 'crudAggregateMany'; local.swmg._crudApi(options, onNext); break; // handle user request case 'DELETE /user/': case 'GET /user/': case 'POST /user/createWithArray': case 'POST /user/createWithList': options.optionsId = { username: request.swmgParamDict.username}; local.swmg._crudApi(options, onNext); break; case 'POST /user': options.data.username = options.data.body.username; options.optionsId = { username: request.swmgParamDict.username}; local.swmg._crudApi(options, onNext); break; case 'PUT /user/': options.data.body.username = options.data.username; options.data.upsert = true; options.operationId = 'crudReplaceOne'; options.optionsId = { username: request.swmgParamDict.username}; local.swmg._crudApi(options, onNext); break; default: nextMiddleware(); } break; default: // validate no error occurred local.utility2.assert(!error, error); // respond with json-object response.end(JSON.stringify(data)); } }, nextMiddleware); }; onNext(); }); // run server-test local.utility2.testRunServer(local); break; }}((function () { 'use strict'; var local; // run shared js-env code (function () { // init local local = {}; // init js-env local.modeJs = (function () { try { return module.exports && typeof process.versions.node === 'string' && typeof require('http').createServer === 'function' && 'node'; } catch (errorCaughtNode) { return typeof navigator.userAgent === 'string' && typeof document.querySelector('body') === 'object' && 'browser'; } }()); // init global local.global = local.modeJs === 'browser' ? window : global; // export local local.global.local = local; // init swagger-mongodb local.swmg = local.modeJs === 'browser' ? window.swmg : require('swagger-mongodb'); // import swmg.local Object.keys(local.swmg.local).forEach(function (key) { local[key] = local[key] || local.swmg.local[key]; }); // init utility2 local.utility2 = local.swmg.local.utility2; // init onReady local.utility2.onReadyInit(); }()); return local;}())));
output from shell
output from phantomjs-lite
npm-dependencies
package-listing
package.json
{ "author": "kai zhu <kaizhu256@gmail.com>", "bin": { "swagger-mongodb": "index.js" }, "dependencies": { "mongodb-minimal": "2015.8.1", "swagger-ui-lite": "2015.6.2", "utility2": "~2015.8.5" }, "description": "lightweight swagger-ui crud-middleware backed by mongodb", "devDependencies": { "phantomjs-lite": "2015.7.1" }, "engines": { "node": ">=0.10 <=0.12" }, "keywords": [ "api", "browser", "cms", "crud", "mongo", "mongodb", "swagger", "swagger-ui", "web" ], "license": "MIT", "name": "swagger-mongodb", "os": ["darwin", "linux"], "repository" : { "type" : "git", "url" : "https://github.com/kaizhu256/node-swagger-mongodb.git" }, "scripts": { "build-ci": "node_modules/.bin/utility2 shRun shReadmeBuild", "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')}} \}\"", "start": "npm_config_mode_auto_restart=1 node_modules/.bin/utility2 shRun node test.js", "test": "node_modules/.bin/utility2 shRun shReadmeExportPackageJson && \node_modules/.bin/utility2 test test.js" }, "version": "2015.8.3"}
todo
- add logging feature
- rename delete to remove for naming consistency
- migrate to travis-ci docker container build
- add cached param for crudGetByQueryMany
- add SwmgUserLoginTokenCapped
- re-enable user login/logout
- test /user/login and /user/logout
- add max / min validation
- none
change since af87c5b9
- npm publish 2015.8.3
- lockdown npm dependencies
- none
changelog of last 50 commits
internal build-script
- build.sh
# build.sh # this shell script will run the build for this package shBuild() { # this function will run the main build local TEST_URL || return $? # init env export npm_config_mode_slimerjs=1 || return $? . node_modules/.bin/utility2 && shInit || return $? # run npm-test on published package shRun shNpmTestPublished || return $? # test example js script export npm_config_timeout_exit=10000 || return $? MODE_BUILD=testExampleJs shRunScreenCapture shReadmeTestJs example.js || return $? unset npm_config_timeout_exit || return $? # run npm-test MODE_BUILD=npmTest shRunScreenCapture npm test || return $? # create api-doc npm run-script build-doc || return $? # if running legacy-node, then do not continue [ "$(node --version)" \< "v0.12" ] && return # deploy app to heroku shRun shHerokuDeploy hrku01-$npm_package_name-$CI_BRANCH || return $? # test deployed app to heroku if [ "$CI_BRANCH" = alpha ] || [ "$CI_BRANCH" = beta ] || [ "$CI_BRANCH" = master ] then TEST_URL="https://hrku01-$npm_package_name-$CI_BRANCH.herokuapp.com" || return $? TEST_URL="$TEST_URL?modeTest=phantom&timeExit={{timeExit}}" || return $? MODE_BUILD=herokuTest shPhantomTest "$TEST_URL" || return $? fi}shBuild # save exit-code EXIT_CODE=$?# create package-listing MODE_BUILD=gitLsTree shRunScreenCapture shGitLsTree || exit $?# create recent changelog of last 50 commits MODE_BUILD=gitLog shRunScreenCapture git log -50 --pretty="%ai\u000a%B" || exit $?# if running legacy-node, then do not continue [ "$(node --version)" \< "v0.12" ] && exit $EXIT_CODE# upload build-artifacts to github, and if number of commits > 16, then squash older commits COMMIT_LIMIT=16 shBuildGithubUpload || exit $?exit $EXIT_CODE