@kontourai/survey 0.4.22 → 0.4.23
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.
|
@@ -91,73 +91,85 @@ export function assertReviewResultMatches(result, expectation) {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
export function mapReviewWorkbenchResultsToApplyActions(options) {
|
|
94
|
-
const issues = [];
|
|
94
|
+
const issues = options.requireUniqueTargets ? duplicateTargetIssues(options.items) : [];
|
|
95
95
|
const itemByName = new Map(options.items.map((item) => [item.metadata.name, item]));
|
|
96
|
-
|
|
97
|
-
const seenTargets = new Set();
|
|
98
|
-
for (const item of options.items) {
|
|
99
|
-
if (seenTargets.has(item.spec.target)) {
|
|
100
|
-
issues.push({
|
|
101
|
-
code: "duplicate-review-target",
|
|
102
|
-
target: item.spec.target,
|
|
103
|
-
message: `Review target ${item.spec.target} appears on more than one ReviewItem.`,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
seenTargets.add(item.spec.target);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const mappings = options.results.flatMap((result) => {
|
|
110
|
-
const item = itemByName.get(result.reviewItemName);
|
|
111
|
-
if (!item) {
|
|
112
|
-
issues.push({
|
|
113
|
-
code: "unknown-review-item",
|
|
114
|
-
reviewItemName: result.reviewItemName,
|
|
115
|
-
message: `Review result ${result.reviewItemName} does not reference a known ReviewItem.`,
|
|
116
|
-
});
|
|
117
|
-
return [];
|
|
118
|
-
}
|
|
119
|
-
const selectedCandidate = item.spec.candidates.find((candidate) => candidate.id === result.selectedCandidateId);
|
|
120
|
-
if (!selectedCandidate
|
|
121
|
-
|| selectedCandidate.role !== result.selectedCandidateRole
|
|
122
|
-
|| !structuralEqual(selectedCandidate.value, result.selectedValue)) {
|
|
123
|
-
issues.push({
|
|
124
|
-
code: "selected-candidate-mismatch",
|
|
125
|
-
reviewItemName: result.reviewItemName,
|
|
126
|
-
message: `Review result ${result.reviewItemName} selected candidate does not match the supplied ReviewItem.`,
|
|
127
|
-
});
|
|
128
|
-
return [];
|
|
129
|
-
}
|
|
130
|
-
const context = {
|
|
131
|
-
result,
|
|
132
|
-
item,
|
|
133
|
-
target: item.spec.target,
|
|
134
|
-
selectedCandidate,
|
|
135
|
-
unselectedCandidates: item.spec.candidates.filter((candidate) => candidate.id !== selectedCandidate.id),
|
|
136
|
-
};
|
|
137
|
-
if (options.skip?.(context)) {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
const action = options.map(context);
|
|
141
|
-
if (action === undefined) {
|
|
142
|
-
issues.push({
|
|
143
|
-
code: "unmapped-review-result",
|
|
144
|
-
reviewItemName: result.reviewItemName,
|
|
145
|
-
message: `Review result ${result.reviewItemName} did not produce an apply action.`,
|
|
146
|
-
});
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
return (Array.isArray(action) ? action : [action]).map((entry) => ({
|
|
150
|
-
result,
|
|
151
|
-
item,
|
|
152
|
-
action: entry,
|
|
153
|
-
}));
|
|
154
|
-
});
|
|
96
|
+
const mappings = options.results.flatMap((result) => mapReviewApplyResultToActions({ result, itemByName, options, issues }));
|
|
155
97
|
if (issues.length > 0) {
|
|
156
98
|
throw new ReviewApplyActionMappingError(issues);
|
|
157
99
|
}
|
|
158
100
|
return mappings;
|
|
159
101
|
}
|
|
160
102
|
export const mapReviewApplyActions = mapReviewWorkbenchResultsToApplyActions;
|
|
103
|
+
function duplicateTargetIssues(items) {
|
|
104
|
+
const issues = [];
|
|
105
|
+
const seenTargets = new Set();
|
|
106
|
+
for (const item of items) {
|
|
107
|
+
if (seenTargets.has(item.spec.target)) {
|
|
108
|
+
issues.push({
|
|
109
|
+
code: "duplicate-review-target",
|
|
110
|
+
target: item.spec.target,
|
|
111
|
+
message: `Review target ${item.spec.target} appears on more than one ReviewItem.`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
seenTargets.add(item.spec.target);
|
|
115
|
+
}
|
|
116
|
+
return issues;
|
|
117
|
+
}
|
|
118
|
+
function mapReviewApplyResultToActions(input) {
|
|
119
|
+
const context = buildReviewApplyActionContext(input.result, input.itemByName, input.issues);
|
|
120
|
+
if (!context || input.options.skip?.(context)) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
const action = input.options.map(context);
|
|
124
|
+
if (action === undefined) {
|
|
125
|
+
input.issues.push({
|
|
126
|
+
code: "unmapped-review-result",
|
|
127
|
+
reviewItemName: input.result.reviewItemName,
|
|
128
|
+
message: `Review result ${input.result.reviewItemName} did not produce an apply action.`,
|
|
129
|
+
});
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
return (Array.isArray(action) ? action : [action]).map((entry) => ({
|
|
133
|
+
result: context.result,
|
|
134
|
+
item: context.item,
|
|
135
|
+
action: entry,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
function buildReviewApplyActionContext(result, itemByName, issues) {
|
|
139
|
+
const item = itemByName.get(result.reviewItemName);
|
|
140
|
+
if (!item) {
|
|
141
|
+
issues.push({
|
|
142
|
+
code: "unknown-review-item",
|
|
143
|
+
reviewItemName: result.reviewItemName,
|
|
144
|
+
message: `Review result ${result.reviewItemName} does not reference a known ReviewItem.`,
|
|
145
|
+
});
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
const selectedCandidate = matchingSelectedCandidate(item, result);
|
|
149
|
+
if (!selectedCandidate) {
|
|
150
|
+
issues.push({
|
|
151
|
+
code: "selected-candidate-mismatch",
|
|
152
|
+
reviewItemName: result.reviewItemName,
|
|
153
|
+
message: `Review result ${result.reviewItemName} selected candidate does not match the supplied ReviewItem.`,
|
|
154
|
+
});
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
result,
|
|
159
|
+
item,
|
|
160
|
+
target: item.spec.target,
|
|
161
|
+
selectedCandidate,
|
|
162
|
+
unselectedCandidates: item.spec.candidates.filter((candidate) => candidate.id !== selectedCandidate.id),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function matchingSelectedCandidate(item, result) {
|
|
166
|
+
const candidate = item.spec.candidates.find((entry) => entry.id === result.selectedCandidateId);
|
|
167
|
+
return candidate
|
|
168
|
+
&& candidate.role === result.selectedCandidateRole
|
|
169
|
+
&& structuralEqual(candidate.value, result.selectedValue)
|
|
170
|
+
? candidate
|
|
171
|
+
: undefined;
|
|
172
|
+
}
|
|
161
173
|
function structuralEqual(left, right) {
|
|
162
174
|
return JSON.stringify(canonicalJson(left)) === JSON.stringify(canonicalJson(right));
|
|
163
175
|
}
|
package/package.json
CHANGED