@ingestron/pack-databricks 0.0.0-stage → 1.0.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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +108 -2
- package/dist/apply-contract.d.ts +17 -0
- package/dist/apply-contract.d.ts.map +1 -0
- package/dist/apply-contract.js +100 -0
- package/dist/apply-contract.js.map +1 -0
- package/dist/build.d.ts +8 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +79 -0
- package/dist/build.js.map +1 -0
- package/dist/bundle.d.ts +31 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +137 -0
- package/dist/bundle.js.map +1 -0
- package/dist/capabilities.d.ts +8 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +78 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/connect-method.d.ts +8 -0
- package/dist/connect-method.d.ts.map +1 -0
- package/dist/connect-method.js +89 -0
- package/dist/connect-method.js.map +1 -0
- package/dist/connect.d.ts +28 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +153 -0
- package/dist/connect.js.map +1 -0
- package/dist/connectors.d.ts +4 -0
- package/dist/connectors.d.ts.map +1 -0
- package/dist/connectors.js +56 -0
- package/dist/connectors.js.map +1 -0
- package/dist/contract.d.ts +4 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +54 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/methods.d.ts +10 -0
- package/dist/methods.d.ts.map +1 -0
- package/dist/methods.js +167 -0
- package/dist/methods.js.map +1 -0
- package/dist/pack.d.ts +9 -0
- package/dist/pack.d.ts.map +1 -0
- package/dist/pack.js +73 -0
- package/dist/pack.js.map +1 -0
- package/dist/pyspark-method.d.ts +4 -0
- package/dist/pyspark-method.d.ts.map +1 -0
- package/dist/pyspark-method.js +41 -0
- package/dist/pyspark-method.js.map +1 -0
- package/dist/python-data.d.ts +22 -0
- package/dist/python-data.d.ts.map +1 -0
- package/dist/python-data.js +50 -0
- package/dist/python-data.js.map +1 -0
- package/dist/query.d.ts +9 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +26 -0
- package/dist/query.js.map +1 -0
- package/dist/request.d.ts +4 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +47 -0
- package/dist/request.js.map +1 -0
- package/dist/sql-data.d.ts +45 -0
- package/dist/sql-data.d.ts.map +1 -0
- package/dist/sql-data.js +111 -0
- package/dist/sql-data.js.map +1 -0
- package/dist/sql.d.ts +22 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +81 -0
- package/dist/sql.js.map +1 -0
- package/package.json +68 -3
- package/templates/apply-contract.py.liquid +58 -0
- package/templates/change-data.sql.liquid +20 -0
- package/templates/materialised-view.py.liquid +28 -0
- package/templates/materialised-view.sql.liquid +4 -0
- package/templates/read-files.liquid +7 -0
- package/templates/streaming-table.sql.liquid +3 -0
- package/templates/table.liquid +17 -0
package/dist/contract.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// What the Databricks pack does with each ODCS contract field: used, not yet
|
|
2
|
+
// (with why), or handled elsewhere. The documentation and the editor show
|
|
3
|
+
// these, and a contract field no pack uses yet is a warning.
|
|
4
|
+
import { contractCommon } from "@ingestron/definitions";
|
|
5
|
+
const used = (does) => ({ use: "used", does });
|
|
6
|
+
/** Used when the platform turns the capability on, after each refresh. */
|
|
7
|
+
const needs = (id, does) => ({ use: "used", does, needs: id });
|
|
8
|
+
/** Used, with the fields under it read as part of it. */
|
|
9
|
+
const whole = (does) => ({ use: "used", does, whole: true });
|
|
10
|
+
const notYet = (reason) => ({ use: "not-yet", reason });
|
|
11
|
+
const elsewhere = (reason) => ({ use: "elsewhere", reason });
|
|
12
|
+
const object = "schema.*";
|
|
13
|
+
const column = "schema.*.properties.*";
|
|
14
|
+
const tags = needs("tags", "Set as Unity Catalog tags after each refresh; key:value gives a value.");
|
|
15
|
+
const relationships = notYet("Relationships between tables are not declared on the tables yet.");
|
|
16
|
+
/** The Databricks pack's use of every contract field, by path. */
|
|
17
|
+
export const contract = {
|
|
18
|
+
...contractCommon,
|
|
19
|
+
schema: used("One object per table; each table uses the object named like it."),
|
|
20
|
+
[`${object}.name`]: used("Matched to the table by its name."),
|
|
21
|
+
[`${object}.logicalType`]: { use: "documentation" },
|
|
22
|
+
[`${object}.physicalName`]: elsewhere("The table's name comes from the standard's naming."),
|
|
23
|
+
[`${object}.physicalType`]: elsewhere("The store method decides: a streaming table or a materialised view."),
|
|
24
|
+
[`${object}.description`]: used("The table's comment."),
|
|
25
|
+
[`${object}.properties`]: used("The table's columns."),
|
|
26
|
+
[`${object}.relationships`]: relationships,
|
|
27
|
+
[`${object}.quality`]: {
|
|
28
|
+
use: "used",
|
|
29
|
+
whole: true,
|
|
30
|
+
needs: "checks",
|
|
31
|
+
does: "Rules with a comparison are counted over all rows after each refresh, then warn or fail the run as quality.default says.",
|
|
32
|
+
},
|
|
33
|
+
[`${object}.tags`]: tags,
|
|
34
|
+
[`${column}.name`]: used("The column's name in the table."),
|
|
35
|
+
[`${column}.logicalType`]: used("The column's type, through the standard's types; a schema hint when reading files."),
|
|
36
|
+
[`${column}.physicalType`]: used("The column's type, as written."),
|
|
37
|
+
[`${column}.required`]: used("A not-null expectation with the standard's quality.required action."),
|
|
38
|
+
[`${column}.primaryKey`]: used("The keys that upsert and history keep the current row by."),
|
|
39
|
+
[`${column}.primaryKeyPosition`]: used("The order of the keys."),
|
|
40
|
+
[`${column}.physicalName`]: used("The column's name at the source: files are read with it renamed to the contract's name. Lakeflow Connect keeps source names, so it refuses physicalName."),
|
|
41
|
+
[`${column}.logicalTypeOptions`]: whole("Limits become expectations with quality.default's action: length, pattern, minimum and maximum, multipleOf and array items. format is documentation."),
|
|
42
|
+
[`${column}.unique`]: needs("checks", "A table check after each refresh that no value repeats."),
|
|
43
|
+
[`${column}.partitioned`]: elsewhere("Tables use liquid clustering; set the store method's cluster_by."),
|
|
44
|
+
[`${column}.partitionKeyPosition`]: elsewhere("Tables use liquid clustering; set the store method's cluster_by."),
|
|
45
|
+
[`${column}.classification`]: needs("tags", "Set as the column's tag classification after each refresh."),
|
|
46
|
+
[`${column}.encryptedName`]: elsewhere("Encryption is managed outside Ingestron."),
|
|
47
|
+
[`${column}.relationships`]: relationships,
|
|
48
|
+
[`${column}.quality`]: whole("Rules that allow no failing row (nullValues, missingValues, invalidValues with mustBe: 0) become expectations; the others are a warning."),
|
|
49
|
+
[`${column}.description`]: needs("comments", "Set as the column's comment after each refresh: a pipeline takes column comments only with every column's type."),
|
|
50
|
+
[`${column}.tags`]: tags,
|
|
51
|
+
[`${column}.properties`]: notYet("The fields of an object column are not read yet; give the column's physicalType, such as STRUCT<street: STRING>."),
|
|
52
|
+
[`${column}.items`]: notYet("The element of an array column is not read yet; give the column's physicalType, such as ARRAY<STRING>."),
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,0EAA0E;AAC1E,6DAA6D;AAC7D,OAAO,EAAE,cAAc,EAAoB,MAAM,wBAAwB,CAAC;AAE1E,MAAM,IAAI,GAAG,CAAC,IAAY,EAAe,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AACpE,0EAA0E;AAC1E,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,IAAY,EAAe,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;AAC5F,yDAAyD;AACzD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAe,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAClF,MAAM,MAAM,GAAG,CAAC,MAAc,EAAe,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7E,MAAM,SAAS,GAAG,CAAC,MAAc,EAAe,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AAClF,MAAM,MAAM,GAAG,UAAU,CAAC;AAC1B,MAAM,MAAM,GAAG,uBAAuB,CAAC;AACvC,MAAM,IAAI,GAAG,KAAK,CAChB,MAAM,EACN,wEAAwE,CACzE,CAAC;AACF,MAAM,aAAa,GAAG,MAAM,CAAC,kEAAkE,CAAC,CAAC;AAEjG,kEAAkE;AAClE,MAAM,CAAC,MAAM,QAAQ,GAAgC;IACnD,GAAG,cAAc;IACjB,MAAM,EAAE,IAAI,CAAC,iEAAiE,CAAC;IAC/E,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI,CAAC,mCAAmC,CAAC;IAC7D,CAAC,GAAG,MAAM,cAAc,CAAC,EAAE,EAAE,GAAG,EAAE,eAAe,EAAE;IACnD,CAAC,GAAG,MAAM,eAAe,CAAC,EAAE,SAAS,CAAC,oDAAoD,CAAC;IAC3F,CAAC,GAAG,MAAM,eAAe,CAAC,EAAE,SAAS,CACnC,qEAAqE,CACtE;IACD,CAAC,GAAG,MAAM,cAAc,CAAC,EAAE,IAAI,CAAC,sBAAsB,CAAC;IACvD,CAAC,GAAG,MAAM,aAAa,CAAC,EAAE,IAAI,CAAC,sBAAsB,CAAC;IACtD,CAAC,GAAG,MAAM,gBAAgB,CAAC,EAAE,aAAa;IAC1C,CAAC,GAAG,MAAM,UAAU,CAAC,EAAE;QACrB,GAAG,EAAE,MAAM;QACX,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,0HAA0H;KACjI;IACD,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI;IACxB,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI,CAAC,iCAAiC,CAAC;IAC3D,CAAC,GAAG,MAAM,cAAc,CAAC,EAAE,IAAI,CAC7B,oFAAoF,CACrF;IACD,CAAC,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,CAAC,gCAAgC,CAAC;IAClE,CAAC,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,CAC1B,qEAAqE,CACtE;IACD,CAAC,GAAG,MAAM,aAAa,CAAC,EAAE,IAAI,CAAC,2DAA2D,CAAC;IAC3F,CAAC,GAAG,MAAM,qBAAqB,CAAC,EAAE,IAAI,CAAC,wBAAwB,CAAC;IAChE,CAAC,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,CAC9B,0JAA0J,CAC3J;IACD,CAAC,GAAG,MAAM,qBAAqB,CAAC,EAAE,KAAK,CACrC,sJAAsJ,CACvJ;IACD,CAAC,GAAG,MAAM,SAAS,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,yDAAyD,CAAC;IAChG,CAAC,GAAG,MAAM,cAAc,CAAC,EAAE,SAAS,CAClC,kEAAkE,CACnE;IACD,CAAC,GAAG,MAAM,uBAAuB,CAAC,EAAE,SAAS,CAC3C,kEAAkE,CACnE;IACD,CAAC,GAAG,MAAM,iBAAiB,CAAC,EAAE,KAAK,CACjC,MAAM,EACN,4DAA4D,CAC7D;IACD,CAAC,GAAG,MAAM,gBAAgB,CAAC,EAAE,SAAS,CAAC,0CAA0C,CAAC;IAClF,CAAC,GAAG,MAAM,gBAAgB,CAAC,EAAE,aAAa;IAC1C,CAAC,GAAG,MAAM,UAAU,CAAC,EAAE,KAAK,CAC1B,0IAA0I,CAC3I;IACD,CAAC,GAAG,MAAM,cAAc,CAAC,EAAE,KAAK,CAC9B,UAAU,EACV,iHAAiH,CAClH;IACD,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI;IACxB,CAAC,GAAG,MAAM,aAAa,CAAC,EAAE,MAAM,CAC9B,kHAAkH,CACnH;IACD,CAAC,GAAG,MAAM,QAAQ,CAAC,EAAE,MAAM,CACzB,wGAAwG,CACzG;CACF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Method } from "@ingestron/definitions";
|
|
2
|
+
/** Reads files where they already are, with Auto Loader (`read_files`). */
|
|
3
|
+
export declare const files: Method;
|
|
4
|
+
/** A streaming table: each file is read once; optionally kept current with AUTO CDC. */
|
|
5
|
+
export declare const streamingTable: Method;
|
|
6
|
+
/** A materialised view, recomputed (incrementally where possible) on each update. */
|
|
7
|
+
export declare const materialisedView: Method;
|
|
8
|
+
/** The transform's own SQL, with `{{ tables.<input> }}` replaced by full names. */
|
|
9
|
+
export declare const sql: Method;
|
|
10
|
+
//# sourceMappingURL=methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methods.d.ts","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,wBAAwB,CAAC;AAoBpE,2EAA2E;AAC3E,eAAO,MAAM,KAAK,EAAE,MA6DnB,CAAC;AAKF,wFAAwF;AACxF,eAAO,MAAM,cAAc,EAAE,MA+C5B,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,EAAE,MAY9B,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,GAAG,EAAE,MAUjB,CAAC"}
|
package/dist/methods.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { renderQuery } from "./query.js";
|
|
2
|
+
const clusterBy = {
|
|
3
|
+
description: "How the table is clustered: auto, none, or a list of columns.",
|
|
4
|
+
anyOf: [
|
|
5
|
+
{
|
|
6
|
+
enum: ["auto", "none"],
|
|
7
|
+
enumDescriptions: ["Databricks chooses the clustering columns.", "No clustering."],
|
|
8
|
+
},
|
|
9
|
+
{ type: "array", minItems: 1, items: { type: "string" } },
|
|
10
|
+
],
|
|
11
|
+
examples: [["customer_id", "ordered_at"]],
|
|
12
|
+
};
|
|
13
|
+
const properties = {
|
|
14
|
+
type: "object",
|
|
15
|
+
description: "Table properties (TBLPROPERTIES).",
|
|
16
|
+
additionalProperties: { type: "string" },
|
|
17
|
+
};
|
|
18
|
+
/** Reads files where they already are, with Auto Loader (`read_files`). */
|
|
19
|
+
export const files = {
|
|
20
|
+
action: "ingest",
|
|
21
|
+
description: "Reads files in cloud storage or a volume with Auto Loader.",
|
|
22
|
+
connectors: ["files"],
|
|
23
|
+
connectivity: ["public", "workspace"],
|
|
24
|
+
produces: "files",
|
|
25
|
+
defaults: {
|
|
26
|
+
format: "parquet",
|
|
27
|
+
schema_evolution: "addNewColumns",
|
|
28
|
+
schema_hints: true,
|
|
29
|
+
options: {},
|
|
30
|
+
},
|
|
31
|
+
settings: {
|
|
32
|
+
type: "object",
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
properties: {
|
|
35
|
+
format: {
|
|
36
|
+
enum: ["parquet", "csv", "json", "avro", "orc", "xml", "text"],
|
|
37
|
+
enumDescriptions: [
|
|
38
|
+
"Parquet files.",
|
|
39
|
+
"CSV text; set options such as header: true.",
|
|
40
|
+
"JSON, one object per line unless options say otherwise.",
|
|
41
|
+
"Avro files.",
|
|
42
|
+
"ORC files.",
|
|
43
|
+
"XML; set options such as rowTag.",
|
|
44
|
+
"Each line as one row.",
|
|
45
|
+
],
|
|
46
|
+
description: "The files' format.",
|
|
47
|
+
},
|
|
48
|
+
schema_evolution: {
|
|
49
|
+
enum: [
|
|
50
|
+
"addNewColumns",
|
|
51
|
+
"addNewColumnsWithTypeWidening",
|
|
52
|
+
"rescue",
|
|
53
|
+
"failOnNewColumns",
|
|
54
|
+
"none",
|
|
55
|
+
],
|
|
56
|
+
enumDescriptions: [
|
|
57
|
+
"Stop when files gain columns, then go on with the columns added.",
|
|
58
|
+
"As addNewColumns, and also widen column types, such as int to bigint.",
|
|
59
|
+
"Keep the columns as they are; values that do not fit go to _rescued_data.",
|
|
60
|
+
"Stop when files gain columns, until the table is changed.",
|
|
61
|
+
"Ignore new columns.",
|
|
62
|
+
],
|
|
63
|
+
description: "What happens when files gain columns.",
|
|
64
|
+
},
|
|
65
|
+
schema_hints: {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
description: "Pass the contract's column types as schemaHints.",
|
|
68
|
+
},
|
|
69
|
+
options: {
|
|
70
|
+
type: "object",
|
|
71
|
+
description: "Other read_files options, such as header: true.",
|
|
72
|
+
additionalProperties: { type: ["string", "number", "boolean"] },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
check: (table) => typeof table.ingest?.connection.settings.path === "string"
|
|
77
|
+
? []
|
|
78
|
+
: ['the connection needs "settings.path"'],
|
|
79
|
+
};
|
|
80
|
+
/** Table properties are the organisation's, such as change data feed: written in the standard. */
|
|
81
|
+
const storePlacement = { standard: ["properties"] };
|
|
82
|
+
/** A streaming table: each file is read once; optionally kept current with AUTO CDC. */
|
|
83
|
+
export const streamingTable = {
|
|
84
|
+
action: "store",
|
|
85
|
+
description: "A streaming table. mode append adds rows; upsert (SCD type 1) and history (SCD type 2) apply changes by key.",
|
|
86
|
+
accepts: ["files"],
|
|
87
|
+
produces: "table",
|
|
88
|
+
defaults: { mode: "append", cluster_by: "auto", properties: {}, changes_suffix: "_changes" },
|
|
89
|
+
placement: storePlacement,
|
|
90
|
+
settings: {
|
|
91
|
+
type: "object",
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
properties: {
|
|
94
|
+
mode: {
|
|
95
|
+
enum: ["append", "upsert", "history"],
|
|
96
|
+
enumDescriptions: [
|
|
97
|
+
"Add every row as it arrives.",
|
|
98
|
+
"Keep the latest row per key (SCD type 1), ordered by sequence_by.",
|
|
99
|
+
"Keep every version of a row with when it was valid (SCD type 2), ordered by sequence_by.",
|
|
100
|
+
],
|
|
101
|
+
description: "How rows are kept.",
|
|
102
|
+
},
|
|
103
|
+
sequence_by: {
|
|
104
|
+
type: "string",
|
|
105
|
+
description: "Column that orders changes; needed for upsert and history.",
|
|
106
|
+
examples: ["updated_at"],
|
|
107
|
+
},
|
|
108
|
+
delete_when: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "SQL condition for rows that are deletions, such as op = 'D'.",
|
|
111
|
+
examples: ["op = 'D'"],
|
|
112
|
+
},
|
|
113
|
+
except_columns: {
|
|
114
|
+
type: "array",
|
|
115
|
+
items: { type: "string" },
|
|
116
|
+
description: "Change columns not to keep, such as op.",
|
|
117
|
+
},
|
|
118
|
+
changes_suffix: {
|
|
119
|
+
type: "string",
|
|
120
|
+
pattern: "^_[a-z0-9_]+$",
|
|
121
|
+
description: "Suffix of the table that keeps changes as received.",
|
|
122
|
+
examples: ["_changes"],
|
|
123
|
+
},
|
|
124
|
+
cluster_by: clusterBy,
|
|
125
|
+
properties,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
check: checkChangeData,
|
|
129
|
+
};
|
|
130
|
+
/** A materialised view, recomputed (incrementally where possible) on each update. */
|
|
131
|
+
export const materialisedView = {
|
|
132
|
+
action: "store",
|
|
133
|
+
description: "A materialised view. Expectations make every refresh a full one.",
|
|
134
|
+
accepts: ["files", "table", "query"],
|
|
135
|
+
produces: "table",
|
|
136
|
+
defaults: { cluster_by: "auto", properties: {} },
|
|
137
|
+
placement: storePlacement,
|
|
138
|
+
settings: {
|
|
139
|
+
type: "object",
|
|
140
|
+
additionalProperties: false,
|
|
141
|
+
properties: { cluster_by: clusterBy, properties },
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
/** The transform's own SQL, with `{{ tables.<input> }}` replaced by full names. */
|
|
145
|
+
export const sql = {
|
|
146
|
+
action: "transform",
|
|
147
|
+
description: "Runs the transform's SQL query.",
|
|
148
|
+
accepts: ["table"],
|
|
149
|
+
produces: "query",
|
|
150
|
+
settings: { type: "object", additionalProperties: false },
|
|
151
|
+
check: (table) => {
|
|
152
|
+
const result = renderQuery(table.transform?.code ?? "", table.transform?.inputs ?? {});
|
|
153
|
+
return "problem" in result ? [result.problem] : [];
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
function checkChangeData(table) {
|
|
157
|
+
const settings = table.store?.settings ?? {};
|
|
158
|
+
if (settings.mode === "append")
|
|
159
|
+
return [];
|
|
160
|
+
const problems = [];
|
|
161
|
+
if (table.contract.primaryKey.length === 0)
|
|
162
|
+
problems.push(`mode ${String(settings.mode)} needs primary key columns in the contract`);
|
|
163
|
+
if (typeof settings.sequence_by !== "string")
|
|
164
|
+
problems.push(`mode ${String(settings.mode)} needs "sequence_by"`);
|
|
165
|
+
return problems;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=methods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"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"}
|
package/dist/pack.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Pack } from "@ingestron/definitions";
|
|
2
|
+
/**
|
|
3
|
+
* The Databricks pack, version 1. Name it in a standard as `pack: databricks@1`.
|
|
4
|
+
* @example
|
|
5
|
+
* const result = loadDefinitions("definitions", { pack: databricks, environment: "dev" });
|
|
6
|
+
* if (result.model) console.log(Object.keys(databricks.build(result.model)));
|
|
7
|
+
*/
|
|
8
|
+
export declare const databricks: Pack;
|
|
9
|
+
//# sourceMappingURL=pack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAUnD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,IAuDxB,CAAC"}
|
package/dist/pack.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// The pack: methods, smart defaults, rules and the build function.
|
|
2
|
+
import { contract } from "./contract.js";
|
|
3
|
+
import { connectors } from "./connectors.js";
|
|
4
|
+
import { build } from "./build.js";
|
|
5
|
+
import { request } from "./request.js";
|
|
6
|
+
import { capabilities, tagProblems } from "./capabilities.js";
|
|
7
|
+
import { lakeflowConnect } from "./connect-method.js";
|
|
8
|
+
import { connectGroups, sharedProblems } from "./connect.js";
|
|
9
|
+
import { files, materialisedView, sql, streamingTable } from "./methods.js";
|
|
10
|
+
import { pyspark } from "./pyspark-method.js";
|
|
11
|
+
/**
|
|
12
|
+
* The Databricks pack, version 1. Name it in a standard as `pack: databricks@1`.
|
|
13
|
+
* @example
|
|
14
|
+
* const result = loadDefinitions("definitions", { pack: databricks, environment: "dev" });
|
|
15
|
+
* if (result.model) console.log(Object.keys(databricks.build(result.model)));
|
|
16
|
+
*/
|
|
17
|
+
export const databricks = {
|
|
18
|
+
id: "databricks",
|
|
19
|
+
version: 1,
|
|
20
|
+
defaults: {
|
|
21
|
+
naming: {
|
|
22
|
+
catalog: "{{ environment }}",
|
|
23
|
+
schema: "{{ layer }}",
|
|
24
|
+
table: "{{ table }}",
|
|
25
|
+
pipeline: "{{ project }}",
|
|
26
|
+
job: "{{ project }}_refresh",
|
|
27
|
+
},
|
|
28
|
+
layers: ["bronze", "silver", "gold"],
|
|
29
|
+
types: {
|
|
30
|
+
string: "STRING",
|
|
31
|
+
integer: "BIGINT",
|
|
32
|
+
number: "DOUBLE",
|
|
33
|
+
boolean: "BOOLEAN",
|
|
34
|
+
date: "DATE",
|
|
35
|
+
timestamp: "TIMESTAMP",
|
|
36
|
+
},
|
|
37
|
+
quality: { required: "warn", default: "warn" },
|
|
38
|
+
},
|
|
39
|
+
rules: {
|
|
40
|
+
ingest: {
|
|
41
|
+
default: "files",
|
|
42
|
+
when: [
|
|
43
|
+
{ connector: "sqlserver", use: "lakeflow-connect" },
|
|
44
|
+
{ connector: "salesforce", use: "lakeflow-connect" },
|
|
45
|
+
{ connector: "kafka", use: "lakeflow-connect", with: { mode: "append" } },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
transform: { default: "sql" },
|
|
49
|
+
store: { default: "streaming-table", when: [{ input: "query", use: "materialised-view" }] },
|
|
50
|
+
},
|
|
51
|
+
methods: {
|
|
52
|
+
files,
|
|
53
|
+
"lakeflow-connect": lakeflowConnect,
|
|
54
|
+
sql,
|
|
55
|
+
pyspark,
|
|
56
|
+
"streaming-table": streamingTable,
|
|
57
|
+
"materialised-view": materialisedView,
|
|
58
|
+
},
|
|
59
|
+
connectors,
|
|
60
|
+
capabilities,
|
|
61
|
+
contract,
|
|
62
|
+
check(model) {
|
|
63
|
+
const problems = [];
|
|
64
|
+
if (!model.platform.compute.serverless)
|
|
65
|
+
problems.push("classic compute is not supported yet; leave compute.serverless at true");
|
|
66
|
+
if (model.tables.length === 0)
|
|
67
|
+
problems.push("there is nothing to generate: add a flow or a transform");
|
|
68
|
+
return [...problems, ...tagProblems(model), ...sharedProblems(connectGroups(model))];
|
|
69
|
+
},
|
|
70
|
+
build,
|
|
71
|
+
request,
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=pack.js.map
|
package/dist/pack.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack.js","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAS;IAC9B,EAAE,EAAE,YAAY;IAChB,OAAO,EAAE,CAAC;IACV,QAAQ,EAAE;QACR,MAAM,EAAE;YACN,OAAO,EAAE,mBAAmB;YAC5B,MAAM,EAAE,aAAa;YACrB,KAAK,EAAE,aAAa;YACpB,QAAQ,EAAE,eAAe;YACzB,GAAG,EAAE,uBAAuB;SAC7B;QACD,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;QACpC,KAAK,EAAE;YACL,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,QAAQ;YACjB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,WAAW;SACvB;QACD,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;KAC/C;IACD,KAAK,EAAE;QACL,MAAM,EAAE;YACN,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE;gBACJ,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,kBAAkB,EAAE;gBACnD,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,kBAAkB,EAAE;gBACpD,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;aAC1E;SACF;QACD,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC7B,KAAK,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,mBAAmB,EAAE,CAAC,EAAE;KAC5F;IACD,OAAO,EAAE;QACP,KAAK;QACL,kBAAkB,EAAE,eAAe;QACnC,GAAG;QACH,OAAO;QACP,iBAAiB,EAAE,cAAc;QACjC,mBAAmB,EAAE,gBAAgB;KACtC;IACD,UAAU;IACV,YAAY;IACZ,QAAQ;IACR,KAAK,CAAC,KAAK;QACT,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU;YACpC,QAAQ,CAAC,IAAI,CAAC,wEAAwE,CAAC,CAAC;QAC1F,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,EAAE,GAAG,cAAc,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC;IACD,KAAK;IACL,OAAO;CACR,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pyspark-method.d.ts","sourceRoot":"","sources":["../src/pyspark-method.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,wFAAwF;AACxF,eAAO,MAAM,OAAO,EAAE,MA4BrB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** A PySpark transform: the code defines a function that gets a DataFrame per input. */
|
|
2
|
+
export const pyspark = {
|
|
3
|
+
action: "transform",
|
|
4
|
+
description: "Calls a function in the transform's Python code with a dict of input DataFrames; it returns one DataFrame.",
|
|
5
|
+
accepts: ["table"],
|
|
6
|
+
produces: "query",
|
|
7
|
+
defaults: { function: "transform" },
|
|
8
|
+
settings: {
|
|
9
|
+
type: "object",
|
|
10
|
+
additionalProperties: false,
|
|
11
|
+
properties: {
|
|
12
|
+
function: {
|
|
13
|
+
type: "string",
|
|
14
|
+
pattern: "^[A-Za-z_][A-Za-z0-9_]*$",
|
|
15
|
+
description: "The function to call. Default: transform.",
|
|
16
|
+
examples: ["build_daily_sales"],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
check: (table) => {
|
|
21
|
+
const code = table.transform?.code ?? "";
|
|
22
|
+
const name = String(table.transform?.settings.function);
|
|
23
|
+
const defined = new RegExp(`^def ${name}\\(`, "m").test(code);
|
|
24
|
+
return [
|
|
25
|
+
...(defined ? [] : [`the code has no function "def ${name}(tables)"`]),
|
|
26
|
+
...unknownInputs(code, Object.keys(table.transform?.inputs ?? {})),
|
|
27
|
+
];
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Each `tables["x"]` in the code whose `x` is not an input: the function gets
|
|
32
|
+
* a dict with one DataFrame per input, so reading another key fails.
|
|
33
|
+
*/
|
|
34
|
+
function unknownInputs(code, inputs) {
|
|
35
|
+
const lines = code.split("\n").map((line) => line.replace(/#.*/, ""));
|
|
36
|
+
const read = [...lines.join("\n").matchAll(/\btables\[\s*["']([^"']+)["']\s*\]/g)].map((match) => match[1]);
|
|
37
|
+
return [...new Set(read)]
|
|
38
|
+
.filter((key) => !inputs.includes(key))
|
|
39
|
+
.map((key) => `the code reads tables["${key}"], which is not an input; the inputs are ${inputs.map((input) => `"${input}"`).join(", ") || "none"}`);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=pyspark-method.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pyspark-method.js","sourceRoot":"","sources":["../src/pyspark-method.ts"],"names":[],"mappings":"AAGA,wFAAwF;AACxF,MAAM,CAAC,MAAM,OAAO,GAAW;IAC7B,MAAM,EAAE,WAAW;IACnB,WAAW,EACT,4GAA4G;IAC9G,OAAO,EAAE,CAAC,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO;IACjB,QAAQ,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE;IACnC,QAAQ,EAAE;QACR,IAAI,EAAE,QAAQ;QACd,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE;YACV,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,0BAA0B;gBACnC,WAAW,EAAE,2CAA2C;gBACxD,QAAQ,EAAE,CAAC,mBAAmB,CAAC;aAChC;SACF;KACF;IACD,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,QAAQ,IAAI,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO;YACL,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,iCAAiC,IAAI,WAAW,CAAC,CAAC;YACtE,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;SACnE,CAAC;IACJ,CAAC;CACF,CAAC;AAEF;;;GAGG;AACH,SAAS,aAAa,CAAC,IAAY,EAAE,MAAgB;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC,CAAC,CAAC,GAAG,CACpF,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CACrB,CAAC;IACF,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;SACtB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;SACtC,GAAG,CACF,CAAC,GAAG,EAAE,EAAE,CACN,0BAA0B,GAAG,6CAA6C,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CACvI,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ResolvedTable } from "@ingestron/definitions";
|
|
2
|
+
/** Everything materialised-view.py.liquid needs. */
|
|
3
|
+
export declare function pythonData(table: ResolvedTable, header: string): {
|
|
4
|
+
header: string;
|
|
5
|
+
code: string;
|
|
6
|
+
name: string;
|
|
7
|
+
comment: string;
|
|
8
|
+
cluster: string;
|
|
9
|
+
properties: string;
|
|
10
|
+
expectations: {
|
|
11
|
+
decorator: "expect_all" | "expect_all_or_drop" | "expect_all_or_fail";
|
|
12
|
+
checks: string;
|
|
13
|
+
count: number;
|
|
14
|
+
}[];
|
|
15
|
+
wrapper: string;
|
|
16
|
+
function: string;
|
|
17
|
+
inputs: {
|
|
18
|
+
name: string;
|
|
19
|
+
table: string;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=python-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-data.d.ts","sourceRoot":"","sources":["../src/python-data.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAQ1E,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;EA4B9D"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Template data for a PySpark transform's Python file. Python string and
|
|
2
|
+
// dict literals are written as JSON, which Python reads the same way.
|
|
3
|
+
import { fullNameText } from "@ingestron/definitions";
|
|
4
|
+
const decorators = {
|
|
5
|
+
warn: "expect_all",
|
|
6
|
+
drop: "expect_all_or_drop",
|
|
7
|
+
fail: "expect_all_or_fail",
|
|
8
|
+
};
|
|
9
|
+
/** Everything materialised-view.py.liquid needs. */
|
|
10
|
+
export function pythonData(table, header) {
|
|
11
|
+
const store = table.store?.settings ?? {};
|
|
12
|
+
const transform = table.transform;
|
|
13
|
+
const properties = (store.properties ?? {});
|
|
14
|
+
return {
|
|
15
|
+
header,
|
|
16
|
+
code: transform.code.trimEnd(),
|
|
17
|
+
name: JSON.stringify(fullNameText(table.fullName)),
|
|
18
|
+
comment: table.contract.description ? JSON.stringify(table.contract.description) : "",
|
|
19
|
+
cluster: clusterOf(store.cluster_by),
|
|
20
|
+
properties: Object.keys(properties).length > 0 ? pythonDict(Object.entries(properties)) : "",
|
|
21
|
+
expectations: ["warn", "drop", "fail"]
|
|
22
|
+
.map((action) => {
|
|
23
|
+
const checks = table.expectations.filter((item) => item.action === action);
|
|
24
|
+
return {
|
|
25
|
+
decorator: decorators[action],
|
|
26
|
+
checks: pythonDict(checks.map((item) => [item.name, item.check])),
|
|
27
|
+
count: checks.length,
|
|
28
|
+
};
|
|
29
|
+
})
|
|
30
|
+
.filter((group) => group.count > 0),
|
|
31
|
+
wrapper: `ingestron_${table.name}`,
|
|
32
|
+
function: String(transform.settings.function),
|
|
33
|
+
inputs: Object.entries(transform.inputs).map(([name, full]) => ({
|
|
34
|
+
name: JSON.stringify(name),
|
|
35
|
+
table: JSON.stringify(full),
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function clusterOf(value) {
|
|
40
|
+
if (value === "auto")
|
|
41
|
+
return "cluster_by_auto=True";
|
|
42
|
+
if (Array.isArray(value))
|
|
43
|
+
return `cluster_by=${JSON.stringify(value)}`;
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
/** `{"a": "b", …}`: a Python dict of strings, written the way Python code usually is. */
|
|
47
|
+
function pythonDict(entries) {
|
|
48
|
+
return `{${entries.map(([key, value]) => `${JSON.stringify(key)}: ${JSON.stringify(value)}`).join(", ")}}`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=python-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-data.js","sourceRoot":"","sources":["../src/python-data.ts"],"names":[],"mappings":"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"}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The SQL with inputs replaced, or a problem to report. A trailing `;` is removed. */
|
|
2
|
+
export declare function renderQuery(code: string, inputs: Record<string, string>): {
|
|
3
|
+
sql: string;
|
|
4
|
+
} | {
|
|
5
|
+
problem: string;
|
|
6
|
+
};
|
|
7
|
+
/** `a.b.c` → `` `a`.`b`.`c` ``, so any valid name is safe in SQL. */
|
|
8
|
+
export declare function quoteName(fullName: string): string;
|
|
9
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAMA,uFAAuF;AACvF,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAavC;AAED,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKlD"}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Renders a transform's SQL: `{{ tables.<input> }}` becomes the input's full name.
|
|
2
|
+
import { Liquid } from "liquidjs";
|
|
3
|
+
// Strict, so an input that the transform did not declare is an error.
|
|
4
|
+
const engine = new Liquid({ strictVariables: true, strictFilters: true });
|
|
5
|
+
/** The SQL with inputs replaced, or a problem to report. A trailing `;` is removed. */
|
|
6
|
+
export function renderQuery(code, inputs) {
|
|
7
|
+
const quoted = Object.fromEntries(Object.entries(inputs).map(([name, full]) => [name, quoteName(full)]));
|
|
8
|
+
try {
|
|
9
|
+
const sql = engine.parseAndRenderSync(code, { tables: quoted });
|
|
10
|
+
return { sql: sql.trim().replace(/;\s*$/, "") };
|
|
11
|
+
}
|
|
12
|
+
catch (cause) {
|
|
13
|
+
const reason = cause instanceof Error ? cause.message.split("\n")[0] : String(cause);
|
|
14
|
+
return {
|
|
15
|
+
problem: `the SQL cannot be rendered (${reason}); inputs are written {{ tables.<name> }} and must be listed in inputs`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** `a.b.c` → `` `a`.`b`.`c` ``, so any valid name is safe in SQL. */
|
|
20
|
+
export function quoteName(fullName) {
|
|
21
|
+
return fullName
|
|
22
|
+
.split(".")
|
|
23
|
+
.map((part) => `\`${part.replaceAll("`", "``")}\``)
|
|
24
|
+
.join(".");
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,sEAAsE;AACtE,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1E,uFAAuF;AACvF,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,MAA8B;IAE9B,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAC/B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CACtE,CAAC;IACF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAW,CAAC;QAC1E,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrF,OAAO;YACL,OAAO,EAAE,+BAA+B,MAAM,wEAAwE;SACvH,CAAC;IACJ,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,OAAO,QAAQ;SACZ,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC;SAClD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RequestSection, ResolvedModel } from "@ingestron/definitions";
|
|
2
|
+
/** The Databricks sections of the platform request. Nothing here is checked. */
|
|
3
|
+
export declare function request(model: ResolvedModel): RequestSection[];
|
|
4
|
+
//# sourceMappingURL=request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5E,gFAAgF;AAChF,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,EAAE,CAS9D"}
|
package/dist/request.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** The Databricks sections of the platform request. Nothing here is checked. */
|
|
2
|
+
export function request(model) {
|
|
3
|
+
return [
|
|
4
|
+
{
|
|
5
|
+
title: "Must exist before deploying",
|
|
6
|
+
intro: "Ingestron has not checked these; the generated files expect them.",
|
|
7
|
+
lines: prerequisites(model),
|
|
8
|
+
},
|
|
9
|
+
{ title: "Schedules", lines: [schedule(model)] },
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
function prerequisites(model) {
|
|
13
|
+
const { platform } = model;
|
|
14
|
+
const names = model.tables.map((table) => table.fullName);
|
|
15
|
+
const catalogs = unique(names.map((name) => name.catalog));
|
|
16
|
+
const schemas = platform.manage.schemas
|
|
17
|
+
? []
|
|
18
|
+
: unique(names.map((name) => `${name.catalog}.${name.schema}`));
|
|
19
|
+
const folders = unique(model.tables.flatMap((table) => {
|
|
20
|
+
const folder = table.ingest?.settings.landing ?? table.ingest?.connection.settings.path;
|
|
21
|
+
return typeof folder === "string" ? [folder] : [];
|
|
22
|
+
}));
|
|
23
|
+
return [
|
|
24
|
+
...catalogs.map((catalog) => `Catalog \`${catalog}\`, which the deploying identity can use.`),
|
|
25
|
+
...schemas.map((schema) => `Schema \`${schema}\`, in which it can create tables and views.`),
|
|
26
|
+
...folders.map((folder) => `Files at \`${folder}\`, which the pipeline can read.`),
|
|
27
|
+
platform.compute.serverless
|
|
28
|
+
? "Serverless compute for pipelines and jobs, turned on in the workspace."
|
|
29
|
+
: "Classic compute that the pipelines may use.",
|
|
30
|
+
...(platform.workspace.host
|
|
31
|
+
? []
|
|
32
|
+
: [
|
|
33
|
+
"No workspace host is set, so `databricks bundle deploy` uses the workspace of the Databricks CLI profile that runs it.",
|
|
34
|
+
]),
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
function schedule(model) {
|
|
38
|
+
const { schedule } = model.platform;
|
|
39
|
+
if (!schedule)
|
|
40
|
+
return "No schedule: the pipelines run only when someone starts them.";
|
|
41
|
+
const state = schedule.paused ? "paused" : "runs";
|
|
42
|
+
return `Job \`${model.names.job}\`: \`${schedule.cron}\` (${schedule.timezone}), ${state}.`;
|
|
43
|
+
}
|
|
44
|
+
function unique(values) {
|
|
45
|
+
return [...new Set(values)];
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAIA,gFAAgF;AAChF,MAAM,UAAU,OAAO,CAAC,KAAoB;IAC1C,OAAO;QACL;YACE,KAAK,EAAE,6BAA6B;YACpC,KAAK,EAAE,mEAAmE;YAC1E,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC;SAC5B;QACD,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE;KACjD,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAoB;IACzC,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO;QACrC,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,CACpB,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;QACxF,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,CAAC,CAAC,CACH,CAAC;IACF,OAAO;QACL,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,OAAO,2CAA2C,CAAC;QAC7F,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,MAAM,8CAA8C,CAAC;QAC5F,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,MAAM,kCAAkC,CAAC;QAClF,QAAQ,CAAC,OAAO,CAAC,UAAU;YACzB,CAAC,CAAC,wEAAwE;YAC1E,CAAC,CAAC,6CAA6C;QACjD,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI;YACzB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,wHAAwH;aACzH,CAAC;KACP,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAoB;IACpC,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC;IACpC,IAAI,CAAC,QAAQ;QAAE,OAAO,+DAA+D,CAAC;IACtF,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;IAClD,OAAO,SAAS,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS,QAAQ,CAAC,IAAI,OAAO,QAAQ,CAAC,QAAQ,MAAM,KAAK,GAAG,CAAC;AAC9F,CAAC;AAED,SAAS,MAAM,CAAC,MAAgB;IAC9B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9B,CAAC"}
|