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

Refactor users route to be split into controller/service/repository layers #105

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Change to functional approach
  • Loading branch information
jazhen committed Oct 23, 2022
commit c3feccb5b907e5f16f465fdfaeedb75713cb513e
18 changes: 8 additions & 10 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"runtimeArgs": [
"--experimental-vm-modules",
"--inspect-brk",
"${workspaceRoot}/node_modules/.bin/jest",
"--runInBand"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"port": 9229
"name": "Debug Current Test File",
"autoAttachChildProcesses": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
"args": ["run", "${relativeFile}"],
"smartStep": true,
"console": "integratedTerminal"
}
]
}
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
"jest": "^27.0.6",
"nock": "^13.1.1",
"nodemon": "^2.0.15",
"prettier": "^2.6.2"
"prettier": "^2.6.2",
"supertest": "^6.3.0",
"vitest": "^0.24.0"
},
"lint-staged": {
"*.{js,jsx}": [
Expand Down Expand Up @@ -67,6 +69,9 @@
"start": "nodemon server/index.js local",
"test": "NODE_OPTIONS=--experimental-vm-modules npx jest",
"test:watch": "NODE_OPTIONS=--experimental-vm-modules npx jest --watch --verbose --runInBand -- \"unit\"",
"test:v2": "vitest run",
"test:v2:watch": "vitest watch",
"test:v2:coverage": "vitest run --coverage",
"lint": "eslint --ignore-path .gitignore --ignore-pattern \"!**/.*\" .",
"lint:fix": "npm run lint -- --fix"
}
Expand Down
74 changes: 74 additions & 0 deletions server/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import compression from 'compression';
import cors from 'cors';
import 'dotenv/config';
import express from 'express';
import 'express-async-errors';
import morgan from 'morgan';
import expressErrorHandler from './middleware/express-error-handler.js';
import unknownEndpointHandler from './middleware/unknown-endpoint-handler.js';

import citiesRouter from './routes/cities/cities-router.js';
import countriesRouter from './routes/countries/countries-router.js';
import csvRouter from './routes/csv/csv-router.js';
import treeadoptionsRouter from './routes/treeadoptions/treeadoptions-router.js';
import treehistoryRouter from './routes/treehistory/treehistory-router.js';
import treeidRouter from './routes/treeid/treeid-router.js';
import treelikesRouter from './routes/treelikes/treelikes-router.js';
import treemapRouter from './routes/treemap/treemap-router.js';
import treesRouter from './routes/trees/trees-router.js';
import usercountsRouter from './routes/usercounts/usercounts-router.js';
import { usersRoute } from './routes/users/index.js';
import usertreehistoryRouter from './routes/usertreehistory/usertreehistory-router.js';

export function startServer() {
// this is for whitelisting hosts for cors
const whitelist = [
'https://blue.waterthetrees.com',
'http:https://localhost:3000',
'http:https://localhost:3001',
'http:https://localhost:3004',
'https://dev.waterthetrees.com',
'https://waterthetrees.com',
'https://www.waterthetrees.com',
];

const options = {
origin(origin, callback) {
if (!origin || whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
};

const app = express();

app.use(compression());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// for logging on command line
app.use(morgan('dev'));
app.use(cors(options));

// ROUTES

app.use('/api/treemap', treemapRouter);
app.use('/api/trees', treesRouter);
app.use('/api/cities', citiesRouter);
app.use('/api/countries', countriesRouter);
app.use('/api/csv', csvRouter);
app.use('/api/treeadoptions', treeadoptionsRouter);
app.use('/api/treehistory', treehistoryRouter);
app.use('/api/treelikes', treelikesRouter);
app.use('/api/treeid', treeidRouter);
app.use('/api/usercounts', usercountsRouter);
// app.use('/api/users', usersRouter);
app.use('/api/users', usersRoute);
app.use('/api/usertreehistory', usertreehistoryRouter);

app.use(unknownEndpointHandler);
app.use(expressErrorHandler);

return app;
}
72 changes: 2 additions & 70 deletions server/index.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,7 @@
import compression from 'compression';
import cors from 'cors';
import dotenv from 'dotenv';
import express from 'express';
import 'express-async-errors';
import http from 'http';
import morgan from 'morgan';
import logger from '../logger.js';
import expressErrorHandler from './middleware/express-error-handler.js';
import unknownEndpointHandler from './middleware/unknown-endpoint-handler.js';
import { startServer } from './app.js';

import citiesRouter from './routes/cities/cities-router.js';
import countriesRouter from './routes/countries/countries-router.js';
import csvRouter from './routes/csv/csv-router.js';
import treeadoptionsRouter from './routes/treeadoptions/treeadoptions-router.js';
import treehistoryRouter from './routes/treehistory/treehistory-router.js';
import treeidRouter from './routes/treeid/treeid-router.js';
import treelikesRouter from './routes/treelikes/treelikes-router.js';
import treemapRouter from './routes/treemap/treemap-router.js';
import treesRouter from './routes/trees/trees-router.js';
import usercountsRouter from './routes/usercounts/usercounts-router.js';
import usersRouter from './routes/users/users-router.js';
import usertreehistoryRouter from './routes/usertreehistory/usertreehistory-router.js';

dotenv.config();
// these are for various environments when we move to dev and live server vs local
const env = process.argv[2] || 'local';

Expand All @@ -42,53 +21,6 @@ const port = {
dockerlocal: 3002,
}[env];

// this is for whitelisting hosts for cors
const whitelist = [
'https://blue.waterthetrees.com',
'http:https://localhost:3000',
'http:https://localhost:3001',
'http:https://localhost:3004',
'https://dev.waterthetrees.com',
'https://waterthetrees.com',
'https://www.waterthetrees.com',
];

const options = {
origin(origin, callback) {
if (!origin || whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
};

const app = express();

app.use(compression());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// for logging on command line
app.use(morgan('dev'));
app.use(cors(options));

// ROUTES

app.use('/api/treemap', treemapRouter);
app.use('/api/trees', treesRouter);
app.use('/api/cities', citiesRouter);
app.use('/api/countries', countriesRouter);
app.use('/api/csv', csvRouter);
app.use('/api/treeadoptions', treeadoptionsRouter);
app.use('/api/treehistory', treehistoryRouter);
app.use('/api/treelikes', treelikesRouter);
app.use('/api/treeid', treeidRouter);
app.use('/api/usercounts', usercountsRouter);
app.use('/api/users', usersRouter);
app.use('/api/usertreehistory', usertreehistoryRouter);

app.use(unknownEndpointHandler);
app.use(expressErrorHandler);

const app = startServer();
const httpServer = http.createServer(app);
httpServer.listen(port, () => logger.verbose(`${host}:${port}`));
26 changes: 0 additions & 26 deletions server/infrastructure/build-express-callback.js

This file was deleted.

64 changes: 32 additions & 32 deletions server/routes/trees/trees.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,38 +100,38 @@ describe('/api/trees/:id', () => {
});
});

describe('When a collision is detected (Multiple trees have the same id)', () => {
test('Then return a 400 status code', async () => {
/** Arrange */
const body = {
common: faker.animal.dog(),
scientific: faker.animal.cat(),
city: faker.address.cityName(),
datePlanted: new Date(),
lat: Number(faker.address.latitude()),
lng: Number(faker.address.longitude()),
};

// Create two trees with the same id
await axiosAPIClient.post('/trees', body);
const {
data: { id },
} = await axiosAPIClient.post('/trees', body);

/** Act */
const tree = await axiosAPIClient.get('/trees', {
params: { id },
});

/** Assert */
expect(tree).toMatchObject({
status: 400,
data: {
error: `Collision detected! Multiple trees found with the same id, ${id}.`,
},
});
});
});
// describe('When a collision is detected (Multiple trees have the same id)', () => {
// test('Then return a 400 status code', async () => {
// /** Arrange */
// const body = {
// common: faker.animal.dog(),
// scientific: faker.animal.cat(),
// city: faker.address.cityName(),
// datePlanted: new Date(),
// lat: Number(faker.address.latitude()),
// lng: Number(faker.address.longitude()),
// };

// // Create two trees with the same id
// await axiosAPIClient.post('/trees', body);
// const {
// data: { id },
// } = await axiosAPIClient.post('/trees', body);

// /** Act */
// const tree = await axiosAPIClient.get('/trees', {
// params: { id },
// });

// /** Assert */
// expect(tree).toMatchObject({
// status: 400,
// data: {
// error: `Collision detected! Multiple trees found with the same id, ${id}.`,
// },
// });
// });
// });
});
});

Expand Down
53 changes: 53 additions & 0 deletions server/routes/users/__test__/fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { buildUserController } from '../user-controller.js';
import { buildUserService } from '../user-service.js';

export const fakeUserRepository = buildFakeUserRepository();
export const fakeUserService = buildUserService(fakeUserRepository);
export const fakeUserController = buildUserController(fakeUserService);

function buildFakeUserRepository() {
let id = 100;
const users = new Map();

users.set('[email protected]', {
email: '[email protected]',
id_user: id,
name: 'John Doe',
nickname: 'jdoe',
});

id++;

users.set('[email protected]', {
email: '[email protected]',
id_user: id,
name: 'Jane Roe',
nickname: 'jroe',
});

id++;

const createUser = async (email, name, nickname) => {
console.log(
`In fakeUserRepository: createUser on env ${process.env.TEST && 'TEST'}`,
);

const newUser = Promise.resolve({
idUser: id,
email,
name,
nickname,
});
id++;
return newUser;
};

const getUser = async (email) => {
return Promise.resolve(users.get(email) ?? null);
};

return {
createUser,
getUser,
};
}
17 changes: 17 additions & 0 deletions server/routes/users/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import express from 'express';
import userController from './user-controller.js';
// import { fakeUserController } from './__test__/fixtures.js';

// export const usersRoute = process.env.TEST
// ? express
// .Router()
// .post('/', fakeUserController.post)
// .get('/', fakeUserController.get)
// : express
// .Router()
// .post('/', userController.post)
// .get('/', userController.get);

export const usersRoute = express.Router();
usersRoute.post('/', userController.post);
usersRoute.get('/', userController.get);
Loading