@narrative.io/data-collaboration-sdk-ts 2.103.0 → 2.105.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.
- package/build/access-tokens/types.d.ts +1 -0
- package/build/access-tokens/types.js +1 -0
- package/build/apps/index.d.ts +7 -0
- package/build/apps/index.js +9 -0
- package/build/collaboration-policy/core/filter-builder.js +7 -1
- package/build/collaboration-policy/core/jsonschema/json-schema-types.d.ts +45 -0
- package/build/collaboration-policy/core/jsonschema/json-schema-types.js +1 -0
- package/build/collaboration-policy/core/jsonschema/policy-branches.d.ts +49 -0
- package/build/collaboration-policy/core/jsonschema/policy-branches.js +1 -0
- package/build/collaboration-policy/core/jsonschema/policy-evaluator.d.ts +22 -0
- package/build/collaboration-policy/core/jsonschema/policy-evaluator.js +250 -0
- package/build/collaboration-policy/core/jsonschema/sql-builder.d.ts +79 -0
- package/build/collaboration-policy/core/jsonschema/sql-builder.js +306 -0
- package/build/collaboration-policy/core/types.d.ts +81 -10
- package/build/collaboration-policy/index.d.ts +5 -4
- package/build/collaboration-policy/index.js +4 -3
- package/build/collaboration-policy/useCollaborationPolicy.d.ts +13 -4
- package/build/collaboration-policy/useCollaborationPolicy.js +23 -1
- package/build/collaboration-policy/utils/path-helpers.d.ts +7 -3
- package/build/collaboration-policy/utils/path-helpers.js +6 -22
- package/build/datasets/index.d.ts +11 -2
- package/build/datasets/index.js +12 -0
- package/build/datasets/types.d.ts +25 -0
- package/package.json +2 -1
- package/build/collaboration-policy/core/filter-utils.d.ts +0 -6
- package/build/collaboration-policy/core/filter-utils.js +0 -49
- package/build/collaboration-policy/core/policy-traverser.d.ts +0 -4
- package/build/collaboration-policy/core/policy-traverser.js +0 -99
- package/build/collaboration-policy/core/policy-validator.d.ts +0 -11
- package/build/collaboration-policy/core/policy-validator.js +0 -185
- package/build/collaboration-policy/core/sql-builder.d.ts +0 -37
- package/build/collaboration-policy/core/sql-builder.js +0 -346
- package/build/collaboration-policy/scripts/generate-policy-schema.d.ts +0 -2
- package/build/collaboration-policy/scripts/generate-policy-schema.js +0 -4
- package/build/collaboration-policy/types/collaboration-policy.d.ts +0 -1370
- package/build/collaboration-policy/types/collaboration-policy.js +0 -274
- package/build/collaboration-policy/types/index.d.ts +0 -2
- package/build/collaboration-policy/types/index.js +0 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { buildSelectAlias, resolveAttributeExpression, } from "../../utils/sql-helpers";
|
|
2
|
+
import { filterToSql } from "../filter-builder";
|
|
3
|
+
import { evaluateJsonSchemaPolicies } from "./policy-evaluator";
|
|
4
|
+
/**
|
|
5
|
+
* Convert an ISO 8601 duration string (e.g. "P1D", "PT1H") into milliseconds.
|
|
6
|
+
* This is an approximation (months and years are treated as 30 and 365 days respectively).
|
|
7
|
+
*
|
|
8
|
+
* @param duration - The ISO 8601 duration string.
|
|
9
|
+
* @returns The approximate duration in milliseconds.
|
|
10
|
+
* @throws If the duration string does not match the ISO 8601 duration format.
|
|
11
|
+
*/
|
|
12
|
+
function parseDurationToMilliseconds(duration) {
|
|
13
|
+
const regex = /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
|
|
14
|
+
const matches = duration.match(regex);
|
|
15
|
+
if (!matches) {
|
|
16
|
+
throw new Error(`Invalid ISO 8601 duration format: ${duration}`);
|
|
17
|
+
}
|
|
18
|
+
const [, years, months, weeks, days, hours, minutes, seconds] = matches.map((m) => Number.parseInt(m || "0", 10));
|
|
19
|
+
const milliseconds = years * 365 * 24 * 60 * 60 * 1000 +
|
|
20
|
+
months * 30 * 24 * 60 * 60 * 1000 +
|
|
21
|
+
weeks * 7 * 24 * 60 * 60 * 1000 +
|
|
22
|
+
days * 24 * 60 * 60 * 1000 +
|
|
23
|
+
hours * 60 * 60 * 1000 +
|
|
24
|
+
minutes * 60 * 1000 +
|
|
25
|
+
seconds * 1000;
|
|
26
|
+
return milliseconds;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Find the "smallest" (most frequent) ISO 8601 duration within a list.
|
|
30
|
+
*
|
|
31
|
+
* @param schedules - Array of ISO 8601 duration strings.
|
|
32
|
+
* @returns The smallest duration as an ISO 8601 string, or "P1M" if none are valid.
|
|
33
|
+
*/
|
|
34
|
+
function getSmallestRefreshSchedule(schedules) {
|
|
35
|
+
const defaultMonthly = "P1M";
|
|
36
|
+
if (schedules.length === 0)
|
|
37
|
+
return defaultMonthly;
|
|
38
|
+
if (schedules.length === 1) {
|
|
39
|
+
try {
|
|
40
|
+
parseDurationToMilliseconds(schedules[0]);
|
|
41
|
+
return schedules[0];
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return defaultMonthly;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
let smallestValue = Number.MAX_SAFE_INTEGER;
|
|
48
|
+
let smallestSchedule = null;
|
|
49
|
+
for (const schedule of schedules) {
|
|
50
|
+
try {
|
|
51
|
+
const value = parseDurationToMilliseconds(schedule);
|
|
52
|
+
if (value < smallestValue) {
|
|
53
|
+
smallestValue = value;
|
|
54
|
+
smallestSchedule = schedule;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// skip invalid schedule
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return smallestSchedule ?? defaultMonthly;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build SQL fragments (SELECT + WHERE) from branch matches across all applied policies.
|
|
65
|
+
*
|
|
66
|
+
* SELECT: one entry per unique required attribute (root level, no subfield paths):
|
|
67
|
+
* company_data."<dataset>"."_rosetta_stone"."<attr>" AS "<attr>"
|
|
68
|
+
*
|
|
69
|
+
* WHERE: structured as root AND → per-policy structure → OR of branches.
|
|
70
|
+
* Each branch contains IS NOT NULL clauses (from additionalRequiredProperties)
|
|
71
|
+
* and filter SQL (from filters), ANDed together.
|
|
72
|
+
*
|
|
73
|
+
* @param branchMatches - Per-policy branch match results from evaluateJsonSchemaPolicies.
|
|
74
|
+
* @param attributeByName - Map of attribute name → Attribute for SQL expression building.
|
|
75
|
+
* @param datasetName - Dataset name used in SQL column paths.
|
|
76
|
+
* @returns PolicySqlFragments with select and where arrays.
|
|
77
|
+
*/
|
|
78
|
+
function buildSqlFromBranchMatches(branchMatches, attributeByName, datasetName) {
|
|
79
|
+
// --- SELECT ---
|
|
80
|
+
// Collect all unique required attributes across all policies and branches
|
|
81
|
+
const globalRequiredAttributes = new Set();
|
|
82
|
+
for (const policyMatch of branchMatches) {
|
|
83
|
+
for (const branch of policyMatch.branches) {
|
|
84
|
+
for (const attrName of branch.requiredAttributes) {
|
|
85
|
+
globalRequiredAttributes.add(attrName);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const selectClauses = new Set();
|
|
90
|
+
for (const attrName of globalRequiredAttributes) {
|
|
91
|
+
const attribute = attributeByName.get(attrName);
|
|
92
|
+
if (!attribute)
|
|
93
|
+
continue;
|
|
94
|
+
const expression = resolveAttributeExpression(attribute, "", datasetName);
|
|
95
|
+
const alias = buildSelectAlias(attrName, "");
|
|
96
|
+
selectClauses.add(`${expression} AS ${alias}`);
|
|
97
|
+
}
|
|
98
|
+
// --- WHERE ---
|
|
99
|
+
const policyWhereGroups = [];
|
|
100
|
+
for (const policyMatch of branchMatches) {
|
|
101
|
+
const branchWheres = [];
|
|
102
|
+
for (const branch of policyMatch.branches) {
|
|
103
|
+
const branchClauses = [];
|
|
104
|
+
// 1. IS NOT NULL clauses derived from $ref/$defs resolution
|
|
105
|
+
for (const req of branch.additionalRequiredProperties) {
|
|
106
|
+
const attribute = attributeByName.get(req.attributeName);
|
|
107
|
+
if (!attribute)
|
|
108
|
+
continue;
|
|
109
|
+
for (const path of req.paths) {
|
|
110
|
+
const expression = resolveAttributeExpression(attribute, path, datasetName);
|
|
111
|
+
branchClauses.push(`(${expression} IS NOT NULL)`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 2. Filter SQL
|
|
115
|
+
for (const filter of branch.filters) {
|
|
116
|
+
const sql = filterToSql(filter, attributeByName, datasetName);
|
|
117
|
+
branchClauses.push(sql);
|
|
118
|
+
}
|
|
119
|
+
if (branchClauses.length === 0) {
|
|
120
|
+
continue; // branch has no WHERE contribution
|
|
121
|
+
}
|
|
122
|
+
if (branchClauses.length === 1) {
|
|
123
|
+
branchWheres.push(branchClauses[0]);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
branchWheres.push({
|
|
127
|
+
operation: "AND",
|
|
128
|
+
fragments: branchClauses,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (branchWheres.length === 0) {
|
|
133
|
+
continue; // policy has no WHERE contribution
|
|
134
|
+
}
|
|
135
|
+
if (branchWheres.length === 1) {
|
|
136
|
+
policyWhereGroups.push(branchWheres[0]);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Multiple branches → OR them (anyOf semantics)
|
|
140
|
+
policyWhereGroups.push({
|
|
141
|
+
operation: "OR",
|
|
142
|
+
fragments: branchWheres,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
let where;
|
|
147
|
+
if (policyWhereGroups.length === 0) {
|
|
148
|
+
where = [];
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// Root AND wrapping all policies
|
|
152
|
+
where = [
|
|
153
|
+
{
|
|
154
|
+
fragments: policyWhereGroups,
|
|
155
|
+
operation: "AND",
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
select: Array.from(selectClauses),
|
|
161
|
+
where,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Categorize JSON-Schema-based connector policies into matching and non-matching
|
|
166
|
+
* groups based on dataset compatibility.
|
|
167
|
+
*
|
|
168
|
+
* @param policies - JSON-Schema-based connector policies.
|
|
169
|
+
* @param dataset - Dataset whose schema is used for validation.
|
|
170
|
+
* @param attributes - Attributes associated with the dataset.
|
|
171
|
+
* @returns Object with matching policies, nonMatching policies, and skip details.
|
|
172
|
+
*/
|
|
173
|
+
export function categorizePolicies(policies, dataset, attributes) {
|
|
174
|
+
const evaluations = evaluateJsonSchemaPolicies(dataset, attributes, policies);
|
|
175
|
+
const matching = [];
|
|
176
|
+
const nonMatching = [];
|
|
177
|
+
const skippedDetails = [];
|
|
178
|
+
for (const evalResult of evaluations) {
|
|
179
|
+
const { policy, isValid, errors } = evalResult;
|
|
180
|
+
if (isValid) {
|
|
181
|
+
matching.push(policy);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
nonMatching.push(policy);
|
|
185
|
+
skippedDetails.push({
|
|
186
|
+
name: policy.name,
|
|
187
|
+
reason: "Dataset schema not compatible with policy JSON Schema",
|
|
188
|
+
details: errors,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { matching, nonMatching, skippedDetails };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Helper that evaluates JSON-Schema-based policies and returns branch-level
|
|
196
|
+
* matches plus scheduling information.
|
|
197
|
+
*
|
|
198
|
+
* This is intended to be consumed by a separate SQL builder layer or for
|
|
199
|
+
* inspection of branch-level match details.
|
|
200
|
+
*
|
|
201
|
+
* @param dataset - The dataset against which policies should be evaluated.
|
|
202
|
+
* @param policies - JSON-Schema-based connector policies.
|
|
203
|
+
* @param attributes - Attributes associated with the dataset.
|
|
204
|
+
* @returns JsonSchemaPolicyMatchResult with applied/skipped policies and branch matches.
|
|
205
|
+
* @throws If no policies are compatible with the dataset.
|
|
206
|
+
*/
|
|
207
|
+
export function buildPolicySqlWithJsonSchema(dataset, policies, attributes) {
|
|
208
|
+
const evaluations = evaluateJsonSchemaPolicies(dataset, attributes, policies);
|
|
209
|
+
const appliedPolicies = [];
|
|
210
|
+
const skippedPolicies = [];
|
|
211
|
+
const warnings = [];
|
|
212
|
+
const branchMatches = [];
|
|
213
|
+
for (const evalResult of evaluations) {
|
|
214
|
+
const { policy, isValid, matches, errors } = evalResult;
|
|
215
|
+
if (!isValid) {
|
|
216
|
+
skippedPolicies.push({
|
|
217
|
+
name: policy.name,
|
|
218
|
+
reason: "Dataset schema not compatible with policy JSON Schema",
|
|
219
|
+
details: errors,
|
|
220
|
+
});
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
appliedPolicies.push(policy.name);
|
|
224
|
+
branchMatches.push({
|
|
225
|
+
policyName: policy.name,
|
|
226
|
+
branches: matches,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (appliedPolicies.length === 0) {
|
|
230
|
+
const errorMessage = policies.length === 0
|
|
231
|
+
? "No policies provided"
|
|
232
|
+
: `No policies are compatible with dataset "${dataset.display_name || dataset.name}".`;
|
|
233
|
+
const details = skippedPolicies.length > 0
|
|
234
|
+
? [`Skipped policies: ${skippedPolicies.map((p) => p.name).join(", ")}`]
|
|
235
|
+
: [];
|
|
236
|
+
throw new Error(`${errorMessage}\n${details.join("\n")}`);
|
|
237
|
+
}
|
|
238
|
+
if (skippedPolicies.length > 0) {
|
|
239
|
+
warnings.push(`Skipped ${skippedPolicies.length} incompatible policies: ${skippedPolicies
|
|
240
|
+
.map((p) => p.name)
|
|
241
|
+
.join(", ")}`);
|
|
242
|
+
}
|
|
243
|
+
const refreshSchedules = evaluations
|
|
244
|
+
.filter((e) => e.isValid && e.policy.metadata.refresh_schedule?.max)
|
|
245
|
+
.map((e) => e.policy.metadata.refresh_schedule?.max)
|
|
246
|
+
.filter((max) => max != null);
|
|
247
|
+
const refresh_schedule = getSmallestRefreshSchedule(refreshSchedules);
|
|
248
|
+
return {
|
|
249
|
+
appliedPolicies,
|
|
250
|
+
skippedPolicies,
|
|
251
|
+
warnings,
|
|
252
|
+
refresh_schedule,
|
|
253
|
+
branchMatches,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Uses JSON Schema branch matching to determine valid policies, then builds
|
|
258
|
+
* SELECT and WHERE SQL fragments.
|
|
259
|
+
*
|
|
260
|
+
* @param dataset - Dataset for which to evaluate policies and produce SQL.
|
|
261
|
+
* @param policies - JSON-Schema-based connector policies.
|
|
262
|
+
* @param attributes - Attributes associated with the dataset.
|
|
263
|
+
* @returns PolicyMatchResult with select, where, applied/skipped policies, warnings, and refresh schedule.
|
|
264
|
+
*/
|
|
265
|
+
export function buildPolicySqlWithValidation(dataset, policies, attributes) {
|
|
266
|
+
const matchResult = buildPolicySqlWithJsonSchema(dataset, policies, attributes);
|
|
267
|
+
const attributeByName = new Map();
|
|
268
|
+
for (const attr of attributes) {
|
|
269
|
+
attributeByName.set(attr.name, attr);
|
|
270
|
+
}
|
|
271
|
+
const sqlFragments = buildSqlFromBranchMatches(matchResult.branchMatches, attributeByName, dataset.name);
|
|
272
|
+
return {
|
|
273
|
+
...sqlFragments,
|
|
274
|
+
appliedPolicies: matchResult.appliedPolicies,
|
|
275
|
+
skippedPolicies: matchResult.skippedPolicies,
|
|
276
|
+
warnings: matchResult.warnings,
|
|
277
|
+
refresh_schedule: matchResult.refresh_schedule,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Build SQL fragments without validation. Assumes you already filtered the policies
|
|
282
|
+
* however you want (e.g. via categorizePolicies). Builds SELECT and WHERE SQL
|
|
283
|
+
* fragments from all provided policies.
|
|
284
|
+
*
|
|
285
|
+
* @param dataset - Dataset for which to build SQL fragments.
|
|
286
|
+
* @param policies - JSON-Schema-based connector policies.
|
|
287
|
+
* @param attributes - Attributes associated with the dataset.
|
|
288
|
+
* @returns PolicySqlFragments with select and where arrays.
|
|
289
|
+
*/
|
|
290
|
+
export function buildPolicySql(dataset, policies, attributes) {
|
|
291
|
+
const evaluations = evaluateJsonSchemaPolicies(dataset, attributes, policies);
|
|
292
|
+
const attributeByName = new Map();
|
|
293
|
+
for (const attr of attributes) {
|
|
294
|
+
attributeByName.set(attr.name, attr);
|
|
295
|
+
}
|
|
296
|
+
const branchMatches = [];
|
|
297
|
+
for (const evalResult of evaluations) {
|
|
298
|
+
if (evalResult.isValid) {
|
|
299
|
+
branchMatches.push({
|
|
300
|
+
policyName: evalResult.policy.name,
|
|
301
|
+
branches: evalResult.matches,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return buildSqlFromBranchMatches(branchMatches, attributeByName, dataset.name);
|
|
306
|
+
}
|
|
@@ -1,20 +1,91 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
/**
|
|
2
|
+
* PathValue describes a path to a subfield, expressed as either dot notation
|
|
3
|
+
* or a JSON Pointer. Used in filter attribute references to specify which
|
|
4
|
+
* subfield of an attribute to operate on.
|
|
5
|
+
*/
|
|
6
|
+
export type PathValue = {
|
|
7
|
+
dot: string;
|
|
8
|
+
} | {
|
|
9
|
+
pointer: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* An attribute reference within a filter expression.
|
|
13
|
+
*/
|
|
14
|
+
export interface FilterAttributeRef {
|
|
15
|
+
type: "attribute";
|
|
16
|
+
attribute_name: string;
|
|
17
|
+
path?: PathValue;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A filter expression: a literal value or an attribute reference.
|
|
21
|
+
*/
|
|
22
|
+
export type FilterExpression = string | number | boolean | FilterAttributeRef;
|
|
23
|
+
/**
|
|
24
|
+
* PolicyFilter is a single filter entry in a policy definition.
|
|
25
|
+
*
|
|
26
|
+
* This is the canonical filter type used across the codebase (JSON-Schema
|
|
27
|
+
* filters, SQL builder, etc.).
|
|
28
|
+
*/
|
|
29
|
+
export type PolicyFilter = PolicyFilterAndOr | PolicyFilterNot | PolicyFilterIsNull | PolicyFilterIn | PolicyFilterCompare | PolicyFilterBetween;
|
|
30
|
+
interface PolicyFilterBase {
|
|
31
|
+
stage?: "generation";
|
|
32
|
+
name?: string;
|
|
33
|
+
required?: boolean;
|
|
34
|
+
}
|
|
35
|
+
interface PolicyFilterAndOr extends PolicyFilterBase {
|
|
36
|
+
op: "and" | "or";
|
|
37
|
+
args: FilterExpression[];
|
|
38
|
+
}
|
|
39
|
+
interface PolicyFilterNot extends PolicyFilterBase {
|
|
40
|
+
op: "not";
|
|
41
|
+
args: FilterExpression[];
|
|
42
|
+
}
|
|
43
|
+
interface PolicyFilterIsNull extends PolicyFilterBase {
|
|
44
|
+
op: "is_null" | "is_not_null";
|
|
45
|
+
left: FilterExpression;
|
|
46
|
+
}
|
|
47
|
+
interface PolicyFilterIn extends PolicyFilterBase {
|
|
48
|
+
op: "in" | "not in";
|
|
49
|
+
left: FilterExpression;
|
|
50
|
+
right: FilterExpression[];
|
|
51
|
+
}
|
|
52
|
+
interface PolicyFilterCompare extends PolicyFilterBase {
|
|
53
|
+
op: "=" | "<>" | ">" | ">=" | "<" | "<=" | "like" | "not like";
|
|
54
|
+
left: FilterExpression;
|
|
55
|
+
right: FilterExpression;
|
|
56
|
+
}
|
|
57
|
+
interface PolicyFilterBetween extends PolicyFilterBase {
|
|
58
|
+
op: "between";
|
|
59
|
+
operand: FilterExpression;
|
|
60
|
+
lower: FilterExpression;
|
|
61
|
+
upper: FilterExpression;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Map from attribute name -> set of normalized paths that must be selected
|
|
65
|
+
* for SQL generation.
|
|
66
|
+
*/
|
|
9
67
|
export type AttributePathIndex = Map<string, Set<string>>;
|
|
68
|
+
/**
|
|
69
|
+
* A logical group of SQL fragments combined by an operation.
|
|
70
|
+
*/
|
|
10
71
|
export interface PolicySqlGroup {
|
|
11
72
|
fragments: Array<string | PolicySqlGroup>;
|
|
12
73
|
operation: "AND" | "OR";
|
|
13
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Core SQL fragments required to represent a set of collaboration policies.
|
|
77
|
+
*/
|
|
14
78
|
export interface PolicySqlFragments {
|
|
15
79
|
select: string[];
|
|
16
80
|
where: Array<PolicySqlGroup | string>;
|
|
17
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Full result of applying collaboration policies to a dataset, including:
|
|
84
|
+
* - which policies were applied
|
|
85
|
+
* - which were skipped and why
|
|
86
|
+
* - warnings
|
|
87
|
+
* - the minimum refresh schedule across applied policies
|
|
88
|
+
*/
|
|
18
89
|
export interface PolicyMatchResult extends PolicySqlFragments {
|
|
19
90
|
appliedPolicies: string[];
|
|
20
91
|
skippedPolicies: Array<{
|
|
@@ -23,7 +94,7 @@ export interface PolicyMatchResult extends PolicySqlFragments {
|
|
|
23
94
|
details: string[];
|
|
24
95
|
}>;
|
|
25
96
|
warnings: string[];
|
|
26
|
-
/** The minimum refresh schedule
|
|
27
|
-
refresh_schedule:
|
|
97
|
+
/** The minimum refresh schedule as an ISO 8601 duration string across all evaluated policies */
|
|
98
|
+
refresh_schedule: string;
|
|
28
99
|
}
|
|
29
100
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export {
|
|
1
|
+
export type { JsonSchemaConnectorPolicy } from "./core/jsonschema/json-schema-types";
|
|
2
|
+
export { evaluateJsonSchemaPolicies } from "./core/jsonschema/policy-evaluator";
|
|
3
|
+
export { buildPolicySql, buildPolicySqlWithJsonSchema, buildPolicySqlWithValidation, categorizePolicies, type JsonSchemaPolicyMatchResult, } from "./core/jsonschema/sql-builder";
|
|
4
|
+
export type { AttributePathIndex, FilterAttributeRef, FilterExpression, PathValue, PolicyFilter, PolicyMatchResult, PolicySqlFragments, PolicySqlGroup, } from "./core/types";
|
|
5
|
+
export { default as useCollaborationPolicy } from "./useCollaborationPolicy";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export { buildPolicySql, buildPolicySqlWithValidation, categorizePolicies, } from "./core/sql-builder";
|
|
3
|
-
|
|
1
|
+
export { evaluateJsonSchemaPolicies } from "./core/jsonschema/policy-evaluator";
|
|
2
|
+
export { buildPolicySql, buildPolicySqlWithJsonSchema, buildPolicySqlWithValidation, categorizePolicies, } from "./core/jsonschema/sql-builder";
|
|
3
|
+
// Hook-style entrypoint
|
|
4
|
+
export { default as useCollaborationPolicy } from "./useCollaborationPolicy";
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import type { Attribute } from "../attributes/types";
|
|
2
2
|
import type { Dataset } from "../datasets/types";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
3
|
+
import type { JsonSchemaConnectorPolicy } from "./core/jsonschema/json-schema-types";
|
|
4
|
+
import type { PolicyMatchResult, PolicySqlFragments } from "./core/types";
|
|
5
|
+
/**
|
|
6
|
+
* Hook-style wrapper that exposes collaboration policy helpers.
|
|
7
|
+
*
|
|
8
|
+
* Public API surface:
|
|
9
|
+
* - categorizePolicies
|
|
10
|
+
* - buildPolicySql
|
|
11
|
+
* - buildPolicySqlWithValidation
|
|
12
|
+
*/
|
|
5
13
|
export default function useCollaborationPolicy(): {
|
|
6
|
-
getEligibleConnectorsPolicies: (dataset: Dataset, policies:
|
|
7
|
-
buildPolicySqlFragments: (dataset: Dataset, policies:
|
|
14
|
+
getEligibleConnectorsPolicies: (dataset: Dataset, policies: JsonSchemaConnectorPolicy[], attributes: Attribute[]) => JsonSchemaConnectorPolicy[];
|
|
15
|
+
buildPolicySqlFragments: (dataset: Dataset, policies: JsonSchemaConnectorPolicy[], attributes: Attribute[]) => PolicySqlFragments;
|
|
16
|
+
buildPolicySqlWithValidationJsonSchema: (dataset: Dataset, policies: JsonSchemaConnectorPolicy[], attributes: Attribute[]) => PolicyMatchResult;
|
|
8
17
|
};
|
|
@@ -1,14 +1,36 @@
|
|
|
1
|
-
import { buildPolicySql, categorizePolicies } from "./core/sql-builder";
|
|
1
|
+
import { buildPolicySql, buildPolicySqlWithValidation, categorizePolicies, } from "./core/jsonschema/sql-builder";
|
|
2
|
+
/**
|
|
3
|
+
* Hook-style wrapper that exposes collaboration policy helpers.
|
|
4
|
+
*
|
|
5
|
+
* Public API surface:
|
|
6
|
+
* - categorizePolicies
|
|
7
|
+
* - buildPolicySql
|
|
8
|
+
* - buildPolicySqlWithValidation
|
|
9
|
+
*/
|
|
2
10
|
export default function useCollaborationPolicy() {
|
|
11
|
+
/**
|
|
12
|
+
* Get policies that are compatible with a dataset using the JSON-Schema policy system.
|
|
13
|
+
*/
|
|
3
14
|
const getEligibleConnectorsPolicies = (dataset, policies, attributes) => {
|
|
4
15
|
const { matching } = categorizePolicies(policies, dataset, attributes);
|
|
5
16
|
return matching;
|
|
6
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Build SQL fragments using JSON-Schema policies, without per-policy validation.
|
|
20
|
+
*/
|
|
7
21
|
const buildPolicySqlFragments = (dataset, policies, attributes) => {
|
|
8
22
|
return buildPolicySql(dataset, policies, attributes);
|
|
9
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Validate each JSON-Schema policy against the dataset and build SQL
|
|
26
|
+
* plus metadata (appliedPolicies, skippedPolicies, etc.).
|
|
27
|
+
*/
|
|
28
|
+
const buildPolicySqlWithValidationJsonSchema = (dataset, policies, attributes) => {
|
|
29
|
+
return buildPolicySqlWithValidation(dataset, policies, attributes);
|
|
30
|
+
};
|
|
10
31
|
return {
|
|
11
32
|
getEligibleConnectorsPolicies,
|
|
12
33
|
buildPolicySqlFragments,
|
|
34
|
+
buildPolicySqlWithValidationJsonSchema,
|
|
13
35
|
};
|
|
14
36
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { PathValue } from "../core/types";
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes a PathValue (dot notation or JSON pointer) into a simple dot-notation string.
|
|
4
|
+
*
|
|
5
|
+
* @param path - A PathValue with either `{ dot: "..." }` or `{ pointer: "..." }` format.
|
|
6
|
+
* @returns Normalized dot-notation string, or empty string for undefined/null.
|
|
7
|
+
*/
|
|
4
8
|
export declare function normalizePathValue(path?: PathValue): string;
|
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
attributePaths.add(normalized);
|
|
8
|
-
index.set(attribute.name, attributePaths);
|
|
9
|
-
}
|
|
10
|
-
function determineDefaultPath(attribute) {
|
|
11
|
-
if (attribute.type === "object" && "properties" in attribute) {
|
|
12
|
-
const properties = attribute.properties ?? {};
|
|
13
|
-
if ("value" in properties) {
|
|
14
|
-
return "value";
|
|
15
|
-
}
|
|
16
|
-
const [firstKey] = Object.keys(properties);
|
|
17
|
-
if (firstKey) {
|
|
18
|
-
return firstKey;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return "";
|
|
22
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes a PathValue (dot notation or JSON pointer) into a simple dot-notation string.
|
|
3
|
+
*
|
|
4
|
+
* @param path - A PathValue with either `{ dot: "..." }` or `{ pointer: "..." }` format.
|
|
5
|
+
* @returns Normalized dot-notation string, or empty string for undefined/null.
|
|
6
|
+
*/
|
|
23
7
|
export function normalizePathValue(path) {
|
|
24
8
|
if (!path) {
|
|
25
9
|
return "";
|
|
@@ -2,9 +2,9 @@ import type { UnknownJob } from "src/jobs/types";
|
|
|
2
2
|
import { BaseApi } from "../base-api";
|
|
3
3
|
import type { ApiRecords } from "../types";
|
|
4
4
|
import type { DatasetStatisticsConfiguration, StatisticsConfigurationResponse } from "./statistics-types";
|
|
5
|
-
import type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetStatus, DatasetTableSummary, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, IngestDatasetFileRequest, RecalculationInfo, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value } from "./types";
|
|
5
|
+
import type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetInterfaceError, DatasetInterfaceRef, DatasetInterfaceValidation, DatasetStatus, DatasetTableSummary, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, IngestDatasetFileRequest, JsonSchemaValidationNode, RecalculationInfo, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value } from "./types";
|
|
6
6
|
export type { CronRefresh, DatasetFieldConfig, DatasetFieldNamespace, DatasetStatisticsConfiguration, HistogramOptions, HistogramOverflow, ManualRefresh, NestedNodeConfig, NestedPropertyConfig, NonPrimitiveNodeConfig, OnUpdateRefresh, PrimitiveNodeConfig, RefreshTrigger, RosettaStoneFieldConfig, RosettaStoneFieldNamespace, StatConfig, StatisticsConfigurationResponse, StatName, StatOptions, } from "./statistics-types";
|
|
7
|
-
export type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetStatus, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value, };
|
|
7
|
+
export type { AdvancedStatistics, AdvancedStatisticsMetadata, BasicStatistics, ColumnStatistics, ColumnSummary, Columns, Configuration, CreateDatasetRefreshScheduleRequest, CreateDatasetRequest, Dataset, DatasetInterfaceError, DatasetInterfaceRef, DatasetInterfaceValidation, DatasetStatus, DatasetTableSummaryAPIResponse, DatasetWriteMode, FilePerSnapshotResponse, Histogram, JsonSchemaValidationNode, RetentionPolicy, RetentionScheduleConfig, RowClock, RowTTLPolicy, Schema, SchemaArrayItems, SchemaArrayItemsArray, SchemaArrayItemsObject, SchemaArrayItemsPrimitive, SchemaArrayProperty, SchemaFileConfig, SchemaFileConfigType, SchemaObjectProperty, SchemaPrimitiveProperty, SchemaProperties, SchemaPropertiesType, SchemaProperty, SnapshotAgeExpression, SnapshotRange, SnapshotTTLPolicy, TableClock, TableClockKind, TableTTLPolicy, TTLPolicy, TTLPolicyKind, UpdateDatasetRefreshScheduleRequest, UpdateDatasetRequest, UpsertRetentionPoliciesRequest, Value, };
|
|
8
8
|
/**
|
|
9
9
|
* @module DatasetApi
|
|
10
10
|
* @description This module provides methods for fetching datasets.
|
|
@@ -221,6 +221,15 @@ export declare class DatasetApi extends BaseApi {
|
|
|
221
221
|
createDatasetRefreshSchedule(datasetId: number, refreshSchedule: CreateDatasetRefreshScheduleRequest): Promise<void>;
|
|
222
222
|
updateDatasetRefreshSchedule(datasetId: number, refreshSchedule: UpdateDatasetRefreshScheduleRequest): Promise<void>;
|
|
223
223
|
deleteDatasetRefreshSchedule(datasetId: number): Promise<void>;
|
|
224
|
+
/**
|
|
225
|
+
* Validate which connector interfaces are compatible with a dataset.
|
|
226
|
+
* Filtered by the user's installed apps and optionally by tags.
|
|
227
|
+
*
|
|
228
|
+
* @param {number} datasetId - The ID of the dataset to validate interfaces against.
|
|
229
|
+
* @param {string[]} [tags] - Optional tags to filter interfaces (e.g. ["audience_delivery"]).
|
|
230
|
+
* @returns {Promise<DatasetInterfaceValidation>} - Accepted interface IDs and per-interface validation errors.
|
|
231
|
+
*/
|
|
232
|
+
getDatasetInterfaces(datasetId: number, tags?: string[]): Promise<DatasetInterfaceValidation>;
|
|
224
233
|
putStatisticsConfiguration(datasetId: number, configuration: DatasetStatisticsConfiguration): Promise<StatisticsConfigurationResponse>;
|
|
225
234
|
getStatisticsConfiguration(datasetId: number): Promise<StatisticsConfigurationResponse>;
|
|
226
235
|
deleteStatisticsConfiguration(datasetId: number): Promise<StatisticsConfigurationResponse>;
|
package/build/datasets/index.js
CHANGED
|
@@ -295,6 +295,18 @@ export class DatasetApi extends BaseApi {
|
|
|
295
295
|
async deleteDatasetRefreshSchedule(datasetId) {
|
|
296
296
|
await this.delete(`${resourceName}/${datasetId}/refresh-schedule`);
|
|
297
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Validate which connector interfaces are compatible with a dataset.
|
|
300
|
+
* Filtered by the user's installed apps and optionally by tags.
|
|
301
|
+
*
|
|
302
|
+
* @param {number} datasetId - The ID of the dataset to validate interfaces against.
|
|
303
|
+
* @param {string[]} [tags] - Optional tags to filter interfaces (e.g. ["audience_delivery"]).
|
|
304
|
+
* @returns {Promise<DatasetInterfaceValidation>} - Accepted interface IDs and per-interface validation errors.
|
|
305
|
+
*/
|
|
306
|
+
async getDatasetInterfaces(datasetId, tags) {
|
|
307
|
+
const queryString = this.constructQueryString(tags != null ? { tags } : undefined);
|
|
308
|
+
return await this.get(`${resourceName}/${datasetId}/interfaces${queryString}`);
|
|
309
|
+
}
|
|
298
310
|
async putStatisticsConfiguration(datasetId, configuration) {
|
|
299
311
|
return await this.put(`${resourceName}/${datasetId}/statistics-configuration`, configuration);
|
|
300
312
|
}
|
|
@@ -294,6 +294,31 @@ export interface CreateDatasetRequest {
|
|
|
294
294
|
export interface IngestDatasetFileRequest {
|
|
295
295
|
source_file: string;
|
|
296
296
|
}
|
|
297
|
+
export interface DatasetInterfaceRef {
|
|
298
|
+
app_id: number;
|
|
299
|
+
interface_id: string;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* A JSON-Schema 2020-12 "Basic" output node, used recursively in validation errors.
|
|
303
|
+
* `errors` is keyed by JSON-Schema keyword (`"required"`, `"type"`, etc.); values
|
|
304
|
+
* are human-readable strings.
|
|
305
|
+
*/
|
|
306
|
+
export interface JsonSchemaValidationNode {
|
|
307
|
+
valid: boolean;
|
|
308
|
+
evaluationPath: string;
|
|
309
|
+
schemaLocation: string;
|
|
310
|
+
instanceLocation: string;
|
|
311
|
+
errors?: Record<string, string>;
|
|
312
|
+
details?: JsonSchemaValidationNode[];
|
|
313
|
+
}
|
|
314
|
+
export interface DatasetInterfaceError extends DatasetInterfaceRef {
|
|
315
|
+
details: JsonSchemaValidationNode;
|
|
316
|
+
}
|
|
317
|
+
export interface DatasetInterfaceValidation {
|
|
318
|
+
dataset_id: number;
|
|
319
|
+
accepted: DatasetInterfaceRef[];
|
|
320
|
+
errors: DatasetInterfaceError[];
|
|
321
|
+
}
|
|
297
322
|
export interface UpdateDatasetRefreshScheduleRequest {
|
|
298
323
|
cron?: string;
|
|
299
324
|
cron_zone_id?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narrative.io/data-collaboration-sdk-ts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.105.0",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"repository": "github:narrative-io/data-collaboration-sdk-ts",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"@commitlint/cli": "21.0.1",
|
|
33
33
|
"@commitlint/config-conventional": "21.0.1",
|
|
34
34
|
"@types/jest": "30.0.0",
|
|
35
|
+
"@types/json-schema": "^7.0.15",
|
|
35
36
|
"babel-jest": "30.4.1",
|
|
36
37
|
"jest": "30.4.2",
|
|
37
38
|
"lefthook": "2.1.6",
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { PathValue, PolicyFilter } from "./types";
|
|
2
|
-
export declare function visitFilterAttributes(filter: PolicyFilter, visitor: (attributeName: string, normalizedPath: string) => void): void;
|
|
3
|
-
export declare function isAttributeReference(node: unknown): node is {
|
|
4
|
-
attribute_name: string;
|
|
5
|
-
path?: PathValue;
|
|
6
|
-
};
|