@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,37 @@
1
+ import * as gcp from './gcp/context';
2
+ import * as dataplex from './gcp/dataplex';
3
+ import * as md from './metadata';
4
+ import { CatalogManifest } from './manifest';
5
+ import { CatalogLayout, Layouts } from './layout';
6
+ export interface SnapshotOptions {
7
+ layout?: Layouts;
8
+ }
9
+ export declare class CatalogSnapshot {
10
+ readonly manifest: CatalogManifest;
11
+ readonly basePath: string;
12
+ private readonly _entryTypes;
13
+ private readonly _aspectTypes;
14
+ private readonly _layout;
15
+ private readonly _catalog;
16
+ private constructor();
17
+ static fromManifest(basePath: string, manifest: CatalogManifest, ctx: gcp.ApiContext, options?: SnapshotOptions): Promise<CatalogSnapshot>;
18
+ static fromPath(basePath: string, ctx: gcp.ApiContext, options?: SnapshotOptions): Promise<CatalogSnapshot>;
19
+ get entryTypes(): Map<string, dataplex.EntryType>;
20
+ get aspectTypes(): Map<string, dataplex.AspectType>;
21
+ get layout(): CatalogLayout;
22
+ listEntries(): Promise<string[]>;
23
+ lookupEntry(name: string): Promise<md.Entry>;
24
+ updateEntry(entry: md.Entry, fields: string[]): Promise<void>;
25
+ createEntry(name: string, entry: md.Entry): Promise<void>;
26
+ deleteEntry(name: string): Promise<void>;
27
+ private _buildTypes;
28
+ _loadEntryType(entryType: string): Promise<void>;
29
+ _storeEntry(entry: dataplex.Entry): Promise<void>;
30
+ _previewStoreEntry(entry: dataplex.Entry): {
31
+ localName: string;
32
+ exists: boolean;
33
+ };
34
+ _fetchEntry(name: string): Promise<dataplex.Entry | undefined>;
35
+ }
36
+ export declare function toLocalEntry(entry: dataplex.Entry, localName: string): md.Entry;
37
+ export declare function toServiceEntry(entry: md.Entry, serviceName: string, manifest: CatalogManifest, entryTypes: Map<string, dataplex.EntryType>, isOkf?: boolean): dataplex.Entry;
@@ -0,0 +1,386 @@
1
+ "use strict";
2
+ // Implements a local catalog interface
3
+ //
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.CatalogSnapshot = void 0;
39
+ exports.toLocalEntry = toLocalEntry;
40
+ exports.toServiceEntry = toServiceEntry;
41
+ const fs = __importStar(require("node:fs"));
42
+ const path = __importStar(require("node:path"));
43
+ const dataplex = __importStar(require("./gcp/dataplex"));
44
+ const manifest_1 = require("./manifest");
45
+ const layout_1 = require("./layout");
46
+ const okf_1 = require("./layouts/okf");
47
+ const source_1 = require("./source");
48
+ class CatalogSnapshot {
49
+ manifest;
50
+ basePath;
51
+ _entryTypes = new Map();
52
+ _aspectTypes = new Map();
53
+ _layout;
54
+ _catalog;
55
+ constructor(basePath, manifest, ctx, options = {}) {
56
+ this.basePath = basePath;
57
+ this.manifest = manifest;
58
+ this._catalog = new dataplex.CatalogClient(ctx);
59
+ const effectiveLayout = options.layout ??
60
+ (manifest.layout === 'okf' ? layout_1.Layouts.OKF : manifest.source.layout);
61
+ if (effectiveLayout === layout_1.Layouts.OKF) {
62
+ const coords = (0, source_1.entryGroupCoordinates)(manifest.source);
63
+ const bundlePath = path.join(this.basePath, okf_1.BUNDLE_DIR);
64
+ this._layout = (0, layout_1.createLayout)(layout_1.Layouts.OKF, bundlePath, coords.container);
65
+ }
66
+ else {
67
+ const catalogPath = path.join(this.basePath, 'catalog');
68
+ this._layout = (0, layout_1.createLayout)(effectiveLayout, catalogPath, manifest.source.entryGroup);
69
+ }
70
+ }
71
+ static async fromManifest(basePath, manifest, ctx, options = {}) {
72
+ const snapshot = new CatalogSnapshot(basePath, manifest, ctx, options);
73
+ if (!(snapshot._layout instanceof okf_1.OkfLayout)) {
74
+ await snapshot._buildTypes(manifest);
75
+ }
76
+ await snapshot._layout.init();
77
+ return snapshot;
78
+ }
79
+ static async fromPath(basePath, ctx, options = {}) {
80
+ const manifestPath = path.join(basePath, 'catalog.yaml');
81
+ if (!fs.existsSync(manifestPath)) {
82
+ throw new Error(`Cannot find catalog manifest at '${manifestPath}'`);
83
+ }
84
+ const manifest = await manifest_1.CatalogManifest.load(manifestPath, ctx);
85
+ return await CatalogSnapshot.fromManifest(basePath, manifest, ctx, options);
86
+ }
87
+ get entryTypes() {
88
+ return this._entryTypes;
89
+ }
90
+ get aspectTypes() {
91
+ return this._aspectTypes;
92
+ }
93
+ get layout() {
94
+ return this._layout;
95
+ }
96
+ // Retrieves the list of locally (pulled and/or created) managed entries
97
+ async listEntries() {
98
+ return this._layout.listEntries();
99
+ }
100
+ // Retrieves the local copy of the entry using its local name
101
+ async lookupEntry(name) {
102
+ return await this._layout.loadEntry(name);
103
+ }
104
+ // Updates the locally managed entry, referenced by its local name.
105
+ // The list of fields can either be "resource" to update the resource-level metadata
106
+ // (which is relevant in case of non-ingested entries) or an aspect identified by it
107
+ // key (project.location.type).
108
+ async updateEntry(entry, fields) {
109
+ const existingEntry = await this._layout.loadEntry(entry.name);
110
+ for (const f of fields) {
111
+ if (f == 'resource') {
112
+ if (!existingEntry.resource) {
113
+ existingEntry.resource = {};
114
+ }
115
+ if (!entry.resource) {
116
+ entry.resource = {};
117
+ }
118
+ existingEntry.resource.description = entry.resource.description;
119
+ }
120
+ else {
121
+ await this._loadEntryType(existingEntry.type);
122
+ const aspectType = dataplex._typeRefToName(aspectTypeRefOf(f), 'aspect');
123
+ if (!this._aspectTypes.has(aspectType)) {
124
+ throw new Error(`The aspect '${f}' is not registered in the snapshot.`);
125
+ }
126
+ if (this.manifest.source.ingestedEntries) {
127
+ const entryType = this._entryTypes.get(existingEntry.type);
128
+ if (!entryType || entryType.requiredAspects?.find((a) => a.type == aspectType)) {
129
+ throw new Error(`The aspect '${f}' is not modifiable on the entry.`);
130
+ }
131
+ }
132
+ if (!existingEntry.aspects) {
133
+ existingEntry.aspects = {};
134
+ }
135
+ if (entry.aspects && entry.aspects[f]) {
136
+ existingEntry.aspects[f] = entry.aspects[f];
137
+ }
138
+ else {
139
+ delete existingEntry.aspects[f];
140
+ }
141
+ }
142
+ }
143
+ await this._layout.saveEntry(entry.name, existingEntry);
144
+ }
145
+ // Creates an entry within the locally catalog snapshot. This capabilitiy is only supported
146
+ // when the associated EntryGroup is user-managed, i.e. not contain ingested metadata.
147
+ async createEntry(name, entry) {
148
+ if (this.manifest.source.ingestedEntries) {
149
+ throw new Error(`Entry cannot be created as entries are ingested.`);
150
+ }
151
+ // TODO: Validate aspect and other things
152
+ if (this._layout.entryExists(name)) {
153
+ throw new Error(`Entry '${name}' already exists`);
154
+ }
155
+ await this._layout.saveEntry(name, entry);
156
+ }
157
+ // Deletes an entry within the locally catalog snapshot. This capabilitiy is only supported
158
+ // when the associated EntryGroup is user-managed, i.e. not contain ingested metadata.
159
+ async deleteEntry(name) {
160
+ if (this.manifest.source.ingestedEntries) {
161
+ throw new Error(`Entry cannot be deleted as entries are ingested.`);
162
+ }
163
+ await this._layout.deleteEntry(name);
164
+ }
165
+ // Loads the entry and aspect types listed under snapshot in catalog.yaml.
166
+ // Types are stored using two keys: the resource name and the 3-part type name.
167
+ async _buildTypes(manifest) {
168
+ for (const entryType of manifest.snapshotConfig?.entries || []) {
169
+ await this._loadEntryType(entryType);
170
+ }
171
+ for (const aspectType of manifest.snapshotConfig?.aspects || []) {
172
+ if (this._aspectTypes.has(aspectType)) {
173
+ continue;
174
+ }
175
+ const parts = aspectType.split('.');
176
+ const res = await this._catalog.getAspectType(parts[0], parts[1], parts[2]);
177
+ let type = res.result;
178
+ // Dataplex refuses the definition of some types whose aspects callers can
179
+ // still read and write. A refused type is registered by its name.
180
+ if (res.status == 403) {
181
+ type = { name: dataplex._typeRefToName(aspectType, 'aspect') };
182
+ console.warn(`Warning: Permission denied reading the definition of aspect type ${aspectType}. ` +
183
+ `Its aspects are still pulled and pushed. If none are pulled, check the type name.`);
184
+ }
185
+ if (!type) {
186
+ throw new Error(`Unable to load type information for aspect type ${aspectType}`);
187
+ }
188
+ this._aspectTypes.set(type.name, type);
189
+ this._aspectTypes.set(aspectType, type);
190
+ }
191
+ }
192
+ // Loads an entry type, given as project.location.type, and the aspect types
193
+ // it requires. Does nothing when entryType is empty or already loaded.
194
+ async _loadEntryType(entryType) {
195
+ if (!entryType || this._entryTypes.has(entryType)) {
196
+ return;
197
+ }
198
+ const parts = entryType.split('.');
199
+ const res = await this._catalog.getEntryType(parts[0], parts[1], parts[2]);
200
+ if (!res.result) {
201
+ throw new Error(`Unable to load type information for entry type ${entryType}`);
202
+ }
203
+ this._entryTypes.set(res.result.name, res.result);
204
+ this._entryTypes.set(entryType, res.result);
205
+ for (const requiredAspect of res.result.requiredAspects ?? []) {
206
+ if (!this._aspectTypes.has(requiredAspect.type)) {
207
+ const parts = requiredAspect.type.split('/');
208
+ const res = await this._catalog.getAspectType(parts[1], parts[3], parts[5]);
209
+ if (!res.result) {
210
+ throw new Error(`Unable to load type information for aspect type ${requiredAspect.type}`);
211
+ }
212
+ this._aspectTypes.set(res.result.name, res.result);
213
+ this._aspectTypes.set(`${parts[1]}.${parts[3]}.${parts[5]}`, res.result);
214
+ }
215
+ }
216
+ }
217
+ // Stores a Dataplex entry into the locally managed catalog snapshot. This will internally map
218
+ // The service representation into the local metadata representation.
219
+ // This is only meant to be used within the syncing process (as part of pull operations).
220
+ async _storeEntry(entry) {
221
+ const localName = this.manifest.source.localName(entry);
222
+ const local = toLocalEntry(entry, localName);
223
+ // Preserve the parent so a pull after a push round-trips it, mirroring the
224
+ // inverse conversion _fetchEntry applies on push: store the parent as a
225
+ // local name (Dataplex requires parentEntry to be in the same entry group,
226
+ // so localName always resolves), not the service name. Ingested entries
227
+ // never carry a parentEntry back to the service (toServiceEntry drops it),
228
+ // so recording one would be misleading -- skip them.
229
+ if (entry.parentEntry &&
230
+ (this._layout instanceof okf_1.OkfLayout || !this.manifest.source.ingestedEntries)) {
231
+ local.resource.parent =
232
+ this.manifest.source.localName({ ...entry, name: entry.parentEntry });
233
+ }
234
+ await this._layout.saveEntry(localName, local);
235
+ }
236
+ // Reports what _storeEntry would do with a Dataplex entry, without writing:
237
+ // the local name it maps to and whether a local copy already exists. Used by
238
+ // `pull --dry-run`.
239
+ _previewStoreEntry(entry) {
240
+ const localName = this.manifest.source.localName(entry);
241
+ return { localName, exists: this._layout.entryExists(localName) };
242
+ }
243
+ // Fetches a Dataplex entry from its local metadata representation.
244
+ // This is only meant to be used within the syncing process (as part of push operations).
245
+ async _fetchEntry(name) {
246
+ const entry = await this._layout.loadEntry(name);
247
+ if (this.manifest.publishingConfig?.entries?.length &&
248
+ !this.manifest.publishingConfig.entries.includes(entry.type)) {
249
+ return undefined;
250
+ }
251
+ if (this._layout instanceof okf_1.OkfLayout) {
252
+ if (!this._entryTypes.has(entry.type)) {
253
+ const typeName = dataplex._typeRefToName(entry.type, 'entry');
254
+ const stub = { name: typeName, requiredAspects: [] };
255
+ this._entryTypes.set(typeName, stub);
256
+ this._entryTypes.set(entry.type, stub);
257
+ }
258
+ }
259
+ else {
260
+ await this._loadEntryType(entry.type);
261
+ }
262
+ const serviceName = this.manifest.source.serviceName(name);
263
+ if (entry.resource?.parent && !entry.resource.parent.startsWith('projects/')) {
264
+ entry.resource.parent = this.manifest.source.serviceName(entry.resource.parent);
265
+ }
266
+ return toServiceEntry(entry, serviceName, this.manifest, this._entryTypes, this._layout instanceof okf_1.OkfLayout);
267
+ }
268
+ }
269
+ exports.CatalogSnapshot = CatalogSnapshot;
270
+ // Converts a Dataplex entry into the local metadata representation.
271
+ function toLocalEntry(entry, localName) {
272
+ const aspects = {};
273
+ if (entry.aspects) {
274
+ for (const key in entry.aspects) {
275
+ aspects[key] = entry.aspects[key].data ?? {};
276
+ }
277
+ }
278
+ const entrySource = entry.entrySource ?? {};
279
+ return {
280
+ name: localName,
281
+ type: dataplex._nameToTypeRef(entry.entryType),
282
+ resource: {
283
+ name: entrySource.resource ?? undefined,
284
+ system: entrySource.system ?? undefined,
285
+ platform: entrySource.platform ?? undefined,
286
+ displayName: entrySource.displayName ?? undefined,
287
+ description: entrySource.description ?? undefined,
288
+ labels: entrySource.labels ?? undefined,
289
+ location: entrySource.location ?? undefined,
290
+ ancestors: entrySource.ancestors ?? undefined,
291
+ createTime: entrySource.createTime ?? undefined,
292
+ updateTime: entrySource.updateTime ?? undefined
293
+ },
294
+ aspects: aspects ?? undefined
295
+ };
296
+ }
297
+ // Converts a local metadata representation into a Dataplex Entry
298
+ function toServiceEntry(entry, serviceName, manifest, entryTypes, isOkf = false) {
299
+ const entryType = entryTypes.get(entry.type);
300
+ if (!entryType) {
301
+ throw new Error(`Unknown entry type ${entry.type} in snapshot`);
302
+ }
303
+ const aspects = {};
304
+ if (entry.aspects) {
305
+ for (const key in entry.aspects) {
306
+ if (manifest.publishingConfig &&
307
+ !manifest.publishingConfig.aspects?.includes(aspectTypeRefOf(key))) {
308
+ continue;
309
+ }
310
+ // A field-level aspect keeps its `@<path>` in the key it is written under.
311
+ const aspectType = dataplex._typeRefToName(aspectTypeRefOf(key), 'aspect');
312
+ if (manifest.source.ingestedEntries &&
313
+ entryType.requiredAspects?.find((aspectInfo) => aspectInfo.type == aspectType)) {
314
+ continue;
315
+ }
316
+ aspects[key] = { aspectType, data: entry.aspects[key] };
317
+ }
318
+ }
319
+ const resource = entry.resource ?? {};
320
+ const entryTypeName = dataplex._typeRefToName(entry.type, 'entry');
321
+ if (manifest.source.ingestedEntries ||
322
+ !entry.resource || !Object.keys(entry.resource).length) {
323
+ return {
324
+ name: serviceName,
325
+ entryType: entryTypeName,
326
+ aspects: aspects
327
+ };
328
+ }
329
+ if (!isOkf) {
330
+ return {
331
+ name: serviceName,
332
+ entryType: entryTypeName,
333
+ parentEntry: resource.parent,
334
+ entrySource: {
335
+ resource: resource.name,
336
+ ancestors: resource.ancestors,
337
+ displayName: resource.displayName,
338
+ description: resource.description,
339
+ labels: resource.labels,
340
+ location: resource.location,
341
+ createTime: resource.createTime,
342
+ updateTime: resource.updateTime
343
+ },
344
+ aspects: aspects
345
+ };
346
+ }
347
+ const entrySource = {};
348
+ if (resource.name !== undefined)
349
+ entrySource.resource = resource.name;
350
+ if (resource.system !== undefined)
351
+ entrySource.system = resource.system;
352
+ if (resource.platform !== undefined)
353
+ entrySource.platform = resource.platform;
354
+ if (resource.ancestors !== undefined)
355
+ entrySource.ancestors = resource.ancestors;
356
+ if (resource.displayName !== undefined)
357
+ entrySource.displayName = resource.displayName;
358
+ if (resource.description !== undefined)
359
+ entrySource.description = resource.description;
360
+ if (resource.labels !== undefined)
361
+ entrySource.labels = resource.labels;
362
+ if (resource.location !== undefined)
363
+ entrySource.location = resource.location;
364
+ if (resource.createTime !== undefined)
365
+ entrySource.createTime = resource.createTime;
366
+ if (resource.updateTime !== undefined)
367
+ entrySource.updateTime = resource.updateTime;
368
+ const out = {
369
+ name: serviceName,
370
+ entryType: entryTypeName,
371
+ aspects: aspects
372
+ };
373
+ if (resource.parent !== undefined) {
374
+ out.parentEntry = resource.parent;
375
+ }
376
+ if (Object.keys(entrySource).length) {
377
+ out.entrySource = entrySource;
378
+ }
379
+ return out;
380
+ }
381
+ // The aspect type reference in an aspect key: the part before any `@<path>`,
382
+ // as in `p.global.guidelines@Schema.col1`.
383
+ function aspectTypeRefOf(key) {
384
+ const at = key.indexOf('@');
385
+ return at < 0 ? key : key.slice(0, at);
386
+ }
@@ -0,0 +1,31 @@
1
+ import * as gcp from './gcp';
2
+ import { Layouts } from './layout';
3
+ export declare enum Sources {
4
+ ENTRYGROUP = "entryGroup",
5
+ BIGQUERY_DATASET = "bq-dataset",
6
+ KB = "kb",
7
+ SEMANTIC_MODEL = "semantic-model"
8
+ }
9
+ export interface CatalogSource {
10
+ readonly type: string;
11
+ readonly name: string;
12
+ readonly ingestedEntries: boolean;
13
+ readonly layout: Layouts;
14
+ readonly project?: string;
15
+ readonly location?: string;
16
+ readonly entryGroup?: string;
17
+ readonly defaultAspects: readonly string[];
18
+ readonly systemAspects: readonly string[];
19
+ entries(ctx: gcp.ApiContext): AsyncGenerator<gcp.Entry, void, unknown>;
20
+ localName(entry: gcp.Entry): string;
21
+ serviceName(localName: string): string;
22
+ }
23
+ export declare function createSource(type: string, name: string, ctx: gcp.ApiContext): Promise<CatalogSource>;
24
+ export interface EntryGroupCoordinates {
25
+ project: string;
26
+ location: string;
27
+ entryGroup: string;
28
+ container: string;
29
+ }
30
+ /** Validates and extracts `<project>.<location>.<entryGroup>` coordinates. */
31
+ export declare function entryGroupCoordinates(source: CatalogSource): EntryGroupCoordinates;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ // Defines a Catalog metadata source abstraction
3
+ //
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.Sources = void 0;
39
+ exports.createSource = createSource;
40
+ exports.entryGroupCoordinates = entryGroupCoordinates;
41
+ const gcp = __importStar(require("./gcp"));
42
+ const bq = __importStar(require("./gcp/bigquery"));
43
+ const dataplex = __importStar(require("./gcp/dataplex"));
44
+ const entrygroup_1 = require("./sources/entrygroup");
45
+ const bq_dataset_1 = require("./sources/bq-dataset");
46
+ const kb_1 = require("./sources/kb");
47
+ const semantic_model_1 = require("./sources/semantic-model");
48
+ var Sources;
49
+ (function (Sources) {
50
+ Sources["ENTRYGROUP"] = "entryGroup";
51
+ Sources["BIGQUERY_DATASET"] = "bq-dataset";
52
+ Sources["KB"] = "kb";
53
+ Sources["SEMANTIC_MODEL"] = "semantic-model";
54
+ })(Sources || (exports.Sources = Sources = {}));
55
+ async function getEntryGroup(name, ctx) {
56
+ const [project, location, entryGroup] = name.split('.');
57
+ if (!project || !location || !entryGroup) {
58
+ throw new Error('EntryGroup must be in format <projectId>.<locationId>.<entryGroupId>');
59
+ }
60
+ const catalog = new gcp.CatalogClient(ctx);
61
+ const res = await catalog.getEntryGroup(project, location, entryGroup);
62
+ if (!res.result) {
63
+ throw new Error(`Failed to locate EntryGroup '${name}'.`);
64
+ }
65
+ return res.result;
66
+ }
67
+ async function getBigQueryDatasets(name, ctx) {
68
+ const datasets = new Map();
69
+ const names = name.split(',');
70
+ const bigQuery = new bq.BigQueryClient(ctx);
71
+ for (const n of names) {
72
+ const [project, dataset] = n.split('.');
73
+ if (!project || !dataset) {
74
+ throw new Error(`BigQuery dataset must be in format <projectId>.<datasetId>: ${n}`);
75
+ }
76
+ const res = await bigQuery.getDataset(project, dataset);
77
+ if (!res.result) {
78
+ throw new Error(`Failed to locate BigQuery dataset '${n}'.`);
79
+ }
80
+ datasets.set(n, res.result);
81
+ }
82
+ return datasets;
83
+ }
84
+ async function createSource(type, name, ctx) {
85
+ switch (type) {
86
+ case Sources.ENTRYGROUP:
87
+ const entryGroup = await getEntryGroup(name, ctx);
88
+ return new entrygroup_1.EntryGroupSource(Sources.ENTRYGROUP, name, entryGroup);
89
+ case Sources.BIGQUERY_DATASET:
90
+ const datasets = await getBigQueryDatasets(name, ctx);
91
+ return new bq_dataset_1.BigQueryDatasetSource(Sources.BIGQUERY_DATASET, name, datasets);
92
+ case Sources.KB:
93
+ const knowledgeBase = await getEntryGroup(name, ctx);
94
+ return new kb_1.KnowledgeBaseSource(Sources.KB, name, knowledgeBase);
95
+ case Sources.SEMANTIC_MODEL:
96
+ return new semantic_model_1.SemanticModelSource(Sources.SEMANTIC_MODEL, name);
97
+ default:
98
+ throw new Error(`Unknown source type: ${type}`);
99
+ }
100
+ }
101
+ /** Validates and extracts `<project>.<location>.<entryGroup>` coordinates. */
102
+ function entryGroupCoordinates(source) {
103
+ if (source.type && source.type !== Sources.ENTRYGROUP && source.type !== Sources.KB) {
104
+ throw new Error(`OKF layout applies only to '${Sources.ENTRYGROUP}' and '${Sources.KB}' scopes, got '${source.type}'.`);
105
+ }
106
+ const project = source.project ?? source.name.split('.')[0];
107
+ const location = source.location ?? source.name.split('.')[1];
108
+ const entryGroup = source.entryGroup ?? source.name.split('.')[2];
109
+ const parts = source.name.split('.');
110
+ if (!project || !location || !entryGroup || parts.length !== 3) {
111
+ throw new Error(`EntryGroup must be in format <projectId>.<locationId>.<entryGroupId>: '${source.name}'`);
112
+ }
113
+ return {
114
+ project,
115
+ location,
116
+ entryGroup,
117
+ container: dataplex.catalogContainer(project, location, entryGroup),
118
+ };
119
+ }
@@ -0,0 +1,17 @@
1
+ import * as gcp from '../gcp';
2
+ import * as bq from '../gcp/bigquery';
3
+ import { Layouts } from '../layout';
4
+ import { CatalogSource } from '../source';
5
+ export declare class BigQueryDatasetSource implements CatalogSource {
6
+ readonly type: string;
7
+ readonly name: string;
8
+ readonly ingestedEntries = true;
9
+ readonly layout = Layouts.STANDARD;
10
+ readonly defaultAspects: string[];
11
+ readonly systemAspects: string[];
12
+ private readonly _datasets;
13
+ constructor(type: string, name: string, datasets: Map<string, bq.Dataset>);
14
+ entries(ctx: gcp.ApiContext): AsyncGenerator<gcp.Entry, void, unknown>;
15
+ localName(entry: gcp.Entry): string;
16
+ serviceName(localName: string): string;
17
+ }