filegrc 0.14.0 → 0.15.1

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/src/validate.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  validCalendarRecurrence
20
20
  } from "./recurrence.js";
21
21
  import { resourceReviewRevisions, retentionReviewResourceIds } from "./retention.js";
22
+ import { retentionScheduleApprovalIssues } from "./retention-schedule-approval.js";
22
23
  import { obligationIsEnabled } from "./program-lifecycle.js";
23
24
  import { currentPartyPeople, partyPeople } from "./parties.js";
24
25
  import { isMarkdownChoice, markdownEntries } from "./resource-markdown.js";
@@ -1296,6 +1297,32 @@ function validateCollectionReview(record, loaded, byId, path, diagnostics) {
1296
1297
  ));
1297
1298
  }
1298
1299
  }
1300
+ if (current && record.resourceType === "retention-schedule-item" && modelSupports(model, "retention-schedule-approval")) {
1301
+ const rows = scopedCollectionRecords(loaded, record.resourceType, program);
1302
+ for (const issue of retentionScheduleApprovalIssues(loaded, program || {}, rows, {
1303
+ informationTypesReviewed: collectionReviewIsCurrent(loaded, "information-type", program)
1304
+ })) {
1305
+ diagnostics.push(error(issue.code, path, issue.message));
1306
+ }
1307
+ const owners = new Set();
1308
+ for (const candidate of loaded.resources.filter((candidate) => (
1309
+ candidate.type === "document"
1310
+ && candidate.documentKind === "schedule"
1311
+ && candidate.workflowScope === "program"
1312
+ && !["superseded", "retired"].includes(candidate.status)
1313
+ || rows.includes(candidate)
1314
+ ))) {
1315
+ for (const id of currentPartyPeople(candidate.ownerIds || [], byId)) owners.add(id);
1316
+ }
1317
+ const conflictedReviewers = (record.reviewedByIds || []).filter((id) => owners.has(id));
1318
+ if (conflictedReviewers.length) {
1319
+ diagnostics.push(error(
1320
+ "conflicted-retention-schedule-reviewer",
1321
+ path,
1322
+ `A Data Retention Schedule reviewer cannot own its governing document or an included schedule row: ${conflictedReviewers.join(", ")}.`
1323
+ ));
1324
+ }
1325
+ }
1299
1326
  if (modelSupports(model, "temporal-collection-reviews")) {
1300
1327
  const temporalValues = [record.coverage, record.knowledgeCutoffAt, record.populationResourceIds];
1301
1328
  const hasTemporalBinding = temporalValues.every((value) => value !== undefined && value !== null);
@@ -1364,6 +1391,25 @@ function validateCollectionReview(record, loaded, byId, path, diagnostics) {
1364
1391
  }
1365
1392
  }
1366
1393
 
1394
+ function collectionReviewIsCurrent(loaded, resourceType, program) {
1395
+ const review = loaded.resources.find((record) => (
1396
+ record.type === "collection-review"
1397
+ && record.resourceType === resourceType
1398
+ && record.status === "active"
1399
+ && (!modelSupports(loaded.model, "program-scope") || (record.scopeResourceIds || []).includes(program?.id))
1400
+ ));
1401
+ if (!review || !(loaded.model.collectionReviews?.[resourceType]?.decisions || ["complete"]).includes(review.decision)) return false;
1402
+ const records = scopedCollectionRecords(loaded, resourceType, program);
1403
+ const revisionMatches = collectionRevisionMatches(loaded, resourceType, review.collectionRevision, { program });
1404
+ if (!modelSupports(loaded.model, "temporal-collection-reviews")) return revisionMatches;
1405
+ return revisionMatches
1406
+ && review.coverage?.kind === "as-of"
1407
+ && review.coverage.on === review.reviewedOn
1408
+ && Boolean(review.knowledgeCutoffAt)
1409
+ && Array.isArray(review.populationResourceIds)
1410
+ && JSON.stringify([...review.populationResourceIds].sort()) === JSON.stringify(records.map(({ id }) => id).sort());
1411
+ }
1412
+
1367
1413
  function validateCollectionReviewSet(resources, pathById, diagnostics) {
1368
1414
  const current = new Map();
1369
1415
  for (const review of resources.filter((record) => record.type === "collection-review" && record.status !== "retired")) {