Skip to content

Commit

Permalink
some more examples
Browse files Browse the repository at this point in the history
  • Loading branch information
electricmonk committed May 16, 2024
1 parent eb71a1e commit 547c8a9
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 58 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"jsdom": "^21.1.2"
},
"dependencies": {
"@nestjs/config": "^3.2.2",
"winston": "^3.10.0"
}
}
2 changes: 2 additions & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
"@nestjs/testing": "^10.3.7",
"@types/jest": "^28.1.8",
"@types/node": "^20.7.1",
"@types/supertest": "^6.0.2",
"jest": "^28.1.3",
"nodemon": "^3.0.1",
"supertest": "^7.0.0",
"ts-jest": "^28.0.8"
},
"dependencies": {
Expand Down
19 changes: 10 additions & 9 deletions packages/server/src/adapters/mongodb.module.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import {MongoClient} from "mongodb";
import {MongoClient, MongoClientOptions} from "mongodb";
import {DynamicModule} from "@nestjs/common";
import {CART_REPO, ORDER_REPO, PRODUCT_REPO} from "./index";
import {MongoDBProductRepository} from "./product.repo";
import {MongoDBOrderRepository} from "./order.repo";
import {MemoryCartRepository} from "./cart.repo";

type Config = {
uri: string;
dbName: string;
} & Pick<MongoClientOptions, 'connectTimeoutMS' | 'serverSelectionTimeoutMS' | 'socketTimeoutMS'>

export class MongoDBModule {
static forRoot(uri: string): DynamicModule {
static forRoot({uri, dbName, ...config}: Config): DynamicModule {

return {
module: MongoDBModule,
providers: [
{
provide: "storeDB",
useFactory: async () => {
const mongo = await new MongoClient(uri, {
connectTimeoutMS: 100,
serverSelectionTimeoutMS: 100,
socketTimeoutMS: 100
}).connect();
const mongo = await new MongoClient(uri, config).connect();

return mongo.db("store");
return mongo.db(dbName);
},
},
{
Expand All @@ -39,4 +40,4 @@ export class MongoDBModule {
exports: [PRODUCT_REPO, ORDER_REPO, CART_REPO]
}
}
}
}
28 changes: 3 additions & 25 deletions packages/server/src/controllers.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,10 @@
import {
ArgumentMetadata,
BadRequestException,
Body,
Controller,
Get, Inject,
Param,
PipeTransform,
Post,
UsePipes
} from '@nestjs/common';
import { ZodSchema} from 'zod';
import { ProductRepository } from './adapters/product.repo';
import {BadRequestException, Body, Controller, Get, Inject, Param, Post, UsePipes} from '@nestjs/common';
import {ProductRepository} from './adapters/product.repo';
import {ProductTemplate} from "./types";
import {OrderRepository} from "./adapters/order.repo";
import {CartRepository} from "./adapters/cart.repo";
import {CART_REPO, ORDER_REPO, PRODUCT_REPO} from "./adapters";

export class ZodValidationPipe implements PipeTransform {
constructor(private schema: ZodSchema) {}

transform(value: unknown, metadata: ArgumentMetadata) {
try {
return this.schema.parse(value);
} catch (error) {
throw new BadRequestException(`failed parsing value ${value} into type ${metadata.type} with ${error}`);
}
}
}
import {ZodValidationPipe} from "./zodValidationPipe";

@Controller("/products")
export class ProductController {
Expand Down
56 changes: 36 additions & 20 deletions packages/server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,56 @@
import { MongoClient } from "mongodb";
import { MongoDBOrderRepository } from "./adapters/order.repo";
import { MongoDBProductRepository } from "./adapters/product.repo";
import {MongoClient} from "mongodb";
import {MongoDBOrderRepository} from "./adapters/order.repo";
import {MongoDBProductRepository} from "./adapters/product.repo";
import {NestFactory} from "@nestjs/core";
import {AppModuleInversionOfControl} from "./app.module.ioc";
import {AppModuleOverrides} from "./app.module.overrides";
import {AppModuleWithRegister} from "./app.module.register";
import {MongoDBModule} from "./adapters/mongodb.module";
import {z} from "zod";

const EnvConfig = z.object({
MONGO_URI: z.string().default('mongodb://root:[email protected]'),
MONGO_DB: z.string().default('store'),
MONGO_CONNECT_TIMEOUT: z.number().default(100),
MONGO_SOCKET_TIMEOUT: z.number().default(100),
MONGO_SERVER_SELECTION_TIMEOUT: z.number().default(100),
}).transform((input) => ({
uri: input.MONGO_URI,
dbName: input.MONGO_DB,
connectTimeoutMS: input.MONGO_CONNECT_TIMEOUT,
socketTimeoutMS: input.MONGO_SOCKET_TIMEOUT,
serverSelectionTimeoutMS: input.MONGO_SERVER_SELECTION_TIMEOUT,
}));

// @ts-ignore
async function startServerIoC() {
const mongo = await new MongoClient(
`mongodb://root:[email protected]?retryWrites=true&writeConcern=majority`
).connect();
const mongo = await new MongoClient(
`mongodb://root:[email protected]?retryWrites=true&writeConcern=majority`
).connect();

const db = mongo.db("store");
const productRepo = new MongoDBProductRepository(db);
const orderRepo = new MongoDBOrderRepository(db);
const db = mongo.db("store");
const productRepo = new MongoDBProductRepository(db);
const orderRepo = new MongoDBOrderRepository(db);

const app = await NestFactory.create(AppModuleInversionOfControl.register(productRepo, orderRepo))
app.enableCors({origin: "*"});
await app.listen(8080);
const app = await NestFactory.create(AppModuleInversionOfControl.register(productRepo, orderRepo))
app.enableCors({origin: "*"});
await app.listen(8080);
}

// @ts-ignore
async function startServerOverrides() {
const app = await NestFactory.create(AppModuleOverrides)
app.enableCors({origin: "*"});
await app.listen(8080);
const app = await NestFactory.create(AppModuleOverrides)
app.enableCors({origin: "*"});
await app.listen(8080);
}

async function startServerRegister() {
const app = await NestFactory.create(AppModuleWithRegister.register(
MongoDBModule.forRoot(`mongodb://root:[email protected]`)
));
app.enableCors({origin: "*"});
await app.listen(8080);
const config = EnvConfig.parse(process.env);
const app = await NestFactory.create(AppModuleWithRegister.register(
MongoDBModule.forRoot(config)
));
app.enableCors({origin: "*"});
await app.listen(8080);
}

void startServerRegister();
Expand Down
15 changes: 15 additions & 0 deletions packages/server/src/zodValidationPipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {ArgumentMetadata, BadRequestException, PipeTransform} from "@nestjs/common";
import {ZodSchema} from "zod";

export class ZodValidationPipe implements PipeTransform {
constructor(private schema: ZodSchema) {
}

transform(value: unknown, {type}: ArgumentMetadata) {
try {
return this.schema.parse(value);
} catch (error) {
throw new BadRequestException(`failed parsing value ${value} into type ${type} with ${error}`);
}
}
}
46 changes: 46 additions & 0 deletions packages/server/test/purchase.system.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {createTestingModule} from "../src/server.testkit";
import {aProduct} from "../src/builders";
import request from 'supertest';

async function createTestHarness() {
const {nest, ...rest} = await createTestingModule();
return {
app: request(nest.getHttpServer()),
...rest
}
}

// this test is not really required, it's wholly contained within purchase.flow.spec.tsx
test('a user can order a product', async () => {
const {app, productRepo, orderRepo} = await createTestHarness();

const moogOne = await productRepo.create(aProduct({title: "Moog One"}));
const cartId = '666';

await app
.post(`/cart/${cartId}`)
.send({productId: moogOne.id})
.expect(201);

await app
.get(`/cart/${cartId}`)
.expect({id: cartId, items: [{
productId: moogOne.id,
price: moogOne.price,
name: moogOne.title
}]});

const orderId = await app
.post(`/checkout/${666}`)
.expect(201)
.then(response => response.text);

expect(orderRepo.orders).toContainEqual(expect.objectContaining({
id: orderId,
items: expect.arrayContaining([
expect.objectContaining({
productId: moogOne.id,
})
])
}));
});
Loading

0 comments on commit 547c8a9

Please sign in to comment.