@defra/grants-reporting-publisher 0.1.2 → 0.3.0

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.
@@ -54,7 +54,7 @@ jobs:
54
54
 
55
55
  - name: Create Release Pull Request or Release
56
56
  id: changesets
57
- uses: changesets/action@8488615a623b1b9c987934bb89eae8af6a946ac1 #v2.1.1
57
+ uses: changesets/action@ae32849d5ba541f9ae29e40e22a623bc13562f51 #v2.1.2
58
58
  with:
59
59
  publish-script: npm run github:release
60
60
  commit-message: 'Create new config version'
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @defra/grants-reporting-publisher
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 37b65ca: Add new parcels element (mandatory on agreement created)
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 4a25189: Allow options to be sent with a status change messgae
14
+
3
15
  ## 0.1.2
4
16
 
5
17
  ### Patch Changes
package/EXAMPLES.md CHANGED
@@ -44,6 +44,7 @@ const agreementCreatedEvent = {
44
44
  agreementEndDate: "2031-12-31",
45
45
  agreementValue: 50000.0,
46
46
  sbi: "200000001",
47
+ parcels: ["SD8545-9935"],
47
48
  options: [
48
49
  {
49
50
  parcelReference: "SD8545-9935",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/grants-reporting-publisher",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "NPM module for publishing grants reporting events",
5
5
  "engines": {
6
6
  "node": ">=24",
@@ -48,14 +48,14 @@
48
48
  "@babel/preset-env": "8.0.2",
49
49
  "@changesets/changelog-github": "1.0.0",
50
50
  "@changesets/cli": "3.0.1",
51
- "@vitest/coverage-v8": "4.1.10",
51
+ "@vitest/coverage-v8": "5.0.0",
52
52
  "eslint": "10.7.0",
53
53
  "eslint-config-prettier": "10.1.1",
54
54
  "husky": "9.1.7",
55
- "lint-staged": "17.3.0",
55
+ "lint-staged": "17.4.1",
56
56
  "neostandard": "0.13.0",
57
57
  "prettier": "3.9.6",
58
- "vitest": "4.1.10",
58
+ "vitest": "5.0.0",
59
59
  "vitest-fetch-mock": "0.4.5"
60
60
  },
61
61
  "overrides": {
@@ -176,6 +176,7 @@ describe("publishAuditEvent", () => {
176
176
  agreementType: "WOODLAND",
177
177
  agreementStatus: "ACCEPTED",
178
178
  sbi: "200000001",
179
+ parcels: ["SD8545-9935"],
179
180
  options: [
180
181
  {
181
182
  parcelReference: "SD8545-9935",
package/src/schema.js CHANGED
@@ -8,6 +8,21 @@ const accountId = Joi.alternatives()
8
8
  .try(Joi.string().max(MAX_50), Joi.number())
9
9
  .custom(String);
10
10
 
11
+ const parcelsSchema = Joi.array().items(Joi.string());
12
+
13
+ const optionsSchema = Joi.array().items(
14
+ Joi.object({
15
+ parcelReference: Joi.string().allow("").required(),
16
+ parcelSizeUnderAgreement: Joi.number().optional(),
17
+ optionCode: Joi.string().required(),
18
+ optionYear: Joi.number().integer().optional(),
19
+ optionStartDate: Joi.date().iso().optional(),
20
+ optionEndDate: Joi.date().iso().optional(),
21
+ optionQuantity: Joi.number().required(),
22
+ optionValue: Joi.number().required(),
23
+ }),
24
+ );
25
+
11
26
  export const eventSchema = Joi.object({
12
27
  correlationId: Joi.string().max(MAX_50).required(),
13
28
  datetime: Joi.date().iso().required(),
@@ -28,20 +43,8 @@ export const eventSchema = Joi.object({
28
43
  agreementEndDate: Joi.date().iso().optional(),
29
44
  agreementValue: Joi.number().optional(),
30
45
  sbi: accountId.required(),
31
- options: Joi.array()
32
- .items(
33
- Joi.object({
34
- parcelReference: Joi.string().allow("").required(),
35
- parcelSizeUnderAgreement: Joi.number().optional(),
36
- optionCode: Joi.string().required(),
37
- optionYear: Joi.number().integer().optional(),
38
- optionStartDate: Joi.date().iso().optional(),
39
- optionEndDate: Joi.date().iso().optional(),
40
- optionQuantity: Joi.number().required(),
41
- optionValue: Joi.number().required(),
42
- }),
43
- )
44
- .required(),
46
+ parcels: parcelsSchema.required(),
47
+ options: optionsSchema.required(),
45
48
  }),
46
49
  })
47
50
  .when(Joi.object({ eventType: AGREEMENT_STATUS_CHANGED }).unknown(), {
@@ -53,6 +56,8 @@ export const eventSchema = Joi.object({
53
56
  agreementEndDate: Joi.date().iso().optional(),
54
57
  agreementValue: Joi.number().optional(),
55
58
  userId: Joi.string().optional(),
59
+ parcels: parcelsSchema.optional(),
60
+ options: optionsSchema.optional(),
56
61
  }),
57
62
  })
58
63
  .unknown()
@@ -139,6 +139,7 @@ describe("validateReportingEvent", () => {
139
139
  agreementEndDate: "2029-08-31",
140
140
  agreementValue: 1575.0,
141
141
  sbi: "200000001",
142
+ parcels: [],
142
143
  options: [],
143
144
  },
144
145
  });
@@ -154,6 +155,7 @@ describe("validateReportingEvent", () => {
154
155
  agreementType: "WOODLAND",
155
156
  agreementStatus: "ACCEPTED",
156
157
  sbi: "200000001",
158
+ parcels: ["SD8545-9935"],
157
159
  options: [
158
160
  {
159
161
  parcelReference: "SD8545-9935",
@@ -180,6 +182,7 @@ describe("validateReportingEvent", () => {
180
182
  agreementType: "WOODLAND",
181
183
  agreementStatus: "ACCEPTED",
182
184
  sbi: "200000001",
185
+ parcels: [],
183
186
  options: [],
184
187
  },
185
188
  });
@@ -195,6 +198,7 @@ describe("validateReportingEvent", () => {
195
198
  agreementType: "WOODLAND",
196
199
  agreementStatus: "ACCEPTED",
197
200
  sbi: "200000001",
201
+ parcels: ["SD8545-9935"],
198
202
  options: [
199
203
  {
200
204
  parcelReference: "SD8545-9935",
@@ -217,6 +221,7 @@ describe("validateReportingEvent", () => {
217
221
  agreementType: "WOODLAND",
218
222
  agreementStatus: "ACCEPTED",
219
223
  sbi: "200000001",
224
+ parcels: ["SD8545-9935"],
220
225
  options: [
221
226
  {
222
227
  parcelReference: "SD8545-9935",
@@ -240,6 +245,7 @@ describe("validateReportingEvent", () => {
240
245
  agreementType: "WOODLAND",
241
246
  agreementStatus: "ACCEPTED",
242
247
  sbi: "200000001",
248
+ parcels: ["SD8545-9935"],
243
249
  options: [
244
250
  {
245
251
  parcelReference: "SD8545-9935",
@@ -263,6 +269,7 @@ describe("validateReportingEvent", () => {
263
269
  agreementType: "WOODLAND",
264
270
  agreementStatus: "ACCEPTED",
265
271
  sbi: "200000001",
272
+ parcels: ["SD8545-9935"],
266
273
  options: [
267
274
  {
268
275
  parcelReference: "SD8545-9935",
@@ -289,6 +296,7 @@ describe("validateReportingEvent", () => {
289
296
  agreementType: "WOODLAND",
290
297
  agreementStatus: "ACCEPTED",
291
298
  sbi: "200000001",
299
+ parcels: ["SD8545-9935"],
292
300
  options: [
293
301
  {
294
302
  parcelReference: "SD8545-9935",
@@ -320,9 +328,62 @@ describe("validateReportingEvent", () => {
320
328
  '"eventData.agreementStatus" is required',
321
329
  );
322
330
  expect(result.errors).toContain('"eventData.sbi" is required');
331
+ expect(result.errors).toContain('"eventData.parcels" is required');
323
332
  expect(result.errors).toContain('"eventData.options" is required');
324
333
  });
325
334
 
335
+ test("rejects Agreement created event if parcels is missing", () => {
336
+ const result = validateReportingEvent({
337
+ ...validBase,
338
+ eventData: {
339
+ eventType: AGREEMENT_CREATED,
340
+ agreementId: "WMP123456789",
341
+ agreementType: "WOODLAND",
342
+ agreementStatus: "ACCEPTED",
343
+ sbi: "200000001",
344
+ options: [],
345
+ },
346
+ });
347
+ expect(result.valid).toBe(false);
348
+ expect(result.errors).toContain('"eventData.parcels" is required');
349
+ });
350
+
351
+ test("rejects Agreement created event if parcels is not an array", () => {
352
+ const result = validateReportingEvent({
353
+ ...validBase,
354
+ eventData: {
355
+ eventType: AGREEMENT_CREATED,
356
+ agreementId: "WMP123456789",
357
+ agreementType: "WOODLAND",
358
+ agreementStatus: "ACCEPTED",
359
+ sbi: "200000001",
360
+ parcels: "SD8545-9935",
361
+ options: [],
362
+ },
363
+ });
364
+ expect(result.valid).toBe(false);
365
+ expect(result.errors).toContain('"eventData.parcels" must be an array');
366
+ });
367
+
368
+ test("rejects Agreement created event if parcels contains non-string items", () => {
369
+ const result = validateReportingEvent({
370
+ ...validBase,
371
+ eventData: {
372
+ eventType: AGREEMENT_CREATED,
373
+ agreementId: "WMP123456789",
374
+ agreementType: "WOODLAND",
375
+ agreementStatus: "ACCEPTED",
376
+ sbi: "200000001",
377
+ parcels: [123],
378
+ options: [],
379
+ },
380
+ });
381
+ expect(result.valid).toBe(false);
382
+ expect(result.errors).toContain(
383
+ '"eventData.parcels[0]" must be a string',
384
+ );
385
+ });
386
+
326
387
  test("rejects Agreement created event if options contain invalid data", () => {
327
388
  const result = validateReportingEvent({
328
389
  ...validBase,
@@ -332,6 +393,7 @@ describe("validateReportingEvent", () => {
332
393
  agreementType: "WOODLAND",
333
394
  agreementStatus: "ACCEPTED",
334
395
  sbi: "200000001",
396
+ parcels: ["SD8545-9935"],
335
397
  options: [
336
398
  {
337
399
  agreementId: "WMP123456789",
@@ -394,6 +456,109 @@ describe("validateReportingEvent", () => {
394
456
  expect(result.valid).toBe(true);
395
457
  });
396
458
 
459
+ test("accepts a valid Agreement status changed event with options", () => {
460
+ const result = validateReportingEvent({
461
+ ...validBase,
462
+ eventData: {
463
+ eventType: AGREEMENT_STATUS_CHANGED,
464
+ agreementId: "WMP123456789",
465
+ agreementStatus: "ACCEPTED",
466
+ statusDate: "2026-09-02T10:00:00Z",
467
+ options: [
468
+ {
469
+ parcelReference: "SD8545-9935",
470
+ optionCode: "WMP1",
471
+ optionQuantity: 15.75,
472
+ optionValue: 1575.0,
473
+ },
474
+ ],
475
+ },
476
+ });
477
+ expect(result.valid).toBe(true);
478
+ });
479
+
480
+ test("accepts a valid Agreement status changed event with empty parcels array", () => {
481
+ const result = validateReportingEvent({
482
+ ...validBase,
483
+ eventData: {
484
+ eventType: AGREEMENT_STATUS_CHANGED,
485
+ agreementId: "WMP123456789",
486
+ agreementStatus: "ACCEPTED",
487
+ statusDate: "2026-09-02T10:00:00Z",
488
+ parcels: [],
489
+ },
490
+ });
491
+ expect(result.valid).toBe(true);
492
+ });
493
+
494
+ test("accepts a valid Agreement status changed event with parcels", () => {
495
+ const result = validateReportingEvent({
496
+ ...validBase,
497
+ eventData: {
498
+ eventType: AGREEMENT_STATUS_CHANGED,
499
+ agreementId: "WMP123456789",
500
+ agreementStatus: "ACCEPTED",
501
+ statusDate: "2026-09-02T10:00:00Z",
502
+ parcels: ["SD8545-9935", "SD8545-9936"],
503
+ },
504
+ });
505
+ expect(result.valid).toBe(true);
506
+ });
507
+
508
+ test("rejects Agreement status changed event if parcels is not an array", () => {
509
+ const result = validateReportingEvent({
510
+ ...validBase,
511
+ eventData: {
512
+ eventType: AGREEMENT_STATUS_CHANGED,
513
+ agreementId: "WMP123456789",
514
+ agreementStatus: "ACCEPTED",
515
+ statusDate: "2026-09-02T10:00:00Z",
516
+ parcels: "SD8545-9935",
517
+ },
518
+ });
519
+ expect(result.valid).toBe(false);
520
+ expect(result.errors).toContain('"eventData.parcels" must be an array');
521
+ });
522
+
523
+ test("rejects Agreement status changed event if parcels contains non-string items", () => {
524
+ const result = validateReportingEvent({
525
+ ...validBase,
526
+ eventData: {
527
+ eventType: AGREEMENT_STATUS_CHANGED,
528
+ agreementId: "WMP123456789",
529
+ agreementStatus: "ACCEPTED",
530
+ statusDate: "2026-09-02T10:00:00Z",
531
+ parcels: [123],
532
+ },
533
+ });
534
+ expect(result.valid).toBe(false);
535
+ expect(result.errors).toContain(
536
+ '"eventData.parcels[0]" must be a string',
537
+ );
538
+ });
539
+
540
+ test("rejects Agreement status changed event if options contain invalid data", () => {
541
+ const result = validateReportingEvent({
542
+ ...validBase,
543
+ eventData: {
544
+ eventType: AGREEMENT_STATUS_CHANGED,
545
+ agreementId: "WMP123456789",
546
+ agreementStatus: "ACCEPTED",
547
+ statusDate: "2026-09-02T10:00:00Z",
548
+ options: [
549
+ {
550
+ parcelReference: "SD8545-9935",
551
+ // missing mandatory option fields like optionCode
552
+ },
553
+ ],
554
+ },
555
+ });
556
+ expect(result.valid).toBe(false);
557
+ expect(result.errors).toContain(
558
+ '"eventData.options[0].optionCode" is required',
559
+ );
560
+ });
561
+
397
562
  test("rejects Agreement status changed event if required fields are missing", () => {
398
563
  const result = validateReportingEvent({
399
564
  ...validBase,