Challenge Overview
Implement the ability to breakdown price using building blocks.
Project Background
Topcoder Project Service is the main backend service of Topcode Connect – client-facing application of Topcoder.
When customers create projects using the project creation wizard they choose various features which we call “Building Blocks”. When a new project is saved we store the price of each building block in the DB in ProjectEstimation model. So as each project may have several building blocks chosen, there could be several ProjectEstimation records per project.
Each ProjectEstimation record so far keeps only the total price of the corresponding building block. We would like to have the ability to breakdown the total price of each ProjectEstimation record into different kind of fees like “community budget”, “Topcoder service fee”, “reference program fee” and so on.
Technology Stack
-
Node.js
-
PostgreSQL
Code access
The work for this challenge has to be done in one repository:
- Project Service repo https://github.com/topcoder-platform/tc-project-service branch feature/price-estimation-items/building-blocks.
- Config for local setup is provided on the forum.
Individual requirements
Hint: whenever below we require to check permission we already have a reusable method to check permissions hasPermissionForProject which can be used.
1. Project Estimation Items
-
[Major requirement] Create ProjectEstimationItems model to store ProjectEstimation breakdown:
- id bigint NOT NULL,
- "projectEstimationId" bigint NOT NULL, // reference to ProjectEstimation model
- price double precision NOT NULL,
- type enum(community, topcoder_services, fee) NOT NULL, // all values of ESTIMATION_TYPE
- "markupUsedReference" string NOT NULL, // will be equal to the string “buildingBlock” for building blocks
- "markupUsedReferenceId" bigint NOT NULL, // will hold the “id” of a buildingBlock record for building blocks
- metadata json NOT NULL DEFAULT '{}'::json,
- "deletedAt" timestamp with time zone,
- "createdAt" timestamp with time zone NOT NULL,
- "updatedAt" timestamp with time zone NOT NULL,
- "deletedBy" bigint,
- "createdBy" bigint NOT NULL,
- "updatedBy" bigint NOT NULL,
-
[Major requirement] Implement “GET /projects/{id}/estimations/{id}/items” endpoint which would return ProjectEstimationItems.
- permission to access this endpoint copitlotAndAbove. -
[Major requirement] We should filter returning records depends on their type and user permissions.
- users who match “permission = { topcoderRoles: MANAGER_ROLES }” will get all the records
- users who match “permission = { projectRoles: [COPILOT] }” will get only records with type=”community”
- other users won’t get any records by this endpoint
- make this configurable which types of ProjectEstimationItems can get users with different permissions. -
[Major requirement] As this is sensitive information, we should make sure that data cannot be accidentally accessed by users with no proper permissions. In particular, we should take precautions so future developers don’t include such information accidentally by making some changes.
- to make sure that we only users with proper permissions get data from the ProjectEstimationItems model we can use beforeFind or beforeFindAfterOptions hook.
- This hook should get reqUser = req.authUser object through options when we call findOne/findAll. If reqUser object is not provided by options, we should throw an error.
- If the user object is provided we should use it to update find query request, to find only the allowed records. Or we can still find all records but filter them based on user permissions inside hook.
- The method described above is my proposition. Any improvements which could make sure future developers don’t return non-filtered data in future are highly welcome. -
Implement unit tests for this endpoint a similar way we do for others.
- [Major requirement] In particular, write extensive unit tests with positive and negative cases to verify that only allowed records are returned to the users.
2. Building Blocks
-
[Major requirement] Create BuildingBlock model
id bigint NOT NULL,
key string NOT NULL,
config json NOT NULL DEFAULT '{}'::json,
"privateConfig" json NOT NULL DEFAULT '{}'::json,
"deletedAt" timestamp with time zone,
"createdAt" timestamp with time zone,
"updatedAt" timestamp with time zone,
"deletedBy" bigint,
"createdBy" bigint NOT NULL,
"updatedBy" bigint NOT NULL,
CONSTRAINT UNIQUE (key) -
[Major requirement] We have endpoint “GET /metadata” which currently returns various kinds of data: “projectTemplates”, “productType”, “forms” and so on. Update this endpoint so it returns one more property “buildingBlocks” with the list of all building blocks from the DB.
-
[Major requirement] The same time we should never return “privateConfig” field using API, including “GET /metadata” endpoint. To make sure that we will never accidentally include field "privateConfig" to the output, we should use the same approach as for ProjectEstimationItems with beforeFind or beforeFindAfterOptions hook to remove "privateConfig" field from the output of findAll/findOne methods or even don’t request this field from the DB.
-
Implement unit test to verify that "privateConfig" is not returned inside “buildingBlocks” via this endpoint.
-
Sometimes we may need to get “privateConfig” field to make calculations inside the Project Service code. To retrieve it for internal usage, support option “includePrivateConfigForInternalUsage”, which we can pass via options for findAll or findOne method instead of reqUser.
3. Calculate Project Estimation Items
-
[Major requirement] When a project is created already creating ProjectEstimation per each building block estimation. We have to additionally create ProjectEstimationItems with the next algorithm:
- for each created ProjectEstimation try to find BuildingBlock record which has “privateConfig”. (better to retrieve all needed BuildingBlock with one DB query).
- - for each item in “privateConfig.priceItems” create a ProjectEstimationItem record with type equal to the “key” from “priceItems” object.
- - - if “priceItems[key]” is a number, then set ProjectEstimationItem.price to that “priceItems[key]”
- - - if “priceItems[key]” is a string with “%” sign than calculate ProjectEstimationItem.price as a percentage from the price of ProjectEstimation.price -
For example, if we have ProjectEstimation and we found a respective BuildingBlock with “privateConfig”. Then we would create 3 ProjectEstimationItem records.
-
Put this logic into a separate function.
-
Implement unit tests to verify, that recalculation is happengin on project creation, and correct ProjectEstimationItes are created with the prices calculated as per algorithm above.
General requirements
-
Create corresponding SQL migration script in /migrations folder to support model changes in the DB.
Validate that migration script is correct and submission works if we use migration SQL over the old DB structure. -
Follow the existent code standards and approaches.
-
Update Swagger. Add new endpoints and create definitions where necessary.
-
Update Postman file.
-
Existent unit tests should pass.
-
Lint should pass.
Verification
Hint
We have a script to populate some demo data https://github.com/topcoder-platform/tc-project-service#import-sample-metadata--projects which could be helpful, as it populates projects and projectTemplates.
You may also, need to setup Connect app locally and use it with local setup of Project Service using this guide. So you with such a local setup you can use the project creating wizard with this link. During project creation if you choose a lot of stuff, some ProjectEstimation items should be created in DB, so you may check how items are generated.
Requirement
Provide a verification guide on how to test the submission. In particular, include steps we should follow to test permissions “Project Estimation” endpoint and price recalculation logic.Provide demo data to populate BuildingBlock model for testings.
Prove an example of project creation request (maybe in Postman) which we can make so it would create multiple ProjectEstimation records and calculated ProjectEstimationItems records.
Final Submission Guidelines
-
Git patch.
-
Verification guide.