Call Of Duty - DavidBK/shampoo GitHub Wiki

Call Of Duty - Duties scheduling system

Estimation time: 20-30 Days


In the 'Call Of Duty' project, you will be managing soldiers and duties using a database and a RESTfull API server.

Call of Duty


Send me back home

[[TOC]]


Instructions

Pre - requirements

Before you start make sure you familiar with these concepts:

General Guidelines

  • The database should be a non relational database.
  • The API server should be a RESTfull API server.
  • You should test the project, and the coverage of the tests should be high as possible.
  • You should PR Each task in the project.
  • Log your logic and errors.
  • The Commits separation decision is up to you.

Recommended Technologies

You can choose which technologies you want to use for this project, but ask your mentor before choosing. Here is my recommended technologies:

Models

Your DB Will contain 2 collections:

  • Soldiers:
interface Soldier {
  _id: string;
  name: string;
  rank: {
    name: string;
    value: number;
  };
  limitations: string[];
  createdAt: ISODate;
  updatedAt: ISODate;
}
  • Duties:
interface Duty {
  _id: ObjectId;
  name: string;
  description: string;
  location: GeoJSON Point;
  startTime: ISODate;
  endTime: ISODate;
  minRank: number;
  maxRank: number;
  constraints: string[];
  soldiersRequired: number;
  value: number;
  soldiers: ObjectId[];
  status: string;
  statusHistory: {
    status: string;
    date: ISODate;
  }[];
  createdAt: ISODate;
  updatedAt: ISODate;
}

Task 1 - Health Check

Estimation time: 3 Days

  1. Create a server and an app.

    • listen on port from ENV var and the default port should be 3000.
  2. Create a health check endpoint:

    • GET /health
    • Return a 200 status code if the server is running.
    • Return a JSON response: {"status": "ok"}

    Run your app and test it using curl command:

    curl http://localhost:3000/health

    You can also use Postman, or any other tool you like (such as hoppscotch, Insomnia or Thunder Client vscode extension).

  3. Create a database connection.

    • Connect to the database using the connection string from ENV var.
    • The connection should be established before the server starts listening.
    • The connection should be closed when the server stops listening.
  4. Create a db health check endpoint:

    • GET /health/db
    • Return a 200 status code if the database is connected.
    • Return a JSON response: {"status": "ok"}
  5. Create a test for your health check endpoint.

    What is the coverage of your tests?

  6. Separate application code from the server code (Why?):

    • Create server.js file.
    • Create app.js file.
  7. Test your app and server.

    You can use "HTTP injection" (Fastify built in app.inject function).

    What is the coverage of your tests? Did you test the error case?

  8. Add linter to your project.

    • Add lint script to your package.json file.
    • Run the script before submit any merge request.
  9. Log your app and server.

    • The log-level should be configurable, and the default level should be info. In the test environment, the default log level should be silent.

    • Add "pino-pretty" to your project (dev only) to format the logs.

      node server.js | pino-pretty

Task 2 - Soldier

Estimation time: 4 Days

  1. Create endpoint for creating a new soldier:

    • POST /soldiers

    • Return a 201 status code if the soldier is created.

    • The body will include the following parameters:

      {
        _id, name, rankValue || rankName, limitations;
      }
    • The _id should be the soldier's private tt number - a 7 digits number.

    • The name should be a string with length between 3 and 50.

    • The rankValue should be a number between 0 and 6. Use the following table to convert the number to the rank name:

      Rank Value Rank Name
      0 private
      1 corporal
      2 sergeant
      3 lieutenant
      4 captain
      5 major
      6 colonel
    • Save the limitations in lower case.

    • Validate that all the above parameters exist, any other property is invalid.

    • Add the createdAt and updatedAt properties and initialize them to the current date.

    • Return the inserted Soldier.

  2. Create endpoint for getting a soldier:

    • GET /soldiers/:id
    • Return a 200 status code if the soldier is found.
    • Return a 404 status code if the soldier is not found.

    Example: a request to /soldiers/0112358 should return the soldier with id 0112358 (if exists).

  3. Create endpoint for getting all soldiers:

    • GET /soldiers
    • A search query can be passed as a query parameter. For example: a request to /soldiers?name=david should return all soldiers (as an array) with the name 'david'.
    • When searching by limitations, the query should be an array of limitations. For example: a request to /soldiers?limitations=food,standing should return all soldiers (as an array) with the limitations 'food' and 'standing'.
    • The rank can be passed as rankValue or rankName. For example: a request to /soldiers?rankValue=3 should return all soldiers (as an array) with the rankValue 3. For example: a request to /soldiers?rankName=lieutenant should return all soldiers (as an array) with the rankName 'lieutenant'.
  4. Create endpoint for deleting a soldier:

    • DELETE /soldiers/:id
    • Return a 204 status code if the soldier is deleted.
    • Return a 404 status code if the soldier is not found.
  5. Create endpoint for updating a soldier:

    • PATCH /soldiers/:id
    • The body will include a Soldier object.
    • The updated Soldier will be an object and should contain the updated properties only.
    • The updated properties will override the existing ones.
    • Update the updatedAt property to the current date.
    • Do not allow this method to add any new properties nor to alter the _id.
    • Return the updated Soldier with a 200 status code if the soldier is updated.

If you using fastify (and you should) Add schema also for the response (Why?).

Task 3 - Duty

Estimation time: 4 Days

  1. Create endpoint for creating a new duty:

    • POST /duties

    • Return a 201 status code if the duty is created.

    • The body will include the following parameters:

      {
        name,
        description,
        location,
        startTime,
        endTime,
        constraints,
        soldiersRequired,
        value,
        minRank?
        maxRank?
      }
    • Generate a unique _id for the object (MongoDB will do it for you).

    • Validate:

      • name is a string with length between 3 and 50.
      • location is a valid GeoJSON Point.
      • startTime is before the endTime and that the startTime is in the future.
      • value is a positive number.
      • minRank and maxRank if exists are numbers between 0 and 6.
      • All the above parameters exist, minRank and maxRank are optional. Any other property is invalid.
    • When a duty is inserted to the database:

      • Add the soldiers property and initialize it to an empty array.
      • Add the status property and initialize it to unscheduled.
      • Add the statusHistory property and initialize it to an array with the current status and date.
    • Return the inserted Duty.

  2. Create endpoint for getting all duties:

    • GET /duties
    • A search query can be passed as a query parameter. For example: a request to /duties?name=hagnash should return all should return all 'hagnash' duties (as an array).
  3. Create endpoint for getting a duty:

    • GET /duties/:id
  4. Create endpoint for deleting a duty:

    • DELETE /duties/:id
    • Scheduled duties cannot be removed.
  5. Create endpoint for updating a duty:

    • PATCH /duties/:id
    • The body will include a Duty object.
    • The updated Duty will be an object and should contain the updated properties only.
    • The updated properties will override the existing ones.
    • Scheduled duties cannot be updated.
    • Do not allow this method to add any new properties nor to alter the id.
    • Return the updated Duty.

Task 4 - Justice Board

Estimation time: 2 Day

The justice board is an array of objects with the keys:

  • _id - The unique identifier of the soldier
  • score - The total value of duties the soldier has been scheduled to.

For example:

[{ _id: '1123581', score: 13 }, { _id: '3141592', score: 12 }, { _id: '2718281', score: 94 } ...]
  1. Create endpoint for getting the Justice Board:

    • GET /justice-board
    • Use mongoDB aggregation to calculate the Justice Board (Why?).

Task 5 - Scheduling

Estimation time: 4 Days

The scheduling process is the process of assigning soldiers to duties. The soldiers' limitations (according to the duty's constraints), the Justice Board, and the rank should be taken into consideration.

The Duty status can be one of the following:

  • unscheduled - The duty is not scheduled yet.
  • scheduled - The duty is scheduled.
  • canceled - The duty is canceled.
  1. Create endpoint for scheduling a duty:

    • PUT /duties/:id/schedule
    • Don't allow to schedule a duty that is already scheduled.
    • Don't allow to schedule a duty that is canceled.
    • Don't allow to schedule a duty that is in the past.
    • The duty status should be updated to scheduled.
    • The duty statusHistory should be updated with the new status and the current date.
    • Use the soldiersRequired property to determine how many soldiers should be scheduled.
    • Make sure that the soldiers are not already scheduled to other duties at the same time.
    • Update the soldiers property in the Duty object with the scheduled soldiers.
  2. Create endpoint for canceling a duty:

    • PUT /duties/:id/cancel
    • Don't allow to cancel a duty that is already canceled.
    • Don't allow to cancel a duty that is in the past.
    • The duty status should be updated to canceled.
    • The duty status history should be updated with the new status and the current date.
    • Update the soldiers property in the Duty object (to an empty array).
  3. Fix the Soldiers routes to take into consideration the scheduled duties.

Automatic Scheduling (Optional)

The auto scheduling mechanism should schedule all unscheduled duties.

  1. Add an auto scheduling mechanism to your app.

    • The time should be configurable and the default time should be every 5 minutes.
    • Duty with higher value should be prioritized.
    • What will happen if there is a change in the Duties or Soldiers collection while the auto scheduling is running?

Task 6 - Make it professional

Estimation time: 2 Days

  1. Add handlers for unhandledRejection and uncaughtException events.

  2. Add handlers for SIGTERM and SIGINT events.

  3. Validate your ENV vars before starting the server.

  4. Add openAPI v3 documentation to your app.

    You can use the fastify-swagger plugin to generate the documentation.

  5. Add basic authentication to your app.

  6. Add a rate limiter for your routes.

    You can use fastify-rate-limit.

  7. Add README.md to your app. Your readme should include explanation to how to use and test the app.

  8. Use helmetJS to protect your app.

    You can use the fastify-helmet plugin.

  9. Optional: Add _link property to your responses.

Task 7 - Extend query parameters (optional)

Ask your mentor which extensions you should implement.

  1. Multiple parameters

    Extend the /soldiers and /duties routes functionality to multiple parameters search query.

    For example: A request to /duties?name=hagnash&soldiers=mishel,shir should return all "hagnash" duties that contains both "mishel" and "shir". (Note that a "hagnash" duty that was scheduled with "mishel", "shir" and "david" should also be returned).

  2. Sorting

    Extend the he /justice-board /soldiers and /duties routes to accept sorting queries also with desire order.

    For example:

    • A request to /duties?sort=value should return the duties sorted by value.
    • A request to /duties?sort=name&order=desc should return the duties sorted by name in descending order.
    • A request to /justice-board?sort=score&order=desc should return the justice board sorted by score in descending order.
  3. Filtering

    Extend the /justice-board /soldiers and /duties functionality to accept filtering queries.

    For example:

    • A request to /justice-board?filter=score>=20 should return the justice board with soldiers with score >= 20.
  4. Pagination

    Extend the /justice-board /soldiers and /duties functionality to accept pagination.

    For example:

    • A request to /justice-board?page=2&limit=10 should return the justice board with the second page of 10 soldiers.
  5. Projection

    Extend the /justice-board /soldiers and /duties functionality to accept projection by fields.

    For example:

    • A request to /soldiers?select=name should return the soldiers with only the name property.
    • A request to /duties?select=name,value should return the duties with only the name and value properties.
  6. Population

    Extend the /justice-board /soldiers and /duties functionality to accept population of fields.

    For example:

    • A request to /duties?populate=soldiers should return the duties with the soldiers property populated with the soldiers data.
  7. Geo Queries

    Extend the /duties functionality to accept geo queries.

    For example:

    • A request to /duties?near=32.0853,34.7818&radius=1000 should return the duties that are near Tel Aviv (32.0853, 34.7818) with a max distance of 1000 meters.

Task 8 - Make it scalable (Advanced)

  1. What will happen if you add more soldiers and duties?

    You may use this concepts:

    • Indexing
    • Pagination
    • Cache
    • Job Queue
  2. Who can modified the soldiers and duties?

    You may use this concepts:

    • Authentication
    • Authorization
  3. What is the performance of your app?

    Make a load test to your app. Profile your app and find the bottlenecks.

Next steps

Let's return to a type safe space

⚠️ **GitHub.com Fallback** ⚠️