Skip to content

Commit 04bbd4c

Browse files
committed
CCM-11228: Add logging functionality and update letter repository methods
1 parent 73a24cc commit 04bbd4c

File tree

5 files changed

+67
-14
lines changed

5 files changed

+67
-14
lines changed

internal/datastore/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"dependencies": {
33
"@aws-sdk/client-dynamodb": "^3.858.0",
44
"@aws-sdk/lib-dynamodb": "^3.858.0",
5+
"pino": "^9.7.0",
56
"zod": "^4.0.14"
67
},
78
"devDependencies": {

internal/datastore/src/__test__/letter-repository.test.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,43 @@
11
import { createTables, DBContext, deleteTables, setupDynamoDBContainer } from './db';
22
import { LetterRepository } from '../letter-repository';
33
import { Letter } from '../types';
4+
import { Logger } from 'pino';
5+
import { createTestLogger, LogStream } from './logs';
46

57
function createLetter(supplierId: string, letterId: string, status: Letter['status'] = 'PENDING'): Letter {
68
return {
7-
supplierId: supplierId,
89
id: letterId,
10+
supplierId: supplierId,
11+
specificationId: 'specification1',
12+
groupId: 'group1',
913
url: `s3://bucket/${letterId}.pdf`,
1014
status: status,
1115
createdAt: new Date().toISOString(),
1216
updatedAt: new Date().toISOString()
1317
};
1418
}
1519

20+
// Database tests can take longer, especially with setup and teardown
1621
jest.setTimeout(30000);
1722

1823
describe('LetterRepository', () => {
1924

2025
let db: DBContext;
2126
let letterRepository: LetterRepository;
27+
let logStream: LogStream;
28+
let logger: Logger;
2229

2330
beforeAll(async () => {
2431
db = await setupDynamoDBContainer();
2532
});
2633

2734
beforeEach(async () => {
2835
await createTables(db);
29-
letterRepository = new LetterRepository(db.docClient, db.config);
36+
(
37+
{ logStream, logger } = createTestLogger()
38+
);
39+
40+
letterRepository = new LetterRepository(db.docClient, logger, db.config);
3041
});
3142

3243
afterEach(async () => {
@@ -124,11 +135,12 @@ describe('LetterRepository', () => {
124135
expect(firstPage.letters[0].id).toBe('letter001');
125136
expect(firstPage.letters[49].id).toBe('letter050');
126137

127-
const secondPage = await letterRepository.getLettersByStatus('supplier1', 'PENDING', firstPage.lastEvaluatedKey);
138+
const secondPage = await letterRepository.getLettersByStatus('supplier1', 'PENDING', {
139+
exclusiveStartKey: firstPage.lastEvaluatedKey
140+
});
128141
expect(secondPage.letters).toHaveLength(49);
129142
expect(secondPage.lastEvaluatedKey).toBeUndefined(); // No more pages
130143
expect(secondPage.letters[0].id).toBe('letter051');
131144
expect(secondPage.letters[48].id).toBe('letter099');
132145
});
133-
134146
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import pino from 'pino';
2+
import { Writable } from 'stream';
3+
4+
export class LogStream extends Writable {
5+
logs: string[] = [];
6+
7+
_write(chunk: any, _encoding: string, callback: () => void) {
8+
this.logs.push(chunk.toString());
9+
callback();
10+
}
11+
}
12+
13+
export function createTestLogger() {
14+
let logStream = new LogStream();
15+
return {
16+
logStream: logStream,
17+
logger: pino(logStream)
18+
};
19+
}

internal/datastore/src/letter-repository.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,21 @@ import {
66
UpdateCommand,
77
UpdateCommandOutput
88
} from '@aws-sdk/lib-dynamodb';
9-
import { Letter, LetterDB, LetterDBSchema, LetterSchema } from './types';
9+
import { Letter, LetterDB, LetterSchema } from './types';
10+
import { Logger } from 'pino';
11+
12+
export type PagingOptions = Partial<{
13+
exclusiveStartKey: Record<string, any>,
14+
pageSize: number
15+
}>
16+
17+
const defaultPagingOptions = {
18+
pageSize: 50
19+
};
1020

1121
export class LetterRepository {
1222
constructor(readonly ddbClient: DynamoDBDocumentClient,
23+
readonly log: Logger,
1324
readonly config: { lettersTableName: string }) {
1425
}
1526

@@ -42,10 +53,13 @@ export class LetterRepository {
4253
return LetterSchema.parse(result.Item);
4354
}
4455

45-
async getLettersByStatus(supplierId: string, status: Letter['status'], exclusiveStartKey?: Record<string, any>, pageSize: number = 50): Promise<{
56+
async getLettersByStatus(supplierId: string, status: Letter['status'], options?: PagingOptions): Promise<{
4657
letters: Letter[],
4758
lastEvaluatedKey?: Record<string, any>
4859
}> {
60+
61+
const extendedOptions = { ...defaultPagingOptions, ...options };
62+
4963
const result = await this.ddbClient.send(new QueryCommand({
5064
TableName: this.config.lettersTableName,
5165
IndexName: 'supplierStatus-index',
@@ -55,21 +69,28 @@ export class LetterRepository {
5569
AttributeValueList: [`${supplierId}#${status}`]
5670
}
5771
},
58-
Limit: pageSize,
59-
ExclusiveStartKey: exclusiveStartKey
72+
Limit: extendedOptions.pageSize,
73+
ExclusiveStartKey: extendedOptions.exclusiveStartKey
6074
}));
6175

6276
if (!result.Items) {
6377
throw new Error(`No letters found for supplier ${supplierId} with status ${status}`);
6478
}
6579
return {
66-
letters: result.Items.map(item => LetterSchema.parse(item)),
80+
letters: result.Items.map(item => LetterSchema.safeParse(item))
81+
.filter((result) => {
82+
if (!result.success) {
83+
this.log.warn(`Invalid letter data: ${result.error}`);
84+
}
85+
return result.success;
86+
})
87+
.map(result => result.data),
6788
lastEvaluatedKey: result.LastEvaluatedKey
6889
}
6990
}
7091

7192
async updateLetterStatus(supplierId: string, letterId: string, status: Letter['status']): Promise<Letter> {
72-
console.debug(`Updating letter ${letterId} to status ${status}`);
93+
this.log.debug(`Updating letter ${letterId} to status ${status}`);
7394
let result: UpdateCommandOutput;
7495
try {
7596
result = await this.ddbClient.send(new UpdateCommand({
@@ -100,7 +121,7 @@ export class LetterRepository {
100121
if (!result.Attributes) {
101122
throw new Error(`Letter with id ${letterId} not found for supplier ${supplierId}`);
102123
}
103-
console.debug(`Updated letter ${letterId} to status ${status}`);
124+
this.log.debug(`Updated letter ${letterId} to status ${status}`);
104125
return LetterSchema.parse(result.Attributes);
105126
}
106127
}

internal/datastore/src/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ export const SupplierSchema = z.object({
1111
}).describe('Supplier');
1212

1313
export type Supplier = z.infer<typeof SupplierSchema>;
14-
export type SupplierId = Supplier['id'];
1514

1615
export const LetterStatus = z.enum([
1716
'PENDING', 'ACCEPTED', 'DISPATCHED', 'FAILED',
1817
'REJECTED', 'DELIVERED', 'CANCELLED']);
1918

2019
export const LetterSchema = z.object({
21-
supplierId: idRef(SupplierSchema),
2220
id: z.string(),
21+
supplierId: idRef(SupplierSchema),
22+
specificationId: z.string(),
23+
groupId: z.string(),
2324
url: z.url(),
2425
status: LetterStatus,
2526
createdAt: z.string(),
2627
updatedAt: z.string(),
2728
}).describe('Letter');
2829

2930
export type Letter = z.infer<typeof LetterSchema>;
30-
export type LetterId = Letter['id'];
3131

3232
export const LetterDBSchema = LetterSchema.extend({
3333
supplierStatus: z.string().describe('Secondary index PK'),

0 commit comments

Comments
 (0)