/fluke-ts

Primary LanguageTypeScript

Fluke

A simple test.

Author Last Commit Last Commit


The algorithm part of the challenge is stored inside the "algorithm" folder. It was written in C and it's a very simple program.


📚 Let me tell you a little bit about this project:

This API is one of the parts of the technical challenge step of the Fluke hiring process. The challenge was to build an application, using Node.js, with the following specifications:

  • The API must be integrated with a NoSQL database;
  • You have full control of the application's scope, make all technical decisions regarding architecture and design patterns as you see fit;
  • Don't forget the docs!

💥 The decisions I made and how I built the API:

Ok, let me show you how I built this project and all the decisions I had to make. Let's take this step-by-step so we don't get lost on the way:

  • Architecture & Design: Are you familiar with DDD (Domain-Driven Design)? If not, let me tell you a little bit about it:
    • First things first, what is a "domain"? Well, the domain is the core of the business we're working at. It's based on a series of ideas, knowledge and business processes. DDD values that developers take part on the process of understanding the business instead of just talking to its specialists. In short, if we as developers do not know how to model our software according the needs of our business, how are we supposed to make it work?
    • DDD uses something called Domain Model Patterns, a series of design patterns that are used to compose the layers we will work with. The patterns I used in this project are:
      • Repositories: They make the connection between the application's routes and the layer of data persistency;
      • Aggregate Objects: different entities that are in the same context and make use of each other;
      • Value Objects: They represent typed values. I find these a little harder to describe, so if you want to know more about them check out this article
    • But why did I choose DDD? I recommend the reading of this article, that helped me understand the benefits of Domain Driven Design a lot better.
    • I also tried my best to apply the SOLID principles while building this;

This was not my first time applying these concepts inside a projject, but it's always interesting to do so and I always learn a lot.


  • The techinical decisions: Let's talk about all the frameworks, libraries and things of the sort that I chose to go with:

    • I built this API using TypeScript;
    • The framework I chose was Express, simply because it's my go-to Node.js framework;
    • I picked MongoDB + TypeORM because one of the rules of this challenge was that I had to use a non-relational database.;
    • I used JWT (JSON Web Token) to create user authentication logic;
    • I picked ts-node-dev to run my server while developing the API;
    • For async error handling, I used express-async-errors
    • The library I used to hash the user passwords before saving them on the database was bcryptjs;
    • reflect-metadata to do runtime reflection on types
    • TL;DR. This is the project's stack:
      • Node.js;
      • TypeScript;
      • Express;
      • MongoDB + TypeORM;
      • JWT;
      • reflect-metadata;
      • express-async-errors;
      • ts-node-dev;
      • Bcryptjs;
  • For data persistency, I used MongoDB Atlas' free tier cluster, hosted on GCP.

Quick observation: If you get the following error while running the application, do not panic. It's an error with MongoDB Atlas' Node.js driver and not the app itself. For me, changing the network I'm connected to fixes the problem. Sadly, all we can do is wait for the Atlas team to fix this issue

UnhandledPromiseRejectionWarning: Error: querySrv EREFUSED _mongodb._tcp.fluke.ju6xj.mongodb.net

Routes:

POST /registerNewCustomer:

curl --request POST
  --url http://localhost:3333/registerNewCustomer
  --header 'Content-Type: application/json'
  --data '{
      "name": "Izabela",
      "cpf": "12345678912",
      "cellphone": "123456789",
      "email": "izabela@teste.com",
      "password": "123"
    }'
  • Response example: If successful, the request should return an object with some of the user's data
  {
    "id": "6027b3a3ff9fc91940792e10",
    "name": "Izabela",
    "email": "izabela@teste.com",
    "created_at": "2021-02-14T11:31:38.376+00:00"
    "updated_at": "2021-02-14T11:31:38.376+00:00"
  }

POST /authentication:

curl --request POST
  --url http://localhost:3333/authentication
  --header 'Content-Type: application/json'
  --data '{
      "email": "izabela@teste.com",
      "password": "123"
    }'
  • Response example: If successful, the request should return the user's ID and a JWT token.
  {
    "user": "6027b3a3ff9fc91940792e10",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MTMyMjgyOTUsImV4cCI6MTYxMzMxNDY5NSwic3ViIjoiNjAyN2IzYTNmZjlmYzkxOTQwNzkyZTEwIn0.57UBry4SGf--wbs8C-VjI3MBw4uC9BKkn0eYak_gpw4"
  }

Take the token from this request's response and use it as a header on requests that need authentication


POST /productsOrder:

For this request, make sure you don't forget to pass the desired product's id as a query param.
This request requires authentication, so don't forget to take the token you've gotten from the /authentication request and use it as a header here.

To make this request, you'll need to know the ID of the product you want to purchase:

  • 602a77babfc6ba117431834a
  • 602a77eebfc6ba117431834b
  • 602a7805bfc6ba117431834c

curl --request POST
  --url 'http://localhost:3333/productsOrder?productId={productId}'
  --header 'Authorization: Bearer Token'
  • Response example: If successful, the request should return the user's purchased product
  {
    "id": "6029249f59437c0cd43c9e5f"
    "price": 129.99
    "package_name": "Pacote Alto Consumo: 30GB"
  }

Let's look at the flow chart of this request:


GET /currentPackage:

This request requires authentication, so don't forget to take the token you've gotten from the /authentication request and use it as a header here.

curl --request GET
  --url http://localhost:3333/currentPackage
  --header 'Authorization: Bearer Token'
  • Response example: If successful, the request should return all of the user's purchased products + the number of products he has bought
  {
    {
      "id": "6029249f59437c0cd43c9e5f"
      "price": 129.99
      "package_name": "Pacote Alto Consumo: 30GB"
    },
    bought_products:
}

POST /portabilityRequest:

This request requires authentication, so don't forget to take the token you've gotten from the /authentication request and use it as a header here.

curl --request POST
  --url http://localhost:3333/portabilityRequest
  --header 'Authorization: Bearer Token
  --header 'Content-Type: application/json'
  --data '{
      "name": "Izabela",
      "cpf": "12345678912",
      "cellphone": "12345"
    }'
  • Response example: If successful, the request should return the user's opened ticket
{
  "tickets": [
    {
      "id": "60290b015c732837c4980fd5",
      "customer_name": "Izabela",
      "customer_cpf": "12345678912",
      "customer_cellphone": "123456",
      "customer_id": "60290a1a34b0772de4fa0456",
      "created_at": "2021-02-14T11:35:29.825Z",
      "updated_at": "2021-02-14T11:35:29.825Z"
    }
  ],
  "opened_tickets": 1
}

Let's look at the flow chart of this request:


GET /portabilities:

This request requires authentication, so don't forget to take the token you've gotten from the /authentication request and use it as a header here.

curl --request GET
  --url http://localhost:3333/portabilities
  --header 'Authorization: Bearer Token
  • Response example: If successful, the request should return all of the user's opened tickets + the total of tickets the user has opened
{
  "tickets": [
    {
      "id": "60290b015c732837c4980fd5",
      "customer_name": "Izabela",
      "customer_cpf": "12345678912",
      "customer_cellphone": "123456",
      "customer_id": "60290a1a34b0772de4fa0456",
      "created_at": "2021-02-14T11:35:29.825Z",
      "updated_at": "2021-02-14T11:35:29.825Z"
    }
  ],
  "opened_tickets": 1
}

GET /pipipi:

BEWARE!!!!!!!! This is the most important route of this application

curl --request GET \
  --url http://localhost:3333/pipipi
  • Response example: If successful, the request should return the following message:
{
  "message": "popopo"
}

🏃 Let's run this thing!!:

  # Clone this repository
  - git clone https://github.com/izabela-am/fluke-ts.git
  
  # Enter project directory
  - cd fluke-ts
  
  # Install all project dependencies
  - yarn
  
  # Start the server
  - yarn dev

🍴 Contributions:

  # Fork this repository
  # Create a branch for your features
  git checkout -b my-feature
  
  # Commit your changes (and make sure to write a nice commit message!)
  git commit -m "My message here"
  
  # Push your branch
  git push origin my-feature

ℹ️ License:

This project uses the MIT License.
Built with ❤️ by Izabela