Skip to content

Commit

Permalink
Merge pull request #49 from domapic/release-1.0.0-beta.20
Browse files Browse the repository at this point in the history
Release 1.0.0 beta.20
  • Loading branch information
javierbrea authored Mar 2, 2019
2 parents 0e9634b + 76e9170 commit 832600b
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ addons:
script:
- npm test
- npm run coveralls
- sonar-scanner
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then sonar-scanner; fi'
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
### Removed

## [1.0.0-beta.20] - 2019-03-02
### Added
- Add "init" method to Server.
- Add "addStatic" method to Server.

### Fixed
- Add rejectUntrusted and auth configuration options to swagger

### Changed
- Reduce timeout in Client requests
- Serve swagger under "/swagger" path, not "doc/api"

## [1.0.0-beta.19] - 2019-01-05
### Added
- Add "auth" option, which allows to disable authentication for all api resources and requests origins.
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ It provides:
* API operations as Promises.
* Automatic error handling mapped to HTTP errors.
* Openapi.json auto generated and served.
* _Swagger_ web interface.
* Built-in _Swagger_ web interface.
* Allows serving statics under paths of your choice.
* __Client__ to other Domapic services:
* Automatic authentication if API resource requires it.
* Requests as Promises.
Expand Down Expand Up @@ -219,11 +220,13 @@ Default options values (or saved values if the `--saveConfig` option is used) ar
The `service.server` object has methods:

* `start` - Starts the server. Returns a promise, resolved when the server is running. Once the server is started, it is not possible to add more open api definitions, operations, or authentication implementations.
* `init` - Only initialize the server, adding all internal middlewares and routers, and returns the server instance. This allows you to add more custom middlewares, sockets, etc. This method should be called just before calling to the "start" method.
* `extendOpenApi` - Add open api definitions to the server. Read the [Adding API resources](#adding-api-resources) chapter for further info.
* `addOperations` - Add operations related to api paths. Read [Adding API resources](#adding-api-resources)).
* `addAuthentication` - Add authentication implementations. Read [Authentication](#authentication).
* `addAuthorization` - Add authorization roles. Read [Authentication](#authentication).
* `addMiddleware` - Add custom middlewares to api. Read [Custom middlewares](#custom-middlewares).
* `addStatic` - Serve statics. First argument defines server path, second argument defines fileSystem path. `addStatic("/assets", path.resolve(__dirname, "assets"))`. Statics added with this method will be served using gzip compression.

[back to top](#table-of-contents)

Expand Down
14 changes: 12 additions & 2 deletions lib/api/config/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
"type": "string"
}
},
"auth": {
"description": "If false, the authentication will be disabled for all api resources and origins",
"type": "boolean"
},
"rejectUntrusted": {
"description": "Reject untrusted ssl certificates when using built-in client for making requests to another services",
"type": "boolean"
},
"color": {
"description": "Use colors in logs",
"type": "boolean"
Expand All @@ -47,7 +55,7 @@
"type": "string"
}
},
"required": ["logLevel", "port", "hostName", "authDisabled"],
"required": ["logLevel", "port", "hostName", "authDisabled", "auth", "rejectUntrusted"],
"additionalProperties": false,
"example": {
"logLevel": "debug",
Expand All @@ -57,7 +65,9 @@
"::1/128"
],
"color": true,
"hostName": "0.0.0.0"
"hostName": "0.0.0.0",
"auth": true,
"rejectUntrusted": false
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/bases/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const uuidv4 = require('uuid/v4')
const successResponses = [200, 201, 204]
const unAuthenticatedResponses = [401]
const serverUnavailableErrors = ['ECONNREFUSED', 'EHOSTDOWN', 'ETIMEDOUT', 'EHOSTUNREACH', 'ENOTFOUND']
const requestTimeOut = 5000
const requestTimeOut = 1000

const Client = function (core) {
const templates = core.utils.templates.compiled.client
Expand Down
181 changes: 111 additions & 70 deletions lib/bases/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ const http = require('http')
const https = require('https')
const path = require('path')
const fs = require('fs')
const compression = require('compression')

const express = require('express')
const Promise = require('bluebird')

const Api = require('./server/api/Api')
const SecurityMethods = require('./server/security')
const Doc = require('./server/Doc')
const Swagger = require('./server/Swagger')
const Middlewares = require('./server/Middlewares')

const Server = function (core) {
Expand All @@ -20,10 +21,12 @@ const Server = function (core) {
const middlewares = new Middlewares(core)
const securityMethods = new SecurityMethods(core)
const api = new Api(core, middlewares, securityMethods)
const doc = new Doc()
const swagger = new Swagger(middlewares)

let started = false
let startPromise
let initPromise
let statics = []

const StartServerErrorHandler = function (startOptions, reject) {
return function (error) {
Expand Down Expand Up @@ -94,82 +97,105 @@ const Server = function (core) {
})
}

const startServer = function (startOptions, routers) {
return new Promise((resolve, reject) => {
const serverMethod = startOptions.sslKey ? startHTTPS : startHTTP
const nodeServerOptions = startOptions.sslKey ? {
key: fs.readFileSync(startOptions.sslKey),
cert: fs.readFileSync(startOptions.sslCert)
} : {}

app.disable('etag')
app.set('view engine', hbs)

app.use('/doc', middlewares.staticPolicies)
app.use('/doc', middlewares.lowerRequestLogLevel)
const initServer = function (startOptions, routers) {
const serverMethod = startOptions.sslKey ? startHTTPS : startHTTP
const nodeServerOptions = startOptions.sslKey ? {
key: fs.readFileSync(startOptions.sslKey),
cert: fs.readFileSync(startOptions.sslCert)
} : {}

app.disable('etag')
app.set('view engine', hbs)

app.use(middlewares.addRequestId)
app.use(middlewares.jsonBodyParser)
app.use(middlewares.urlEncoded)
app.use(middlewares.logRequest)
app.use(middlewares.helmet)
app.use(middlewares.cors)

app.use('/swagger', routers.swagger)
app.use('/swagger/*', middlewares.notFound)
app.use('/api', routers.api)
app.use('/api/*', middlewares.notFound)

if (!statics.length) {
app.use('/assets', middlewares.staticPolicies)
app.use('/assets', middlewares.lowerRequestLogLevel)
app.use('/assets', compression())
app.use('/assets', express.static(path.resolve(__dirname, 'server', 'assets')))

app.use(middlewares.addRequestId)
app.use(middlewares.jsonBodyParser)
app.use(middlewares.urlEncoded)
app.use(middlewares.logRequest)
app.use(middlewares.helmet)
app.use(middlewares.cors)

app.use('/doc', routers.doc)
app.use('/api', routers.api)

// temporarily redirect index to api docs
app.get('/', (req, res) => {
res.redirect('/doc/api')
res.redirect('/swagger')
})
}
statics.forEach(staticResource => {
app.use(staticResource.serverPath, middlewares.staticPolicies)
app.use(staticResource.serverPath, middlewares.lowerRequestLogLevel)
app.use(staticResource.serverPath, compression())
app.use(staticResource.serverPath, express.static(staticResource.staticsPath, {
maxAge: 31536000
}))
})

app.use(middlewares.notFound)
app.use(middlewares.errorTrace)
app.use(middlewares.errorHandler)
app.use(middlewares.errorTrace)
app.use(middlewares.errorHandler)

serverMethod(nodeServerOptions, app)
.then((server) => {
const serverOptions = {
port: startOptions.port
}
if (startOptions.hostName && startOptions.hostName.length > 0) {
serverOptions.host = startOptions.hostName
}
server.on('error', new StartServerErrorHandler(startOptions, reject))
return serverMethod(nodeServerOptions, app)
}

server.listen(serverOptions, new ServerStarted(resolve, reject, server))
})
const startServer = function (server, startOptions) {
return new Promise((resolve, reject) => {
const serverOptions = {
port: startOptions.port
}
if (startOptions.hostName && startOptions.hostName.length > 0) {
serverOptions.host = startOptions.hostName
}
server.on('error', new StartServerErrorHandler(startOptions, reject))

server.listen(serverOptions, new ServerStarted(resolve, reject, server))
})
}

const getRouters = function () {
return Promise.props({
api: api.initRouter(),
doc: doc.initRouter()
swagger: swagger.initRouter()
})
}

const validateAndStart = function (startOptions) {
return validateOptions(startOptions)
.then(registerViewPartials)
.then(getRouters)
.then((routers) => {
return startServer(startOptions, routers)
})
.then((server) => {
return core.tracer.group([
{ info: templates.serverStarted({port: startOptions.port}) },
{ trace: [templates.serverOptionsLogTitle(), startOptions] }
])
})
.catch((err) => {
if (core.errors.isControlled(err)) {
return core.tracer.error(err.message)
}
return Promise.reject(err)
const initAndGetOptions = function () {
if (!initPromise) {
initPromise = core.config.get()
.then(config => {
const startOptions = {
sslKey: config.sslKey,
sslCert: config.sslCert,
hostName: config.hostName,
port: config.port,
name: config.name
}
return validateOptions(startOptions)
.then(registerViewPartials)
.then(getRouters)
.then(routers => {
return initServer(startOptions, routers)
.then(server => {
return Promise.resolve({
options: startOptions,
server: server
})
})
})
})
}
return initPromise
}

const init = () => {
return initAndGetOptions()
.then(serverAndOptions => {
return Promise.resolve(serverAndOptions.server)
})
}

Expand All @@ -178,26 +204,41 @@ const Server = function (core) {
return Promise.reject(new core.errors.Conflict(templates.serverStartedError()))
}
if (!startPromise) {
startPromise = core.config.get()
.then((config) => {
return validateAndStart({
sslKey: config.sslKey,
sslCert: config.sslCert,
hostName: config.hostName,
port: config.port,
name: config.name
})
startPromise = initAndGetOptions()
.then((serverAndOptions) => {
return startServer(serverAndOptions.server, serverAndOptions.options)
.then(() => {
return core.tracer.group([
{ info: templates.serverStarted({port: serverAndOptions.options.port}) },
{ trace: [templates.serverOptionsLogTitle(), serverAndOptions.options] }
])
})
})
.catch((err) => {
if (core.errors.isControlled(err)) {
return core.tracer.error(err.message)
}
return Promise.reject(err)
})
}
return startPromise
}

const addStatic = (serverPath, staticsPath) => {
statics.push({
serverPath,
staticsPath
})
}

return {
extendOpenApi: api.extendOpenApi,
addOperations: api.addOperations,
addMiddleware: api.addMiddleware,
addAuthentication: api.addAuthentication,
addAuthorization: api.addAuthorization,
addStatic: addStatic,
init: init,
start: start
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/bases/server/Middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const Middlewares = function (core) {
}

const staticPolicies = (req, res, next) => {
res.set('Content-Security-Policy', "default-src 'self' 'unsafe-inline' *.googleapis.com *.gstatic.com; img-src 'self' data:")
res.set('Content-Security-Policy', "default-src 'self' 'unsafe-inline' *.gravatar.com *.googleapis.com *.gstatic.com data:; img-src 'self' *.gravatar.com data:;")
res.set('Referrer-Policy', 'no-referrer')
res.set('Feature-Policy', "payment 'none'; microphone 'none'; camera 'none'; geolocation 'none'; sync-xhr 'self'")
next()
Expand Down
13 changes: 8 additions & 5 deletions lib/bases/server/Doc.js → lib/bases/server/Swagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ const Promise = require('bluebird')

const swaggerUiAssetPath = require('swagger-ui-dist').getAbsoluteFSPath()

const Doc = function () {
const Swagger = function (middlewares) {
const initRouter = function () {
const router = express.Router()

router.use('/assets/swagger', express.static(swaggerUiAssetPath))
router.use('/assets', express.static(swaggerUiAssetPath))

router.route('/api').get((req, res, next) => {
router.use(middlewares.staticPolicies)
router.use(middlewares.lowerRequestLogLevel)

router.route('/').get((req, res, next) => {
res.type('html').render(path.resolve(__dirname, 'views', 'swagger.hbs'), {
swaggerUiAssetPath: '/doc/assets/swagger',
swaggerUiAssetPath: '/swagger/assets',
apiDefinitionUrl: '/api/openapi.json'
})
})
Expand All @@ -28,4 +31,4 @@ const Doc = function () {
}
}

module.exports = Doc
module.exports = Swagger
Loading

0 comments on commit 832600b

Please sign in to comment.