NivaldoFarias / tractian-challenge

🧭 Full Stack TypeScript Application Developed as PoC

Home Page:https://tractian-poc.herokuapp.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool


Logo

Tractian Challenge POC

Full Stack Development Project
Browse Back End code» - Browse Front End code»

Built With


Table of Contents

Deployed Instances

  • Server https://tractian-poc.herokuapp.com/
  • Client https://tractian-poc.vercel.app/

Installation and Usage

Pre-requisites: Node.js ^16.14.0, TypeScript ^4.7.4

Download the zip file and extract it in the root of a new project folder by running these commands:

wget https://github.com/NivaldoFarias/tractian-challenge/archive/main.zip

Then run the following command to install the project's dependencies:

npm install

That's it! You can now start developing your TypeScript Project by running the command below. Happy coding!

npm run dev

Error Handling and Logging

While dealing with errors in a Layered Structure Project enviroment, you may notice that the project's debugging complexity scales beyond common console.log() usage. The AppLog Object and AppError Object structures were set to counter that exact issue, by trying to keep the Development process as clean and concise as possible. Both are frequently referenced in the code, but do have a specific usage.

AppError

An AppError Object is used to handle errors in the application. It that takes four parameters:

  • log: A string containing a simplified error message, for Server side use. This is the message that will be used by the AppLog Object
  • statusCode: An integer containing the HTTP status code.
  • message: A string containing a simplified error message, for Client side use. This is the message that will be displayed to the user.
  • details: A string containing a detailed error message, for Client side use. Can be used to provide more information about the error, such as the stack trace, or suggestions on how to counter the error.

Example Usage

  // ..../middlewares/auth.middleware.ts

  import * as repository from './../repositories/auth.repository.ts';
  import AppError from './../events/AppError';
  ...
  ..

  async function usersExists(req: Request,...){
    ...
    ..
    const user = await repository.findbyId(req.body.id);

    if (!user){
      throw new AppError(
        'User not found',
        404,
        'User not found',
        'Ensure to provide a valid user ID.'
      );
    }
    ..
    ...
  }

AppLog

An AppLog Object is used to handle logs in the application. It takes two parameters:

  • type: A string containing the main Layer Structure that contains the log. There are seven allowed values: Error, Server, Controller, Middleware, Repository, Service, and Util.
  • text: A descriptive string containing the log message. Generally, a short message that describes the output event of the function that generated the log.

Example Usage

  // ..../middlewares/auth.middleware.ts

  import AppLog from './events/AppLog';
  ...
  ..

  async function usersExists(req: Request,...){
    ...
    ..

    // output: [Middleware] User Found
    AppLog('Middleware', 'User found');
    res.locals.user = user;
    return next();
  }
  ..
  ...

Middlewares

While aiming to provide a reusable, modular and extensible architecture, the middlewares are generally the first structures to be refactored into self-contained modules. The validateSchema(), processHeader() and requireToken() middlewares were set in order to achieve that goal. The following section describes useMiddleware(), which incorporates the forementioned functions as key–value pairs in an Object, along with their structure and usage.

UseMiddleware

The useMiddleware() function takes two parameters:

  • middlewares: An Object containing the key–value pairs of the middlewares to be used, takes one to three parameters:
    • schema: A Joi Schema Object that will be used to validate the data provided by the client. If the data provided by the client is not valid, an AppError Object will be thrown.
    • header: A string containing the name of the header that will be used to authenticate the action. If the client-provided header is missing, an AppError Object will be thrown.
    • token: A boolean indicating whether the token provided by the client will be verified or not. If the token is not valid, an AppError Object will be thrown.
  • endpoint: A string that will be used to identify the endpoint at which the client–api interaction is undergoing, which will be logged to console by the AppLog Object.
Reference: useMiddleware function declaration

Example Usage

// ..../routes/admin.route.ts
import useMiddleware from '../utils/middleware.util';
import * as schema from '../models/admin.model';
...
..
const endpoint = '/admin';

const registerEndpoint = '/create';
adminRouter.post(endpoint,
  createEndpoint,
  useMiddleware({
    schema: schema.create,
    header: 'admin-api-key',
    token: true
  },
  endpoint + createEndpoint),
  middleware.createValidations,
  controller.create,
);
..
...

API Reference

In this section, you will find the example API's endpoints and their respective descriptions, along with the request and response examples, as well as the MongoDB BSON types for each entity, that can be used as guide for data formatting. All data is sent and received as JSON.

Models

User model User

  • _id: A unique identifier for each user. ObjectId
  • full_name: The user's full name. String required max(100)
  • username: The user's username. String required unique max(25)
  • password: The user's password. String required max(50)
  • last_update: The date and time when the user was last updated. Date
  • created_at: The date and time when the user was created. Date

Company model Company

  • _id: A unique identifier for each company. ObjectId
  • name: The companys's name. String required unique max(100)
  • units: An array containing the company's units. Unit[]
  • users: An array containing the company's users. User[]
  • x-api-key: The company's API key. String required
  • last_update: The date and time when the company was last updated. Date
  • created_at: The date and time when the company was created. Date

Unit model Unit

  • _id: A unique identifier for each unit. ObjectId
  • name: The units's name. String required unique max(50)
  • street: The unit's street. String max(100)
  • number: The unit's number. String max(10)
  • city: The unit's city. String required max(50)
  • state: The unit's state. String required max(50)
  • postal_code: The unit's postal code. String max(20)
  • assets: An array containing the unit's assets. Asset[]
  • opens_at: The date and time when the unit opens. String required length(5)
  • closes_at: The date and time when the unit closes. String required length(5)
  • last_update: The date and time when the unit was last updated. Date
  • created_at: The date and time when the unit was created. Date

Asset model Asset

  • _id: A unique identifier for each asset. ObjectId
  • name: The assets's name. String required max(50)
  • description: The assets's description. String
  • model: The assets's model. String required max(100)
  • owner: The assets owner's user. User required
  • image: The assets's image URL. String
  • status: The assets's status. String required enum('RUNNING', 'ALERTING', 'STOPPED')
  • health: The assets's healthscore. Number required min(0) max(100)
  • last_update: The date and time when the asset was last updated. Date
  • created_at: The date and time when the asset was created. Date

Routes

Authentication /auth

Users /users

Companies /companies

Units /units

Assets /assets

Authentication

Sign in

POST /auth/sign-in
Request
Body
{
  "username": "JohnDoe",
  "password": "123456789"
}
Headers
{
  "Content-Type": "application/json"
}
Responses
Status Code Description Properties
200 OK data: { token }
400 Invalid Syntax error: { message, detail }
404 User not Found error: { message, detail }
409 User has Active Session error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

  ‣   Sign out

POST /auth/sign-out
Request
Headers
{
  "Content-Type": "application/json",
  "token": "server-generated-token"
}
Responses
Status Code Description Properties
200 OK data: null
404 Session not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Users

Create an User

######POST /users/create

Request
Body
{
  "full_name": "John Doe Junior the Third",
  "username": "JohnDoe",
  "password": "123456789",
  "company": "5f9f1b9f9d1b9d1b9f1b9d1b"
}
Headers
{
  "Content-Type": "application/json",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
201 Created data: null
400 Invalid Syntax error: { message, detail }
403 Forbidden x-api-key error: { message, detail }
409 Username Already Registered error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search all Users

GET /users/all
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Query Parameters
Name Type Description Default
per_page Number The number of results per page (max 100) 10
page Number Page number of the results to fetch 1
Responses
Status Code Description Properties
200 OK data: { User[] | null}
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Session not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search User by id

GET /users/:id
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Responses
Status Code Description Properties
200 OK data: User
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 User not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Update an User

PUT /users/:id/update
Request
Body
{
  "full_name": "John Doe Junior the Second",
  "username": "JohnDoe"
}
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 User not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Delete an User

DELETE /users/:id/delete
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>".
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 User not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Companies

Create a Company

POST /companies/create
Request
Body
{
  "name": "Acme Inc."
}
Headers
{
  "Content-Type": "application/json",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
201 Created data: null
400 Invalid Syntax error: { message, detail }
401 Missing x-api-key error: { message, detail }
403 Forbidden x-api-key error: { message, detail }
409 Company Already Registered error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search all Companies

GET /companies/all
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Query Parameters
Name Type Description Default
per_page Number The number of results per page (max 100) 5
page Number Page number of the results to fetch 1
Responses
Status Code Description Properties
200 OK data: { Company[] | null}
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Session not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search Company by id

GET /companies/:id
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Responses
Status Code Description Properties
200 OK data: Company
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Company not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Update a Company

PUT /companies/:id/update
Request
Body
{
  "name": "Acme Inc. 2"
}
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Company not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Delete a Company

DELETE /companies/:id/delete
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Company not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Units

Create an Unit

POST /units/create
Request
Body
{
  "name": "Acme Inc. - Unit 1",
  "description": "Lorem Ipsum Dolor Sit Amet, Consectetur Adipiscing Elit.",
  "city": "New York",
  "state": "NY",
  "opens_at": "08:00",
  "closes_at": "18:00",
  "company": "5f9f1b9f9d1b9d1b9f1b9d1b"
}
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
201 Created data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
409 Unit Already Registered error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search all Units

GET /units/all
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Query Parameters
Name Type Description Default
per_page Number The number of results per page (max 100) 5
page Number Page number of the results to fetch 1
Responses
Status Code Description Properties
200 OK data: { Unit[] | null}
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Session not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search Unit by id

GET /units/:id
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Responses
Status Code Description Properties
200 OK data: Unit
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Unit not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Update an Unit

PUT /units/:id/update
Request
Body
{
  "name": "Acme Inc. - Unit 1",
  "description": "Now at a new location!",
  "address": "Main Street",
  "number": "123",
  "city": "New York",
  "state": "NY",
  "postal_code": "12345",
  "opens_at": "08:00",
  "closes_at": "18:00"
}
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Unit not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Delete an Unit

DELETE /units/:id/delete
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Unit not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Assets

Create an Asset

POST /assets/create
Request
Body
{
  "name": "Assembly Machine",
  "description": "This is a machine for assembly",
  "model": "AM-123",
  "owner": "7f9f1b9f9d1b9d1b9f1b9342",
  "image": "https://www.example.com/image.jpg",
  "status": "STOPPED",
  "health": 94
}
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
201 Created data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
409 Asset Already Registered error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search all Assets

GET /assets/all
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Query Parameters
Name Type Description Default
per_page Number The number of results per page (max 100) 10
page Number Page number of the results to fetch 1
owner String Username of the owner of the asset -
status String Status of the asset -
model String Model of the asset -
Responses
Status Code Description Properties
200 OK data: { Asset[] | null}
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Session not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Search Asset by id

GET /assets/:id
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>"
}
Responses
Status Code Description Properties
200 OK data: Asset
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Asset not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Update an Asset

PUT /assets/:id/update
Request
Body
{
  "name": "Assembly Machine - Now with more assembly",
  "description": "This is a machine for assembly, but now we use the Assembly programming language",
  "model": "AM-123",
  "status": "RUNNING",
  "health": 81
}
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Asset not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

Delete an Asset

DELETE /assets/:id/delete
Request
Headers
{
  "Content-Type": "application/json",
  "Authorization": "Bearer <token>",
  "x-api-key": "extremely-secure-hash-key"
}
Responses
Status Code Description Properties
200 OK data: null
400 Invalid Syntax error: { message, detail }
401 Missing Token error: { message, detail }
403 Forbidden Token error: { message, detail }
404 Asset not Found error: { message, detail }
422 Invalid Request Input error: { message, detail }
500 Internal Server Error error: { message, detail }

About

🧭 Full Stack TypeScript Application Developed as PoC

https://tractian-poc.herokuapp.com/

License:MIT License


Languages

Language:TypeScript 98.9%Language:HTML 1.1%