@google-cloud/kcmd 0.0.0-stage → 0.2.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.
Files changed (150) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +264 -2
  3. package/build/ts/kcmd/cli.js +20252 -0
  4. package/build/ts/kcmd/gcp/alloydb.d.ts +12 -0
  5. package/build/ts/kcmd/gcp/alloydb.js +70 -0
  6. package/build/ts/kcmd/gcp/api.d.ts +29 -0
  7. package/build/ts/kcmd/gcp/api.js +162 -0
  8. package/build/ts/kcmd/gcp/biglake.d.ts +18 -0
  9. package/build/ts/kcmd/gcp/biglake.js +59 -0
  10. package/build/ts/kcmd/gcp/bigquery.d.ts +57 -0
  11. package/build/ts/kcmd/gcp/bigquery.js +134 -0
  12. package/build/ts/kcmd/gcp/context.d.ts +10 -0
  13. package/build/ts/kcmd/gcp/context.js +74 -0
  14. package/build/ts/kcmd/gcp/crm.d.ts +13 -0
  15. package/build/ts/kcmd/gcp/crm.js +91 -0
  16. package/build/ts/kcmd/gcp/dataplex.d.ts +147 -0
  17. package/build/ts/kcmd/gcp/dataplex.js +406 -0
  18. package/build/ts/kcmd/gcp/index.d.ts +3 -0
  19. package/build/ts/kcmd/gcp/index.js +21 -0
  20. package/build/ts/kcmd/gcp/spanner.d.ts +73 -0
  21. package/build/ts/kcmd/gcp/spanner.js +126 -0
  22. package/build/ts/kcmd/index.d.ts +7 -0
  23. package/build/ts/kcmd/index.js +46 -0
  24. package/build/ts/kcmd/layout.d.ts +16 -0
  25. package/build/ts/kcmd/layout.js +31 -0
  26. package/build/ts/kcmd/layouts/documents.d.ts +24 -0
  27. package/build/ts/kcmd/layouts/documents.js +264 -0
  28. package/build/ts/kcmd/layouts/okf.d.ts +31 -0
  29. package/build/ts/kcmd/layouts/okf.js +386 -0
  30. package/build/ts/kcmd/layouts/semantic-model.d.ts +74 -0
  31. package/build/ts/kcmd/layouts/semantic-model.js +621 -0
  32. package/build/ts/kcmd/layouts/standard.d.ts +13 -0
  33. package/build/ts/kcmd/layouts/standard.js +108 -0
  34. package/build/ts/kcmd/manifest.d.ts +34 -0
  35. package/build/ts/kcmd/manifest.js +262 -0
  36. package/build/ts/kcmd/metadata.d.ts +25 -0
  37. package/build/ts/kcmd/metadata.js +8 -0
  38. package/build/ts/kcmd/okf/catalog_residue.d.ts +14 -0
  39. package/build/ts/kcmd/okf/catalog_residue.js +124 -0
  40. package/build/ts/kcmd/okf/concept.d.ts +14 -0
  41. package/build/ts/kcmd/okf/concept.js +110 -0
  42. package/build/ts/kcmd/okf/concept_to_entry.d.ts +10 -0
  43. package/build/ts/kcmd/okf/concept_to_entry.js +281 -0
  44. package/build/ts/kcmd/okf/init_okf_workspace.d.ts +15 -0
  45. package/build/ts/kcmd/okf/init_okf_workspace.js +113 -0
  46. package/build/ts/kcmd/okf/links.d.ts +23 -0
  47. package/build/ts/kcmd/okf/links.js +468 -0
  48. package/build/ts/kcmd/okf/okf_metadata.d.ts +12 -0
  49. package/build/ts/kcmd/okf/okf_metadata.js +99 -0
  50. package/build/ts/kcmd/semantic/bigquery.d.ts +22 -0
  51. package/build/ts/kcmd/semantic/bigquery.js +469 -0
  52. package/build/ts/kcmd/semantic/binding.d.ts +3 -0
  53. package/build/ts/kcmd/semantic/binding.js +92 -0
  54. package/build/ts/kcmd/semantic/catalog_entry.d.ts +24 -0
  55. package/build/ts/kcmd/semantic/catalog_entry.js +35 -0
  56. package/build/ts/kcmd/semantic/converters/owl/convert.d.ts +26 -0
  57. package/build/ts/kcmd/semantic/converters/owl/convert.js +41 -0
  58. package/build/ts/kcmd/semantic/converters/owl/model.d.ts +98 -0
  59. package/build/ts/kcmd/semantic/converters/owl/model.js +30 -0
  60. package/build/ts/kcmd/semantic/converters/owl/parse.d.ts +11 -0
  61. package/build/ts/kcmd/semantic/converters/owl/parse.js +634 -0
  62. package/build/ts/kcmd/semantic/converters/owl/to_ir.d.ts +19 -0
  63. package/build/ts/kcmd/semantic/converters/owl/to_ir.js +390 -0
  64. package/build/ts/kcmd/semantic/deploy_bigquery.d.ts +20 -0
  65. package/build/ts/kcmd/semantic/deploy_bigquery.js +226 -0
  66. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.d.ts +104 -0
  67. package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +1367 -0
  68. package/build/ts/kcmd/semantic/deploy_spanner.d.ts +15 -0
  69. package/build/ts/kcmd/semantic/deploy_spanner.js +150 -0
  70. package/build/ts/kcmd/semantic/deployment_target.d.ts +93 -0
  71. package/build/ts/kcmd/semantic/deployment_target.js +379 -0
  72. package/build/ts/kcmd/semantic/graph_ddl.d.ts +44 -0
  73. package/build/ts/kcmd/semantic/graph_ddl.js +656 -0
  74. package/build/ts/kcmd/semantic/import_model.d.ts +72 -0
  75. package/build/ts/kcmd/semantic/import_model.js +1698 -0
  76. package/build/ts/kcmd/semantic/ir.d.ts +589 -0
  77. package/build/ts/kcmd/semantic/ir.js +247 -0
  78. package/build/ts/kcmd/semantic/kc_actions.d.ts +45 -0
  79. package/build/ts/kcmd/semantic/kc_actions.js +531 -0
  80. package/build/ts/kcmd/semantic/kc_constraints.d.ts +44 -0
  81. package/build/ts/kcmd/semantic/kc_constraints.js +279 -0
  82. package/build/ts/kcmd/semantic/kc_converter.d.ts +37 -0
  83. package/build/ts/kcmd/semantic/kc_converter.js +839 -0
  84. package/build/ts/kcmd/semantic/kc_custom_types.d.ts +63 -0
  85. package/build/ts/kcmd/semantic/kc_custom_types.js +751 -0
  86. package/build/ts/kcmd/semantic/kc_entries.d.ts +155 -0
  87. package/build/ts/kcmd/semantic/kc_entries.js +284 -0
  88. package/build/ts/kcmd/semantic/kc_ids.d.ts +102 -0
  89. package/build/ts/kcmd/semantic/kc_ids.js +188 -0
  90. package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +29 -0
  91. package/build/ts/kcmd/semantic/knowledge_catalog.js +960 -0
  92. package/build/ts/kcmd/semantic/loader.d.ts +258 -0
  93. package/build/ts/kcmd/semantic/loader.js +3576 -0
  94. package/build/ts/kcmd/semantic/names.d.ts +10 -0
  95. package/build/ts/kcmd/semantic/names.js +85 -0
  96. package/build/ts/kcmd/semantic/osi_converter.d.ts +60 -0
  97. package/build/ts/kcmd/semantic/osi_converter.js +910 -0
  98. package/build/ts/kcmd/semantic/parameters.d.ts +19 -0
  99. package/build/ts/kcmd/semantic/parameters.js +171 -0
  100. package/build/ts/kcmd/semantic/pull_kc.d.ts +10 -0
  101. package/build/ts/kcmd/semantic/pull_kc.js +272 -0
  102. package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +59 -0
  103. package/build/ts/kcmd/semantic/resolve_inheritance.js +346 -0
  104. package/build/ts/kcmd/semantic/resolve_profiles.d.ts +291 -0
  105. package/build/ts/kcmd/semantic/resolve_profiles.js +2003 -0
  106. package/build/ts/kcmd/semantic/runtime/agent_tools.d.ts +93 -0
  107. package/build/ts/kcmd/semantic/runtime/agent_tools.js +243 -0
  108. package/build/ts/kcmd/semantic/runtime/dialect.d.ts +13 -0
  109. package/build/ts/kcmd/semantic/runtime/dialect.js +39 -0
  110. package/build/ts/kcmd/semantic/runtime/runtime.d.ts +88 -0
  111. package/build/ts/kcmd/semantic/runtime/runtime.js +301 -0
  112. package/build/ts/kcmd/semantic/runtime/store.d.ts +112 -0
  113. package/build/ts/kcmd/semantic/runtime/store.js +344 -0
  114. package/build/ts/kcmd/semantic/skills.d.ts +110 -0
  115. package/build/ts/kcmd/semantic/skills.js +697 -0
  116. package/build/ts/kcmd/semantic/source_dialect.d.ts +17 -0
  117. package/build/ts/kcmd/semantic/source_dialect.js +311 -0
  118. package/build/ts/kcmd/semantic/source_entries.d.ts +12 -0
  119. package/build/ts/kcmd/semantic/source_entries.js +341 -0
  120. package/build/ts/kcmd/semantic/spanner.d.ts +18 -0
  121. package/build/ts/kcmd/semantic/spanner.js +60 -0
  122. package/build/ts/kcmd/semantic/sql_expr_utils.d.ts +23 -0
  123. package/build/ts/kcmd/semantic/sql_expr_utils.js +394 -0
  124. package/build/ts/kcmd/semantic/sql_identifiers.d.ts +6 -0
  125. package/build/ts/kcmd/semantic/sql_identifiers.js +267 -0
  126. package/build/ts/kcmd/semantic/sql_parser.d.ts +47 -0
  127. package/build/ts/kcmd/semantic/sql_parser.js +400 -0
  128. package/build/ts/kcmd/semantic/transpile.d.ts +76 -0
  129. package/build/ts/kcmd/semantic/transpile.js +581 -0
  130. package/build/ts/kcmd/semantic/validate.d.ts +70 -0
  131. package/build/ts/kcmd/semantic/validate.js +2266 -0
  132. package/build/ts/kcmd/semantic/yaml_options.d.ts +6 -0
  133. package/build/ts/kcmd/semantic/yaml_options.js +58 -0
  134. package/build/ts/kcmd/semantic/yaml_update.d.ts +26 -0
  135. package/build/ts/kcmd/semantic/yaml_update.js +782 -0
  136. package/build/ts/kcmd/snapshot.d.ts +37 -0
  137. package/build/ts/kcmd/snapshot.js +386 -0
  138. package/build/ts/kcmd/source.d.ts +31 -0
  139. package/build/ts/kcmd/source.js +119 -0
  140. package/build/ts/kcmd/sources/bq-dataset.d.ts +17 -0
  141. package/build/ts/kcmd/sources/bq-dataset.js +131 -0
  142. package/build/ts/kcmd/sources/entrygroup.d.ts +21 -0
  143. package/build/ts/kcmd/sources/entrygroup.js +83 -0
  144. package/build/ts/kcmd/sources/kb.d.ts +21 -0
  145. package/build/ts/kcmd/sources/kb.js +82 -0
  146. package/build/ts/kcmd/sources/semantic-model.d.ts +18 -0
  147. package/build/ts/kcmd/sources/semantic-model.js +48 -0
  148. package/build/ts/kcmd/sync.d.ts +35 -0
  149. package/build/ts/kcmd/sync.js +734 -0
  150. package/package.json +62 -4
@@ -0,0 +1,301 @@
1
+ "use strict";
2
+ // Creating a semantic runtime: the model as authored, merged with one binding
3
+ // profile, paired with the store the deployment that uses that profile names.
4
+ //
5
+ // A semantic model on disk is a declaration. A runtime is that declaration
6
+ // made operational -- bindings applied, inheritance resolved, a store to run
7
+ // against. Every caller that does anything with a model wants the pair, so the
8
+ // pair is what this hands back, rather than a model the caller then has to
9
+ // find a store for.
10
+ //
11
+ // Both halves were the CLI's private business until an agent needed them. An
12
+ // agent skill derives its tools from the same bound model `kcmd skills-generate`
13
+ // reads, against the same store, so it has to reach them the same way -- and a
14
+ // second loader that merges profiles slightly differently is a demo that passes
15
+ // while the product fails. The CLI calls this too, so there is one answer to
16
+ // "what does this scope say" rather than one per caller.
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.runtimeStoreError = runtimeStoreError;
52
+ exports.createSemanticRuntimes = createSemanticRuntimes;
53
+ const alloydb_1 = require("../../gcp/alloydb");
54
+ const context = __importStar(require("../../gcp/context"));
55
+ const dataplex_1 = require("../../gcp/dataplex");
56
+ const semantic_model_1 = require("../../layouts/semantic-model");
57
+ const snapshot_1 = require("../../snapshot");
58
+ const source_1 = require("../../source");
59
+ const loader_1 = require("../loader");
60
+ const resolve_inheritance_1 = require("../resolve_inheritance");
61
+ const resolve_profiles_1 = require("../resolve_profiles");
62
+ const source_dialect_1 = require("../source_dialect");
63
+ const sql_parser_1 = require("../sql_parser");
64
+ const store_1 = require("./store");
65
+ /**
66
+ * Why this runtime cannot execute SQL DML statements, or null when it can.
67
+ *
68
+ * Having a store is the whole requirement. Every backend a profile can deploy
69
+ * to -- Spanner, AlloyDB, BigQuery -- executes DML, so the kind of store is not
70
+ * a reason to refuse an action.
71
+ */
72
+ function runtimeStoreError(runtime) {
73
+ if (!runtime.store) {
74
+ return runtime.storeError ??
75
+ `Model '${runtime.model.name}' has no store under profile '${runtime.profile}'.`;
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * One runtime per model document in the scope, in discovery order.
81
+ *
82
+ * A lighter path than push's: nothing is pruned or validated for
83
+ * deployment, because running an action needs the model as authored rather
84
+ * than the subset a deployed graph can answer. Bindings stay optional, so a
85
+ * purely logical model yields a runtime with no store -- readable and
86
+ * inspectable, not runnable -- instead of an error that stops the scope.
87
+ */
88
+ async function createSemanticRuntimes(options = {}) {
89
+ const base = options.path ?? '.';
90
+ const ctx = options.ctx ?? context.ApiContext.default();
91
+ const warn = options.onWarning ?? ((text) => console.warn(text));
92
+ let snapshot;
93
+ try {
94
+ snapshot = options.snapshot ?? await snapshot_1.CatalogSnapshot.fromPath(base, ctx);
95
+ }
96
+ catch (err) {
97
+ return { error: `${err?.message ?? err}` };
98
+ }
99
+ if (snapshot.manifest.source.type !== source_1.Sources.SEMANTIC_MODEL) {
100
+ return { error: `'${base}' is not a semantic-model scope.` };
101
+ }
102
+ const layout = snapshot.layout;
103
+ const source = snapshot.manifest.source;
104
+ const profile = options.profile ?? resolve_profiles_1.DEFAULT_PROFILE;
105
+ const docs = layout.modelDocuments();
106
+ if (!docs.length)
107
+ return { error: 'no semantic model documents found.' };
108
+ // Reading a GOOGLE block profile checks its field bindings for SQL, which
109
+ // needs the SQL parser.
110
+ try {
111
+ await (0, sql_parser_1.loadSqlEngine)();
112
+ }
113
+ catch (err) {
114
+ return { error: `${err?.message ?? err}` };
115
+ }
116
+ // The runtime reads every model's profiles first, as `kcmd push` does: its
117
+ // profile files, then the profiles a vanilla model carries in its GOOGLE
118
+ // block. It refuses the scope when
119
+ // profileDocuments rejects a file, such as a `<model>.profile.<name>.yaml`
120
+ // file whose `name:` does not match its file name, or when a GOOGLE block
121
+ // profile does not load. It also refuses it when a model declares a profile
122
+ // named `default` in any letter case.
123
+ const profilesByDoc = new Map();
124
+ // Each model's profile files alone, which an error applying one names.
125
+ const filesByDoc = new Map();
126
+ for (const doc of docs) {
127
+ let files;
128
+ let available;
129
+ try {
130
+ files = layout.profileDocuments(doc.name);
131
+ available = (0, resolve_profiles_1.declaredProfiles)(doc, files);
132
+ }
133
+ catch (err) {
134
+ return { error: err?.message ?? String(err) };
135
+ }
136
+ const reserved = (0, resolve_profiles_1.reservedProfileNameError)(doc.name, available);
137
+ if (reserved)
138
+ return { error: reserved };
139
+ profilesByDoc.set(doc.name, available);
140
+ filesByDoc.set(doc.name, files);
141
+ }
142
+ // A Cloud SQL table written as its resource name takes its dialect from its
143
+ // Knowledge Catalog entry, looked up once for the scope before anything
144
+ // loads. A table whose entry cannot be read stops the scope, as push does.
145
+ // A third-party table is not looked up, since no runtime reads one, and a
146
+ // Spanner table is SPANNER, the dialect the runtime's Spanner store speaks.
147
+ const lookup = await (0, source_dialect_1.sourceDialects)([...docs.map(d => d.text),
148
+ ...[...filesByDoc.values()].flat().map(f => f.text)], new dataplex_1.CatalogClient(ctx), { thirdParty: false });
149
+ if (lookup.errors.length)
150
+ return { error: lookup.errors.join('\n') };
151
+ const load = { sourceDialects: lookup.dialects };
152
+ // The model files the inline bindings are used for, loaded together.
153
+ const inline = [];
154
+ // A profile applies to its model file loaded on its own, with its
155
+ // exclusions already marked.
156
+ const appliedByDoc = new Map();
157
+ // The warnings applying each profile gave.
158
+ const fileWarnings = new Map();
159
+ for (const doc of docs) {
160
+ if (profile === resolve_profiles_1.DEFAULT_PROFILE) {
161
+ inline.push({ name: doc.name, text: doc.text });
162
+ continue;
163
+ }
164
+ const available = profilesByDoc.get(doc.name) ?? [];
165
+ const res = (0, resolve_profiles_1.profileForDocument)(doc, available, profile, load);
166
+ if (res.kind === 'missing') {
167
+ return { error: (0, resolve_profiles_1.unknownProfileMessage)(profile, doc.name, res.declared) };
168
+ }
169
+ if (res.kind === 'error') {
170
+ const files = (filesByDoc.get(doc.name) ?? []).map(p => ({ ...p, fileName: (0, semantic_model_1.fileNameOf)(layout, doc.name, p.path) }));
171
+ return { error: (0, resolve_profiles_1.profileErrorMessage)(doc.name, res, profile, files) };
172
+ }
173
+ fileWarnings.set(doc.name, res.warnings);
174
+ appliedByDoc.set(doc.name, res.model);
175
+ }
176
+ const loaded = (0, loader_1.loadSemanticModels)(inline, { bindingOptional: true, ...load });
177
+ if (loaded.error)
178
+ return { error: loaded.error };
179
+ // In the order of the documents, as one load of them all gives. The load
180
+ // prefixes each warning with its document's name.
181
+ for (const doc of docs) {
182
+ const own = fileWarnings.get(doc.name) ??
183
+ loaded.warnings.filter(w => w.startsWith(`[${doc.name}] `));
184
+ for (const w of own)
185
+ warn(`Warning: ${w}`);
186
+ }
187
+ const loadedByDoc = new Map(loaded.models.map(m => [m.document, m.model]));
188
+ const models = docs.flatMap(doc => {
189
+ const model = appliedByDoc.get(doc.name) ?? loadedByDoc.get(doc.name);
190
+ return model ? [{ document: doc.name, model }] : [];
191
+ });
192
+ // A deployment that names a profile the model does not declare would be
193
+ // used by no profile. Push refuses it too.
194
+ for (const { document, model } of models) {
195
+ const declared = (profilesByDoc.get(document) ?? []).map(p => p.name);
196
+ const error = (0, resolve_profiles_1.undeclaredProfileError)(document, model, declared);
197
+ if (error)
198
+ return { error };
199
+ }
200
+ // Push then loads every profile file, whichever profile it deploys, and
201
+ // refuses one that does not load.
202
+ for (const doc of docs) {
203
+ for (const p of filesByDoc.get(doc.name) ?? []) {
204
+ try {
205
+ (0, resolve_profiles_1.loadProfileFile)(p.text, p.name, { skipActions: true });
206
+ }
207
+ catch (err) {
208
+ if (!(err instanceof loader_1.SemanticModelError))
209
+ throw err;
210
+ const file = (0, semantic_model_1.fileNameOf)(layout, doc.name, p.path);
211
+ return { error: `[${file}] ${err.message}` };
212
+ }
213
+ }
214
+ }
215
+ // kcmd has no engine that reads a third-party table.
216
+ for (const { document, model } of models) {
217
+ const bound = (model.entities ?? []).find(loader_1.bindsThirdPartyTable);
218
+ if (bound) {
219
+ return {
220
+ error: `[${document}] model '${model.name}' binds entity '${bound.name}' to third-party table '${bound.authoredSource}', and kcmd has no engine for a ` +
221
+ `third-party table.`,
222
+ };
223
+ }
224
+ }
225
+ if (options.oneModel && models.length > 1) {
226
+ return {
227
+ error: typeof options.oneModel === 'function' ?
228
+ options.oneModel(models.length) :
229
+ `this scope holds ${models.length} models, which may name ` +
230
+ `different databases, so there is no single store. Narrow ` +
231
+ `the scope to one model.`,
232
+ };
233
+ }
234
+ // Inheritance is resolved for the same reason both push legs resolve it: an
235
+ // inherited field is a field, and every reader downstream reads
236
+ // `entity.fields`. The runtime is the reader where skipping it is unsafe
237
+ // rather than merely incomplete. It would not see a subtype's inherited
238
+ // `name`, so resolving a reference by name would report a row missing that
239
+ // is there; and it would not see an inherited key's TYPE, so the check that
240
+ // refuses a generated UUID for an Integer key would read the key as a
241
+ // String, pass, and let the store take the mismatch instead.
242
+ const runtimes = [];
243
+ const alloyDb = new alloydb_1.AlloyDbClient(ctx);
244
+ for (const { document, model: authored } of models) {
245
+ // An excluded metric is not available under the profile.
246
+ const excludedMetrics = new Set(authored.excludedMetrics ?? []);
247
+ if (excludedMetrics.size) {
248
+ authored.metrics =
249
+ (authored.metrics ?? []).filter(m => !excludedMetrics.has(m.name));
250
+ }
251
+ // The store is the one the profile's deployments name. Finding it reads
252
+ // only the deployment targets and the tables the model binds, which
253
+ // inheritance does not change.
254
+ const chosen = (0, store_1.resolveStore)(authored, profile);
255
+ // Resolving throws on an `extends` that names an entity the model does
256
+ // not declare, on a cycle, on a field inherited from two unrelated
257
+ // ancestors, and on two fields of one entity whose names differ only in
258
+ // case. The loader lets each through, and push reports it as an
259
+ // error. An AlloyDB database runs an inherited text as PostgreSQL, where
260
+ // `#` starts no comment.
261
+ let resolved;
262
+ try {
263
+ resolved = (0, resolve_inheritance_1.resolveInheritance)(authored, !('error' in chosen) && chosen.kind === 'alloydb' ? 'POSTGRES' :
264
+ undefined);
265
+ }
266
+ catch (err) {
267
+ const message = `${err?.message ?? err}`;
268
+ return {
269
+ error: `[${document}] model '${authored.name}': ${message}${message.endsWith('.') ? '' : '.'}`,
270
+ };
271
+ }
272
+ for (const w of resolved.warnings)
273
+ warn(`Warning: ${w}`);
274
+ // An AlloyDB database is reached through its cluster's primary instance.
275
+ const runtime = {
276
+ model: resolved.model,
277
+ document,
278
+ profile,
279
+ entryGroup: source.entryGroup,
280
+ };
281
+ if ('error' in chosen) {
282
+ runtime.storeError = chosen.error;
283
+ if (chosen.named)
284
+ runtime.storeFailed = true;
285
+ if (chosen.rejected)
286
+ runtime.deploymentsRejected = true;
287
+ }
288
+ else {
289
+ const store = await (0, store_1.withPrimaryInstance)(chosen, alloyDb);
290
+ if ('error' in store) {
291
+ runtime.storeError = store.error;
292
+ runtime.storeFailed = true;
293
+ }
294
+ else {
295
+ runtime.store = store;
296
+ }
297
+ }
298
+ runtimes.push(runtime);
299
+ }
300
+ return runtimes;
301
+ }
@@ -0,0 +1,112 @@
1
+ import { AlloyDbClient } from '../../gcp/alloydb';
2
+ import { SemanticModel } from '../ir';
3
+ /** A Spanner database. An action's statements can write to it. */
4
+ export interface SpannerStore {
5
+ kind: 'spanner';
6
+ /** `projects/<p>/instances/<i>/databases/<d>`. The store's identity. */
7
+ name: string;
8
+ project: string;
9
+ instance: string;
10
+ database: string;
11
+ }
12
+ /**
13
+ * An AlloyDB database. An action's statements can write to it. The instance a
14
+ * connection goes through may not be known yet.
15
+ */
16
+ export interface AlloyDbDatabase {
17
+ kind: 'alloydb';
18
+ /**
19
+ * `projects/<p>/locations/<l>/clusters/<c>/databases/<d>`. The store's
20
+ * identity, which on AlloyDB reaches all the way down to the PostgreSQL
21
+ * database: one cluster holds several, and two of them are two stores.
22
+ */
23
+ name: string;
24
+ project: string;
25
+ location: string;
26
+ cluster: string;
27
+ database: string;
28
+ }
29
+ /** An AlloyDB database, with the instance a connection goes through. */
30
+ export interface AlloyDbStore extends AlloyDbDatabase {
31
+ instance: string;
32
+ }
33
+ /** A BigQuery dataset. An action's statements can write to it. */
34
+ export interface BigQueryStore {
35
+ kind: 'bigquery';
36
+ /** `projects/<p>/datasets/<d>`. The store's identity. */
37
+ name: string;
38
+ project: string;
39
+ dataset: string;
40
+ }
41
+ export type Store = SpannerStore | AlloyDbStore | BigQueryStore;
42
+ /**
43
+ * The store resolveStore chooses. An AlloyDB database may still need its
44
+ * instance found with withPrimaryInstance.
45
+ */
46
+ export type ChosenStore = SpannerStore | AlloyDbDatabase | BigQueryStore;
47
+ /**
48
+ * Why resolveStore gives no store.
49
+ *
50
+ * `named` is set when the model's deployments name a target and still no
51
+ * store results, so a caller that runs against the store fails. `rejected` is
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, a GOOGLE block whose
54
+ * data is not valid JSON, or several targets for the profile. `named` alone
55
+ * means the model binds a table outside its Spanner or AlloyDB store.
56
+ *
57
+ * Neither is set when the profile names no store, unless a target is in no
58
+ * accepted form, or a GOOGLE block is not valid JSON. Neither is set either
59
+ * when a BigQuery model binds a table outside the graph's dataset.
60
+ */
61
+ export interface StoreError {
62
+ error: string;
63
+ named?: boolean;
64
+ rejected?: boolean;
65
+ }
66
+ /**
67
+ * The store a model runs against under binding profile `profile`, which
68
+ * defaults to `default`, the model file's own bindings. It is the one the
69
+ * deployments for that profile name: those that name it in `profile`, or that
70
+ * name none or `default` for `default`. A model with no `deployments` list
71
+ * names no profile for any of its targets, so they are all for `default`.
72
+ * With several targets, the store is the single operational one, Spanner or
73
+ * AlloyDB.
74
+ *
75
+ * Fails six ways:
76
+ *
77
+ * 1. A GOOGLE block's data is not valid JSON, so its targets cannot be read.
78
+ * 2. A target names no supported destination. Push checks the target of every
79
+ * deployment, so this fails the store under every profile.
80
+ * 3. No deployment uses the profile.
81
+ * 4. There are several targets and no single operational one, so which store
82
+ * is meant is ambiguous.
83
+ * 5. There is no target, so there is nothing to run against.
84
+ * 6. The model binds a table outside the store. For a Spanner or AlloyDB
85
+ * store, a write would land in the wrong place, and the error is named. A
86
+ * BigQuery graph may read tables in any dataset, so
87
+ * for a BigQuery store the error is not named.
88
+ */
89
+ export declare function resolveStore(model: SemanticModel, profile?: string): ChosenStore | StoreError;
90
+ /**
91
+ * The targets binding profile `profile` uses, as resolveStore reads them: its
92
+ * deployments' targets, or every target in a GOOGLE block for `default` when
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.
95
+ */
96
+ export declare function profileTargets(model: SemanticModel, profile: string): string[];
97
+ /**
98
+ * `store` with the instance a connection goes through. An AlloyDB target
99
+ * names a database in a cluster, so this asks the AlloyDB API for the
100
+ * cluster's primary instance. Fails, naming the cluster, when it has none,
101
+ * when the primary is not READY, or when the listing fails. Any other store
102
+ * comes back unchanged.
103
+ */
104
+ export declare function withPrimaryInstance(store: ChosenStore, client: AlloyDbClient): Promise<Store | {
105
+ error: string;
106
+ }>;
107
+ /**
108
+ * How a store is written down for a reader: the resource it addresses, with
109
+ * the backend named ahead of it for everything but Spanner, which is the one
110
+ * an unprefixed line has always meant.
111
+ */
112
+ export declare function storeLine(store: Store): string;