@ingestron/cli 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.
Files changed (175) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/README.md +44 -2
  4. package/dist/args.d.ts +105 -0
  5. package/dist/args.d.ts.map +1 -0
  6. package/dist/args.js +161 -0
  7. package/dist/args.js.map +1 -0
  8. package/dist/ask.d.ts +5 -0
  9. package/dist/ask.d.ts.map +1 -0
  10. package/dist/ask.js +17 -0
  11. package/dist/ask.js.map +1 -0
  12. package/dist/compare.d.ts +13 -0
  13. package/dist/compare.d.ts.map +1 -0
  14. package/dist/compare.js +90 -0
  15. package/dist/compare.js.map +1 -0
  16. package/dist/environments.d.ts +6 -0
  17. package/dist/environments.d.ts.map +1 -0
  18. package/dist/environments.js +25 -0
  19. package/dist/environments.js.map +1 -0
  20. package/dist/generate.d.ts +16 -0
  21. package/dist/generate.d.ts.map +1 -0
  22. package/dist/generate.js +118 -0
  23. package/dist/generate.js.map +1 -0
  24. package/dist/index.d.ts +21 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +46 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/init-choice.d.ts +10 -0
  29. package/dist/init-choice.d.ts.map +1 -0
  30. package/dist/init-choice.js +36 -0
  31. package/dist/init-choice.js.map +1 -0
  32. package/dist/init-preflight.d.ts +4 -0
  33. package/dist/init-preflight.d.ts.map +1 -0
  34. package/dist/init-preflight.js +18 -0
  35. package/dist/init-preflight.js.map +1 -0
  36. package/dist/init.d.ts +5 -0
  37. package/dist/init.d.ts.map +1 -0
  38. package/dist/init.js +136 -0
  39. package/dist/init.js.map +1 -0
  40. package/dist/io.d.ts +8 -0
  41. package/dist/io.d.ts.map +1 -0
  42. package/dist/io.js +3 -0
  43. package/dist/io.js.map +1 -0
  44. package/dist/main.d.ts +3 -0
  45. package/dist/main.d.ts.map +1 -0
  46. package/dist/main.js +11 -0
  47. package/dist/main.js.map +1 -0
  48. package/dist/outputs.d.ts +12 -0
  49. package/dist/outputs.d.ts.map +1 -0
  50. package/dist/outputs.js +53 -0
  51. package/dist/outputs.js.map +1 -0
  52. package/dist/packs.d.ts +4 -0
  53. package/dist/packs.d.ts.map +1 -0
  54. package/dist/packs.js +5 -0
  55. package/dist/packs.js.map +1 -0
  56. package/dist/project-choices.d.ts +27 -0
  57. package/dist/project-choices.d.ts.map +1 -0
  58. package/dist/project-choices.js +44 -0
  59. package/dist/project-choices.js.map +1 -0
  60. package/dist/project-questions.d.ts +5 -0
  61. package/dist/project-questions.d.ts.map +1 -0
  62. package/dist/project-questions.js +43 -0
  63. package/dist/project-questions.js.map +1 -0
  64. package/dist/project-recipes.d.ts +14 -0
  65. package/dist/project-recipes.d.ts.map +1 -0
  66. package/dist/project-recipes.js +115 -0
  67. package/dist/project-recipes.js.map +1 -0
  68. package/dist/request.d.ts +13 -0
  69. package/dist/request.d.ts.map +1 -0
  70. package/dist/request.js +110 -0
  71. package/dist/request.js.map +1 -0
  72. package/dist/sample-contract.d.ts +11 -0
  73. package/dist/sample-contract.d.ts.map +1 -0
  74. package/dist/sample-contract.js +91 -0
  75. package/dist/sample-contract.js.map +1 -0
  76. package/dist/sample-read.d.ts +8 -0
  77. package/dist/sample-read.d.ts.map +1 -0
  78. package/dist/sample-read.js +89 -0
  79. package/dist/sample-read.js.map +1 -0
  80. package/dist/samples.d.ts +33 -0
  81. package/dist/samples.d.ts.map +1 -0
  82. package/dist/samples.js +108 -0
  83. package/dist/samples.js.map +1 -0
  84. package/dist/scaffold.d.ts +34 -0
  85. package/dist/scaffold.d.ts.map +1 -0
  86. package/dist/scaffold.js +106 -0
  87. package/dist/scaffold.js.map +1 -0
  88. package/dist/schemas.d.ts +5 -0
  89. package/dist/schemas.d.ts.map +1 -0
  90. package/dist/schemas.js +39 -0
  91. package/dist/schemas.js.map +1 -0
  92. package/dist/sources.d.ts +17 -0
  93. package/dist/sources.d.ts.map +1 -0
  94. package/dist/sources.js +103 -0
  95. package/dist/sources.js.map +1 -0
  96. package/dist/templates.d.ts +64 -0
  97. package/dist/templates.d.ts.map +1 -0
  98. package/dist/templates.js +88 -0
  99. package/dist/templates.js.map +1 -0
  100. package/package.json +68 -3
  101. package/project-templates/kafka/README.md +12 -0
  102. package/project-templates/kafka/connections/events.yaml +8 -0
  103. package/project-templates/kafka/contracts/page_views.yaml +13 -0
  104. package/project-templates/kafka/contracts/purchases.yaml +13 -0
  105. package/project-templates/kafka/flows/events.yaml +10 -0
  106. package/project-templates/kafka/project.yaml +10 -0
  107. package/project-templates/kafka/standard.yaml +4 -0
  108. package/project-templates/kafka/template.yaml +3 -0
  109. package/project-templates/minimal/README.md +10 -0
  110. package/project-templates/minimal/connections/shop.yaml +7 -0
  111. package/project-templates/minimal/contracts/orders.yaml +14 -0
  112. package/project-templates/minimal/flows/orders.yaml +6 -0
  113. package/project-templates/minimal/project.yaml +11 -0
  114. package/project-templates/minimal/standard.yaml +4 -0
  115. package/project-templates/minimal/template.yaml +7 -0
  116. package/project-templates/private-files/README.md +29 -0
  117. package/project-templates/private-files/connections/share.yaml +10 -0
  118. package/project-templates/private-files/contracts/sales.yaml +14 -0
  119. package/project-templates/private-files/contracts/stock.yaml +14 -0
  120. package/project-templates/private-files/flows/stores.yaml +9 -0
  121. package/project-templates/private-files/platform.yaml +11 -0
  122. package/project-templates/private-files/project.yaml +19 -0
  123. package/project-templates/private-files/standard.yaml +11 -0
  124. package/project-templates/private-files/template.yaml +10 -0
  125. package/project-templates/private-sql-server/README.md +25 -0
  126. package/project-templates/private-sql-server/connections/erp.yaml +10 -0
  127. package/project-templates/private-sql-server/contracts/customers.yaml +14 -0
  128. package/project-templates/private-sql-server/contracts/invoices.yaml +15 -0
  129. package/project-templates/private-sql-server/flows/erp.yaml +13 -0
  130. package/project-templates/private-sql-server/platform.yaml +11 -0
  131. package/project-templates/private-sql-server/project.yaml +19 -0
  132. package/project-templates/private-sql-server/standard.yaml +17 -0
  133. package/project-templates/private-sql-server/template.yaml +10 -0
  134. package/project-templates/pyspark/README.md +12 -0
  135. package/project-templates/pyspark/connections/billing.yaml +7 -0
  136. package/project-templates/pyspark/contracts/invoices.yaml +16 -0
  137. package/project-templates/pyspark/contracts/monthly_revenue.yaml +14 -0
  138. package/project-templates/pyspark/flows/billing.yaml +6 -0
  139. package/project-templates/pyspark/project.yaml +11 -0
  140. package/project-templates/pyspark/standard.yaml +4 -0
  141. package/project-templates/pyspark/template.yaml +3 -0
  142. package/project-templates/pyspark/transforms/monthly_revenue.py +10 -0
  143. package/project-templates/pyspark/transforms/monthly_revenue.yaml +10 -0
  144. package/project-templates/retail/README.md +22 -0
  145. package/project-templates/retail/connections/shop.yaml +8 -0
  146. package/project-templates/retail/contracts/customers.yaml +12 -0
  147. package/project-templates/retail/contracts/daily_sales.yaml +19 -0
  148. package/project-templates/retail/contracts/orders.yaml +16 -0
  149. package/project-templates/retail/flows/shop.yaml +18 -0
  150. package/project-templates/retail/platform.yaml +21 -0
  151. package/project-templates/retail/project.yaml +18 -0
  152. package/project-templates/retail/standard.yaml +15 -0
  153. package/project-templates/retail/template.yaml +10 -0
  154. package/project-templates/retail/transforms/daily_sales.sql +7 -0
  155. package/project-templates/retail/transforms/daily_sales.yaml +7 -0
  156. package/project-templates/salesforce/README.md +6 -0
  157. package/project-templates/salesforce/connections/salesforce.yaml +7 -0
  158. package/project-templates/salesforce/contracts/accounts.yaml +13 -0
  159. package/project-templates/salesforce/contracts/opportunities.yaml +14 -0
  160. package/project-templates/salesforce/flows/salesforce.yaml +7 -0
  161. package/project-templates/salesforce/project.yaml +9 -0
  162. package/project-templates/salesforce/standard.yaml +4 -0
  163. package/project-templates/salesforce/template.yaml +3 -0
  164. package/project-templates/sql-server/README.md +17 -0
  165. package/project-templates/sql-server/connections/erp.yaml +8 -0
  166. package/project-templates/sql-server/contracts/customer_balances.yaml +13 -0
  167. package/project-templates/sql-server/contracts/customers.yaml +13 -0
  168. package/project-templates/sql-server/contracts/invoices.yaml +14 -0
  169. package/project-templates/sql-server/flows/erp.yaml +13 -0
  170. package/project-templates/sql-server/platform.yaml +6 -0
  171. package/project-templates/sql-server/project.yaml +13 -0
  172. package/project-templates/sql-server/standard.yaml +9 -0
  173. package/project-templates/sql-server/template.yaml +3 -0
  174. package/project-templates/sql-server/transforms/customer_balances.sql +5 -0
  175. package/project-templates/sql-server/transforms/customer_balances.yaml +6 -0
@@ -0,0 +1,10 @@
1
+ # Used by `ingestron init --template private-files`.
2
+ title: Private file share through Azure Data Factory
3
+ summary: "Two packs: ADF copies new files from a private share into landing storage and Databricks reads them."
4
+ questions:
5
+ - value: landing
6
+ ask: "Landing folder for the copied files (abfss://…)"
7
+ default: abfss://landing@storeslakedev.dfs.core.windows.net/stores
8
+ - value: key_vault
9
+ ask: "Key Vault holding the share account's password"
10
+ default: https://kv-stores-dev.vault.azure.net
@@ -0,0 +1,25 @@
1
+ # Private SQL Server
2
+
3
+ The ERP of the sql-server starter, but now **unreachable** from the workspace
4
+ (`connectivity: private`). The standard uses two packs,
5
+ `[databricks@1, adf@1]`. Databricks' Lakeflow Connect rule is skipped
6
+ because it cannot reach a private source, and the ADF pack's rule chooses
7
+ `adf-copy`:
8
+
9
+ - **ADF** ([build/prod/adf](build/prod/adf/)), in the folder layout ADF
10
+ keeps in Git:
11
+ - a [SQL Server linked service](build/prod/adf/linkedService/ls_erp_erp_erp.json)
12
+ through the self-hosted integration runtime `shir-erp`, with the password
13
+ from Key Vault;
14
+ - a [pipeline](build/prod/adf/pipeline/pl_erp_erp.json) with one copy per
15
+ table, selecting only the contract's columns changed in the window, into
16
+ `<landing>/erp.dbo.Customers/` as Parquet;
17
+ - an hourly [tumbling window trigger](build/prod/adf/trigger/tr_erp_erp.json),
18
+ started in production and stopped elsewhere.
19
+ - **Databricks** ([build/prod/databricks](build/prod/databricks/)): streaming
20
+ tables over the same landing folders that keep the current row per key
21
+ (`mode: upsert`), and the refresh job.
22
+
23
+ Each side runs on its own schedule; Auto Loader picks up new files whenever
24
+ the pipeline runs. The integration runtime and Key Vault are shared
25
+ infrastructure: they must exist and are only referenced.
@@ -0,0 +1,10 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: erp
4
+ description: The ERP's SQL Server, inside the corporate network only.
5
+ connector: sqlserver
6
+ connectivity: private
7
+ settings:
8
+ server: erp-sql01.corp.local
9
+ user: ingest_reader
10
+ password: ${secret:erp-sql-password} # a Key Vault secret name
@@ -0,0 +1,14 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: erp.customers
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: customers
8
+ logicalType: object
9
+ description: ERP customers.
10
+ properties:
11
+ - { name: customer_id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: name, logicalType: string }
13
+ - { name: country, logicalType: string }
14
+ - { name: modified_at, logicalType: timestamp, required: true }
@@ -0,0 +1,15 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: erp.invoices
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: invoices
8
+ logicalType: object
9
+ description: ERP invoices.
10
+ properties:
11
+ - { name: invoice_id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: customer_id, logicalType: integer, required: true }
13
+ - { name: amount, logicalType: number, physicalType: "DECIMAL(18,2)" }
14
+ - { name: status, logicalType: string }
15
+ - { name: updated_at, logicalType: timestamp, required: true }
@@ -0,0 +1,13 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: erp
4
+ source: erp
5
+ tables:
6
+ customers: { object: erp.dbo.Customers, contract: erp.customers }
7
+ invoices:
8
+ object: erp.dbo.Invoices
9
+ contract: erp.invoices
10
+ ingest:
11
+ with: { watermark_column: updated_at }
12
+ store:
13
+ with: { sequence_by: updated_at }
@@ -0,0 +1,11 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Platform
3
+ id: lakehouse
4
+ schedule:
5
+ cron: 0 15 * * * ?
6
+ timezone: Pacific/Auckland
7
+ settings:
8
+ adf-copy: # shared infrastructure, referenced by name
9
+ landing: ${values.landing}
10
+ key_vault: ${values.key_vault}
11
+ integration_runtime: shir-erp
@@ -0,0 +1,19 @@
1
+ # The same ERP as the sql-server starter, but the workspace cannot reach it. ADF copies
2
+ # the changed rows through a self-hosted integration runtime into a landing
3
+ # folder; Databricks keeps the tables current from those files.
4
+ id: erp
5
+ environments:
6
+ dev: {}
7
+ prod:
8
+ values:
9
+ landing: abfss://landing@erplakeprod.dfs.core.windows.net/erp
10
+ key_vault: https://kv-erp-prod.vault.azure.net
11
+ values:
12
+ landing: abfss://landing@erplakedev.dfs.core.windows.net/erp
13
+ key_vault: https://kv-erp-dev.vault.azure.net
14
+ imports:
15
+ - standard.yaml
16
+ - platform.yaml
17
+ - connections/*.yaml
18
+ - flows/*.yaml
19
+ - contracts/*.yaml
@@ -0,0 +1,17 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Standard
3
+ id: erp
4
+ packs: [databricks@1, adf@1] # Databricks first: it provides the defaults
5
+ naming:
6
+ catalog: erp_{{ environment }}
7
+ ingest:
8
+ settings:
9
+ adf-copy: # the schedule; where it lands is the platform's
10
+ mode: incremental
11
+ watermark_column: modified_at
12
+ every: { frequency: Hour, interval: 1 }
13
+ start: 2026-10-01T00:00:00Z
14
+ store:
15
+ settings:
16
+ # Each window's file holds the rows changed in it; keep the current version.
17
+ streaming-table: { mode: upsert, sequence_by: modified_at }
@@ -0,0 +1,10 @@
1
+ # Used by `ingestron init --template private-sql-server`.
2
+ title: Private SQL Server through Azure Data Factory
3
+ summary: "Two packs: ADF copies a private SQL Server into landing storage and Databricks keeps the tables current."
4
+ questions:
5
+ - value: landing
6
+ ask: "Landing folder for the copied files (abfss://…)"
7
+ default: abfss://landing@erplakedev.dfs.core.windows.net/erp
8
+ - value: key_vault
9
+ ask: "Key Vault holding the SQL login's password"
10
+ default: https://kv-erp-dev.vault.azure.net
@@ -0,0 +1,12 @@
1
+ # PySpark
2
+
3
+ `monthly_revenue` is written in PySpark (`transform: { uses: pyspark }`).
4
+ [Its code](transforms/monthly_revenue.py) only defines
5
+ `transform(tables)`, which receives a DataFrame per input and returns the
6
+ result. The pack generates [a Python file](build/dev/src/subscriptions/silver/monthly_revenue.py)
7
+ that copies the code unchanged and wraps it as a materialised view with the
8
+ pipelines Python API (`from pyspark import pipelines as dp`): the full name,
9
+ comment and clustering from the definitions, and the checks as
10
+ `expect_all…` decorators.
11
+
12
+ The bronze table stays SQL; one pipeline runs both files.
@@ -0,0 +1,7 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: billing
4
+ connector: files
5
+ connectivity: public
6
+ settings:
7
+ path: /Volumes/dev/landing/billing
@@ -0,0 +1,16 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: billing.invoices
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: invoices
8
+ logicalType: object
9
+ description: Invoices from the billing system.
10
+ properties:
11
+ - { name: invoice_id, logicalType: string, required: true, primaryKey: true }
12
+ - { name: customer_id, logicalType: string, required: true }
13
+ - { name: plan, logicalType: string }
14
+ - { name: amount, logicalType: number, physicalType: "DECIMAL(18,2)" }
15
+ - { name: status, logicalType: string }
16
+ - { name: issued_at, logicalType: timestamp }
@@ -0,0 +1,14 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: billing.monthly_revenue
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: monthly_revenue
8
+ logicalType: object
9
+ description: Paid revenue and customers per month and plan.
10
+ properties:
11
+ - { name: month, logicalType: timestamp, required: true }
12
+ - { name: plan, logicalType: string }
13
+ - { name: revenue, logicalType: number, physicalType: "DECIMAL(18,2)" }
14
+ - { name: customers, logicalType: integer }
@@ -0,0 +1,6 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: billing
4
+ source: billing
5
+ tables:
6
+ invoices: { object: invoices, contract: billing.invoices }
@@ -0,0 +1,11 @@
1
+ # A transform written in PySpark: the code is a function that receives a
2
+ # DataFrame per input and returns the result.
3
+ id: subscriptions
4
+ environments:
5
+ dev: {}
6
+ imports:
7
+ - standard.yaml
8
+ - connections/*.yaml
9
+ - flows/*.yaml
10
+ - transforms/*.yaml
11
+ - contracts/*.yaml
@@ -0,0 +1,4 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Standard
3
+ id: subscriptions
4
+ packs: [databricks@1]
@@ -0,0 +1,3 @@
1
+ # Used by `ingestron init --template pyspark`.
2
+ title: PySpark transform
3
+ summary: A PySpark transform wrapped as a materialised view.
@@ -0,0 +1,10 @@
1
+ from pyspark.sql import functions as F
2
+
3
+
4
+ def transform(tables):
5
+ invoices = tables["invoices"]
6
+ return (
7
+ invoices.where(F.col("status") == "paid")
8
+ .groupBy(F.date_trunc("month", "issued_at").alias("month"), "plan")
9
+ .agg(F.sum("amount").alias("revenue"), F.countDistinct("customer_id").alias("customers"))
10
+ )
@@ -0,0 +1,10 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Transform
3
+ id: monthly_revenue
4
+ inputs: [invoices]
5
+ transform:
6
+ uses: pyspark
7
+ code: ${file:monthly_revenue.py}
8
+ contract: billing.monthly_revenue
9
+ expectations:
10
+ - { name: revenue_not_negative, check: revenue >= 0, action: fail }
@@ -0,0 +1,22 @@
1
+ # Retail example
2
+
3
+ A complete project: web shop files brought into bronze, orders kept current
4
+ from change records, and a silver table of daily sales.
5
+
6
+ | File | What it shows |
7
+ | ----------------------------- | ------------------------------------------------------------------ |
8
+ | `project.yaml` | Values that differ between `dev` and `prod` |
9
+ | `standard.yaml` | The organisation's catalog naming, quality and a fixed setting |
10
+ | `platform.yaml` | Workspace, identity, tags, permissions, schedule and notifications |
11
+ | `connections/shop.yaml` | A files source whose path differs per environment |
12
+ | `flows/shop.yaml` | Change data (`mode: upsert`), CSV options and an expectation |
13
+ | `transforms/daily_sales.yaml` | SQL in its own file, reading two flow tables |
14
+ | `contracts/*.yaml` | ODCS contracts: types, keys and descriptions |
15
+
16
+ ```sh
17
+ pnpm ingestron plan --definitions examples/08-retail --out examples/08-retail/build
18
+ ```
19
+
20
+ The generated files for both environments are in [build/](build/).
21
+ For editor help, run `ingestron schemas` in this folder and add the printed
22
+ setting to VS Code.
@@ -0,0 +1,8 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: shop
4
+ description: Files the web shop exports every hour.
5
+ connector: files
6
+ connectivity: public
7
+ settings:
8
+ path: ${values.storage}/shop
@@ -0,0 +1,12 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: shop.customers
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: customers
8
+ logicalType: object
9
+ description: Web shop customers.
10
+ properties:
11
+ - { name: id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: email, logicalType: string, required: true }
@@ -0,0 +1,19 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: shop.daily_sales
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: daily_sales
8
+ logicalType: object
9
+ description: Order totals per customer and day.
10
+ properties:
11
+ - {
12
+ name: email,
13
+ logicalType: string,
14
+ required: true,
15
+ primaryKey: true,
16
+ primaryKeyPosition: 1,
17
+ }
18
+ - { name: day, logicalType: date, required: true, primaryKey: true, primaryKeyPosition: 2 }
19
+ - { name: total, logicalType: number, physicalType: "DECIMAL(18,2)" }
@@ -0,0 +1,16 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: shop.orders
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: orders
8
+ logicalType: object
9
+ description: One row per order, kept current.
10
+ properties:
11
+ - { name: id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: customer_id, logicalType: integer, required: true }
13
+ - { name: ordered_at, logicalType: timestamp, required: true }
14
+ - { name: total, logicalType: number, physicalType: "DECIMAL(18,2)" }
15
+ - { name: updated_at, logicalType: timestamp, required: true }
16
+ - { name: op, logicalType: string }
@@ -0,0 +1,18 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: shop
4
+ description: Orders and customers from the web shop.
5
+ source: shop
6
+ tables:
7
+ orders:
8
+ object: orders
9
+ contract: shop.orders
10
+ store:
11
+ with: { mode: upsert, sequence_by: updated_at, delete_when: "op = 'D'", except_columns: [op] }
12
+ expectations:
13
+ - { name: positive_total, check: total >= 0 }
14
+ customers:
15
+ object: customers
16
+ contract: shop.customers
17
+ ingest:
18
+ with: { format: csv, options: { header: true } }
@@ -0,0 +1,21 @@
1
+ # Where this project's assets go. Values differ per environment.
2
+ apiVersion: ingestron.io/v1
3
+ kind: Platform
4
+ id: lakehouse
5
+ workspace:
6
+ host: ${values.host}
7
+ run_as:
8
+ service_principal_name: 7f3c2a10-0000-4000-8000-000000000001
9
+ tags:
10
+ team: sales
11
+ environment: ${environment.name}
12
+ permissions:
13
+ - group_name: sales-engineers
14
+ level: CAN_MANAGE
15
+ - group_name: sales-analysts
16
+ level: CAN_VIEW
17
+ schedule:
18
+ cron: 0 0 5 * * ?
19
+ timezone: Pacific/Auckland
20
+ notifications:
21
+ on_failure: [sales-data@example.com]
@@ -0,0 +1,18 @@
1
+ id: retail
2
+ name: Retail sales
3
+ values:
4
+ host: https://adb-1111111111111111.1.azuredatabricks.net
5
+ storage: abfss://landing@retaildev.dfs.core.windows.net
6
+ environments:
7
+ dev: {}
8
+ prod:
9
+ values:
10
+ host: https://adb-2222222222222222.2.azuredatabricks.net
11
+ storage: abfss://landing@retailprod.dfs.core.windows.net
12
+ imports:
13
+ - standard.yaml
14
+ - platform.yaml
15
+ - connections/*.yaml
16
+ - flows/*.yaml
17
+ - transforms/*.yaml
18
+ - contracts/*.yaml
@@ -0,0 +1,15 @@
1
+ # How the organisation builds data products. Shared by every project.
2
+ apiVersion: ingestron.io/v1
3
+ kind: Standard
4
+ id: acme
5
+ packs: [databricks@1]
6
+ naming:
7
+ catalog: "{{ environment }}_sales"
8
+ quality:
9
+ required: drop
10
+ store:
11
+ settings:
12
+ streaming-table:
13
+ properties: { delta.enableChangeDataFeed: "true" }
14
+ fixed:
15
+ streaming-table: { cluster_by: auto }
@@ -0,0 +1,10 @@
1
+ # Used by `ingestron init --template retail`.
2
+ title: Retail
3
+ summary: "A complete project: files into bronze, orders kept current from change records, and daily sales in silver."
4
+ questions:
5
+ - value: host
6
+ ask: "Databricks workspace URL"
7
+ default: https://adb-1111111111111111.1.azuredatabricks.net
8
+ - value: storage
9
+ ask: "Storage account URL for the landing files"
10
+ default: abfss://landing@retaildev.dfs.core.windows.net
@@ -0,0 +1,7 @@
1
+ SELECT
2
+ c.email,
3
+ DATE(o.ordered_at) AS day,
4
+ SUM(o.total) AS total
5
+ FROM {{ tables.orders }} AS o
6
+ JOIN {{ tables.customers }} AS c ON c.id = o.customer_id
7
+ GROUP BY c.email, DATE(o.ordered_at)
@@ -0,0 +1,7 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Transform
3
+ id: daily_sales
4
+ description: Order totals per customer and day.
5
+ inputs: [orders, customers]
6
+ code: ${file:daily_sales.sql}
7
+ contract: shop.daily_sales
@@ -0,0 +1,6 @@
1
+ # Salesforce
2
+
3
+ Two Salesforce objects through a Unity Catalog connection. SaaS sources need
4
+ no gateway: [one ingestion pipeline](build/dev/resources/crm_salesforce_ingestion.pipeline.yml)
5
+ writes `accounts` and `opportunities`, keyed by `Id`, with only the
6
+ contract's fields.
@@ -0,0 +1,7 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: salesforce
4
+ connector: salesforce
5
+ connectivity: public
6
+ settings:
7
+ uc_connection: salesforce_prod
@@ -0,0 +1,13 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: crm.accounts
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: accounts
8
+ logicalType: object
9
+ description: Salesforce accounts.
10
+ properties:
11
+ - { name: Id, logicalType: string, required: true, primaryKey: true }
12
+ - { name: Name, logicalType: string }
13
+ - { name: Industry, logicalType: string }
@@ -0,0 +1,14 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: crm.opportunities
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: opportunities
8
+ logicalType: object
9
+ description: Salesforce opportunities.
10
+ properties:
11
+ - { name: Id, logicalType: string, required: true, primaryKey: true }
12
+ - { name: AccountId, logicalType: string }
13
+ - { name: Amount, logicalType: number }
14
+ - { name: StageName, logicalType: string }
@@ -0,0 +1,7 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: salesforce
4
+ source: salesforce
5
+ tables:
6
+ accounts: { object: Account, contract: crm.accounts }
7
+ opportunities: { object: Opportunity, contract: crm.opportunities }
@@ -0,0 +1,9 @@
1
+ # Salesforce objects ingested with Lakeflow Connect; no gateway is needed.
2
+ id: crm
3
+ environments:
4
+ dev: {}
5
+ imports:
6
+ - standard.yaml
7
+ - connections/*.yaml
8
+ - flows/*.yaml
9
+ - contracts/*.yaml
@@ -0,0 +1,4 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Standard
3
+ id: crm
4
+ packs: [databricks@1]
@@ -0,0 +1,3 @@
1
+ # Used by `ingestron init --template salesforce`.
2
+ title: Salesforce through Lakeflow Connect
3
+ summary: Two Salesforce objects through a Unity Catalog connection, keyed and limited to the contract's fields.
@@ -0,0 +1,17 @@
1
+ # SQL Server
2
+
3
+ The ERP database is reachable from the workspace over private networking
4
+ (`connectivity: workspace`), so the pack ingests it with Lakeflow Connect:
5
+
6
+ - [a gateway](build/prod/resources/erp_erp_gateway.pipeline.yml) that captures
7
+ changes continuously, with the cluster policy from the standard;
8
+ - [an ingestion pipeline](build/prod/resources/erp_erp_ingestion.pipeline.yml)
9
+ that writes `customers` (SCD type 1) and `invoices` (SCD type 2, from
10
+ `mode: history`), each with only its contract's columns;
11
+ - [our pipeline](build/prod/src/erp/silver/customer_balances.sql), whose
12
+ transform reads both, using the current invoice versions;
13
+ - [a job](build/prod/resources/erp_refresh.job.yml) that refreshes ingestion
14
+ first and our pipeline after it.
15
+
16
+ The Unity Catalog connection `erp_sqlserver_<environment>` must exist; the
17
+ definitions name it but hold no credentials.
@@ -0,0 +1,8 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Connection
3
+ id: erp
4
+ description: The ERP's SQL Server, reachable from the workspace through ExpressRoute.
5
+ connector: sqlserver
6
+ connectivity: workspace
7
+ settings:
8
+ uc_connection: erp_sqlserver_${environment.name} # created by an administrator beforehand
@@ -0,0 +1,13 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: erp.customer_balances
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: customer_balances
8
+ logicalType: object
9
+ description: Open balance per customer.
10
+ properties:
11
+ - { name: customer_id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: name, logicalType: string }
13
+ - { name: balance, logicalType: number, physicalType: "DECIMAL(18,2)" }
@@ -0,0 +1,13 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: erp.customers
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: customers
8
+ logicalType: object
9
+ description: ERP customers.
10
+ properties:
11
+ - { name: customer_id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: name, logicalType: string }
13
+ - { name: country, logicalType: string }
@@ -0,0 +1,14 @@
1
+ apiVersion: v3.1.0
2
+ kind: DataContract
3
+ id: erp.invoices
4
+ version: 1.0.0
5
+ status: active
6
+ schema:
7
+ - name: invoices
8
+ logicalType: object
9
+ description: ERP invoices with every past version.
10
+ properties:
11
+ - { name: invoice_id, logicalType: integer, required: true, primaryKey: true }
12
+ - { name: customer_id, logicalType: integer, required: true }
13
+ - { name: amount, logicalType: number, physicalType: "DECIMAL(18,2)" }
14
+ - { name: status, logicalType: string }
@@ -0,0 +1,13 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Flow
3
+ id: erp
4
+ source: erp
5
+ tables:
6
+ customers:
7
+ object: erp.dbo.Customers
8
+ contract: erp.customers
9
+ invoices:
10
+ object: erp.dbo.Invoices
11
+ contract: erp.invoices
12
+ ingest:
13
+ with: { mode: history } # keep every version of an invoice
@@ -0,0 +1,6 @@
1
+ apiVersion: ingestron.io/v1
2
+ kind: Platform
3
+ id: lakehouse
4
+ schedule:
5
+ cron: 0 0 * * * ?
6
+ timezone: Pacific/Auckland
@@ -0,0 +1,13 @@
1
+ # A SQL Server database reached over private networking from the workspace,
2
+ # ingested with Lakeflow Connect, then summarised by our own pipeline.
3
+ id: erp
4
+ environments:
5
+ dev: {}
6
+ prod: {}
7
+ imports:
8
+ - standard.yaml
9
+ - platform.yaml
10
+ - connections/*.yaml
11
+ - flows/*.yaml
12
+ - transforms/*.yaml
13
+ - contracts/*.yaml