Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
PORT=3000
MONGODB_URI="mongourl"
MONGO_URI_TEST="test mongourl"
SECRET_KEY="secretKey"

ADMIN_PASSWORD=Admin0007
NON_ADMIN_PASSWORD=User0007

HASHED_ADMIN_PASSWORD='$2a$10$4IIoa9h4th7aPsMhWP7/Xu97SdwcUjImhyDHDsSK1wssiaIr0M.hm'
HASHED_NON_ADMIN_PASSWORD='$2a$10$WSwcXM1dIaygWLaSQMxAD.cNBDZmykPNJOWOkjwpiFiPr8CrT68ha'
File renamed without changes.
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
coverage/
doc/
.github/
package.json
package-lock.json
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"endOfLine": "lf"
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"deno.enable": false
}
113 changes: 112 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,114 @@
# support
# customer-service-app

[![Build Status](https://www.travis-ci.com/dbytecoderc/support.svg?branch=main)](https://www.travis-ci.com/dbytecoderc/support) [![Coverage Status](https://coveralls.io/repos/github/dbytecoderc/support/badge.svg?branch=main)](https://coveralls.io/github/dbytecoderc/support?branch=main)

## Overview

The **customer-service-app** is an application that allows users logs complaints or request for support.

- Key Application features

1. Support Request

- Creation of Support requests
- Fetching support requests
- Updating support requests
- Closing support requests logged

2. Comment
- Users can comment on a support request

## Technology Stack

- Nodejs
- Typescript
- Express
- Mongodb
- Jest

## Libraries used

- You can get the details of the libraries used in the package.json file in the root directory of this project

### Setting Up For Local Development and Testing

- Check that NodeJs is installed on your machine, if not installed follow this [link](https://nodejs.org/en/) to download and install nodejs:

- Clone the repo and cd into it:

```
git clone https://github.com/dbytecoderc/test-app.git
```

- Install dependencies using the command bellow:

```
yarn install
```

- Make a copy of the .env.sample file in the app folder and rename it to .env and update the variables accordingly, **it important that you copy the email and password details in that file just the way it is, you would need it to test admin functionalities and make sure the db urls are set to make sure the tests run**:

```
PORT=3000
MONGODB_URI="mongourl"
MONGO_URI_TEST="test mongourl"
SECRET_KEY="secretKey"
ADMIN_PASSWORD="Admin0007"
NON_ADMIN_PASSWORD="User0007"
HASHED_ADMIN_PASSWORD='$2a$10$4IIoa9h4th7aPsMhWP7/Xu97SdwcUjImhyDHDsSK1wssiaIr0M.hm'
HASHED_NON_ADMIN_PASSWORD='$2a$10$WSwcXM1dIaygWLaSQMxAD.cNBDZmykPNJOWOkjwpiFiPr8CrT68ha'
```

* Run the application with the command

```

yarn dev

```

- Data is seeded into the application as soon as you fire up the server, without needing to create a user you can login and create a json web token which is to be attached to the header in this format

```
Bearer 'sample token'
```

- Use these details to login an admin user

```
{
"email": "admin@admin.com",
"password": "Admin0007"
}
```

- Use these details to login an non-admin user

```
{
"email": "nonadmin@nonadmin.com",
"password": "User0007"
}
```

## Running tests

Make sure the test database is set for this to work

```

yarn test

```

## API Endpoints

- Use the link below in the thumbnail to download a postman collection for the endpoints
[![Run in Postman](https://run.pstmn.io/button.svg)](https://app.getpostman.com/run-collection/9452a28c0f505b49eea3)

- Alternatively you can use this [link](https://documenter.getpostman.com/view/6057580/T1DjkziE?version=latest#a2542775-3976-45ca-a981-4453e29e2a6e) to view the api documentation in your browser.

## Notes

- For feedback I thought the assessment specs could be better in terms of clarifying some of the instructions for ease of understanding.
- Due to time constraints I couldn't increase the test coverage, although I covered all the essential parts of the application.
73 changes: 46 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,63 +1,82 @@
{
"name": "support",
"name": "customer-service-app",
"version": "1.0.0",
"description": "The system allows customers to be able to place support requests, and support agents to process the request.",
"description": "Welcome to my Fliqpay assessment",
"main": "index.js",
"module": "--experimental-modules",
"scripts": {
"lint": "tslint --project tsconfig.json --fix",
"prettier:base": "prettier --parser typescript --single-quote",
"prettier:check": "yarn prettier:base --list-different \"src/**/*.{ts,tsx}\"",
"prettier:write": "yarn prettier:base -- --write \"src/**/*.{ts,tsx}\"",
"start": "node ./dist/src/index.js",
"build": "yarn clean && tsc ",
"start:dev": "cross-env NODE_ENV=development ts-node-dev --files --debug --clear ./src/index.ts",
"clean": "rm -rf dist && mkdir dist",
"prestart:prod": "yarn build",
"start:prod": "cross-env NODE_ENV=staging node ./dist/src/index.js",
"test": "cross-env NODE_ENV=test PORT=3000 jest --no-cache --detectOpenHandles --runInBand --forceExit --verbose",
"test:watch": "cross-env NODE_ENV=test PORT=3000 jest --runInBand --detectOpenHandles --watch",
"test:cov": "cross-env NODE_ENV=test PORT=3000 jest --no-cache --detectOpenHandles --runInBand --forceExit --coverage",
"test": "cross-env NODE_ENV=test PORT=5050 jest --no-cache --detectOpenHandles --runInBand --forceExit --verbose",
"test:watch": "cross-env NODE_ENV=test PORT=5050 jest --runInBand --detectOpenHandles --watch",
"test:cov": "cross-env NODE_ENV=test PORT=5050 jest --no-cache --detectOpenHandles --runInBand --forceExit --coverage",
"ts-watch": "yarn clean && tsc -w",
"coveralls": "jest --coverage --coverageReporters=text-lcov | coveralls"
},
"repository": {
"type": "git",
"url": "git+https://github.com/dbytecoderc/support.git"
"url": "git+https://github.com/dbytecoderc/customer-service-app.git"
},
"author": "DC",
"keywords": [],
"author": "Oparah DC",
"license": "ISC",
"bugs": {
"url": "https://github.com/dbytecoderc/support/issues"
},
"homepage": "https://github.com/dbytecoderc/support#readme",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongodb": "^3.6.6",
"mongoose": "^5.12.3",
"typescript": "^4.2.4"
"url": "https://github.com/dbytecoderc/customer-service-app/issues"
},
"homepage": "https://github.com/dbytecoderc/customer-service-app#readme",
"devDependencies": {
"@types/app-root-path": "^1.2.4",
"@types/bcryptjs": "^2.4.2",
"@types/cors": "^2.8.10",
"@types/express": "^4.17.11",
"@types/mongoose": "^5.10.4",
"husky": "^6.0.0",
"jest": "^26.6.3",
"nodemon": "^2.0.7",
"prettier": "^2.2.1",
"ts-node": "^9.1.1",
"ts-node-dev": "^1.1.6",
"tslint": "^6.1.3",
"tslint-config-airbnb": "^5.11.2",
"tslint-config-prettier": "^1.18.0"
},
"dependencies": {
"@hapi/joi": "^17.1.1",
"@types/bcrypt": "^3.0.1",
"@types/hapi__joi": "^17.1.6",
"@types/jest": "^26.0.22",
"@types/json2csv": "^5.0.1",
"@types/jsonwebtoken": "^8.5.1",
"@types/mongodb": "^3.6.12",
"@types/morgan": "^1.9.2",
"@types/node": "^14.14.37",
"@types/supertest": "^2.0.11",
"@types/underscore": "^1.11.1",
"app-root-path": "^3.0.0",
"codecov": "^3.8.1",
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"coveralls": "^3.1.0",
"jest": "^26.6.3",
"cross-env": "^7.0.3",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"json2csv": "^5.0.6",
"jsonwebtoken": "^8.5.1",
"mongodb": "^3.6.6",
"mongodb-memory-server": "^6.9.6",
"mongoose": "^5.12.3",
"morgan": "^1.10.0",
"nodemon": "^2.0.7",
"supertest": "^6.1.3",
"ts-jest": "^26.5.4",
"ts-node": "^9.1.1",
"ts-node-dev": "^1.1.6",
"tslint": "^6.1.3",
"typescript": "^4.2.4",
"underscore": "^1.13.0",
"winston": "^3.3.3"
}
}
26 changes: 26 additions & 0 deletions src/@types/express/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Router, Request } from "express";

import { Document } from "mongoose";

interface CreateUserInput {
name: string;
email: string;
password: string;
}

interface User extends Document {
name: string;
email: string;
password: string;
createdAt: Date;
admin: boolean;
}


declare global {
namespace Express {
interface Request {
user: User;
}
}
}
68 changes: 31 additions & 37 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,48 @@
// Package Imports
import dotenv from "dotenv";
import express, { Application } from 'express';
import dotenv from 'dotenv';
dotenv.config();
import express, { Application } from "express";
import cors from "cors";
import morgan from "morgan";
import cors from 'cors';
import * as bodyparser from 'body-parser';
import dbconnect from './config/connection.db';
import morgan from 'morgan';

// File Imports
import { stream } from "./config/logger";
import modules from "./modules";
import dbconnect from "./config/connection";
import { env } from "./config";
import logger from "./config/logger";
import modules from './modules';
// import seedData from './database/seeders/seeder';

const app: Application = express();

const { PORT } = process.env;

app.use(cors());
app.use(morgan("combined", { stream }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(bodyparser.json());

if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'));
}

// API routes
modules(app);

// catch all routers
app.use("*", (req, res) => {
res.status(404).json({
message: "Not Found. Use /api/{app version} to access the Api",
});
app.use('*', (req, res) => {
res.status(404).json({
message: 'Not Found. Use /api/{app version} to access the Api',
});
});

dbconnect().then(async () => {
// if (process.env.NODE_ENV !== 'test') {
// await seedData();
// }

logger.info(
`Server running on ${process.env.NODE_ENV} environment, on port ${
env.PORT || 5000
}`
);

// if (!module.parent) {
// app.listen(env.PORT, () => {
// logger.info(
// `Server running on ${process.env.NODE_ENV} environment, on port ${
// env.PORT || 5000
// }`
// );
// });
// }
// if (process.env.NODE_ENV !== 'test') {
// await seedData();
// }
if (!module.parent) {
app.listen(PORT, () => {
console.log(
`Server running on ${process.env.NODE_ENV} environment, on port ${
PORT || 5000
}`,
);
});
}
});

export default app;
File renamed without changes.
2 changes: 1 addition & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './env';
export * from './connection';
export * from './connection.db';
21 changes: 21 additions & 0 deletions src/database/models/Comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import mongoose from 'mongoose';

const { Schema } = mongoose;

// Create Schema
const CommentSchema = new Schema({
comment: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
owner: {
type: Schema.Types.ObjectId,
ref: 'User',
},
});

export default mongoose.model<any>('Comment', CommentSchema);
Loading