forked from Offspend/public-hiring-test
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
15,616 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
DATABASE_HOST=localhost | ||
DATABASE_PORT=5432 | ||
DATABASE_TEST_PORT=5433 | ||
DATABASE_USERNAME=user | ||
DATABASE_PASSWORD=pwd | ||
DATABASE_NAME=database | ||
MIGRATIONS_RUN=true | ||
NODE_ENV=development |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"editor.formatOnSave": true, | ||
"typescript.preferences.importModuleSpecifier": "relative", | ||
"editor.codeActionsOnSave": [ | ||
"source.fixAll", | ||
"source.organizeImports", | ||
"source.addMissingImports" | ||
], | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,94 @@ | ||
# public-hiring-test | ||
Technical tests for hiring software engineering @Greenly | ||
# How to use | ||
|
||
Stack: NestJs + TypeORM + Postgres | ||
|
||
## Requirements | ||
|
||
- Docker / Docker Compose | ||
- Node.js 18 | ||
|
||
## Installation | ||
|
||
```bash | ||
$ yarn | ||
``` | ||
|
||
## Running the app | ||
|
||
First you need to start, migrate and seed the db : | ||
|
||
```bash | ||
$ yarn init-project | ||
``` | ||
|
||
you can then start the server: | ||
|
||
```bash | ||
# development | ||
$ yarn start | ||
|
||
# watch mode | ||
$ yarn start:dev | ||
``` | ||
|
||
## Test | ||
|
||
To run unit tests: | ||
|
||
```bash | ||
# unit tests | ||
$ yarn test | ||
|
||
# e2e tests | ||
$ yarn test:e2e | ||
|
||
# test coverage | ||
$ yarn test:cov | ||
``` | ||
|
||
## Database Migration | ||
|
||
When the data schema is updated, the database needs to be synchronised with the code. This is done by creating a migration with Typeorm using the following command: | ||
|
||
```bash | ||
migrationName=<name> yarn migration:generate | ||
``` | ||
|
||
################################################################################################################################## | ||
|
||
# Hiring Test | ||
|
||
################################################################################################################################## | ||
|
||
When working on the following exercise, in addition to answering the product need, to give particular attention to the following points: | ||
|
||
- Readability | ||
- Maintainability | ||
- Unit testing | ||
- Handling of corner cases | ||
- Error-handling | ||
|
||
We want to compute the Agrybalise carbonfootprint of a foodproduct (e.g.: a hamCheesePizza) that we characterize by its ingredients as shown below | ||
|
||
```js | ||
const hamCheesePizza = { | ||
ingredients: [ | ||
{ name: "ham", quantity: 0.1, unit: "kg" }, | ||
{ name: "cheese", quantity: 0.15, unit: "kg" }, | ||
{ name: "tomato", quantity: 0.4, unit: "kg" }, | ||
{ name: "floor", quantity: 0.7, unit: "kg" }, | ||
{ name: "oliveOil", quantity: 0.3, unit: "kg" }, | ||
], | ||
}; | ||
``` | ||
|
||
The calculation of the Agrybalise carbon footprint can be described as below: | ||
|
||
- The Agrybalise carbon footprint of one ingredient is obtained by multiplying the quantity of the ingredient by the emission of a matching emission factor (same name and same unit). | ||
- The carbon footprint of the food product is then obtained by summing the carbon footprint of all ingredients. | ||
- If the carbon footprint of one ingredient cannot be calculated, then the carbon footprint of the whole product is set to null. | ||
|
||
The tasks of this exercice are as follows: | ||
1/ Implement the carbon footprint calculation of a product and persist the results in database. | ||
2/ Implement a GET endpoint to retrieve the result. | ||
3/ Implement a POST endpoint to trigger the calculation and the saving in the database. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { registerAs } from "@nestjs/config"; | ||
import { config as dotenvConfig } from "dotenv"; | ||
import { join } from "path"; | ||
import { DataSource, DataSourceOptions } from "typeorm"; | ||
|
||
dotenvConfig({ path: ".env" }); | ||
|
||
const dataSourceOptions: DataSourceOptions = { | ||
type: "postgres", | ||
port: parseInt( | ||
`${process.env.NODE_ENV === "test" ? process.env.DATABASE_TEST_PORT : process.env.DATABASE_PORT}` | ||
), | ||
username: process.env.DATABASE_USERNAME, | ||
password: process.env.DATABASE_PASSWORD, | ||
database: process.env.DATABASE_NAME, | ||
synchronize: false, | ||
logging: false, | ||
entities: [join(__dirname, "../src/**/*.entity.{js,ts}")], | ||
migrations: [join(__dirname, "../migrations", "*.*")], | ||
migrationsTableName: "migrations", | ||
migrationsRun: process.env.MIGRATIONS_RUN === "true", | ||
}; | ||
|
||
export const typeorm = registerAs("typeorm", () => dataSourceOptions); | ||
|
||
export class GreenlyDataSource { | ||
private static dataSource: DataSource; | ||
private static testDataSource: DataSource; | ||
|
||
public static getInstance() { | ||
if (process.env.NODE_ENV === "test") { | ||
if (this.testDataSource) { | ||
console.log(`testDataSource already set for ${process.env.NODE_ENV}`); | ||
return this.testDataSource; | ||
} | ||
this.testDataSource = new DataSource(dataSourceOptions); | ||
|
||
return this.testDataSource; | ||
} | ||
if (this.dataSource) { | ||
console.log(`dataSource already set for ${process.env.NODE_ENV}`); | ||
return this.dataSource; | ||
} | ||
this.dataSource = new DataSource(dataSourceOptions); | ||
|
||
return this.dataSource; | ||
} | ||
|
||
public static async cleanDatabase(): Promise<void> { | ||
try { | ||
const entities = dataSource.entityMetadatas; | ||
const tableNames = entities | ||
.map((entity) => `"${entity.tableName}"`) | ||
.join(", "); | ||
|
||
await dataSource.query(`TRUNCATE ${tableNames} CASCADE;`); | ||
} catch (error) { | ||
throw new Error(`ERROR: Cleaning test database: ${error}`); | ||
} | ||
} | ||
} | ||
|
||
export const dataSource = GreenlyDataSource.getInstance(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
services: | ||
db: | ||
image: postgres | ||
restart: always | ||
environment: | ||
POSTGRES_USER: ${DATABASE_USERNAME} | ||
POSTGRES_PASSWORD: ${DATABASE_PASSWORD} | ||
POSTGRES_DB: ${DATABASE_NAME} | ||
ports: | ||
- "${DATABASE_PORT}:5432" | ||
db_test: | ||
image: postgres | ||
restart: always | ||
environment: | ||
POSTGRES_USER: ${DATABASE_USERNAME} | ||
POSTGRES_PASSWORD: ${DATABASE_PASSWORD} | ||
POSTGRES_DB: ${DATABASE_NAME} | ||
# PGPORT | ||
ports: | ||
- "${DATABASE_TEST_PORT}:5432" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"moduleFileExtensions": ["js", "json", "ts"], | ||
"rootDir": "./src", | ||
"testEnvironment": "node", | ||
"testRegex": ".test.ts", | ||
"transform": { | ||
"^.+\\.(t|j)s$": "ts-jest" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { MigrationInterface, QueryRunner } from "typeorm"; | ||
|
||
export class CarbonEmissionFactor1708367794381 implements MigrationInterface { | ||
name = "CarbonEmissionFactor1708367794381"; | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query( | ||
`CREATE TABLE "carbon_emission_factors" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "unit" character varying NOT NULL, "emissionCO2eInKgPerUnit" float NOT NULL, "source" character varying NOT NULL, CONSTRAINT "PK_e6a201ea58a7b4cdec0ca1c0c61" PRIMARY KEY ("id"))` | ||
); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`DROP TABLE "carbon_emission_factors"`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/nest-cli", | ||
"collection": "@nestjs/schematics", | ||
"sourceRoot": "src", | ||
"compilerOptions": { | ||
"deleteOutDir": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
## Sources | ||
to integrate typeorm and generate migrations | ||
|
||
https://dev.to/amirfakour/using-typeorm-migration-in-nestjs-with-postgres-database-3c75 | ||
|
||
|
||
|
||
|
||
Technical | ||
|
||
- Start from a boiler plate with simple database: | ||
-postgre /Typeorm/express//jest | ||
|
||
```js | ||
const carbonEmissionFactors = [ | ||
{ | ||
Name: "ham", | ||
unit: "kg", | ||
valueInKgCO2: 0.12, | ||
source: "Agrybalise", | ||
}, | ||
{ | ||
name: cheese, | ||
unit: "kg", | ||
valueInKgCO2: 0.12, | ||
source: "Agrybalise", | ||
}, | ||
{ | ||
name: tomato, | ||
unit: "kg", | ||
valueInKgCO2: 0.12, | ||
source: "Agrybalise", | ||
}, | ||
{ | ||
name: oliveOil, | ||
unit: l, | ||
valueInKgCO2: 0.12, | ||
source: "Agrybalise", | ||
}, | ||
]; | ||
|
||
``` | ||
|
||
|
||
When working on the following exercise, give particular attention to the following points: | ||
Readability of your code | ||
Unit Testing | ||
Architecture and organization of your functions | ||
Handling of corner cases and errors | ||
Overall performance of your code | ||
|
||
1/ Create an endpoint to compute carbonFootprint of food and persist data in database. | ||
```js | ||
const hamCheesePizza = { | ||
ingredients: [ | ||
{ name: "ham", value: "2", unit: "g" }, | ||
{ name: "cheese", value: "15", unit: "g" }, | ||
{ name: "tomato", value: "4", unit: "g" }, | ||
{ name: "floor", value: "7", unit: "g" }, | ||
{ name: "oliveOil", value: "0.7", unit: "l" }, | ||
], | ||
}; | ||
``` | ||
2/Agrybalise has updated its coefficients and we want to update accordingly our knowledge base. In order to do that, we need to develop and endpoint allowing to update and/or insert new values in our referential of emission factors. | ||
|
||
In particular, we want to add these values: | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Test Criterion for technical test: | ||
Problem solving solution: should solve efficiently the problem | ||
Clean code aspects: readability (naming, architecture), test | ||
Performance of code & algorithmic | ||
Knowledge of database | ||
Error handling & exceptions | ||
|
||
Test Criterion for technical test: | ||
Clarity of explanation of approach and database | ||
General engineering culture | ||
Reactivity on advices & inputs | ||
|
||
|
||
|
||
|
||
Questions: | ||
|
||
|
||
|
||
2/ | ||
|
||
Fonction buggé | ||
|
||
|
||
Performance | ||
|
||
Asynchronous feature | ||
Utiliser un ORM | ||
Rest API | ||
Base de donnée | ||
|
||
Bonus: | ||
authentification |
Oops, something went wrong.