Initial Release
This commit is contained in:
commit
9e468f86b3
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-transform-runtime",
|
||||||
|
"@babel/plugin-proposal-export-default-from"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
# EditorConfig helps developers define and maintain consistent
|
||||||
|
# coding styles between different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# Change these settings to your own preference
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# We recommend you to keep these unchanged
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
|
@ -0,0 +1,3 @@
|
||||||
|
MASTER_KEY=masterKey
|
||||||
|
PORT=0000
|
||||||
|
MONGODB_URI=mongodb://localhost:27017/test
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"extends": [
|
||||||
|
"standard"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"jest": true,
|
||||||
|
"jasmine": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
public
|
||||||
|
.tmp
|
||||||
|
.idea
|
||||||
|
.log
|
||||||
|
dist
|
||||||
|
docs
|
||||||
|
.env
|
||||||
|
npm-debug.log
|
||||||
|
.nyc_output
|
||||||
|
coverage
|
||||||
|
cassettes
|
|
@ -0,0 +1,18 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- v4
|
||||||
|
- v5
|
||||||
|
- v6
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- gcc-4.8
|
||||||
|
- g++-4.8
|
||||||
|
script:
|
||||||
|
- npm test -- -i --coverage
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- CXX=g++-4.8
|
||||||
|
- MASTER_KEY=masterKey
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"generator-rest": {
|
||||||
|
"srcDir": "src",
|
||||||
|
"apiDir": "api",
|
||||||
|
"authMethods": []
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"name": "node-deployer",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "src",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "node .",
|
||||||
|
"test": "jest",
|
||||||
|
"coverage": "npm test -- --coverage",
|
||||||
|
"postcoverage": "opn coverage/lcov-report/index.html",
|
||||||
|
"dev": "cross-env NODE_ENV=developemnt nodemon -i \"*.test.js\" .",
|
||||||
|
"prod": "cross-env NODE_ENV=production nodemon -i \"*.test.js\" -r dotenv-safe/config .",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"docs": "apidoc -i src -o docs && apidoc-markdown -p docs -o DOCS.md",
|
||||||
|
"postdocs": "open-cli docs/index.html"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"setupFilesAfterEnv": ["<rootDir>/test/setup.js"]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"apidoc": "^0.20.0",
|
||||||
|
"apidoc-markdown": "^0.2.1",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"babel-jest": "^25.1.0",
|
||||||
|
"cross-env": "^7.0.2",
|
||||||
|
"dotenv-safe": "^8.2.0",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"eslint-config-standard": "^14.1.0",
|
||||||
|
"eslint-plugin-import": "^2.20.0",
|
||||||
|
"eslint-plugin-node": "^11.0.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.0",
|
||||||
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
|
"jest-cli": "^25.1.0",
|
||||||
|
"mongodb-memory-server": "^6.3.3",
|
||||||
|
"nock": "^12.0.2",
|
||||||
|
"nodemon": "^2.0.2",
|
||||||
|
"open-cli": "^6.0.0",
|
||||||
|
"sinon": "^4.0.1",
|
||||||
|
"supertest": "^4.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.9.0",
|
||||||
|
"@babel/plugin-proposal-export-default-from": "^7.9.0",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.9.0",
|
||||||
|
"@babel/preset-env": "^7.9.0",
|
||||||
|
"@babel/register": "^7.9.0",
|
||||||
|
"@babel/runtime": "^7.9.0",
|
||||||
|
"bluebird": "^3.7.2",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"bodymen": "^1.1.1",
|
||||||
|
"compression": "^1.7.4",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"mongoose": "^5.9.4",
|
||||||
|
"mongoose-keywords": "^0.4.0",
|
||||||
|
"morgan": "^1.9.1",
|
||||||
|
"querymen": "^2.1.4",
|
||||||
|
"request": "^2.88.2",
|
||||||
|
"request-promise": "^4.2.5"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
const pkg = require('../../../package.json')
|
||||||
|
|
||||||
|
export const index = (req, res, next) =>
|
||||||
|
res.status(200).json({
|
||||||
|
title: 'Node Deployer API',
|
||||||
|
version: pkg.version,
|
||||||
|
mode: process.env.NODE_ENV
|
||||||
|
})
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Router } from 'express'
|
||||||
|
import { middleware as query } from 'querymen'
|
||||||
|
import { index } from './controller'
|
||||||
|
|
||||||
|
const router = new Router()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /homes Retrieve homes
|
||||||
|
* @apiName RetrieveHomes
|
||||||
|
* @apiGroup Home
|
||||||
|
* @apiUse listParams
|
||||||
|
* @apiSuccess {Object[]} homes List of homes.
|
||||||
|
* @apiError {Object} 400 Some parameters may contain invalid values.
|
||||||
|
*/
|
||||||
|
router.get('/',
|
||||||
|
index)
|
||||||
|
|
||||||
|
export default router
|
|
@ -0,0 +1,13 @@
|
||||||
|
import request from 'supertest'
|
||||||
|
import { apiRoot } from '../../config'
|
||||||
|
import express from '../../services/express'
|
||||||
|
import routes from '.'
|
||||||
|
|
||||||
|
const app = () => express(apiRoot, routes)
|
||||||
|
|
||||||
|
test('GET /homes 200', async () => {
|
||||||
|
const { status, body } = await request(app())
|
||||||
|
.get(`${apiRoot}`)
|
||||||
|
expect(status).toBe(200)
|
||||||
|
expect(Array.isArray(body)).toBe(true)
|
||||||
|
})
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { Router } from 'express'
|
||||||
|
import postJob from './postJob'
|
||||||
|
import home from './home'
|
||||||
|
|
||||||
|
const router = new Router()
|
||||||
|
|
||||||
|
router.use('/postJob', postJob)
|
||||||
|
router.use('/', home)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @apiDefine master Master access only
|
||||||
|
* You must pass `access_token` parameter or a Bearer Token authorization header
|
||||||
|
* to access this endpoint.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @apiDefine admin Admin access only
|
||||||
|
* You must pass `access_token` parameter or a Bearer Token authorization header
|
||||||
|
* to access this endpoint.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @apiDefine user User access only
|
||||||
|
* You must pass `access_token` parameter or a Bearer Token authorization header
|
||||||
|
* to access this endpoint.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @apiDefine listParams
|
||||||
|
* @apiParam {String} [q] Query to search.
|
||||||
|
* @apiParam {Number{1..30}} [page=1] Page number.
|
||||||
|
* @apiParam {Number{1..100}} [limit=30] Amount of returned items.
|
||||||
|
* @apiParam {String[]} [sort=-createdAt] Order of returned items.
|
||||||
|
* @apiParam {String[]} [fields] Fields to be returned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default router
|
|
@ -0,0 +1,118 @@
|
||||||
|
import {
|
||||||
|
success,
|
||||||
|
notFound
|
||||||
|
} from '../../services/response/'
|
||||||
|
import {
|
||||||
|
PostJob
|
||||||
|
} from '.'
|
||||||
|
const {
|
||||||
|
exec
|
||||||
|
} = require("child_process");
|
||||||
|
|
||||||
|
export const create = ({
|
||||||
|
bodymen: {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}, res, next) => {
|
||||||
|
return PostJob.create(body)
|
||||||
|
.then(success(res, 201))
|
||||||
|
.catch(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const index = ({
|
||||||
|
querymen: {
|
||||||
|
query,
|
||||||
|
select,
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
}, res, next) =>
|
||||||
|
PostJob.find(query, select, cursor)
|
||||||
|
.then(success(res))
|
||||||
|
.catch(next)
|
||||||
|
|
||||||
|
export const show = ({
|
||||||
|
params
|
||||||
|
}, res, next) =>
|
||||||
|
PostJob.findById(params.id)
|
||||||
|
.then(notFound(res))
|
||||||
|
.then(success(res))
|
||||||
|
.catch(next)
|
||||||
|
|
||||||
|
export const update = ({
|
||||||
|
bodymen: {
|
||||||
|
body
|
||||||
|
},
|
||||||
|
params
|
||||||
|
}, res, next) =>
|
||||||
|
PostJob.findById(params.id)
|
||||||
|
.then(notFound(res))
|
||||||
|
.then((postJob) => postJob ? Object.assign(postJob, body).save() : null)
|
||||||
|
.then(success(res))
|
||||||
|
.catch(next)
|
||||||
|
|
||||||
|
export const destroy = ({
|
||||||
|
params
|
||||||
|
}, res, next) => {
|
||||||
|
PostJob.findById(params.id)
|
||||||
|
.then(notFound(res))
|
||||||
|
.then((postJob) => postJob ? postJob.remove() : null)
|
||||||
|
.then(success(res, 204))
|
||||||
|
.catch(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const activate = ({
|
||||||
|
params
|
||||||
|
}, res, next) => {
|
||||||
|
return PostJob.findById(params.id)
|
||||||
|
.then(notFound(res))
|
||||||
|
.then((postJob) => {
|
||||||
|
if (postJob) {
|
||||||
|
postJob.active = true
|
||||||
|
postJob.save()
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(success(res, 204))
|
||||||
|
.catch(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deactivate = ({
|
||||||
|
params
|
||||||
|
}, res, next) => {
|
||||||
|
PostJob.findById(params.id)
|
||||||
|
.then(notFound(res))
|
||||||
|
.then((postJob) => {
|
||||||
|
if (postJob) {
|
||||||
|
postJob.active = false;
|
||||||
|
postJob.save();
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(success(res, 204))
|
||||||
|
.catch(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deploy = ({
|
||||||
|
params
|
||||||
|
}, res, next) => {
|
||||||
|
PostJob.findById(params.id)
|
||||||
|
.then(notFound(res))
|
||||||
|
.then((postJob) => {
|
||||||
|
if (postJob) {
|
||||||
|
exec(`cd ${postJob.deployLocation} && git fetch --all && git reset --hard origin/${postJob.gitBranch} && git pull `, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.log(`error: ${error.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.log(`stderr: ${stderr}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
});
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(success(res, 204))
|
||||||
|
.catch(next)
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { Router } from 'express'
|
||||||
|
import { middleware as query } from 'querymen'
|
||||||
|
import { middleware as body } from 'bodymen'
|
||||||
|
import { create, index, show, update, destroy, activate, deactivate, deploy } from './controller'
|
||||||
|
import { schema } from './model'
|
||||||
|
export PostJob, { schema } from './model'
|
||||||
|
|
||||||
|
const router = new Router()
|
||||||
|
const { gitUrl, deployLocation, deploySys } = schema.tree
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {post} /postJobs Create post job
|
||||||
|
* @apiName CreatePostJob
|
||||||
|
* @apiGroup PostJob
|
||||||
|
* @apiParam gitUrl Post job's gitUrl.
|
||||||
|
* @apiParam deployLocation Post job's deployLocation.
|
||||||
|
* @apiParam deploySys Post job's deploySys.
|
||||||
|
* @apiSuccess {Object} postJob Post job's data.
|
||||||
|
* @apiError {Object} 400 Some parameters may contain invalid values.
|
||||||
|
* @apiError 404 Post job not found.
|
||||||
|
*/
|
||||||
|
router.post('/',
|
||||||
|
body({ gitUrl, deployLocation, deploySys }),
|
||||||
|
create)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /postJobs Retrieve post jobs
|
||||||
|
* @apiName RetrievePostJobs
|
||||||
|
* @apiGroup PostJob
|
||||||
|
* @apiUse listParams
|
||||||
|
* @apiSuccess {Object[]} postJobs List of post jobs.
|
||||||
|
* @apiError {Object} 400 Some parameters may contain invalid values.
|
||||||
|
*/
|
||||||
|
router.get('/',
|
||||||
|
query(),
|
||||||
|
index)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /postJobs/:id Retrieve post job
|
||||||
|
* @apiName RetrievePostJob
|
||||||
|
* @apiGroup PostJob
|
||||||
|
* @apiSuccess {Object} postJob Post job's data.
|
||||||
|
* @apiError {Object} 400 Some parameters may contain invalid values.
|
||||||
|
* @apiError 404 Post job not found.
|
||||||
|
*/
|
||||||
|
router.get('/:id',
|
||||||
|
show)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {put} /postJobs/:id Update post job
|
||||||
|
* @apiName UpdatePostJob
|
||||||
|
* @apiGroup PostJob
|
||||||
|
* @apiParam gitUrl Post job's gitUrl.
|
||||||
|
* @apiParam deployLocation Post job's deployLocation.
|
||||||
|
* @apiParam deploySys Post job's deploySys.
|
||||||
|
* @apiSuccess {Object} postJob Post job's data.
|
||||||
|
* @apiError {Object} 400 Some parameters may contain invalid values.
|
||||||
|
* @apiError 404 Post job not found.
|
||||||
|
*/
|
||||||
|
router.put('/:id',
|
||||||
|
body({ gitUrl, deployLocation, deploySys }),
|
||||||
|
update)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {delete} /postJobs/:id Delete post job
|
||||||
|
* @apiName DeletePostJob
|
||||||
|
* @apiGroup PostJob
|
||||||
|
* @apiSuccess (Success 204) 204 No Content.
|
||||||
|
* @apiError 404 Post job not found.
|
||||||
|
*/
|
||||||
|
router.delete('/:id',
|
||||||
|
destroy)
|
||||||
|
|
||||||
|
router.get('/:id/activate',
|
||||||
|
activate)
|
||||||
|
|
||||||
|
router.get('/:id/deactivate',
|
||||||
|
deactivate)
|
||||||
|
|
||||||
|
router.post('/:id/deploy',
|
||||||
|
deploy)
|
||||||
|
|
||||||
|
export default router
|
|
@ -0,0 +1,75 @@
|
||||||
|
import request from 'supertest'
|
||||||
|
import { apiRoot } from '../../config'
|
||||||
|
import express from '../../services/express'
|
||||||
|
import routes, { PostJob } from '.'
|
||||||
|
|
||||||
|
const app = () => express(apiRoot, routes)
|
||||||
|
|
||||||
|
let postJob
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
postJob = await PostJob.create({})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('POST /postJobs 201', async () => {
|
||||||
|
const { status, body } = await request(app())
|
||||||
|
.post(`${apiRoot}`)
|
||||||
|
.send({ gitUrl: 'test', deployLocation: 'test', deploySys: 'test' })
|
||||||
|
expect(status).toBe(201)
|
||||||
|
expect(typeof body).toEqual('object')
|
||||||
|
expect(body.gitUrl).toEqual('test')
|
||||||
|
expect(body.deployLocation).toEqual('test')
|
||||||
|
expect(body.deploySys).toEqual('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('GET /postJobs 200', async () => {
|
||||||
|
const { status, body } = await request(app())
|
||||||
|
.get(`${apiRoot}`)
|
||||||
|
expect(status).toBe(200)
|
||||||
|
expect(Array.isArray(body)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('GET /postJobs/:id 200', async () => {
|
||||||
|
const { status, body } = await request(app())
|
||||||
|
.get(`${apiRoot}/${postJob.id}`)
|
||||||
|
expect(status).toBe(200)
|
||||||
|
expect(typeof body).toEqual('object')
|
||||||
|
expect(body.id).toEqual(postJob.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('GET /postJobs/:id 404', async () => {
|
||||||
|
const { status } = await request(app())
|
||||||
|
.get(apiRoot + '/123456789098765432123456')
|
||||||
|
expect(status).toBe(404)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('PUT /postJobs/:id 200', async () => {
|
||||||
|
const { status, body } = await request(app())
|
||||||
|
.put(`${apiRoot}/${postJob.id}`)
|
||||||
|
.send({ gitUrl: 'test', deployLocation: 'test', deploySys: 'test' })
|
||||||
|
expect(status).toBe(200)
|
||||||
|
expect(typeof body).toEqual('object')
|
||||||
|
expect(body.id).toEqual(postJob.id)
|
||||||
|
expect(body.gitUrl).toEqual('test')
|
||||||
|
expect(body.deployLocation).toEqual('test')
|
||||||
|
expect(body.deploySys).toEqual('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('PUT /postJobs/:id 404', async () => {
|
||||||
|
const { status } = await request(app())
|
||||||
|
.put(apiRoot + '/123456789098765432123456')
|
||||||
|
.send({ gitUrl: 'test', deployLocation: 'test', deploySys: 'test' })
|
||||||
|
expect(status).toBe(404)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('DELETE /postJobs/:id 204', async () => {
|
||||||
|
const { status } = await request(app())
|
||||||
|
.delete(`${apiRoot}/${postJob.id}`)
|
||||||
|
expect(status).toBe(204)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('DELETE /postJobs/:id 404', async () => {
|
||||||
|
const { status } = await request(app())
|
||||||
|
.delete(apiRoot + '/123456789098765432123456')
|
||||||
|
expect(status).toBe(404)
|
||||||
|
})
|
|
@ -0,0 +1,51 @@
|
||||||
|
import mongoose, { Schema } from 'mongoose'
|
||||||
|
|
||||||
|
const postJobSchema = new Schema({
|
||||||
|
gitUrl: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
deployLocation: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
deploySys: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
gitBranch: {
|
||||||
|
type: String,
|
||||||
|
default: 'master'
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
timestamps: true,
|
||||||
|
toJSON: {
|
||||||
|
virtuals: true,
|
||||||
|
transform: (obj, ret) => { delete ret._id }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
postJobSchema.methods = {
|
||||||
|
view (full) {
|
||||||
|
const view = {
|
||||||
|
// simple view
|
||||||
|
id: this.id,
|
||||||
|
gitUrl: this.gitUrl,
|
||||||
|
deployLocation: this.deployLocation,
|
||||||
|
deploySys: this.deploySys,
|
||||||
|
createdAt: this.createdAt,
|
||||||
|
updatedAt: this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
return full ? {
|
||||||
|
...view
|
||||||
|
// add properties for a full view
|
||||||
|
} : view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = mongoose.model('PostJob', postJobSchema)
|
||||||
|
|
||||||
|
export const schema = model.schema
|
||||||
|
export default model
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { PostJob } from '.'
|
||||||
|
|
||||||
|
let postJob
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
postJob = await PostJob.create({ gitUrl: 'test', deployLocation: 'test', deploySys: 'test' })
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('view', () => {
|
||||||
|
it('returns simple view', () => {
|
||||||
|
const view = postJob.view()
|
||||||
|
expect(typeof view).toBe('object')
|
||||||
|
expect(view.id).toBe(postJob.id)
|
||||||
|
expect(view.gitUrl).toBe(postJob.gitUrl)
|
||||||
|
expect(view.deployLocation).toBe(postJob.deployLocation)
|
||||||
|
expect(view.deploySys).toBe(postJob.deploySys)
|
||||||
|
expect(view.createdAt).toBeTruthy()
|
||||||
|
expect(view.updatedAt).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns full view', () => {
|
||||||
|
const view = postJob.view(true)
|
||||||
|
expect(typeof view).toBe('object')
|
||||||
|
expect(view.id).toBe(postJob.id)
|
||||||
|
expect(view.gitUrl).toBe(postJob.gitUrl)
|
||||||
|
expect(view.deployLocation).toBe(postJob.deployLocation)
|
||||||
|
expect(view.deploySys).toBe(postJob.deploySys)
|
||||||
|
expect(view.createdAt).toBeTruthy()
|
||||||
|
expect(view.updatedAt).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,21 @@
|
||||||
|
import http from 'http'
|
||||||
|
import { env, mongo, port, ip, apiRoot } from './config'
|
||||||
|
import mongoose from './services/mongoose'
|
||||||
|
import express from './services/express'
|
||||||
|
import api from './api'
|
||||||
|
|
||||||
|
const app = express(apiRoot, api)
|
||||||
|
const server = http.createServer(app)
|
||||||
|
|
||||||
|
if (mongo.uri) {
|
||||||
|
mongoose.connect(mongo.uri)
|
||||||
|
}
|
||||||
|
mongoose.Promise = Promise
|
||||||
|
|
||||||
|
setImmediate(() => {
|
||||||
|
server.listen(port, ip, () => {
|
||||||
|
console.log('Express server listening on http://%s:%d, in %s mode', ip, port, env)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default app
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
import path from 'path'
|
||||||
|
import merge from 'lodash/merge'
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
const requireProcessEnv = (name) => {
|
||||||
|
if (!process.env[name]) {
|
||||||
|
throw new Error('You must set the ' + name + ' environment variable')
|
||||||
|
}
|
||||||
|
return process.env[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
const dotenv = require('dotenv-safe')
|
||||||
|
dotenv.config({
|
||||||
|
path: path.join(__dirname, '../.env'),
|
||||||
|
example: path.join(__dirname, '../.env.example')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
all: {
|
||||||
|
env: process.env.NODE_ENV || 'development',
|
||||||
|
root: path.join(__dirname, '..'),
|
||||||
|
port: process.env.PORT || 9000,
|
||||||
|
ip: process.env.IP || '0.0.0.0',
|
||||||
|
apiRoot: process.env.API_ROOT || '',
|
||||||
|
masterKey: requireProcessEnv('MASTER_KEY'),
|
||||||
|
mongo: {
|
||||||
|
options: {
|
||||||
|
useUnifiedTopology: true,
|
||||||
|
useNewUrlParser: true,
|
||||||
|
useCreateIndex: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
test: { },
|
||||||
|
development: {
|
||||||
|
mongo: {
|
||||||
|
uri: 'mongodb://localhost/node-deployer-dev',
|
||||||
|
options: {
|
||||||
|
debug: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
ip: process.env.IP || undefined,
|
||||||
|
port: process.env.PORT || 8080,
|
||||||
|
mongo: {
|
||||||
|
uri: process.env.MONGODB_URI || 'mongodb://localhost/node-deployer'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = merge(config.all, config[config.all.env])
|
||||||
|
export default module.exports
|
|
@ -0,0 +1,3 @@
|
||||||
|
require('@babel/register')
|
||||||
|
|
||||||
|
exports = module.exports = require('./app')
|
|
@ -0,0 +1,27 @@
|
||||||
|
import express from 'express'
|
||||||
|
import cors from 'cors'
|
||||||
|
import compression from 'compression'
|
||||||
|
import morgan from 'morgan'
|
||||||
|
import bodyParser from 'body-parser'
|
||||||
|
import { errorHandler as queryErrorHandler } from 'querymen'
|
||||||
|
import { errorHandler as bodyErrorHandler } from 'bodymen'
|
||||||
|
import { env } from '../../config'
|
||||||
|
|
||||||
|
export default (apiRoot, routes) => {
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (env === 'production' || env === 'development') {
|
||||||
|
app.use(cors())
|
||||||
|
app.use(compression())
|
||||||
|
app.use(morgan('dev'))
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false }))
|
||||||
|
app.use(bodyParser.json())
|
||||||
|
app.use(apiRoot, routes)
|
||||||
|
app.use(queryErrorHandler())
|
||||||
|
app.use(bodyErrorHandler())
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Promise from 'bluebird'
|
||||||
|
import mongoose from 'mongoose'
|
||||||
|
import { mongo } from '../../config'
|
||||||
|
|
||||||
|
Object.keys(mongo.options || { }).forEach((key) => {
|
||||||
|
mongoose.set(key, mongo.options[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
mongoose.Promise = Promise
|
||||||
|
/* istanbul ignore next */
|
||||||
|
mongoose.Types.ObjectId.prototype.view = function () {
|
||||||
|
return { id: this.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
mongoose.connection.on('error', (err) => {
|
||||||
|
console.error('MongoDB connection error: ' + err)
|
||||||
|
process.exit(-1)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default mongoose
|
|
@ -0,0 +1,26 @@
|
||||||
|
export const success = (res, status) => (entity) => {
|
||||||
|
if (entity) {
|
||||||
|
res.status(status || 200).json(entity)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notFound = (res) => (entity) => {
|
||||||
|
if (entity) {
|
||||||
|
return entity
|
||||||
|
}
|
||||||
|
res.status(404).end()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authorOrAdmin = (res, user, userField) => (entity) => {
|
||||||
|
if (entity) {
|
||||||
|
const isAdmin = user.role === 'admin'
|
||||||
|
const isAuthor = entity[userField] && entity[userField].equals(user.id)
|
||||||
|
if (isAuthor || isAdmin) {
|
||||||
|
return entity
|
||||||
|
}
|
||||||
|
res.status(401).end()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
import * as response from '.'
|
||||||
|
|
||||||
|
let res
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
res = {
|
||||||
|
status: jest.fn(() => res),
|
||||||
|
json: jest.fn(() => res),
|
||||||
|
end: jest.fn(() => res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('success', () => {
|
||||||
|
it('responds with passed object and status 200', () => {
|
||||||
|
expect(response.success(res)({ prop: 'value' })).toBeNull()
|
||||||
|
expect(res.status).toBeCalledWith(200)
|
||||||
|
expect(res.json).toBeCalledWith({ prop: 'value' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('responds with passed object and status 201', () => {
|
||||||
|
expect(response.success(res, 201)({ prop: 'value' })).toBeNull()
|
||||||
|
expect(res.status).toBeCalledWith(201)
|
||||||
|
expect(res.json).toBeCalledWith({ prop: 'value' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not send any response when object has not been passed', () => {
|
||||||
|
expect(response.success(res, 201)()).toBeNull()
|
||||||
|
expect(res.status).not.toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('notFound', () => {
|
||||||
|
it('responds with status 404 when object has not been passed', () => {
|
||||||
|
expect(response.notFound(res)()).toBeNull()
|
||||||
|
expect(res.status).toBeCalledWith(404)
|
||||||
|
expect(res.end).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the passed object and does not send any response', () => {
|
||||||
|
expect(response.notFound(res)({ prop: 'value' })).toEqual({ prop: 'value' })
|
||||||
|
expect(res.status).not.toBeCalled()
|
||||||
|
expect(res.end).not.toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authorOrAdmin', () => {
|
||||||
|
let user, entity
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
user = {
|
||||||
|
id: 1,
|
||||||
|
role: 'user'
|
||||||
|
}
|
||||||
|
entity = {
|
||||||
|
author: {
|
||||||
|
id: 1,
|
||||||
|
equals (id) {
|
||||||
|
return id === this.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the passed entity when author is the same', () => {
|
||||||
|
expect(response.authorOrAdmin(res, user, 'author')(entity)).toEqual(entity)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the passed entity when author is admin', () => {
|
||||||
|
user.role = 'admin'
|
||||||
|
expect(response.authorOrAdmin(res, user, 'user')(entity)).toEqual(entity)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('responds with status 401 when author is not the same or admin', () => {
|
||||||
|
user.id = 2
|
||||||
|
expect(response.authorOrAdmin(res, user, 'author')(entity)).toBeNull()
|
||||||
|
expect(res.status).toBeCalledWith(401)
|
||||||
|
expect(res.end).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns null without sending response when entity has not been passed', () => {
|
||||||
|
expect(response.authorOrAdmin(res, user, 'author')()).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { EventEmitter } from 'events'
|
||||||
|
import MongodbMemoryServer from 'mongodb-memory-server'
|
||||||
|
import mongoose from '../src/services/mongoose'
|
||||||
|
|
||||||
|
EventEmitter.defaultMaxListeners = Infinity
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000
|
||||||
|
|
||||||
|
global.Array = Array
|
||||||
|
global.Date = Date
|
||||||
|
global.Function = Function
|
||||||
|
global.Math = Math
|
||||||
|
global.Number = Number
|
||||||
|
global.Object = Object
|
||||||
|
global.RegExp = RegExp
|
||||||
|
global.String = String
|
||||||
|
global.Uint8Array = Uint8Array
|
||||||
|
global.WeakMap = WeakMap
|
||||||
|
global.Set = Set
|
||||||
|
global.Error = Error
|
||||||
|
global.TypeError = TypeError
|
||||||
|
global.parseInt = parseInt
|
||||||
|
global.parseFloat = parseFloat
|
||||||
|
|
||||||
|
let mongoServer
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
mongoServer = new MongodbMemoryServer()
|
||||||
|
const mongoUri = await mongoServer.getUri()
|
||||||
|
await mongoose.connect(mongoUri, undefined, (err) => {
|
||||||
|
if (err) console.error(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await mongoose.disconnect()
|
||||||
|
await mongoServer.stop()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const { collections } = mongoose.connection
|
||||||
|
const promises = []
|
||||||
|
Object.keys(collections).forEach((collection) => {
|
||||||
|
promises.push(collections[collection].deleteMany({}))
|
||||||
|
})
|
||||||
|
await Promise.all(promises)
|
||||||
|
})
|
Loading…
Reference in New Issue