Skip to content

Commit 547c8a9

Browse files
committed
some more examples
1 parent eb71a1e commit 547c8a9

File tree

8 files changed

+405
-58
lines changed

8 files changed

+405
-58
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"jsdom": "^21.1.2"
1717
},
1818
"dependencies": {
19+
"@nestjs/config": "^3.2.2",
1920
"winston": "^3.10.0"
2021
}
2122
}

packages/server/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
"@nestjs/testing": "^10.3.7",
1717
"@types/jest": "^28.1.8",
1818
"@types/node": "^20.7.1",
19+
"@types/supertest": "^6.0.2",
1920
"jest": "^28.1.3",
2021
"nodemon": "^3.0.1",
22+
"supertest": "^7.0.0",
2123
"ts-jest": "^28.0.8"
2224
},
2325
"dependencies": {

packages/server/src/adapters/mongodb.module.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
1-
import {MongoClient} from "mongodb";
1+
import {MongoClient, MongoClientOptions} from "mongodb";
22
import {DynamicModule} from "@nestjs/common";
33
import {CART_REPO, ORDER_REPO, PRODUCT_REPO} from "./index";
44
import {MongoDBProductRepository} from "./product.repo";
55
import {MongoDBOrderRepository} from "./order.repo";
66
import {MemoryCartRepository} from "./cart.repo";
77

8+
type Config = {
9+
uri: string;
10+
dbName: string;
11+
} & Pick<MongoClientOptions, 'connectTimeoutMS' | 'serverSelectionTimeoutMS' | 'socketTimeoutMS'>
12+
813
export class MongoDBModule {
9-
static forRoot(uri: string): DynamicModule {
14+
static forRoot({uri, dbName, ...config}: Config): DynamicModule {
1015

1116
return {
1217
module: MongoDBModule,
1318
providers: [
1419
{
1520
provide: "storeDB",
1621
useFactory: async () => {
17-
const mongo = await new MongoClient(uri, {
18-
connectTimeoutMS: 100,
19-
serverSelectionTimeoutMS: 100,
20-
socketTimeoutMS: 100
21-
}).connect();
22+
const mongo = await new MongoClient(uri, config).connect();
2223

23-
return mongo.db("store");
24+
return mongo.db(dbName);
2425
},
2526
},
2627
{
@@ -39,4 +40,4 @@ export class MongoDBModule {
3940
exports: [PRODUCT_REPO, ORDER_REPO, CART_REPO]
4041
}
4142
}
42-
}
43+
}

packages/server/src/controllers.ts

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,10 @@
1-
import {
2-
ArgumentMetadata,
3-
BadRequestException,
4-
Body,
5-
Controller,
6-
Get, Inject,
7-
Param,
8-
PipeTransform,
9-
Post,
10-
UsePipes
11-
} from '@nestjs/common';
12-
import { ZodSchema} from 'zod';
13-
import { ProductRepository } from './adapters/product.repo';
1+
import {BadRequestException, Body, Controller, Get, Inject, Param, Post, UsePipes} from '@nestjs/common';
2+
import {ProductRepository} from './adapters/product.repo';
143
import {ProductTemplate} from "./types";
154
import {OrderRepository} from "./adapters/order.repo";
165
import {CartRepository} from "./adapters/cart.repo";
176
import {CART_REPO, ORDER_REPO, PRODUCT_REPO} from "./adapters";
18-
19-
export class ZodValidationPipe implements PipeTransform {
20-
constructor(private schema: ZodSchema) {}
21-
22-
transform(value: unknown, metadata: ArgumentMetadata) {
23-
try {
24-
return this.schema.parse(value);
25-
} catch (error) {
26-
throw new BadRequestException(`failed parsing value ${value} into type ${metadata.type} with ${error}`);
27-
}
28-
}
29-
}
7+
import {ZodValidationPipe} from "./zodValidationPipe";
308

319
@Controller("/products")
3210
export class ProductController {

packages/server/src/index.ts

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,56 @@
1-
import { MongoClient } from "mongodb";
2-
import { MongoDBOrderRepository } from "./adapters/order.repo";
3-
import { MongoDBProductRepository } from "./adapters/product.repo";
1+
import {MongoClient} from "mongodb";
2+
import {MongoDBOrderRepository} from "./adapters/order.repo";
3+
import {MongoDBProductRepository} from "./adapters/product.repo";
44
import {NestFactory} from "@nestjs/core";
55
import {AppModuleInversionOfControl} from "./app.module.ioc";
66
import {AppModuleOverrides} from "./app.module.overrides";
77
import {AppModuleWithRegister} from "./app.module.register";
88
import {MongoDBModule} from "./adapters/mongodb.module";
9+
import {z} from "zod";
10+
11+
const EnvConfig = z.object({
12+
MONGO_URI: z.string().default('mongodb://root:[email protected]'),
13+
MONGO_DB: z.string().default('store'),
14+
MONGO_CONNECT_TIMEOUT: z.number().default(100),
15+
MONGO_SOCKET_TIMEOUT: z.number().default(100),
16+
MONGO_SERVER_SELECTION_TIMEOUT: z.number().default(100),
17+
}).transform((input) => ({
18+
uri: input.MONGO_URI,
19+
dbName: input.MONGO_DB,
20+
connectTimeoutMS: input.MONGO_CONNECT_TIMEOUT,
21+
socketTimeoutMS: input.MONGO_SOCKET_TIMEOUT,
22+
serverSelectionTimeoutMS: input.MONGO_SERVER_SELECTION_TIMEOUT,
23+
}));
924

1025
// @ts-ignore
1126
async function startServerIoC() {
12-
const mongo = await new MongoClient(
13-
`mongodb://root:[email protected]?retryWrites=true&writeConcern=majority`
14-
).connect();
27+
const mongo = await new MongoClient(
28+
`mongodb://root:[email protected]?retryWrites=true&writeConcern=majority`
29+
).connect();
1530

16-
const db = mongo.db("store");
17-
const productRepo = new MongoDBProductRepository(db);
18-
const orderRepo = new MongoDBOrderRepository(db);
31+
const db = mongo.db("store");
32+
const productRepo = new MongoDBProductRepository(db);
33+
const orderRepo = new MongoDBOrderRepository(db);
1934

20-
const app = await NestFactory.create(AppModuleInversionOfControl.register(productRepo, orderRepo))
21-
app.enableCors({origin: "*"});
22-
await app.listen(8080);
35+
const app = await NestFactory.create(AppModuleInversionOfControl.register(productRepo, orderRepo))
36+
app.enableCors({origin: "*"});
37+
await app.listen(8080);
2338
}
2439

2540
// @ts-ignore
2641
async function startServerOverrides() {
27-
const app = await NestFactory.create(AppModuleOverrides)
28-
app.enableCors({origin: "*"});
29-
await app.listen(8080);
42+
const app = await NestFactory.create(AppModuleOverrides)
43+
app.enableCors({origin: "*"});
44+
await app.listen(8080);
3045
}
3146

3247
async function startServerRegister() {
33-
const app = await NestFactory.create(AppModuleWithRegister.register(
34-
MongoDBModule.forRoot(`mongodb://root:[email protected]`)
35-
));
36-
app.enableCors({origin: "*"});
37-
await app.listen(8080);
48+
const config = EnvConfig.parse(process.env);
49+
const app = await NestFactory.create(AppModuleWithRegister.register(
50+
MongoDBModule.forRoot(config)
51+
));
52+
app.enableCors({origin: "*"});
53+
await app.listen(8080);
3854
}
3955

4056
void startServerRegister();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {ArgumentMetadata, BadRequestException, PipeTransform} from "@nestjs/common";
2+
import {ZodSchema} from "zod";
3+
4+
export class ZodValidationPipe implements PipeTransform {
5+
constructor(private schema: ZodSchema) {
6+
}
7+
8+
transform(value: unknown, {type}: ArgumentMetadata) {
9+
try {
10+
return this.schema.parse(value);
11+
} catch (error) {
12+
throw new BadRequestException(`failed parsing value ${value} into type ${type} with ${error}`);
13+
}
14+
}
15+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {createTestingModule} from "../src/server.testkit";
2+
import {aProduct} from "../src/builders";
3+
import request from 'supertest';
4+
5+
async function createTestHarness() {
6+
const {nest, ...rest} = await createTestingModule();
7+
return {
8+
app: request(nest.getHttpServer()),
9+
...rest
10+
}
11+
}
12+
13+
// this test is not really required, it's wholly contained within purchase.flow.spec.tsx
14+
test('a user can order a product', async () => {
15+
const {app, productRepo, orderRepo} = await createTestHarness();
16+
17+
const moogOne = await productRepo.create(aProduct({title: "Moog One"}));
18+
const cartId = '666';
19+
20+
await app
21+
.post(`/cart/${cartId}`)
22+
.send({productId: moogOne.id})
23+
.expect(201);
24+
25+
await app
26+
.get(`/cart/${cartId}`)
27+
.expect({id: cartId, items: [{
28+
productId: moogOne.id,
29+
price: moogOne.price,
30+
name: moogOne.title
31+
}]});
32+
33+
const orderId = await app
34+
.post(`/checkout/${666}`)
35+
.expect(201)
36+
.then(response => response.text);
37+
38+
expect(orderRepo.orders).toContainEqual(expect.objectContaining({
39+
id: orderId,
40+
items: expect.arrayContaining([
41+
expect.objectContaining({
42+
productId: moogOne.id,
43+
})
44+
])
45+
}));
46+
});

0 commit comments

Comments
 (0)