Our users have points in their accounts. Users only see a single balance in their accounts. But for reporting purposes we actually track their points per payer/partner. In our system, each transaction record contains: payer (string), points (integer), timestamp (date).
For earning points it is easy to assign a payer, we know which actions earned the points. And thus which partner should be paying for the points. When a user spends points, they don't know or care which payer the points come from. But, our accounting team does care how the points are spent.
There are two rules for determining what points to "spend" first:
- We want the oldest points to be spent first (oldest based on transaction timestamp, not the order they’re received)
- We want no payer's points to go negative.
-
To start the server using
npm
, run the following command at root$ npm start
-
To start the server using
nodemon
, run the following command at root$ nodemon
-
To run tests, run the following command at root.
$ npm test
For any modifications, modify package.json or nodemon.json files for npm and nodemon respectively.
. +-- index.js +-- main.js +-- util.js +-- impl.js +-- test | +-- app.test.js | +-- test-constants.js +-- package.json +-- nodemon.json +-- ReadMe.md
Details
- Entry point is
index.js
util.js
contains implementation of common functions needed for the main functionality.impl.js
is the implementation of main functionality of the end points.test
folder contains the test case related files.package.json
,nodemon.js
are configuration files needed fornpm
andnodemon
respectively.
POST /add
Pushes the record into the database.API End point
POST http://localhost:9090/add Request Body Format: { "payer": "[payer-name]", "points": [points], "timestamp": "[timestamp]" [sample format: "2021-03-01T14:00:00Z"] }
Returns
- The request returns status code
200
and{code:200, msg: 'success'}
upon successful add operation. - Returns status code
400
and{code:200, msg: <error-msg>}
for failures.
Example
Request: POST http://localhost:9090/add { "payer": "DANNON1", "points": -200, "timestamp": "2021-03-01T14:00:00Z" } Response: { "code": 200, "msg": "success" }
POST /spend
This request calculates the points spent from each payer from the records sorted based on the timestamp.API End point
POST http://localhost:9090/spend Request Body Format: { "points": [points-to-spend], }
Returns
- List of all spent payers along with their points.
- If the points are insufficient or not available, then
Spend Fail
property will appear in the response [Spend Fail: [points]
]
Example
Request: POST http://localhost:9090/spend { "points": 450 } Response: { "DANNON1": 200, "Spend Fail": 250 }
GET /balance
Returns the available points from each payer.API End point
GET http://localhost:9090/balance
Returns
- List of all payer remaining points.
- Returns empty list if none of the payers has any points.
Example
Request: GET http://localhost:9090/balance Response: { "DANNON1": 200 }
- The Server will listen on 9090 port.
- There are three end points provided as explained in End points section.
- Server pushes the record to the
Database
for everyadd
request and updates the record inData
based on the timestamp. - Servers requests data from
Data
for everyspend
andbalance
request. - The original data is kept untouched in the Database. all the operations are performed on
Data
object - Thus, the
Data
and theDatabase
will remain in sync at all times.
The
Data
and theDatabase
here are just two variables in the implementation.
- The
Data
is sorted every time a new record is inserted into theDatabase
for record retrieval with low timestamp forspend
requests. - The sort is implemented using Low bound binary Search and Insertion sort approaches.
- For every record inserted, We get index to insert the record into the
Data
using Low bound binary Search. - For
spend
requests, the points are calculated from 0 index, every time all points are used, the 0th record in theData
is deleted and every record in the right of 0 index will shift right.This may cause performance complexity since shifting records every time is expensive. to avoid this heap trees can be used which reduces the complexity from O(n) to O(logn) for delete operation.