Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sample boilerplate framework agnostic #21

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,597 changes: 1,597 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions src/entity/DeliveryPerson.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class DeliveryPerson {

constructor({id, name, age}) {
this.id = id;
this.name = name;
this.age = age;
}

getId() {
return this.id;
}

setId(id) {
this.id = id;
}

getName() {
return this.name;
}

setName(name) {
this.name = name;
}

getAge() {
return this.age;
}

setAge(age) {
this.age = age;
}

}

module.exports = { DeliveryPerson };
19 changes: 19 additions & 0 deletions src/entrypoint/web_express/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const express = require("express");

const { DeliveryPersonRouter } = require("./route/DeliveryPersonRouter");


module.exports = (diTransactionUseCase) => {

const deliveryPersonRouter = new DeliveryPersonRouter(diTransactionUseCase);

const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));


app.use("/deliveryperson", deliveryPersonRouter.getRouter());

return app;
}
65 changes: 65 additions & 0 deletions src/entrypoint/web_express/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require("dotenv").config();

const dependencyInjectionWiring = require("../../usecase/deliveryperson/MemoryDI").MemoryDI;
var app = require('./app')(dependencyInjectionWiring);
var http = require('http');


var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);


var server = http.createServer(app);


server.listen(port);
server.on('error', onError);
server.on('listening', onListening);


function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
return val;
}

if (port >= 0) {
return port;
}

return false;
}

function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}

var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}

function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log("listening on " + bind);
// debug('Listening on ' + bind);
}
59 changes: 59 additions & 0 deletions src/entrypoint/web_express/route/DeliveryPersonRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
var express = require("express");

const { Presenter } = require("../../../usecase/Presenter");


class DeliveryPersonRouter extends Presenter {

constructor(diUseCase) {
super();
this.diUseCase = diUseCase;
this.router = express.Router();
this.loadRoutes();
}

getRouter() {
return this.router;
}

loadRoutes() {
this.router.get("/test", this.test.bind(this));
this.router.get("/list/:id", this.list.bind(this));
}

test(req, res) {
res.json({ok:true, message: "it worked"});
}

async list(req, res) {
const commandUseCase = await this.diUseCase.listDeliveryPersonByIdUseCase(this);
await commandUseCase.execute(req.params);
this.sendDtoResponseBack(res);
}

sendDtoResponseBack(res) {
res.status(this.responseCode).json(this.payload);
}

ok(payloadDto) {
this.responseCode = 200;
this.payload = payloadDto;
}

notFound() {
this.responseCode = 404;
this.payload = null;
}

invalidError(message) {
this.responseCode = 422;
this.payload = { ok: false, message };
}

internalError(message) {
this.responseCode = 500;
this.payload = { ok: false, message };
}
}

module.exports = { DeliveryPersonRouter };
Empty file added src/service/.keep
Empty file.
21 changes: 21 additions & 0 deletions src/usecase/Presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const notImplemented = () => {throw new Error("Not implemented. Presenter should be extended.")};

class Presenter {
ok(payload) {
notImplemented();
}

notFound() {
notImplemented();
}

validationError(invalidFields) {
notImplemented();
}

internalError(message) {
notImplemented();
}
}

module.exports = { Presenter };
8 changes: 8 additions & 0 deletions src/usecase/UseCaseCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class UseCaseCommand {

execute() {
throw new Error("Not implemented. UseCase should be extended.");
}
}

module.exports = { UseCaseCommand };
28 changes: 28 additions & 0 deletions src/usecase/deliveryperson/ListDeliveryPersonByIdUseCase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const { UseCaseCommand } = require("../UseCaseCommand");


class ListDeliveryPersonByIdUseCase extends UseCaseCommand {

constructor(presenter, deliveryPersonRepository) {
super();
this.presenter = presenter;
this.deliveryPersonRepository = deliveryPersonRepository;
}

async execute({ id }) {

try {
const deliveryPerson = await this.deliveryPersonRepository.findById(id);
if(deliveryPerson)
return this.presenter.ok(deliveryPerson);
else
return this.presenter.notFound();
} catch(error) {
return this.presenter.internalError(error.message);
}

}

}

module.exports = { ListDeliveryPersonByIdUseCase };
14 changes: 14 additions & 0 deletions src/usecase/deliveryperson/MemoryDI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { ListDeliveryPersonByIdUseCase } = require("./ListDeliveryPersonByIdUseCase");
const { MemoryDeliveryPersonRepository } = require("./MemoryDeliveryPersonRepository");

class MemoryDI {

static listDeliveryPersonByIdUseCase(presenter) {
const repository = new MemoryDeliveryPersonRepository;
const useCase = new ListDeliveryPersonByIdUseCase(presenter, repository);
return useCase;
}

}

module.exports = { MemoryDI };
33 changes: 33 additions & 0 deletions src/usecase/deliveryperson/MemoryDeliveryPersonRepository.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { DeliveryPerson } = require("../../entity/DeliveryPerson");

let database = {};

class MemoryDeliveryPersonRepository {

async findById(id) {
if(database[id]) {
return new DeliveryPerson(database[id]);
}
return null;
}

async add(deliveryPerson) {
const dto = this.toDto(deliveryPerson);
database[dto.id] = dto;
}

// as the mapping becomes more complex, it should move to mapper classes.
toDto(deliveryPerson) {
return {
id: deliveryPerson.getId(),
name: deliveryPerson.getName(),
age: deliveryPerson.getAge()
};
}

static clearDatabase() {
database = {};
}
}

module.exports = { MemoryDeliveryPersonRepository };
5 changes: 5 additions & 0 deletions src/usecase/deliveryperson/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { MemoryDI } = require("./MemoryDI");

const dependencyInjector = new MemoryDI;

module.exports = dependencyInjector;
52 changes: 52 additions & 0 deletions test/unit/usecase/deliveryperson/ListDeliveryPersonByIdUseCase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use strict";

const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);

const { Presenter } = require("../../../../src/usecase/Presenter");
const { MemoryDeliveryPersonRepository } = require("../../../../src/usecase/deliveryperson/MemoryDeliveryPersonRepository");
const { ListDeliveryPersonByIdUseCase } = require("../../../../src/usecase/deliveryperson/ListDeliveryPersonByIdUseCase");
const { DeliveryPerson } = require("../../../../src/entity/DeliveryPerson");


class DeliveryPresenter extends Presenter {
ok(payload) {}
notFound() {}
}


describe("List delivery person by id #unit", () => {

it("Should return no delivery person if none was added to db", async () => {

const deliveryPersonRepository = new MemoryDeliveryPersonRepository;

const presenter = new DeliveryPresenter;
sinon.spy(presenter, "notFound");

const listDeliveryPersonByIdUseCase = new ListDeliveryPersonByIdUseCase(presenter, deliveryPersonRepository);
await listDeliveryPersonByIdUseCase.execute({id: "1234"});

expect(presenter.notFound).to.have.been.called;
});

it("Should return one delivery person if one was added to db", async () => {

const deliveryPersonDto = {id: "1234", name: "Zaphod", age: 42};

const deliveryPersonRepository = new MemoryDeliveryPersonRepository;
await deliveryPersonRepository.add(new DeliveryPerson(deliveryPersonDto));

const presenter = new DeliveryPresenter;
sinon.spy(presenter, "ok");

const listDeliveryPersonByIdUseCase = new ListDeliveryPersonByIdUseCase(presenter, deliveryPersonRepository);
await listDeliveryPersonByIdUseCase.execute({id: deliveryPersonDto.id});

const response = presenter.ok.getCall(0).args[0];
expect(response).to.deep.equal(deliveryPersonDto);
});
});
38 changes: 38 additions & 0 deletions test/web/deliverypersonendpoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use stricct";

const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
const request = require('supertest');


const { DeliveryPerson } = require("../../src/entity/DeliveryPerson");
const { MemoryDeliveryPersonRepository } = require("../../src/usecase/deliveryperson/MemoryDeliveryPersonRepository");
const { MemoryDI } = require("../../src/usecase/deliveryperson/MemoryDI");
const app = require("../../src/entrypoint/web_express/app")(MemoryDI);

beforeEach(() => MemoryDeliveryPersonRepository.clearDatabase());



describe("Delivery Person Use Cases Routes #integration #web_express", () => {

it("Should return Ok on test route", async () => {
await request(app).get('/deliveryperson/test').expect(200).expect({ok:true, message: "it worked"});
});

it("Should return 404 when no delivery person is found", async () => {
await request(app).get('/deliveryperson/list/1234').expect(404);
});

it("Should return delivery person when providing valid id", async () => {
const deliveryPersonDto = {id: "1234", name: "Zaphod", age: 42};
const deliveryPersonRepository = new MemoryDeliveryPersonRepository;
await deliveryPersonRepository.add(new DeliveryPerson(deliveryPersonDto));

await request(app).get('/deliveryperson/list/1234').expect(200).expect(deliveryPersonDto);
});

});