-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b13dd21
Showing
12 changed files
with
7,231 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"env": { | ||
"commonjs": true, | ||
"es6": true, | ||
"node": true | ||
}, | ||
"extends": "standard", | ||
"globals": { | ||
"Atomics": "readonly", | ||
"SharedArrayBuffer": "readonly" | ||
}, | ||
"parserOptions": { | ||
"ecmaVersion": 2018 | ||
}, | ||
"rules": { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules/ | ||
.nyc_output/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module.exports = { | ||
Controller: require('./src/controller'), | ||
buildRouter: require('./src/router'), | ||
utils: require('./src/utils') | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "express-toolkit", | ||
"version": "1.0.0", | ||
"description": "Handy tools for building expressjs based http microservices", | ||
"main": "index.js", | ||
"scripts": { | ||
"lint": "npx eslint index.js test/* --fix", | ||
"test": "nyc --reporter=text ava test/*.spec.js --verbose", | ||
"commit": "npx git-cz", | ||
"changelog": "rm CHANGELOG.md; npx conventional-changelog -t -i CHANGELOG.md --same-file", | ||
"release": "sh scripts/release.sh" | ||
}, | ||
"author": "Mattia Alfieri", | ||
"license": "MIT", | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "npm test", | ||
"pre-push": "npm test" | ||
} | ||
}, | ||
"keywords": [ | ||
"http", | ||
"microservices", | ||
"express" | ||
], | ||
"dependencies": { | ||
"express": "^4.16.4", | ||
"throwable-http-errors": "^1.0.1" | ||
}, | ||
"devDependencies": { | ||
"ava": "^1.4.1", | ||
"eslint": "^5.16.0", | ||
"eslint-config-standard": "^12.0.0", | ||
"eslint-plugin-import": "^2.17.2", | ||
"eslint-plugin-node": "^8.0.1", | ||
"eslint-plugin-promise": "^4.1.1", | ||
"eslint-plugin-standard": "^4.0.0", | ||
"husky": "^1.3.1", | ||
"mongodb-memory-server": "^5.1.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#!/bin/sh | ||
# Based on https://gist.github.com/fatmatto/9e989d582c391446dcf4bf0c8116cb6a | ||
# Increments the project version (e.g. from 2.3.0 to 2.4.0) | ||
# It handles stuff like | ||
# * CHANGELOG | ||
# * NPM package version | ||
# * Git tags | ||
|
||
|
||
# Calculating the new version requires to know which kind of update this is | ||
# The default version increment is patch | ||
# Used values: major|minor|patch where in x.y.z : | ||
# major=x | ||
# minor=y | ||
# patch=z | ||
|
||
if [ -z "$1" ] | ||
then | ||
versionType="patch" | ||
else | ||
versionType=$1 | ||
fi | ||
|
||
# Increment version without creating a tag and a commit (we will create them later) | ||
npm --no-git-tag-version version $versionType || exit 1 | ||
|
||
# Using the package.json version | ||
version="$(grep '"version"' package.json | cut -d'"' -f4)" | ||
|
||
# Generate changelog from commits | ||
rm CHANGELOG.md; | ||
npx conventional-changelog -t -i CHANGELOG.md --same-file; | ||
|
||
# Build the commit | ||
git add package.json; | ||
git add CHANGELOG.md; | ||
|
||
git commit -m "📦 Release $version" | ||
|
||
# Create an annotated tag | ||
git tag -a $version -m "📦 Release $version" | ||
|
||
# Gotta push them all | ||
git push origin master --follow-tags; | ||
|
||
# Release it! | ||
npm publish |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
|
||
const DEFAULT_LIMIT_VALUE = 100 | ||
const DEFAULT_SKIP_VALUE = 0 | ||
const { getSorting } = require('./utils') | ||
const Errors = require('throwable-http-errors') | ||
|
||
class Controller { | ||
/** | ||
* | ||
* @param {Object} config The configuration object | ||
* @param {String} config.name The resource name | ||
* @param {Number} config.defaultSkipValue The default skip value to be used in find() queries | ||
* @param {Number} config.defaultLimitValue The default skip value to be used in find() queries | ||
* @param {Object} config.model A mongoose model | ||
* | ||
*/ | ||
constructor (config) { | ||
this.Model = config.model | ||
this.name = config.name | ||
this.defaultSkipValue = config.defaultSkipValue || DEFAULT_SKIP_VALUE | ||
this.defaultLimitValue = config.defaultLimitValue || DEFAULT_LIMIT_VALUE | ||
} | ||
list (query) { | ||
let skip = this.defaultSkipValue | ||
let limit = this.defaultLimitValue | ||
if (query.hasOwnProperty('limit')) { | ||
limit = Number(query.limit) | ||
} | ||
if (query.hasOwnProperty('skip')) { | ||
skip = Number(query.skip) | ||
} | ||
|
||
let sort = getSorting(query) | ||
|
||
// Deleting modifiers from the query | ||
delete query.skip | ||
delete query.limit | ||
delete query.sortby | ||
delete query.sortorder | ||
|
||
return this.Model | ||
.find(query) | ||
.sort(sort) | ||
.skip(skip) | ||
.limit(limit) | ||
} | ||
|
||
async findOne (query) { | ||
const instance = await this.Model.findOne(query) | ||
if (instance === null) { | ||
throw new Errors.NotFound() | ||
} else { | ||
return instance | ||
} | ||
} | ||
async getById (id) { | ||
const instance = await this.Model.findOne({ | ||
_id: id | ||
}) | ||
|
||
if (instance === null) { | ||
throw new Errors.NotFound() | ||
} else { | ||
return instance | ||
} | ||
} | ||
|
||
async create (data) { | ||
const instance = new this.Model(data) | ||
|
||
// In testing we don't have the | ||
|
||
let validationError = instance.validateSync() | ||
if (validationError) { | ||
throw new Errors.BadRequest(validationError.message) | ||
} | ||
|
||
let savedInstance = await instance.save() | ||
|
||
return savedInstance.toJSON() | ||
} | ||
|
||
async updateOne (query, update) { | ||
let instance = await this.Model.findOne(query) | ||
|
||
if (instance === null) { | ||
throw new Errors.NotFound() | ||
} | ||
|
||
for (var k in update) { | ||
instance.set(k, update[k]) | ||
} | ||
|
||
let validationError = instance.validateSync() | ||
if (validationError) { | ||
throw new Errors.BadRequest(validationError.message) | ||
} | ||
|
||
return instance.save() | ||
} | ||
|
||
async updateById (id, update) { | ||
let instance = await this.Model.findOne({ | ||
_id: id | ||
}) | ||
|
||
if (instance === null) { | ||
throw new Errors.NotFound() | ||
} | ||
|
||
for (var k in update) { | ||
instance.set(k, update[k]) | ||
} | ||
|
||
let validationError = instance.validateSync() | ||
if (validationError) { | ||
throw new Errors.BadRequest(validationError.message) | ||
} | ||
|
||
return instance.save() | ||
} | ||
|
||
/** | ||
* Removes resources by query | ||
* @param {Object} query Match resources to remove. | ||
*/ | ||
delete (query) { | ||
return this.Model.remove(query) | ||
} | ||
|
||
/** | ||
* Removes a resource by id | ||
* @param {String} id The resource's id. | ||
*/ | ||
deleteById (id) { | ||
return this.Model.remove({ _id: id }) | ||
} | ||
} | ||
|
||
module.exports = Controller |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
|
||
const express = require('express') | ||
const { asyncMiddleware } = require('./utils') | ||
/** | ||
* Builds an expressjs router instance | ||
* @param {Object} config | ||
* @param {Object} config.controller | ||
*/ | ||
function buildRouter (config) { | ||
const router = express.Router() | ||
router.get('/count', asyncMiddleware(async (req, res, next) => { | ||
let query = req.query | ||
let count = await config.controller.count(query) | ||
res.send({ status: true, data: { count: count } }) | ||
})) | ||
|
||
router.get('/', asyncMiddleware(async (req, res, next) => { | ||
let query = req.query | ||
let resources = await config.controller.list(query) | ||
res.send({ status: true, data: resources }) | ||
})) | ||
|
||
router.get('/:id', asyncMiddleware(async (req, res, next) => { | ||
let resource = await config.controller.getById(req.params.id) | ||
|
||
if (resource === null) { throw new Errors.NotFound('Cannot find resource with id ' + req.params.id) } | ||
|
||
res.send({ status: true, data: resource }) | ||
})) | ||
|
||
router.post('/', asyncMiddleware(async (req, res, next) => { | ||
let resource = await config.controller.create(req.body) | ||
res.send({ status: true, data: resource }) | ||
})) | ||
|
||
router.put('/:id', asyncMiddleware(async (req, res, next) => { | ||
let resource = await config.controller.update(req.params.id, req.body) | ||
|
||
res.send({ status: true, data: resource }) | ||
})) | ||
|
||
router.delete('/:id', asyncMiddleware(async (req, res, next) => { | ||
await config.controller.delete(req.params.id) | ||
res.send({ status: true }) | ||
})) | ||
|
||
return router | ||
} | ||
|
||
module.exports = buildRouter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
'use strict' | ||
|
||
const Errors = require('throwable-http-errors') | ||
/** | ||
* | ||
* @param {Function} fn The function to wrap | ||
*/ | ||
const asyncMiddleware = fn => | ||
(req, res, next) => { | ||
Promise.resolve(fn(req, res, next)) | ||
.catch(next) | ||
} | ||
|
||
/** | ||
* | ||
* @param {String} str The string to test | ||
* @return true if the string is valid json | ||
*/ | ||
let isJSON = (str) => { | ||
try { | ||
return (JSON.parse(str) && !!str) | ||
} catch (e) { | ||
return false | ||
} | ||
} | ||
|
||
/** | ||
* Returns the sort object to pass to MongoDB | ||
* @param {Object} query The parsed query string object | ||
*/ | ||
function getSorting (query) { | ||
let sortBy = query.sortby || '_id' | ||
let sortOrder = query.sortorder || 'DESC' | ||
|
||
if (sortOrder === 'DESC') { | ||
sortOrder = -1 | ||
} else if (sortOrder === 'ASC') { | ||
sortOrder = 1 | ||
} else { | ||
throw new Errors.BadRequest(`sortorder parameter can be "ASC" or "DESC". Got "${sortOrder}."`) | ||
} | ||
|
||
let sorting = {} | ||
|
||
sorting[sortBy] = sortOrder | ||
|
||
return sorting | ||
} | ||
|
||
module.exports = { | ||
getSorting, | ||
asyncMiddleware, | ||
isJSON | ||
} |
Oops, something went wrong.