mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-21 16:52:00 -07:00
Merge pull request #2 from christroutner/koa-api-boilerplate
Syncing with upstream Koa api boilerplate
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Business logic for the /contact endpoint.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
|
||||
const config = require('../../config')
|
||||
|
||||
const NodeMailer = require('../lib/nodemailer')
|
||||
const nodemailer = new NodeMailer()
|
||||
const wlogger = require('./wlogger')
|
||||
|
||||
let _this
|
||||
|
||||
class ContactLib {
|
||||
constructor () {
|
||||
_this = this
|
||||
_this.config = config
|
||||
_this.nodemailer = nodemailer
|
||||
}
|
||||
|
||||
async sendEmail (emailObj) {
|
||||
try {
|
||||
// Validate input
|
||||
if (!emailObj.email || typeof emailObj.email !== 'string') {
|
||||
throw new Error("Property 'email' must be a string!")
|
||||
}
|
||||
|
||||
if (!emailObj.formMessage || typeof emailObj.formMessage !== 'string') {
|
||||
throw new Error("Property 'formMessage' must be a string!")
|
||||
}
|
||||
|
||||
// If an email list exists, the email will be sended to that list
|
||||
// otherwhise will be sended by default to the variable "_this.config.emailUser"
|
||||
let _to = [_this.config.emailUser]
|
||||
|
||||
// Email list is optional
|
||||
if (emailObj.emailList) {
|
||||
if (
|
||||
!Array.isArray(emailObj.emailList) ||
|
||||
!emailObj.emailList.length > 0
|
||||
) {
|
||||
throw new Error("Property 'emailList' must be a array of emails!")
|
||||
} else {
|
||||
_to = emailObj.emailList
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Trying send message to : ${_to}`)
|
||||
|
||||
emailObj.subject = 'Someone wants contact with you.'
|
||||
emailObj.to = _to
|
||||
|
||||
const result = await _this.nodemailer.sendEmail(emailObj)
|
||||
return result
|
||||
} catch (err) {
|
||||
wlogger.error('Error in lib/contact.js/sendEmail()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = ContactLib
|
||||
@@ -1,78 +1,77 @@
|
||||
/*
|
||||
Controller or the /contact REST API endpoints.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
|
||||
const config = require('../../../config')
|
||||
|
||||
const NodeMailer = require('../../lib/nodemailer')
|
||||
const nodemailer = new NodeMailer()
|
||||
const ContactLib = require('../../lib/contact')
|
||||
const contactLib = new ContactLib()
|
||||
|
||||
let _this
|
||||
|
||||
class Contact {
|
||||
class ContactController {
|
||||
constructor () {
|
||||
_this = this
|
||||
_this.config = config
|
||||
_this.nodemailer = nodemailer
|
||||
_this.contactLib = contactLib
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /contact/email Send Email
|
||||
* @apiName SendMail
|
||||
* @apiGroup Contact
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "obj": { "email": "email@format.com", "formMessage": "a message" } }' localhost:5001/contact/email
|
||||
*
|
||||
* @apiParam {Object} obj object (required)
|
||||
* @apiParam {String} obj.email Sender Email.
|
||||
* @apiParam {String} obj.formMessage Message.
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
*
|
||||
* success:true
|
||||
*
|
||||
* }
|
||||
*
|
||||
* @apiError UnprocessableEntity Missing required parameters
|
||||
*
|
||||
* @apiErrorExample {json} Error-Response:
|
||||
* HTTP/1.1 422 Unprocessable Entity
|
||||
* {
|
||||
* "status": 422,
|
||||
* "error": "Unprocessable Entity"
|
||||
* }
|
||||
*/
|
||||
async email (ctx) {
|
||||
try {
|
||||
const data = ctx.request.body
|
||||
|
||||
const emailObj = data.obj
|
||||
// Validate input
|
||||
if (!emailObj.email || typeof emailObj.email !== 'string') {
|
||||
throw new Error("Property 'email' must be a string!")
|
||||
}
|
||||
const isEmail = await _this.nodemailer.validateEmail(emailObj.email)
|
||||
|
||||
if (!isEmail) {
|
||||
throw new Error("Property 'email' must be email format!")
|
||||
}
|
||||
|
||||
if (!emailObj.formMessage || typeof emailObj.formMessage !== 'string') {
|
||||
throw new Error("Property 'message' must be a string!")
|
||||
}
|
||||
|
||||
if (!emailObj.payloadTitle || typeof emailObj.payloadTitle !== 'string') {
|
||||
throw new Error("Property 'payloadTitle' must be a string!")
|
||||
}
|
||||
|
||||
// If an email list exists, the email will be sended to that list
|
||||
// otherwhise will be sended by default to the variable "_this.config.emailUser"
|
||||
let _to = [_this.config.emailUser]
|
||||
|
||||
// Email list is optional
|
||||
if (emailObj.emailList) {
|
||||
if (
|
||||
!Array.isArray(emailObj.emailList) ||
|
||||
!emailObj.emailList.length > 0
|
||||
) {
|
||||
throw new Error("Property 'emailList' must be a array of emails!")
|
||||
} else {
|
||||
_to = emailObj.emailList
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Trying send message to : ${_to}`)
|
||||
|
||||
emailObj.subject = 'Someone wants to share a document with you.'
|
||||
emailObj.to = _to
|
||||
|
||||
await _this.nodemailer.sendEmail(emailObj)
|
||||
await _this.contactLib.sendEmail(emailObj)
|
||||
|
||||
ctx.body = {
|
||||
success: true
|
||||
}
|
||||
} catch (err) {
|
||||
// ctx.body = {
|
||||
// success: false
|
||||
// }
|
||||
// console.error(`Error: `, err)
|
||||
// throw err
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
if (err.status) {
|
||||
if (err.message) {
|
||||
ctx.throw(err.status, err.message)
|
||||
} else {
|
||||
ctx.throw(err.status)
|
||||
}
|
||||
} else {
|
||||
// By default use a 422 error if the HTTP status is not specified.
|
||||
ctx.throw(422, err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = Contact
|
||||
module.exports = ContactController
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
const config = require('../../../config')
|
||||
const axios = require('axios').default
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Mock data
|
||||
// const mockData = require('./mocks/contact-mocks')
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
const ContactController = require('../../../src/modules/contact/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('Contact', () => {
|
||||
beforeEach(() => {
|
||||
uut = new ContactController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('POST /contact/email', () => {
|
||||
it('should throw error if email property is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
formMessage: 'message'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if formMessage property is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'formMessage' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is not a array', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
emailList: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'emailList' must be a array of emails!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is a empty array', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
emailList: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'emailList' must be a array of emails!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email with minimun input', async () => {
|
||||
try {
|
||||
// Mock live network calls.
|
||||
sandbox.stub(uut.contactLib, 'sendEmail').resolves(true)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message'
|
||||
}
|
||||
}
|
||||
}
|
||||
await uut.email(ctx)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email with all inputs', async () => {
|
||||
try {
|
||||
// Mock live network calls.
|
||||
sandbox.stub(uut.contactLib, 'sendEmail').resolves(true)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
emailList: ['email@email.com']
|
||||
}
|
||||
}
|
||||
}
|
||||
await uut.email(ctx)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,121 @@
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const ContactLib = require('../../../src/lib/contact')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('Contact', () => {
|
||||
beforeEach(() => {
|
||||
uut = new ContactLib()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('sendEmail()', () => {
|
||||
it('should throw error if email property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
formMessage: 'test msg'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if formMessage property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'formMessage' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is not a array', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com',
|
||||
emailList: 'test@email.com'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'emailList' must be a array of emails!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is a empty array', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com',
|
||||
emailList: []
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'emailList' must be a array of emails!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email to default server email', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com'
|
||||
}
|
||||
const result = await uut.sendEmail(data)
|
||||
assert.isTrue(result)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should catch and throw nodemailer lib error', async () => {
|
||||
try {
|
||||
// Force an error with the database.
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').throws(new Error('test error'))
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email to specifics email list', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com',
|
||||
emailList: ['testcontact@email.com']
|
||||
}
|
||||
const result = await uut.sendEmail(data)
|
||||
assert.isTrue(result)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Unit tests for the REST API handler for the /users endpoints.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const ContactController = require('../../../src/modules/contact/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
let ctx
|
||||
|
||||
const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
|
||||
describe('Users', () => {
|
||||
before(async () => {
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new ContactController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// Mock the context object.
|
||||
ctx = mockContext()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#POST /contact', () => {
|
||||
it('should return 422 status on biz logic error', async () => {
|
||||
try {
|
||||
await uut.email(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
sandbox.stub(uut.contactLib, 'sendEmail').resolves(true)
|
||||
|
||||
ctx.request.body = {
|
||||
email: 'test02@test.com',
|
||||
formMessage: 'test'
|
||||
}
|
||||
|
||||
await uut.email(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
|
||||
// Assert that expected properties exist in the returned data.
|
||||
assert.property(ctx.response.body, 'success')
|
||||
assert.isTrue(ctx.response.body.success)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user