Skip to content

Instantly share code, notes, and snippets.

@pphetra
Last active March 3, 2023 05:37
Show Gist options
  • Save pphetra/6ccbd32178742d49ef2096eb7c575191 to your computer and use it in GitHub Desktop.
Save pphetra/6ccbd32178742d49ef2096eb7c575191 to your computer and use it in GitHub Desktop.
aggregate
/** @format */
import { PrismaClient } from "@prisma/client";
import { Batch, Product } from "./app.model";
import { IProductRepository } from "./app.repository";
import { AppService } from "./app.service";
import { AbstractUnitOfWork, PrismaUnitOfWork } from "./unitOfWork";
class FakeProductRepository implements IProductRepository {
constructor(private batches: Batch[]) {}
async get(sku: string): Promise<Product> {
const product = new Product({ sku, version: 0 });
product.batches = this.batches;
return product;
}
async add(product: Product): Promise<Product> {
return product;
}
async update(product: Product): Promise<Product> {
return product;
}
}
class FakeUnitOfWork implements AbstractUnitOfWork {
constructor(private repo: FakeProductRepository) {}
get productRepository(): IProductRepository {
return this.repo;
}
async run<T>(fn: (uow: AbstractUnitOfWork) => Promise<T>): Promise<T> {
return await fn(this);
}
}
// interface AbstractUnitOfWork {
// get batchRepository(): IBatchRepository;
// /*
// * run in transaction
// */
// run<T>(fn: (uow: AbstractUnitOfWork) => Promise<T>): Promise<T>;
// }
describe("AppService", () => {
const client = new PrismaClient();
afterAll(async () => {
await client.$disconnect();
});
beforeEach(async () => {
await client.line.deleteMany();
await client.batch.deleteMany();
await client.product.deleteMany();
});
it("Fake version: should allocate", async () => {
const batch = new Batch({
ref: "batch-001",
sku: "SMALL-TABLE",
qty: 20,
eta: new Date(),
});
const repo = new FakeProductRepository([batch]);
const uow = new FakeUnitOfWork(repo);
const service = new AppService();
const ref = await service.allocate("test", "SMALL-TABLE", 10, uow);
expect(ref).toBe("batch-001");
});
it("integration version: should allocate", async () => {
const uow = new PrismaUnitOfWork(client);
const batch = new Batch({
ref: "batch-001",
sku: "SMALL-TABLE",
qty: 20,
eta: new Date(),
});
const product = await uow.productRepository.get("SMALL-TABLE");
product.addBatch(batch);
await uow.productRepository.update(product);
const service = new AppService();
const ref = await service.allocate("test", "SMALL-TABLE", 10, uow);
expect(ref).toBe("batch-001");
});
});
/** @format */
import { Injectable } from "@nestjs/common";
import { Line } from "./app.model";
import { AbstractUnitOfWork } from "./unitOfWork";
@Injectable()
export class AppService {
async allocate(
name: string,
sku: string,
qty: number,
uow: AbstractUnitOfWork
): Promise<string> {
const line = new Line({ name, sku, qty });
return await uow.run(async (uow) => {
const product = await uow.productRepository.get(sku);
const batch = product.allocate(line);
if (batch) {
await uow.productRepository.update(product);
return batch.ref;
}
throw new Error("out of stock");
});
}
}
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
previewFeatures = ["extendedWhereUnique"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Product {
id Int @id @default(autoincrement())
sku String @unique
batches Batch[]
version Int @default(0)
}
model Batch {
id Int @id @default(autoincrement())
ref String
sku String
qty Int
eta DateTime?
allocates Line[]
Product Product? @relation(fields: [productId], references: [id], onDelete: Cascade)
productId Int?
}
model Line {
id Int @id @default(autoincrement())
name String
sku String
qty Int
batchId Int
batch Batch @relation(fields: [batchId], references: [id], onDelete: Cascade)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment