Dealer Alchemist · Senior Software Engineer interview

Code review exercise

Pull request: inventory list and repricing

A mid-level developer on our team opened this pull request against our multi-tenant dealer platform. Review it the way you would review a real pull request: think aloud, say what you would block the merge on, what you would ask to change, and what you would let go. Ask us anything you would ask the author.

What you need to know about the codebase

  • Tenancy. Each dealer is a tenant. Dealers belong to dealer groups (organizations). A user holds grants: a role, which maps to scopes such as inventory.read or inventory.update, granted either on one dealer, on a whole organization, or globally (platform staff).
  • Authentication and authorization. Every route is protected with @Authorize(scope, options) from our auth library. It verifies the caller's JWT (or a service-account key), checks that the caller holds the scope, and resolves which dealer the request is about from the tenant source given in the options:
    • no options: the :dealerId route parameter
    • { dealer: { from: "body" | "query", key } }: that field of the body or query string
    • { dealer: { from: "document", model: Vehicle.name } }: loads the document by the :id route parameter and uses its dealerId
    • { tenant: "none" }: a platform-level action with no dealer; only global grants pass
    If the configured source yields no dealer id, the guard runs the platform-level check, so only global grants pass. The caller's identity is available on the request if a handler needs it. @Authorize is applied in this pull request. Whether it is applied correctly is part of your review.
  • Data. Vehicles live in MongoDB through Mongoose. A Vehicle document has _id, dealerId, vin, status, price and about sixty feed fields. Vehicles arrive from dealer inventory feeds. The same VIN can appear under more than one dealer. Leads and price history live in PostgreSQL through drizzle: tables leads(dealer_id, vin, …) and price_history(dealer_id, vin, price, created_at).
  • If you have not used drizzle: this.database.db is the drizzle client. db.execute(sql.raw(text)) runs raw SQL text as given. The tagged template sql`… ${value} …` is the parameterised form. db.insert(table).values({…}) builds an insert that runs when awaited.
  • Testing. Every controller ships with an authorization conformance test that runs the real guard against each handler for each credential type.
libs/inventory/src/lib/inventory.controller.ts
1import { Body, Controller, Get, Param, Patch, Post, Query } from "@nestjs/common";
2import { Authorize } from "@dealer-alchemist/auth";
3import { InventoryService } from "./inventory.service";
4
5@Controller("inventory")
6export class InventoryController
7{
8	constructor(private readonly inventory: InventoryService) {}
9
10	@Get("/")
11	@Authorize("inventory.read", { tenant: "none" })
12	list(@Query("dealerId") dealerId: string, @Query("status") status: string)
13	{
14		return this.inventory.list(dealerId, status);
15	}
16
17	@Patch(":id/price")
18	@Authorize("inventory.update")
19	updatePrice(@Param("id") id: string, @Body() body: any)
20	{
21		return this.inventory.updatePrice(id, body.price);
22	}
23
24	@Post("bulk-reprice")
25	@Authorize("inventory.update", { dealer: { from: "body", key: "dealerId" } })
26	bulkReprice(@Body() body: { dealerId: string; vins: string[]; price: number })
27	{
28		return this.inventory.bulkReprice(body.vins, body.price);
29	}
30}
libs/inventory/src/lib/inventory.service.ts
31import { Injectable } from "@nestjs/common";
32import { InjectModel } from "@nestjs/mongoose";
33import { Model } from "mongoose";
34import { sql } from "drizzle-orm";
35import { Vehicle } from "@dealer-alchemist/models";
36import { DatabaseService, priceHistory } from "@dealer-alchemist/db/nestjs";
37
38@Injectable()
39export class InventoryService
40{
41	constructor(
42		@InjectModel(Vehicle.name) private readonly vehicles: Model<Vehicle>,
43		private readonly database: DatabaseService
44	) {}
45
46	async list(dealerId: string, status: string)
47	{
48		const vehicles = await this.vehicles.find({ dealerId, status }).lean();
49		for (const v of vehicles)
50		{
51			const rows = await this.database.db.execute(
52				sql.raw(`SELECT count(*) AS n FROM leads WHERE dealer_id = '${dealerId}' AND vin = '${v.vin}'`)
53			);
54			(v as any).leadCount = Number(rows[0].n);
55		}
56		return vehicles;
57	}
58
59	async updatePrice(id: string, price: number)
60	{
61		const vehicle = await this.vehicles.findById(id);
62		vehicle.price = price;
63		await vehicle.save();
64		this.database.db.insert(priceHistory).values({ dealerId: vehicle.dealerId, vin: vehicle.vin, price });
65		return vehicle;
66	}
67
68	async bulkReprice(vins: string[], price: number)
69	{
70		const result = await this.vehicles.updateMany({ vin: { $in: vins } }, { $set: { price } });
71		return { updated: result.modifiedCount };
72	}
73}
Your task: review this as a real pull request. Think aloud. Tell us what you would block the merge on, what you would ask to change, and what you would let go. You can ask us anything you would ask the author. About 20 minutes.