Challenge Overview
Bright Harvest web app will provide our clients with flexible residential solar designs, faster turnaround, time and lower cost of delivering our innovative precision remote PV design services.
The goal of this challenge is to build a REST API backend for the web application.
Challenge Requirements
You will address the following in this challenge:
NodeJS Framework and Modules
-
- You will use MySQL, NodeJS, ExpressJS, and AngularJS stack in this challenge.
-
- You are required to use open source node module such as async to deliver a high quality, well organized code.
-
- Use passportjs to implement google login/signup.
-
- Third party code must not have GPL or AGPL license. MIT and Apache licenses are acceptable.
Entity Models and Database Schema
Provided in challenge forums the Database schema document, please follow it to build the models and database schema
Note the following :
-
- If the field of any object is required unless it has (optional) label.
-
- primary Ids will be auto generated, not passed to endpoints.
-
- Auditing fields will be set in backend not passed to endpoints
-
- if access_token is passed to the endpoint, then createdUserId/modifiedUserId will be set to that user.
-
- created field will set to current date.
-
- modified field will set to modified date.
-
Validation
Perform the following validation in each API endpoint :
-
- Validate access token for API endpoints that require access_token
-
- Path parameters :
-
- They usually represents the ID of existing objects, a validation should be done that the entity with ID exists, otherwise return error.
-
- Required input parameters in create endpoints must be present. For update endpoints we can pass only the fields that should be updated.
-
- Required input parameters should be validated against their expected type.
-
- Optional input parameters should be validated against their expected type if provided.
-
- Foreign keys must be validated.
-
-
- Validation between callbacks must be performed.
-
- Errors should be in json format with three fields :
-
- code : http status code
-
- status : success/failed
-
- message : reason of failure
-
Logging
-
- Add proper logging for method entry, method exit at INFO level.
-
- Add proper logging for input parameters, and return parameters at DEBUG level.
-
- Add proper logging for errors in ERROR level.
-
- Use watson for logging, make the logging level configurable, and preferred to separate log files by level.
Request and Response
-
- GET endpoints parameters will accept parameters as query strings, in key/value format.
-
- Create/Update endpoint parameters will accept input as JSON format.
-
- Exclude file upload endpoints which cannot work with application/json content type.
-
-
- Response should be in json format always.
Role Based Authorization
Currently we have only two roles : admin and client roles.
You need to apply authorization per role, here is a breakdown by table for what each role can do what in the application :
-
users: Client can create a new record and edit only their own record. Admin has full access to any record (CRUD).
-
companies: Client has no access except to read (but not edit) which company they are a member of. Admin has full access.
-
projects: Client can create a record. Client can edit record but only if clientStatus is "In queue", "Hold", "Payment pending" or "Revision requested". In all other cases, client has read only permissions. We don't want to allow the client to change the parameters of the order after we have started working on it. Admin has full access.
-
siteModels: Client has read only access. Admin has full access.
-
layouts: Client has read only access to layouts created by BHS Admin (the UI is not clearly defined for this case). Client has read/write (but not delete) to layouts created by any other Client who has access to the same layout. BHS Admin has full access.
-
notes: Client can only create new notes (no edit or delete) and has read access to all notes attached to the project except internal notes. BHS Admin has full access.
-
attachments: Client and BHS Admin both have full access.
REST API Endpoints
You will implement basic CRUD API endpoints for all the tables in addition to authorization endpoints, here are a breakdown of the requirements :
-
Login
-
- Route : POST /login
-
- Request body parameters :
-
- username (required)
-
- password (required)
-
-
- It returns output as json object :
-
- access_token : should be used in all secured api endpoints
-
- user_role : this is the user role
-
- user_name : the user name
-
-
-
- Create New User
-
- Route : POST /users
-
- Headers parameter :
-
- access_token (optional) : if passed it should be for an admin role.
-
-
- Request body parameters - Should be the users table fields excluding auditing fields,
-
- Note that the following are set only if the caller is an admin
-
- role
-
- initials
-
-
-
- The endpoint will create a new User record in database with role ‘client’ if the creator is not ‘admin’.
-
- Send email to user :
-
- Send welcome email if user registered manually.
-
- Send notification email to notify the user she was added to the application.
-
- Both email templates should be configurable.
-
-
- It returns 200 success response with users object as response.
-
-
- Register/Login with Google
-
- Route: GET /login/google and GET /login/google/callback
-
- Use passportjs to implement login with google
-
- The GET /login/google endpoint will use the passportjs to create the authorization url to google, response will be the authorization url.
-
- The GET /login/google/callback will handle the callback from google
-
- it will check if user with email already exists, if yes, then it will connect user with google account and login the user
-
- If no user with email exists then return error that “user with email does not exist. you should register first”
-
-
- The response will be a json object same as the one return from login endpoint.
-
-
- Forgot password
-
- Route : POST /forgotpassword
-
- Input parameter is :
-
- email address (required)
-
-
- The endpoint logic :
-
- validate the email address using regex
-
- check if there is a user with email, if not, return error
-
- create a reset password link with expiration date
-
- send reset password email to user
-
- the email template should be configurable
-
- the from email should be configurable
-
-
-
- Response should be json with ‘success’ : true value on success, or success : false with message field set for reason of failure.
-
-
- Reset User Password
-
- Route : GET /resetpassword
-
- Input parameters are the query string parameters constructed in Forgot Password endpoint.
-
- response will be same as Forgot Password response, it should be a ‘success’ attribute with true/false depends on the execution result, with ‘message’ attribute if it is a failure.
-
-
- Update User
-
- Route: PUT /user/{id}
-
- Path parameter :
-
- Id : represents users object identifier
-
-
- Input parameters are :
-
- Users fields to be updated.
-
-
- This endpoint will update the users object with provided new information
-
- It should add additional validation :
-
- username/email should be unique so we need to validate that the new username/email don’t already taken by another user
-
-
- It returns 200 success response with user object as response.
-
-
- GET User
-
- Route : GET /user/{id}
-
- Path parameters :
-
- id : represents the users object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Return the users object.
-
-
- Delete User
-
- Route : DELETE /user/{id}
-
- Path parameters :
-
- id : represents the users object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Perform a hard delete of the record of the passed ID.
-
-
- Users list
-
- Route : GET /users
-
- This endpoint return list of User objects
-
- Input parameters :
-
- offset (optional) - default to 0
-
- limit (optional) - default to 10
-
- sort_column (optional ) - default to modified field.
-
- sort_direction (optional) - default to desc, from newest to oldest.
-
-
- Using the pagination and sorting information retrieve the users objects with following fields:
-
- userId
-
- userName
-
- fullName
-
- email
-
- role
-
- created
-
- modified
-
-
-
- Companies List
-
- Route: GET /companies
-
- This endpoint return list of “companies” objects.
-
- Input parameters :
-
- offset (optional) - default to 0
-
- limit (optional) - default to 10
-
- sort_column (optional ) - default to Modified date
-
- sort_direction (optional) - default to desc, from newest to oldest.
-
-
- Using the pagination and sorting information retrieve the “companies” objects, and for each companies object in result calculate members count using companiesUsers table and set it in the companies object json object.
-
-
- Create New Company
-
- Route: POST /companies
-
- The endpoints is to create new company
-
- Headers
-
- access_token (required)
-
-
- Input body parameters are
-
- all companies table fields excluding auditing fields.
-
- array of member ids to associate with it. (optional)
-
-
- Create new company entity and associate passed members with the company entity in usersCompanies table.
-
- Return the created companies object with members as child array.
-
-
- Update Existing Company
-
- Route: PUT /company/{id}
-
- The endpoints is to update existing company
-
- Path parameters :
-
- id : represents the companies object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Input body parameters :
-
- companies fields to be updated.
-
- array of member ids to associate with it. (optional)
-
-
- Update company entity and associate passed members with the passed in information.
-
- Return the updated companies object with members as child array.
-
-
- GET Company
-
- Route : GET /company/{id}
-
- Path parameters :
-
- id : represents the companies object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Return the companies object with members as child array.
-
-
- Delete Company
-
- Route : DELETE /company/{id}
-
- Path parameters :
-
- id : represents the companies object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Perform a hard delete of the record of the passed ID.
-
-
- Projects Lists
-
- Route: GET /projects
-
- The endpoint return list of projects.
-
- Headers :
-
- access_token (required)
-
-
- Input parameters :
-
- clientStatus - represents projects.clientStatus
-
- offset (optional) - default to 0
-
- limit (optional) - default to 10
-
- sort_column (optional ) - default to Modified date for client role, default to Due date for admin role.
-
- sort_direction (optional) - default to desc, from newest to oldest.
-
-
- If the user is admin then search all projects, if the user is client then search only projects for companies the user belongs to.
-
- Using the provided input parameters to retrieve the projects objects with only the following fields to be returned:
-
- projectId
-
- projectName
-
- projectAddress
-
- created
-
- modified
-
- clientStatus
-
- bhsStatus
-
-
- For each project pull the most recent note associated. (if exists)
-
- Return the objects.
-
-
- Search Projects
-
- Route : GET /projects/search
-
- The endpoint return list of projects that match the search criteria
-
- Headers :
-
- access_token (required)
-
-
- Input parameters :
-
- keyword (required) : represents the text to search Projects documents.
-
-
- If the user is admin then search all projects, if the user is client then search only projects for companies the user belongs to.
-
- The search logic will search projectName and projectAddress for matches
-
- Response will be array of Project json objects with following attributes:
-
- projectName
-
- projectId
-
- projectAddress
-
-
-
- Get Project
-
- Route : GET /project/{id}
-
- This endpoint is used to retrieve Project of the provided identifier
-
- Headers :
-
- access_token (required)
-
-
- Path parameters :
-
- id : represents the project document identifier
-
-
- Return project object of the provided id in json format.
-
-
- New Project
-
- Route: POST /projects
-
- The endpoint creates new project.
-
- Headers :
-
- access_token (required)
-
-
- Input body parameters will be :
-
- the projects table fields excluding auditing fields, clientStatus and bhsStatus.
-
- list of attachment ids
-
-
- Add proper validation for input parameters
-
- Insert new Project record using the provided input parameters.
-
- clientStatus: default is "Payment pending". If company.creditAccount == true, default is "In queue"
-
- bhsStatus: default is "Not started"
-
-
- Update attachments records of the passed ids to reference the created projects record.
-
- It returns 200 success response. with project object as response.
-
-
- Update Project
-
- Route : PUT /project/{id}
-
- The endpoint creates new project.
-
- Path parameter :
-
- {id} represents the project id
-
-
- Headers :
-
- access_token (required)
-
-
- Input body parameters will be :
-
- the projects table fields excluding auditing fields.
-
- list of attachment ids
-
-
- Update the existing project with provided parameters
-
- There are several automatically triggered transitions:
-
- clientStatus = "Canceled" then change bhsStatus to "Canceled"
-
- If BHS Admin changes bhsStatus to "Modeling", "Ready for PV", "Adding PV", "In review", "QA revision", "Updated" or "Approved", clientStatus should be automatically changed to "In progress"
-
- If bhsStatus is "Approved" and deliveryTime of the most recently added siteModel is less than current time, the clientStatus and bhsStatus should be changed to "Delivered"
-
- If clientStatus = "Request revision" then change bhsStatus to "Not started"
-
-
-
- Update attachments records of the passed ids to reference the created projects record.
-
- It returns 200 success response. with project object as response.
-
-
- Delete Project
-
- Route : DELETE /project/{id}
-
- Path parameters :
-
- id : represents the projects object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Perform a hard delete of the record of the passed ID.
-
-
- Get Company Users
-
- Route: GET /company/{id}/users
-
- headers :
-
- access_token (required)
-
-
- path parameter :
-
- id : represents the company identifier
-
-
- This endpoint returns the users of the passed in company from companiesUsers table.
-
- The response will be array of user objects in json format.
-
-
- Create New Site Model
-
- Route : POST /siteModel
-
- headers :
-
- access_token (required)
-
-
- input parameters will be the siteModels table fields excluding auditing fields.
-
- The response will be the created siteModels record.
-
-
- Update Site Model
-
- Route : PUT /siteModel/{id}
-
- path parameter :
-
- id : represents the siteModels record id.
-
-
- headers :
-
- access_token (required)
-
-
- input parameters will be the siteModels input parameters to be updated excluding auditing fields.
-
- The response will be the created siteModels record.
-
-
- Get Projects SiteModels
-
- Route: GET /project/{id}/siteModels
-
- headers :
-
- access_token (required)
-
-
- path parameter :
-
- id : represents the projects identifier
-
-
- This endpoint returns the siteModels of the passed in project id.
-
- The response will be array of siteModels objects in json format.
-
-
- GET SiteModels
-
- Route : GET /siteModel/{id}
-
- Path parameters :
-
- id : represents the siteModels object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Return the siteModels object.
-
-
- Delete SiteModels
-
- Route : DELETE /siteModel/{id}
-
- Path parameters :
-
- id : represents the siteModels object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Perform a hard delete of the record of the passed ID.
-
-
- Create New Layout
-
- Route : POST /layouts
-
- headers :
-
- access_token (required)
-
-
- Input parameters will be the layouts table fields excluding siteModels repeated fields and auditing fields.
-
- When creating the new record we pull the siteModels of passed siteModels id, and populate the repeated fields with the passed input parameters.
-
- Add validation for input parameters.
-
- The response will be the created layouts record.
-
-
- Update Existing Layout
-
- Route : PUT /layout/{id}
-
- Path parameters :
-
- {id} - represents the layouts parameter.
-
-
- Headers :
-
- access_token (required)
-
-
- Input parameters will be the layouts table fields excluding auditing fields.
-
- When updating we only update the fields of the passed input parameters.
-
- Add validation for input parameters.
-
- The response will be the updated layouts record.
-
-
- Get SiteModel Layouts
-
- Route: GET /siteModel/{id}/layouts
-
- headers :
-
- access_token (required)
-
-
- path parameter :
-
- id : represents the siteModels identifier
-
-
- This endpoint returns the layouts of the passed in siteModel id.
-
- The response will be array of layouts objects in json format.
-
-
- GET Layout
-
- Route : GET /layout/{id}
-
- Path parameters :
-
- id : represents the layout object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Return the layouts object.
-
-
- Delete Layout
-
- Route : DELETE /layout/{id}
-
- Path parameters :
-
- id : represents the layouts object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Perform a hard delete of the record of the passed ID.
-
-
- New Note
-
Route : POST /notes
-
- headers :
-
- access_token (required)
-
-
- The input parameters are the notes table fields.
-
- add proper validation.
-
- insert new notes record in database.
-
-
- Get Note
-
- Route : GET /Note/{id}
-
- Path parameters :
-
- id : represents the note object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Return the note object.
-
-
- Get Project Notes
-
- Route: GET /project/{id}/notes
-
- headers :
-
- access_token (required)
-
-
- path parameter :
-
- id : represents the project identifier
-
-
- This endpoint returns the notes of the passed in project id.
-
- The response will be array of notes objects in json format.
-
-
- Delete Note
-
- Route : DELETE /Note/{id}
-
- Path parameters :
-
- id : represents the notes object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Perform a hard delete of the record of the passed ID.
-
-
- Upload files
-
- Route: PUT /files
-
- this is used to upload files, it should return unique identifier of the file to be used or associated with the project
-
- it should accept multiple files upload
-
- Upload file to Google Cloud Storage via API
-
- you can use this library https://github.com/GoogleCloudPlatform/gcloud-node
-
-
- create new attachments records in database, it should reference the google cloud uploaded file.
-
- projectId will be set to null for newly uploaded files.
-
- response will be json array of attachment ids.
-
-
- Get File
-
- Route : GET /file/{id}
-
- Path parameters :
-
- id : represents the file object identifier
-
-
- Headers
-
- access_token (required)
-
-
- Return the file object from google cloud.
-
-
- Get Project Files
-
- Route: GET /project/{id}/files
-
- headers :
-
- access_token (required)
-
-
- path parameter :
-
- id : represents the project identifier
-
-
- This endpoint returns the attachments records of the passed in project id.
-
- The response will be array of attachments in json format.
-
-
- Role Lookup
-
- Route GET /roles
-
- This returns list of roles
-
- This is public endpoint
-
- Response will be list of role mongo documents in json format.
-
-
- Timezones Lookup
-
- Route : GET /timezones
-
- Lookup endpoint to get list of timezones supported in the system
-
- Response is array of id and display fields
-
Postman Client JSON
-
- Create postman json file listing all calls and sample data.
-
- Provide description for endpoints (recent postman version support endpoint descriptions)
-
- Get Started with Postman : http://www.getpostman.com/
Hosting
It is preferred if you provide scripts and steps to deploy the application in google cloud platform
https://cloud.google.com/nodejs/
Documentation
Provide a detailed README documentation for how to setup and configure the application.
Configurations
You are expected to use environment variables to store sensitive information and environment-specific configurations.
Folder Structure and Configuration
Follow this folder structure :
-
- config/
-
- config.js
-
-
- app.js
-
- controllers/
-
- models/
-
- services/
-
- helpers/
-
- README.md
-
- env-sample (don't include .env in your submission)
-
- .. other files if needed
For configuration, we expect routes and other sensitive config will be configured in config/config.js, we prefer if you use node-config module for that. but We will leave it up to you to use the proper approach.
Final Submission Guidelines
Deliverable
-
- All source code that implement the requirement.
-
- README in markup language
-
- Verification document contains steps to verify your solution.
- - Sample input file.