@defra/grants-reporting-publisher 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/EXAMPLES.md +127 -0
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/publish.test.js +33 -1
- package/src/schema.js +2 -2
- package/src/validate.test.js +96 -0
package/CHANGELOG.md
CHANGED
package/EXAMPLES.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Usage Examples
|
|
2
|
+
|
|
3
|
+
This page provides examples of how to use the `@defra/grants-reporting-publisher` library to publish reporting events.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
To publish events, you need to provide a configuration object that includes an AWS SNS client and the target topic ARN.
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
import { SNSClient } from "@aws-sdk/client-sns";
|
|
11
|
+
import {
|
|
12
|
+
publishReportingEvent,
|
|
13
|
+
AGREEMENT_CREATED,
|
|
14
|
+
AGREEMENT_STATUS_CHANGED,
|
|
15
|
+
} from "@defra/grants-reporting-publisher";
|
|
16
|
+
|
|
17
|
+
const snsClient = new SNSClient({ region: "eu-west-2" });
|
|
18
|
+
|
|
19
|
+
const config = {
|
|
20
|
+
snsClient,
|
|
21
|
+
sns: {
|
|
22
|
+
topicArn: "arn:aws:sns:eu-west-2:123456789012:your-topic-name",
|
|
23
|
+
},
|
|
24
|
+
application: "YOUR_APPLICATION_CODE", // e.g., "GAS or grants-payments-service"
|
|
25
|
+
service: "your-service-name", // optional, defaults to "grants", just omit this for now
|
|
26
|
+
generateCorrelationId: true, // automatically generate a UUID for each event
|
|
27
|
+
};
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Event Types
|
|
31
|
+
|
|
32
|
+
### Agreement Created (`AGREEMENT_CREATED`)
|
|
33
|
+
|
|
34
|
+
Use this event type when a new agreement is created.
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const agreementCreatedEvent = {
|
|
38
|
+
eventData: {
|
|
39
|
+
eventType: AGREEMENT_CREATED,
|
|
40
|
+
agreementId: "AG-123456",
|
|
41
|
+
agreementType: "WOODLAND",
|
|
42
|
+
agreementStatus: "ACCEPTED",
|
|
43
|
+
agreementStartDate: "2026-01-01",
|
|
44
|
+
agreementEndDate: "2031-12-31",
|
|
45
|
+
agreementValue: 50000.0,
|
|
46
|
+
sbi: "200000001",
|
|
47
|
+
options: [
|
|
48
|
+
{
|
|
49
|
+
parcelReference: "SD8545-9935",
|
|
50
|
+
parcelSizeUnderAgreement: 10.5,
|
|
51
|
+
optionCode: "WD1",
|
|
52
|
+
optionYear: 1,
|
|
53
|
+
optionStartDate: "2026-01-01",
|
|
54
|
+
optionEndDate: "2026-12-31",
|
|
55
|
+
optionQuantity: 10.5,
|
|
56
|
+
optionValue: 10500.0,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const { messageId } = await publishReportingEvent(
|
|
64
|
+
agreementCreatedEvent,
|
|
65
|
+
config,
|
|
66
|
+
);
|
|
67
|
+
console.log(`Event published with Message ID: ${messageId}`);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error("Failed to publish event:", error.message);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Agreement Status Changed (`AGREEMENT_STATUS_CHANGED`)
|
|
74
|
+
|
|
75
|
+
Use this event type when the status of an existing agreement changes.
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const statusChangedEvent = {
|
|
79
|
+
eventData: {
|
|
80
|
+
eventType: AGREEMENT_STATUS_CHANGED,
|
|
81
|
+
agreementId: "AG-123456",
|
|
82
|
+
agreementStatus: "COMPLETED",
|
|
83
|
+
statusDate: new Date().toISOString(),
|
|
84
|
+
agreementStartDate: "2026-01-01",
|
|
85
|
+
agreementEndDate: "2031-12-31",
|
|
86
|
+
agreementValue: 50000.0,
|
|
87
|
+
userId: "user-uuid-123", // optional
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const { messageId } = await publishReportingEvent(statusChangedEvent, config);
|
|
93
|
+
console.log(`Event published with Message ID: ${messageId}`);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error("Failed to publish event:", error.message);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Validation Only
|
|
100
|
+
|
|
101
|
+
If you only want to validate an event without publishing it:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
import { validateReportingEvent } from "@defra/grants-reporting-publisher";
|
|
105
|
+
|
|
106
|
+
const event = {
|
|
107
|
+
correlationId: "your-correlation-id",
|
|
108
|
+
datetime: new Date().toISOString(),
|
|
109
|
+
version: "1.0.0",
|
|
110
|
+
application: "FCP",
|
|
111
|
+
service: "grants",
|
|
112
|
+
eventData: {
|
|
113
|
+
eventType: AGREEMENT_STATUS_CHANGED,
|
|
114
|
+
agreementId: "AG-123",
|
|
115
|
+
agreementStatus: "ACTIVE",
|
|
116
|
+
statusDate: new Date().toISOString(),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const { valid, errors, value } = validateReportingEvent(event);
|
|
121
|
+
|
|
122
|
+
if (!valid) {
|
|
123
|
+
console.error("Validation errors:", errors);
|
|
124
|
+
} else {
|
|
125
|
+
console.log("Event is valid:", value);
|
|
126
|
+
}
|
|
127
|
+
```
|
package/README.md
CHANGED
package/package.json
CHANGED
package/src/publish.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { publishReportingEvent } from "./publish.js";
|
|
2
|
-
import { AGREEMENT_STATUS_CHANGED } from "./constants.js";
|
|
2
|
+
import { AGREEMENT_CREATED, AGREEMENT_STATUS_CHANGED } from "./constants.js";
|
|
3
3
|
|
|
4
4
|
const mockSend = vi.hoisted(() =>
|
|
5
5
|
vi.fn().mockResolvedValue({ MessageId: "msg-001" }),
|
|
@@ -162,6 +162,38 @@ describe("publishAuditEvent", () => {
|
|
|
162
162
|
await publishReportingEvent(validEvent, baseConfig);
|
|
163
163
|
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
164
164
|
});
|
|
165
|
+
|
|
166
|
+
test("publishes AGREEMENT_CREATED event with minimal options", async () => {
|
|
167
|
+
const agreementCreatedEvent = {
|
|
168
|
+
correlationId: "abc-123",
|
|
169
|
+
datetime: "2025-12-01T12:51:41.381Z",
|
|
170
|
+
version: "1.0.0",
|
|
171
|
+
application: "FCP001",
|
|
172
|
+
service: "grants",
|
|
173
|
+
eventData: {
|
|
174
|
+
eventType: AGREEMENT_CREATED,
|
|
175
|
+
agreementId: "WMP123456789",
|
|
176
|
+
agreementType: "WOODLAND",
|
|
177
|
+
agreementStatus: "ACCEPTED",
|
|
178
|
+
sbi: "200000001",
|
|
179
|
+
options: [
|
|
180
|
+
{
|
|
181
|
+
parcelReference: "SD8545-9935",
|
|
182
|
+
optionCode: "WMP1",
|
|
183
|
+
optionQuantity: 15.75,
|
|
184
|
+
optionValue: 1575.0,
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const result = await publishReportingEvent(
|
|
191
|
+
agreementCreatedEvent,
|
|
192
|
+
baseConfig,
|
|
193
|
+
);
|
|
194
|
+
expect(result).toEqual({ messageId: "msg-001" });
|
|
195
|
+
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
196
|
+
});
|
|
165
197
|
});
|
|
166
198
|
|
|
167
199
|
describe("event validation", () => {
|
package/src/schema.js
CHANGED
|
@@ -35,8 +35,8 @@ export const eventSchema = Joi.object({
|
|
|
35
35
|
parcelSizeUnderAgreement: Joi.number().optional(),
|
|
36
36
|
optionCode: Joi.string().required(),
|
|
37
37
|
optionYear: Joi.number().integer().optional(),
|
|
38
|
-
optionStartDate: Joi.date().iso().
|
|
39
|
-
optionEndDate: Joi.date().iso().
|
|
38
|
+
optionStartDate: Joi.date().iso().optional(),
|
|
39
|
+
optionEndDate: Joi.date().iso().optional(),
|
|
40
40
|
optionQuantity: Joi.number().required(),
|
|
41
41
|
optionValue: Joi.number().required(),
|
|
42
42
|
}),
|
package/src/validate.test.js
CHANGED
|
@@ -187,6 +187,28 @@ describe("validateReportingEvent", () => {
|
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
test("accepts Agreement created event with minimal options", () => {
|
|
190
|
+
const result = validateReportingEvent({
|
|
191
|
+
...validBase,
|
|
192
|
+
eventData: {
|
|
193
|
+
eventType: AGREEMENT_CREATED,
|
|
194
|
+
agreementId: "WMP123456789",
|
|
195
|
+
agreementType: "WOODLAND",
|
|
196
|
+
agreementStatus: "ACCEPTED",
|
|
197
|
+
sbi: "200000001",
|
|
198
|
+
options: [
|
|
199
|
+
{
|
|
200
|
+
parcelReference: "SD8545-9935",
|
|
201
|
+
optionCode: "WMP1",
|
|
202
|
+
optionQuantity: 15.75,
|
|
203
|
+
optionValue: 1575.0,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
expect(result.valid).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("accepts Agreement created event with option containing optionStartDate only", () => {
|
|
190
212
|
const result = validateReportingEvent({
|
|
191
213
|
...validBase,
|
|
192
214
|
eventData: {
|
|
@@ -200,6 +222,28 @@ describe("validateReportingEvent", () => {
|
|
|
200
222
|
parcelReference: "SD8545-9935",
|
|
201
223
|
optionCode: "WMP1",
|
|
202
224
|
optionStartDate: "2026-09-01",
|
|
225
|
+
optionQuantity: 15.75,
|
|
226
|
+
optionValue: 1575.0,
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
expect(result.valid).toBe(true);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("accepts Agreement created event with option containing optionEndDate only", () => {
|
|
235
|
+
const result = validateReportingEvent({
|
|
236
|
+
...validBase,
|
|
237
|
+
eventData: {
|
|
238
|
+
eventType: AGREEMENT_CREATED,
|
|
239
|
+
agreementId: "WMP123456789",
|
|
240
|
+
agreementType: "WOODLAND",
|
|
241
|
+
agreementStatus: "ACCEPTED",
|
|
242
|
+
sbi: "200000001",
|
|
243
|
+
options: [
|
|
244
|
+
{
|
|
245
|
+
parcelReference: "SD8545-9935",
|
|
246
|
+
optionCode: "WMP1",
|
|
203
247
|
optionEndDate: "2029-08-31",
|
|
204
248
|
optionQuantity: 15.75,
|
|
205
249
|
optionValue: 1575.0,
|
|
@@ -210,6 +254,58 @@ describe("validateReportingEvent", () => {
|
|
|
210
254
|
expect(result.valid).toBe(true);
|
|
211
255
|
});
|
|
212
256
|
|
|
257
|
+
test("rejects Agreement created event if optionStartDate is not a valid date", () => {
|
|
258
|
+
const result = validateReportingEvent({
|
|
259
|
+
...validBase,
|
|
260
|
+
eventData: {
|
|
261
|
+
eventType: AGREEMENT_CREATED,
|
|
262
|
+
agreementId: "WMP123456789",
|
|
263
|
+
agreementType: "WOODLAND",
|
|
264
|
+
agreementStatus: "ACCEPTED",
|
|
265
|
+
sbi: "200000001",
|
|
266
|
+
options: [
|
|
267
|
+
{
|
|
268
|
+
parcelReference: "SD8545-9935",
|
|
269
|
+
optionCode: "WMP1",
|
|
270
|
+
optionStartDate: "invalid-date",
|
|
271
|
+
optionQuantity: 15.75,
|
|
272
|
+
optionValue: 1575.0,
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
expect(result.valid).toBe(false);
|
|
278
|
+
expect(result.errors).toContain(
|
|
279
|
+
'"eventData.options[0].optionStartDate" must be in ISO 8601 date format',
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("rejects Agreement created event if optionEndDate is not a valid date", () => {
|
|
284
|
+
const result = validateReportingEvent({
|
|
285
|
+
...validBase,
|
|
286
|
+
eventData: {
|
|
287
|
+
eventType: AGREEMENT_CREATED,
|
|
288
|
+
agreementId: "WMP123456789",
|
|
289
|
+
agreementType: "WOODLAND",
|
|
290
|
+
agreementStatus: "ACCEPTED",
|
|
291
|
+
sbi: "200000001",
|
|
292
|
+
options: [
|
|
293
|
+
{
|
|
294
|
+
parcelReference: "SD8545-9935",
|
|
295
|
+
optionCode: "WMP1",
|
|
296
|
+
optionEndDate: "invalid-date",
|
|
297
|
+
optionQuantity: 15.75,
|
|
298
|
+
optionValue: 1575.0,
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
expect(result.valid).toBe(false);
|
|
304
|
+
expect(result.errors).toContain(
|
|
305
|
+
'"eventData.options[0].optionEndDate" must be in ISO 8601 date format',
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
|
|
213
309
|
test("rejects Agreement created event if required fields are missing", () => {
|
|
214
310
|
const result = validateReportingEvent({
|
|
215
311
|
...validBase,
|