Skip to content

Commit

Permalink
create express consumer
Browse files Browse the repository at this point in the history
  • Loading branch information
AleksanderKoko committed Mar 22, 2018
1 parent e7926de commit 621ba4d
Show file tree
Hide file tree
Showing 13 changed files with 458 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/express-consumer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/logs
*.log
lerna-debug.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

/node_modules
/coverage

yarn.lock
package-lock.json
npm-shrinkwrap.json

/.eslintcache
/.npm
.yarn-integrity
.env

/.idea
/.vscode
.DS_Store
Thumbs.db
3 changes: 3 additions & 0 deletions packages/express-consumer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sapphire Package

A starter package serving as a template for future Sapphire packages. Not intended for public use.
7 changes: 7 additions & 0 deletions packages/express-consumer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const ExpressConsumer = require('./lib/express-consumer')
const helpers = require('./lib/helpers')

module.exports = {
ExpressConsumer,
helpers
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class ExpressConsumerInvalidArgument extends Error {
constructor(message, code = null) {
super(message, code)
Error.captureStackTrace(this, ExpressConsumerInvalidArgument)
this.code = code
}
}

module.exports = ExpressConsumerInvalidArgument
30 changes: 30 additions & 0 deletions packages/express-consumer/lib/errors/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const configExample = `{
cors: true,
publicFolder: '/public',
port: 4000,
}`

const errorHandler = `class SampleErrorHandler {
handle() {
}
}`

module.exports = {
shouldBeObject: () => ({
title: `The configuration argument should be an object`,
code: configExample
}),
shouldHaveKey: (key) => ({
title: `Config object should have: ${key}`,
code: configExample
}),
errorHandler: () => ({
title: 'Error handler should be an object and have a handle method',
code: errorHandler
}),
container: () => ({
title: 'Container should be an instance of sapphire container',
code: ''
})
}
200 changes: 200 additions & 0 deletions packages/express-consumer/lib/express-consumer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
const { Request, Adapter } = require('@sapphirejs/request')
const Response = require('@sapphirejs/response')
const { Container, resolver } = require('@sapphirejs/container')

const express = require('express')
const cors = require('cors')
const is = require('is')

const ExpressConsumerInvalidArgument = require('./errors/express-consumer-invalid-arguments')
const ExpressResponseConsumer = require('./express-response-consumer')
const message = require('./errors/messages')
const { objectHasMethod } = require('./helpers')

const methods = {
get: 'get',
post: 'post',
put: 'put',
patch: 'patch',
delete: 'delete'
}

const type = {
http: 'http',
command: 'command'
}

/**
* ExpressConsumer class.
* Consume a router and build an express server.
*
* @class ExpressConsumer
*/
class ExpressConsumer {
/**
*
* @param {Object} config
* @param {Object} errorHandler
* @param {Container} container
* @throws {ExpressConsumerInvalidArgument} if arguments are invalid
*/
constructor(config = null, errorHandler = null, container = null) {
this.__config = config
this.__errorHandler = errorHandler
this.__container = container
this.__httpInstance = null
this.__validateConfig()
this.__validateErrorHandler()
this.__validateContainer()
this.__initExpress()
}

/**
* Validates the config.
*
* @private
* @throws {ExpressConsumerInvalidArgument} if config is invalid
*/
__validateConfig() {
const config = this.__config

if(!is.object(config))
throw new ExpressConsumerInvalidArgument(
message.shouldBeObject().title,
message.shouldBeObject().code
)

const keys = ['publicFolder', 'port']
keys.forEach((key) => {
if(!config.hasOwnProperty(key))
throw new ExpressConsumerInvalidArgument(
message.shouldHaveKey(key).title,
message.shouldHaveKey(key).code
)
})
}

/**
*
* @private
*/
__validateContainer() {
if(!(this.__container instanceof Container))
throw new ExpressConsumerInvalidArgument(
message.container().title,
message.container().code,
)
}

/**
* Validates the error handler.
*
* @private
* @throws {ExpressConsumerInvalidArgument}
*/
__validateErrorHandler() {
if( !(
is.object(this.__errorHandler) &&
objectHasMethod(this.__errorHandler, 'handle')
))
throw new ExpressConsumerInvalidArgument(
message.errorHandler().title,
message.errorHandler().message,
)
}

/**
* Creates an express server.
*
* @private
*/
__initExpress() {
const { publicFolder } = this.__config
this.__route = express()
this.__route.use(cors())

// this.__route.use((error, req, resp, next) => {
// this.__errorHandler.handle(error)
// })

this.__route.use(express.static(publicFolder))
}

/**
* Register express routes
*
* @param routes
*/
createRoutes(routes = []) {
const router = this.__route
routes
.filter( route => route.type === type.http)
.forEach( route => {
const { path, meta, handler, middleware } = route

console.log(middleware)

const consumableMiddlewares = middleware.map(
item => (req, resp, next) => {
item(
resolver(this.__container, {
Request: new Request(new Adapter.Express(req)),
Response: new Response()
}),
next
)
}
)

//console.log(middleware)

if(!methods.hasOwnProperty(meta.method))
throw new Error()

router[meta.method](path, ...consumableMiddlewares, (req, resp) => {
try{
(
new ExpressResponseConsumer(
resp,
handler(
resolver(this.__container, {
Request: new Request(new Adapter.Express(req)),
Response: new Response()
})
)
)
).send()
}catch(e) {
(
new ExpressResponseConsumer(
resp,
this.__errorHandler.handle(new Response(), e)
)
).send()
}
})

})
}

/**
* Starts an Express server.
*
* @returns {void}
*/
start() {
this.__httpInstance = this.__route.listen(this.__config.port)
}

/**
* Closes the express server.
*
* @returns {void}
*/
close() {
if(this.__httpInstance)
this.__httpInstance.close()
}
}

module.exports = ExpressConsumer
13 changes: 13 additions & 0 deletions packages/express-consumer/lib/express-response-consumer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class ExpressResponseConsumer {
constructor(expressResponse, response) {
this.__expressResponse = expressResponse
this.__response = response
}

send() {
console.log(this.__response)
this.__expressResponse.json({ route: '/some-group/lol' })
}
}

module.exports = ExpressResponseConsumer
12 changes: 12 additions & 0 deletions packages/express-consumer/lib/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
/**
*
* @param object {object}
* @param method {string}
* @returns {boolean}
*/
objectHasMethod: (object, method) =>
Object.getOwnPropertyNames(
Object.getPrototypeOf(object)
).filter( key => key === method ).length === 1
}
39 changes: 39 additions & 0 deletions packages/express-consumer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@sapphirejs/express-consumer",
"version": "0.0.11",
"description": "A consumer package for @sapphire/router that uses express to build a server",
"license": "MIT",
"author": "Aleksander Koko <aleksanderkoko@gmail.com>",
"keywords": [
"sapphire",
"framework",
"router",
"router consumer"
],
"main": "index.js",
"engines": {
"node": ">= 8.3.0"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"test": "jest",
"lint": "eslint ./src",
"test-ci": "jest --coverage && cat ./coverage/lcov.info | coveralls"
},
"dependencies": {
"@sapphirejs/router": "0.0.12",
"@sapphirejs/request": "0.0.14",
"@sapphirejs/response": "0.0.12",
"@sapphirejs/container": "0.0.14",
"cors": "^2.8.4",
"express": "^4.16.3",
"is": "^3.2.1"
},
"devDependencies": {
"axios": "^0.18.0",
"coveralls": "^3.0.0",
"jest": "^22.0.4"
}
}
Loading

0 comments on commit 621ba4d

Please sign in to comment.