@google-cloud/kcmd 0.2.0 → 0.2.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/build/ts/kcmd/cli.js +241 -382
- package/build/ts/kcmd/okf/init_okf_workspace.js +1 -1
- package/build/ts/kcmd/semantic/deploy_bigquery.js +6 -13
- package/build/ts/kcmd/semantic/deploy_knowledge_catalog.d.ts +3 -3
- package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +16 -44
- package/build/ts/kcmd/semantic/deploy_spanner.js +7 -14
- package/build/ts/kcmd/semantic/deployment_target.d.ts +1 -4
- package/build/ts/kcmd/semantic/deployment_target.js +6 -62
- package/build/ts/kcmd/semantic/import_model.js +5 -8
- package/build/ts/kcmd/semantic/ir.d.ts +2 -4
- package/build/ts/kcmd/semantic/kc_converter.js +12 -22
- package/build/ts/kcmd/semantic/kc_custom_types.js +27 -21
- package/build/ts/kcmd/semantic/loader.js +3 -28
- package/build/ts/kcmd/semantic/osi_converter.js +13 -63
- package/build/ts/kcmd/semantic/runtime/runtime.d.ts +4 -6
- package/build/ts/kcmd/semantic/runtime/store.d.ts +14 -19
- package/build/ts/kcmd/semantic/runtime/store.js +21 -37
- package/build/ts/kcmd/semantic/validate.js +4 -30
- package/package.json +1 -1
|
@@ -83,15 +83,13 @@ exports.modelDocument = modelDocument;
|
|
|
83
83
|
exports.compact = compact;
|
|
84
84
|
exports.nonEmpty = nonEmpty;
|
|
85
85
|
const yaml = __importStar(require("yaml"));
|
|
86
|
-
const deployment_target_1 = require("./deployment_target");
|
|
87
86
|
const ir_1 = require("./ir");
|
|
88
87
|
const loader_1 = require("./loader");
|
|
89
88
|
const names_1 = require("./names");
|
|
90
89
|
const resolve_inheritance_1 = require("./resolve_inheritance");
|
|
91
90
|
const resolve_profiles_1 = require("./resolve_profiles");
|
|
92
91
|
// The vendor tag for Google-specific extension blocks on the IR (kept in sync
|
|
93
|
-
// with loader.GOOGLE_VENDOR).
|
|
94
|
-
// target list rides in one as a folded shim alongside `model.deployments`.
|
|
92
|
+
// with loader.GOOGLE_VENDOR).
|
|
95
93
|
const GOOGLE_VENDOR = 'GOOGLE';
|
|
96
94
|
// The dialect label for the IR's target/canonical `expression` when `dialects`
|
|
97
95
|
// is absent and `stringForm` is not set. BIGQUERY is the loader's default
|
|
@@ -340,17 +338,16 @@ function modelDoc(model, warnings, logical, version) {
|
|
|
340
338
|
`at least one and will not load until an entity is present.`);
|
|
341
339
|
}
|
|
342
340
|
const tableKey = isGoogleVersion ? 'entities' : 'datasets';
|
|
343
|
-
// Under GOOGLE_VERSION, deployments come from `model.deployments
|
|
344
|
-
//
|
|
345
|
-
//
|
|
341
|
+
// Under GOOGLE_VERSION, deployments come from `model.deployments`. Under
|
|
342
|
+
// OSSIE_VERSION, named deployments and profiles go in a GOOGLE block on the
|
|
343
|
+
// model.
|
|
346
344
|
const namedDeployments = (model.deployments ?? []).map(d => compact({
|
|
347
345
|
name: d.name,
|
|
348
346
|
target: d.target,
|
|
349
347
|
profile: d.profile
|
|
350
348
|
}));
|
|
351
349
|
const deployments = placement.model.deployments === 'native' ?
|
|
352
|
-
nonEmpty(namedDeployments
|
|
353
|
-
deploymentsFromGoogleExtensions(model)) :
|
|
350
|
+
nonEmpty(namedDeployments) :
|
|
354
351
|
undefined;
|
|
355
352
|
const blockProfiles = placement.model.profiles === 'block' ?
|
|
356
353
|
nonEmpty(profileDocuments(model, warnings).map(p => p.doc)) :
|
|
@@ -385,8 +382,7 @@ function modelDoc(model, warnings, logical, version) {
|
|
|
385
382
|
constraints: placement.model.constraints === 'native' ?
|
|
386
383
|
nonEmpty((model.constraints ?? []).map(c => constraintDoc(c))) :
|
|
387
384
|
undefined,
|
|
388
|
-
custom_extensions: customExtensionsDoc(model.customExtensions, `model '${model.name}'`, isGoogleVersion,
|
|
389
|
-
/* isModel= */ true, warnings, /* isField= */ false, blockPayload),
|
|
385
|
+
custom_extensions: customExtensionsDoc(model.customExtensions, `model '${model.name}'`, isGoogleVersion, warnings, /* isField= */ false, blockPayload),
|
|
390
386
|
});
|
|
391
387
|
}
|
|
392
388
|
// Warns about each IR construct OSSIE_VERSION has no representation for,
|
|
@@ -449,8 +445,7 @@ function datasetDoc(entity, warnings, logical, version) {
|
|
|
449
445
|
ai_context: aiContextDoc(entity.aiContext, isGoogleVersion),
|
|
450
446
|
fields: nonEmpty((entity.fields ?? [])
|
|
451
447
|
.map(f => fieldDoc(f, warnings, logical || (isGoogleVersion && !!entity.abstract), isGoogleVersion))),
|
|
452
|
-
custom_extensions: customExtensionsDoc(entity.customExtensions, `entity '${entity.name}'`, isGoogleVersion,
|
|
453
|
-
/* isModel= */ false, warnings, /* isField= */ false, blockPayload),
|
|
448
|
+
custom_extensions: customExtensionsDoc(entity.customExtensions, `entity '${entity.name}'`, isGoogleVersion, warnings, /* isField= */ false, blockPayload),
|
|
454
449
|
});
|
|
455
450
|
}
|
|
456
451
|
// Filters out the IR's `'Opaque'` placeholder so untyped fields and metrics
|
|
@@ -482,8 +477,7 @@ function fieldDoc(field, warnings, logical, isGoogleVersion) {
|
|
|
482
477
|
dimension: dimensionDoc(field),
|
|
483
478
|
description: field.description,
|
|
484
479
|
ai_context: aiContextDoc(field.aiContext, isGoogleVersion),
|
|
485
|
-
custom_extensions: customExtensionsDoc(field.customExtensions, `field '${field.name}'`, isGoogleVersion,
|
|
486
|
-
/* isModel= */ false, warnings, /* isField= */ true),
|
|
480
|
+
custom_extensions: customExtensionsDoc(field.customExtensions, `field '${field.name}'`, isGoogleVersion, warnings, /* isField= */ true),
|
|
487
481
|
});
|
|
488
482
|
}
|
|
489
483
|
function metricDoc(metric, warnings, version) {
|
|
@@ -508,8 +502,7 @@ function metricDoc(metric, warnings, version) {
|
|
|
508
502
|
datatype: datatypeDoc(metric.type),
|
|
509
503
|
description: metric.description,
|
|
510
504
|
ai_context: aiContextDoc(metric.aiContext, isGoogleVersion),
|
|
511
|
-
custom_extensions: customExtensionsDoc(metric.customExtensions, `metric '${metric.name}'`, isGoogleVersion,
|
|
512
|
-
/* isModel= */ false, warnings, /* isField= */ false, blockPayload),
|
|
505
|
+
custom_extensions: customExtensionsDoc(metric.customExtensions, `metric '${metric.name}'`, isGoogleVersion, warnings, /* isField= */ false, blockPayload),
|
|
513
506
|
});
|
|
514
507
|
}
|
|
515
508
|
// The `Concept.field` pairs this document will still resolve on reload. A
|
|
@@ -697,8 +690,7 @@ function relationshipDoc(rel, warnings, version) {
|
|
|
697
690
|
description: placement.relationship.description === 'native' ? description :
|
|
698
691
|
undefined,
|
|
699
692
|
ai_context: aiContextDoc(rel.aiContext, isGoogleVersion),
|
|
700
|
-
custom_extensions: customExtensionsDoc(rel.customExtensions, `relationship '${rel.name}'`, isGoogleVersion,
|
|
701
|
-
/* isModel= */ false, warnings, /* isField= */ false, blockPayload),
|
|
693
|
+
custom_extensions: customExtensionsDoc(rel.customExtensions, `relationship '${rel.name}'`, isGoogleVersion, warnings, /* isField= */ false, blockPayload),
|
|
702
694
|
});
|
|
703
695
|
}
|
|
704
696
|
// Renders a field or metric `expression` for the target version. The IR comes
|
|
@@ -796,24 +788,14 @@ function aiContextDoc(ai, isGoogleVersion) {
|
|
|
796
788
|
}
|
|
797
789
|
return isGoogleVersion ? { ...base, custom } : { ...custom, ...base };
|
|
798
790
|
}
|
|
799
|
-
// The deployments a vanilla model lists only as targets in its GOOGLE blocks,
|
|
800
|
-
// named as a pull names them. A block whose data is not valid JSON gives none,
|
|
801
|
-
// and customExtensionsDoc warns that it was dropped. The other blocks still
|
|
802
|
-
// give theirs.
|
|
803
|
-
function deploymentsFromGoogleExtensions(model) {
|
|
804
|
-
const targets = (model.customExtensions ??
|
|
805
|
-
[]).flatMap(ext => blockTargets(ext, model.name));
|
|
806
|
-
return (0, deployment_target_1.unnamedDeployments)(targets);
|
|
807
|
-
}
|
|
808
791
|
// Serializes `custom_extensions` for one IR element. Under OSSIE_VERSION,
|
|
809
792
|
// third-party extensions are emitted, followed by a synthesized GOOGLE block
|
|
810
|
-
// when `blockPayload` carries any keys. Any
|
|
793
|
+
// when `blockPayload` carries any keys. Any raw GOOGLE block on the IR
|
|
811
794
|
// is dropped with a warning so it cannot collide with the synthesized block or
|
|
812
795
|
// write keys the loader rejects. Under GOOGLE_VERSION,
|
|
813
796
|
// third-party extensions are written out unchanged without warning, while
|
|
814
|
-
// `GOOGLE` blocks are skipped
|
|
815
|
-
|
|
816
|
-
function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, isField = false, blockPayload) {
|
|
797
|
+
// `GOOGLE` blocks are skipped with a warning.
|
|
798
|
+
function customExtensionsDoc(exts, where, isGoogleVersion, warnings, isField = false, blockPayload) {
|
|
817
799
|
const payload = blockPayload ? { ...blockPayload } : {};
|
|
818
800
|
if (!exts?.length && Object.keys(payload).length === 0)
|
|
819
801
|
return undefined;
|
|
@@ -825,12 +807,6 @@ function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, is
|
|
|
825
807
|
kept.push({ vendor_name: e.vendorName, data: e.data });
|
|
826
808
|
continue;
|
|
827
809
|
}
|
|
828
|
-
// The loader's copy of the model's `deployments`, which the GOOGLE
|
|
829
|
-
// block below writes in their own form.
|
|
830
|
-
if (isModel && payload.deployments !== undefined &&
|
|
831
|
-
isDeploymentTargetsBlock(e)) {
|
|
832
|
-
continue;
|
|
833
|
-
}
|
|
834
810
|
droppedGoogle += 1;
|
|
835
811
|
}
|
|
836
812
|
if (droppedGoogle > 0) {
|
|
@@ -859,8 +835,6 @@ function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, is
|
|
|
859
835
|
kept.push({ vendor_name: ext.vendorName, data: ext.data });
|
|
860
836
|
continue;
|
|
861
837
|
}
|
|
862
|
-
if (isModel && blockTargets(ext, '').length > 0)
|
|
863
|
-
continue;
|
|
864
838
|
droppedGoogle += 1;
|
|
865
839
|
}
|
|
866
840
|
if (droppedGoogle > 0) {
|
|
@@ -869,30 +843,6 @@ function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, is
|
|
|
869
843
|
}
|
|
870
844
|
return nonEmpty(kept);
|
|
871
845
|
}
|
|
872
|
-
/**
|
|
873
|
-
* Returns true if `e.data` is a JSON object with a `'deploymentTargets'`
|
|
874
|
-
* property.
|
|
875
|
-
*
|
|
876
|
-
* The loader folds a Google-flavor model's `deployments` into such a block.
|
|
877
|
-
*/
|
|
878
|
-
function isDeploymentTargetsBlock(e) {
|
|
879
|
-
try {
|
|
880
|
-
const d = JSON.parse(e.data);
|
|
881
|
-
return !!d && typeof d === 'object' &&
|
|
882
|
-
Object.hasOwn(d, 'deploymentTargets');
|
|
883
|
-
}
|
|
884
|
-
catch {
|
|
885
|
-
return false;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
function blockTargets(ext, modelName) {
|
|
889
|
-
try {
|
|
890
|
-
return (0, deployment_target_1.googleBlockTargets)([ext], modelName);
|
|
891
|
-
}
|
|
892
|
-
catch {
|
|
893
|
-
return [];
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
846
|
// Drops undefined-valued keys so the emitted YAML only shows fields the model
|
|
897
847
|
// actually set (matching the emitters' `compact` convention).
|
|
898
848
|
function compact(obj) {
|
|
@@ -23,17 +23,15 @@ export interface SemanticRuntime {
|
|
|
23
23
|
* profile's several targets may leave the store ambiguous, the model may
|
|
24
24
|
* bind a table outside its Spanner or AlloyDB store, or finding how to
|
|
25
25
|
* connect may fail, such as the lookup of an AlloyDB cluster's primary
|
|
26
|
-
* instance.
|
|
27
|
-
* targets cannot be read. `storeError` says why.
|
|
26
|
+
* instance. `storeError` says why.
|
|
28
27
|
*/
|
|
29
28
|
storeFailed?: boolean;
|
|
30
29
|
/**
|
|
31
30
|
* Set with `storeFailed` when the model's deployments give no store
|
|
32
31
|
* whatever the model is used for. One such case is a target in
|
|
33
|
-
* no accepted form in any deployment. Another is
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* not such cases.
|
|
32
|
+
* no accepted form in any deployment. Another is several targets for the
|
|
33
|
+
* profile with no single store. A table bound outside the store and a failed
|
|
34
|
+
* AlloyDB lookup are not such cases.
|
|
37
35
|
*/
|
|
38
36
|
deploymentsRejected?: boolean;
|
|
39
37
|
/** Which binding profile produced this. Provenance, for messages. */
|
|
@@ -50,13 +50,13 @@ export type ChosenStore = SpannerStore | AlloyDbDatabase | BigQueryStore;
|
|
|
50
50
|
* `named` is set when the model's deployments name a target and still no
|
|
51
51
|
* store results, so a caller that runs against the store fails. `rejected` is
|
|
52
52
|
* set with it when the deployments give no store whatever the model is used
|
|
53
|
-
* for: a target in no accepted form in any deployment,
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* for: a target in no accepted form in any deployment, or several targets for
|
|
54
|
+
* the profile. `named` alone means the model binds a table outside its Spanner
|
|
55
|
+
* or AlloyDB store.
|
|
56
56
|
*
|
|
57
57
|
* Neither is set when the profile names no store, unless a target is in no
|
|
58
|
-
* accepted form
|
|
59
|
-
*
|
|
58
|
+
* accepted form. Neither is set either when a BigQuery model binds a table
|
|
59
|
+
* outside the graph's dataset.
|
|
60
60
|
*/
|
|
61
61
|
export interface StoreError {
|
|
62
62
|
error: string;
|
|
@@ -67,21 +67,18 @@ export interface StoreError {
|
|
|
67
67
|
* The store a model runs against under binding profile `profile`, which
|
|
68
68
|
* defaults to `default`, the model file's own bindings. It is the one the
|
|
69
69
|
* deployments for that profile name: those that name it in `profile`, or that
|
|
70
|
-
* name none or `default` for `default`.
|
|
71
|
-
*
|
|
72
|
-
* With several targets, the store is the single operational one, Spanner or
|
|
73
|
-
* AlloyDB.
|
|
70
|
+
* name none or `default` for `default`. With several targets, the store is the
|
|
71
|
+
* single operational one, Spanner or AlloyDB.
|
|
74
72
|
*
|
|
75
|
-
* Fails
|
|
73
|
+
* Fails five ways:
|
|
76
74
|
*
|
|
77
|
-
* 1. A
|
|
78
|
-
* 2. A target names no supported destination. Push checks the target of every
|
|
75
|
+
* 1. A target names no supported destination. Push checks the target of every
|
|
79
76
|
* deployment, so this fails the store under every profile.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
77
|
+
* 2. No deployment uses the profile.
|
|
78
|
+
* 3. There are several targets and no single operational one, so which store
|
|
82
79
|
* is meant is ambiguous.
|
|
83
|
-
*
|
|
84
|
-
*
|
|
80
|
+
* 4. There is no target, so there is nothing to run against.
|
|
81
|
+
* 5. The model binds a table outside the store. For a Spanner or AlloyDB
|
|
85
82
|
* store, a write would land in the wrong place, and the error is named. A
|
|
86
83
|
* BigQuery graph may read tables in any dataset, so
|
|
87
84
|
* for a BigQuery store the error is not named.
|
|
@@ -89,9 +86,7 @@ export interface StoreError {
|
|
|
89
86
|
export declare function resolveStore(model: SemanticModel, profile?: string): ChosenStore | StoreError;
|
|
90
87
|
/**
|
|
91
88
|
* The targets binding profile `profile` uses, as resolveStore reads them: its
|
|
92
|
-
* deployments' targets
|
|
93
|
-
* the model has no `deployments` list. Empty when no deployment is for the
|
|
94
|
-
* profile. Throws when a GOOGLE block's data is not valid JSON.
|
|
89
|
+
* deployments' targets. Empty when no deployment is for the profile.
|
|
95
90
|
*/
|
|
96
91
|
export declare function profileTargets(model: SemanticModel, profile: string): string[];
|
|
97
92
|
/**
|
|
@@ -101,21 +101,18 @@ function strayBindings(model, store) {
|
|
|
101
101
|
* The store a model runs against under binding profile `profile`, which
|
|
102
102
|
* defaults to `default`, the model file's own bindings. It is the one the
|
|
103
103
|
* deployments for that profile name: those that name it in `profile`, or that
|
|
104
|
-
* name none or `default` for `default`.
|
|
105
|
-
*
|
|
106
|
-
* With several targets, the store is the single operational one, Spanner or
|
|
107
|
-
* AlloyDB.
|
|
104
|
+
* name none or `default` for `default`. With several targets, the store is the
|
|
105
|
+
* single operational one, Spanner or AlloyDB.
|
|
108
106
|
*
|
|
109
|
-
* Fails
|
|
107
|
+
* Fails five ways:
|
|
110
108
|
*
|
|
111
|
-
* 1. A
|
|
112
|
-
* 2. A target names no supported destination. Push checks the target of every
|
|
109
|
+
* 1. A target names no supported destination. Push checks the target of every
|
|
113
110
|
* deployment, so this fails the store under every profile.
|
|
114
|
-
*
|
|
115
|
-
*
|
|
111
|
+
* 2. No deployment uses the profile.
|
|
112
|
+
* 3. There are several targets and no single operational one, so which store
|
|
116
113
|
* is meant is ambiguous.
|
|
117
|
-
*
|
|
118
|
-
*
|
|
114
|
+
* 4. There is no target, so there is nothing to run against.
|
|
115
|
+
* 5. The model binds a table outside the store. For a Spanner or AlloyDB
|
|
119
116
|
* store, a write would land in the wrong place, and the error is named. A
|
|
120
117
|
* BigQuery graph may read tables in any dataset, so
|
|
121
118
|
* for a BigQuery store the error is not named.
|
|
@@ -123,15 +120,7 @@ function strayBindings(model, store) {
|
|
|
123
120
|
function resolveStore(model, profile = resolve_profiles_1.DEFAULT_PROFILE) {
|
|
124
121
|
// Push checks the target of every deployment the model declares, so the
|
|
125
122
|
// targets of every deployment are read before the profile's are picked.
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
all = (0, deployment_target_1.googleDeploymentTargets)(model);
|
|
129
|
-
}
|
|
130
|
-
catch (err) {
|
|
131
|
-
// It throws when a GOOGLE block's data is not valid JSON. Push reports
|
|
132
|
-
// that as an error, so the store fails.
|
|
133
|
-
return { error: `${err?.message || err}`, named: true, rejected: true };
|
|
134
|
-
}
|
|
123
|
+
const all = (0, deployment_target_1.googleDeploymentTargets)(model);
|
|
135
124
|
// The loader reads a vanilla model's `deployments` out of its GOOGLE block,
|
|
136
125
|
// so a message names the block's `deployments`, as push does.
|
|
137
126
|
const vanilla = model.version === loader_1.OSSIE_VERSION;
|
|
@@ -141,17 +130,19 @@ function resolveStore(model, profile = resolve_profiles_1.DEFAULT_PROFILE) {
|
|
|
141
130
|
// A target in no accepted form is reported, alone or beside one that gives
|
|
142
131
|
// a store, whichever profile its deployment is for.
|
|
143
132
|
if (all.malformed.length) {
|
|
144
|
-
const unparsed = model.deployments
|
|
145
|
-
|
|
146
|
-
(0, deployment_target_1.
|
|
147
|
-
|
|
133
|
+
const unparsed = model.deployments.find(d => d.target === all.malformed[0]);
|
|
134
|
+
return {
|
|
135
|
+
error: (0, deployment_target_1.deploymentTargetError)(model, unparsed),
|
|
136
|
+
named: true,
|
|
137
|
+
rejected: true,
|
|
138
|
+
};
|
|
148
139
|
}
|
|
149
140
|
const used = deploymentsFor(model, profile);
|
|
150
141
|
if ('error' in used)
|
|
151
142
|
return used;
|
|
152
143
|
const scoped = used.model;
|
|
153
|
-
// The profile's targets are among those read above
|
|
154
|
-
//
|
|
144
|
+
// The profile's targets are among those read above, so none of them is in no
|
|
145
|
+
// accepted form.
|
|
155
146
|
const targets = (0, deployment_target_1.googleDeploymentTargets)(scoped);
|
|
156
147
|
const { spanner, alloyDb, bigQuery } = targets;
|
|
157
148
|
const declared = spanner.length + alloyDb.length + bigQuery.length;
|
|
@@ -202,9 +193,7 @@ function resolveStore(model, profile = resolve_profiles_1.DEFAULT_PROFILE) {
|
|
|
202
193
|
return store;
|
|
203
194
|
}
|
|
204
195
|
// The model with only the deployments `profile` uses, or why there are none.
|
|
205
|
-
// A model with no `deployments` list
|
|
206
|
-
// for `default`. An empty list names no deployment. Either way, any other
|
|
207
|
-
// profile has none.
|
|
196
|
+
// A model with no or empty `deployments` list names no deployment.
|
|
208
197
|
function deploymentsFor(model, profile) {
|
|
209
198
|
const nothingToRun = `so there is no database to run it against`;
|
|
210
199
|
const namedProfile = `binding profile '${profile}' is a named profile, ` +
|
|
@@ -235,21 +224,16 @@ function deploymentsFor(model, profile) {
|
|
|
235
224
|
}
|
|
236
225
|
/**
|
|
237
226
|
* The targets binding profile `profile` uses, as resolveStore reads them: its
|
|
238
|
-
* deployments' targets
|
|
239
|
-
* the model has no `deployments` list. Empty when no deployment is for the
|
|
240
|
-
* profile. Throws when a GOOGLE block's data is not valid JSON.
|
|
227
|
+
* deployments' targets. Empty when no deployment is for the profile.
|
|
241
228
|
*/
|
|
242
229
|
function profileTargets(model, profile) {
|
|
243
230
|
const used = deploymentsFor(model, profile);
|
|
244
231
|
return 'error' in used ? [] : (0, deployment_target_1.googleDeploymentTargets)(used.model).uris;
|
|
245
232
|
}
|
|
246
|
-
// The deployments whose targets are `uris`, for a message
|
|
247
|
-
// the targets come from a GOOGLE block, which names none. `profile` is the
|
|
233
|
+
// The deployments whose targets are `uris`, for a message. `profile` is the
|
|
248
234
|
// profile they are for.
|
|
249
235
|
function deploymentNames(model, uris, profile) {
|
|
250
|
-
const deployments = model.deployments;
|
|
251
|
-
if (!deployments)
|
|
252
|
-
return '';
|
|
236
|
+
const deployments = model.deployments ?? [];
|
|
253
237
|
const names = uris.map(uri => deployments.find(d => d.target === uri)?.name ?? uri);
|
|
254
238
|
return ` for profile '${profile}' (${names.join(', ')})`;
|
|
255
239
|
}
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
// empty array means valid); push prints them and aborts, and `kcmd import`
|
|
8
8
|
// reports them. Kept separate from the loader -- which validates a document
|
|
9
9
|
// against the schema -- because these are deployment requirements, not schema
|
|
10
|
-
// rules
|
|
11
|
-
// owns.
|
|
10
|
+
// rules.
|
|
12
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
12
|
exports.validatePushRequirements = validatePushRequirements;
|
|
14
13
|
exports.validateDeployments = validateDeployments;
|
|
@@ -60,18 +59,7 @@ function validatePushRequirements(models, opts = {}) {
|
|
|
60
59
|
const errors = [];
|
|
61
60
|
for (const { document, model } of models) {
|
|
62
61
|
errors.push(...nameAndDescriptionErrors(model, document, !!opts.v2Aspects));
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
// One pass over the model's GOOGLE extension(s): both checks below read
|
|
66
|
-
// the same parse rather than re-parsing the JSON per reader.
|
|
67
|
-
deployInfo = (0, deploy_bigquery_1.googleDeploymentTargets)(model);
|
|
68
|
-
}
|
|
69
|
-
catch (err) {
|
|
70
|
-
// Malformed GOOGLE extension JSON: surface it as a validation error here
|
|
71
|
-
// rather than letting it throw out of a later leg as an uncaught stack.
|
|
72
|
-
errors.push(`${err.message || err} (${document})`);
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
62
|
+
const deployInfo = (0, deploy_bigquery_1.googleDeploymentTargets)(model);
|
|
75
63
|
// A model that targets a graph (BigQuery OR Spanner) must have every
|
|
76
64
|
// relationship's join columns bound. The loader accepts a column-less
|
|
77
65
|
// relationship so a purely logical model (an OWL import) loads and pushes
|
|
@@ -2089,16 +2077,8 @@ async function validateBigQueryDataSources(models, bq, defaultProject, graph) {
|
|
|
2089
2077
|
// bills to: the project of the model's first BigQuery Graph deployment target
|
|
2090
2078
|
// (where the CREATE PROPERTY GRAPH runs), falling back to the scope's default
|
|
2091
2079
|
// project when the model declares no parseable BigQuery Graph target.
|
|
2092
|
-
// googleDeploymentTargets is safe here: validatePushRequirements ran first and
|
|
2093
|
-
// already rejected a malformed GOOGLE extension.
|
|
2094
2080
|
function billingProject(model, defaultProject) {
|
|
2095
|
-
|
|
2096
|
-
return (0, deploy_bigquery_1.googleDeploymentTargets)(model).bigQuery[0]?.project ??
|
|
2097
|
-
defaultProject;
|
|
2098
|
-
}
|
|
2099
|
-
catch {
|
|
2100
|
-
return defaultProject;
|
|
2101
|
-
}
|
|
2081
|
+
return (0, deploy_bigquery_1.googleDeploymentTargets)(model).bigQuery[0]?.project ?? defaultProject;
|
|
2102
2082
|
}
|
|
2103
2083
|
// A source that can be probed as a BigQuery table: the canonical `dataSource`,
|
|
2104
2084
|
// trimmed, or null when it is empty or a non-BigQuery resource URI. Unlike a
|
|
@@ -2181,13 +2161,7 @@ async function validateSpannerActionStatements(models, spanner) {
|
|
|
2181
2161
|
// The database an action writes to is the one this profile deploys its
|
|
2182
2162
|
// graph to: on Spanner the published graph and the operational rows live in
|
|
2183
2163
|
// the same database. A model with no Spanner target contributes nothing.
|
|
2184
|
-
|
|
2185
|
-
try {
|
|
2186
|
-
target = (0, deployment_target_1.spannerGraphTargets)(loaded.model).targets[0];
|
|
2187
|
-
}
|
|
2188
|
-
catch {
|
|
2189
|
-
continue;
|
|
2190
|
-
}
|
|
2164
|
+
const target = (0, deployment_target_1.spannerGraphTargets)(loaded.model).targets[0];
|
|
2191
2165
|
if (!target)
|
|
2192
2166
|
continue;
|
|
2193
2167
|
const key = `${target.project}/${target.instance}/${target.database}`;
|