Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 411c3c6

Browse files
committed
[RND-623] fix reference collection issue
1 parent f679d95 commit 411c3c6

File tree

2 files changed

+516
-343
lines changed

2 files changed

+516
-343
lines changed

Meadowlark-js/packages/meadowlark-core/src/extraction/DocumentReferenceExtractor.ts

Lines changed: 86 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,54 @@
22
// Licensed to the Ed-Fi Alliance under one or more agreements.
33
// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0.
44
// See the LICENSE and NOTICES files in the project root for more information.
5+
import invariant from 'ts-invariant';
56
import { JSONPath as jsonPath } from 'jsonpath-plus';
6-
import { DocumentIdentity } from '../model/DocumentIdentity';
77
import { DocumentReference } from '../model/DocumentReference';
88
import { DocumentObjectKey } from '../model/api-schema/DocumentObjectKey';
99
import { DocumentPaths } from '../model/api-schema/DocumentPaths';
10-
import { JsonPath } from '../model/api-schema/JsonPath';
1110
import { ResourceSchema } from '../model/api-schema/ResourceSchema';
11+
import { DocumentIdentity } from '../model/DocumentIdentity';
12+
13+
/**
14+
* In extracting DocumentReferences, there is an intermediate step where document values are resolved
15+
* from a JsonPath. JsonPaths return arrays of values when the path goes into an array.
16+
* This is the case for collections of document references.
17+
*
18+
* This means that each path resolves to one document value in *each* document reference in the collection.
19+
* For each DocumentObjectKey of a reference, IntermediateDocumentReferences holds the array of resolved document values
20+
* for a path.
21+
*
22+
* For example, given a document with a collection of ClassPeriod references:
23+
*
24+
* classPeriods: [
25+
* {
26+
* classPeriodReference: {
27+
* schoolId: '24',
28+
* classPeriodName: 'z1',
29+
* },
30+
* },
31+
* {
32+
* classPeriodReference: {
33+
* schoolId: '25',
34+
* classPeriodName: 'z2',
35+
* },
36+
* },
37+
* ]
38+
*
39+
* With JsonPaths for ClassPeriod references:
40+
* "* $.classPeriods[*].classPeriodReference.schoolId" for schoolId and
41+
* "$.classPeriods[*].classPeriodReference.classPeriodName" for classPeriodName,
42+
* the IntermediateDocumentReferences would be:
43+
*
44+
* {
45+
* schoolId: ['24', '25'],
46+
* classPeriodName: ['z1', 'z2']
47+
* }
48+
*
49+
* IntermediateDocumentReferences here contains information for two DocumentReferences, but as "slices" in the wrong
50+
* orientation.
51+
*/
52+
type IntermediateDocumentReferences = { [key: DocumentObjectKey]: any[] };
1253

1354
/**
1455
* Takes a resource schema and an API document for that resource and
@@ -24,24 +65,55 @@ export function extractDocumentReferences(resourceSchema: ResourceSchema, docume
2465
// Only applies to non-descriptor references
2566
if (documentPaths.isDescriptor) return;
2667

27-
const documentIdentity: DocumentIdentity = [];
28-
// Build up documentIdentity in order
29-
documentPaths.pathOrder.forEach((documentKey: DocumentObjectKey) => {
30-
const documentJsonPath: JsonPath = documentPaths.paths[documentKey];
31-
const documentValue: any = jsonPath({
68+
// Build up intermediateDocumentReferences
69+
const intermediateDocumentReferences: IntermediateDocumentReferences = {};
70+
Object.entries(documentPaths.paths).forEach(([documentKey, documentJsonPath]) => {
71+
const documentValuesSlice: any[] = jsonPath({
3272
path: documentJsonPath,
3373
json: documentBody,
3474
flatten: true,
3575
});
36-
documentIdentity.push({ documentKey, documentValue });
37-
});
3876

39-
result.push({
40-
documentIdentity,
41-
isDescriptor: documentPaths.isDescriptor,
42-
projectName: documentPaths.projectName,
43-
resourceName: documentPaths.resourceName,
77+
invariant(
78+
Array.isArray(documentValuesSlice),
79+
`JsonPath ${documentJsonPath} should have returned an array but instead was ${documentValuesSlice}`,
80+
);
81+
82+
// Path can be empty if reference is optional
83+
if (documentValuesSlice.length === 0) return;
84+
85+
intermediateDocumentReferences[documentKey] = documentValuesSlice;
4486
});
87+
88+
const documentValuesSlices = Object.values(intermediateDocumentReferences);
89+
90+
// Empty if reference is optional and no values
91+
if (documentValuesSlices.length === 0) return;
92+
93+
// Number of document values from resolved JsonPaths should all be the same, otherwise something is very wrong
94+
invariant(
95+
documentValuesSlices.every(
96+
(documentValuesSlice) => documentValuesSlice.length === documentValuesSlices[0].length,
97+
`Length of document value slices are not equal`,
98+
),
99+
);
100+
101+
// Reorient intermediateDocumentReferences into actual references
102+
for (let index = 0; index < documentValuesSlices[0].length; index += 1) {
103+
const documentIdentity: DocumentIdentity = [];
104+
105+
// Build the document identity in the correct path order
106+
documentPaths.pathOrder.forEach((documentKey: DocumentObjectKey) => {
107+
documentIdentity.push({ documentKey, documentValue: intermediateDocumentReferences[documentKey][index] });
108+
});
109+
110+
result.push({
111+
documentIdentity,
112+
isDescriptor: documentPaths.isDescriptor,
113+
projectName: documentPaths.projectName,
114+
resourceName: documentPaths.resourceName,
115+
});
116+
}
45117
});
46118

47119
return result;

0 commit comments

Comments
 (0)