@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.
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +44 -2
- package/dist/args.d.ts +105 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +161 -0
- package/dist/args.js.map +1 -0
- package/dist/ask.d.ts +5 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +17 -0
- package/dist/ask.js.map +1 -0
- package/dist/compare.d.ts +13 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +90 -0
- package/dist/compare.js.map +1 -0
- package/dist/environments.d.ts +6 -0
- package/dist/environments.d.ts.map +1 -0
- package/dist/environments.js +25 -0
- package/dist/environments.js.map +1 -0
- package/dist/generate.d.ts +16 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +118 -0
- package/dist/generate.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/init-choice.d.ts +10 -0
- package/dist/init-choice.d.ts.map +1 -0
- package/dist/init-choice.js +36 -0
- package/dist/init-choice.js.map +1 -0
- package/dist/init-preflight.d.ts +4 -0
- package/dist/init-preflight.d.ts.map +1 -0
- package/dist/init-preflight.js +18 -0
- package/dist/init-preflight.js.map +1 -0
- package/dist/init.d.ts +5 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +136 -0
- package/dist/init.js.map +1 -0
- package/dist/io.d.ts +8 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +3 -0
- package/dist/io.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +11 -0
- package/dist/main.js.map +1 -0
- package/dist/outputs.d.ts +12 -0
- package/dist/outputs.d.ts.map +1 -0
- package/dist/outputs.js +53 -0
- package/dist/outputs.js.map +1 -0
- package/dist/packs.d.ts +4 -0
- package/dist/packs.d.ts.map +1 -0
- package/dist/packs.js +5 -0
- package/dist/packs.js.map +1 -0
- package/dist/project-choices.d.ts +27 -0
- package/dist/project-choices.d.ts.map +1 -0
- package/dist/project-choices.js +44 -0
- package/dist/project-choices.js.map +1 -0
- package/dist/project-questions.d.ts +5 -0
- package/dist/project-questions.d.ts.map +1 -0
- package/dist/project-questions.js +43 -0
- package/dist/project-questions.js.map +1 -0
- package/dist/project-recipes.d.ts +14 -0
- package/dist/project-recipes.d.ts.map +1 -0
- package/dist/project-recipes.js +115 -0
- package/dist/project-recipes.js.map +1 -0
- package/dist/request.d.ts +13 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +110 -0
- package/dist/request.js.map +1 -0
- package/dist/sample-contract.d.ts +11 -0
- package/dist/sample-contract.d.ts.map +1 -0
- package/dist/sample-contract.js +91 -0
- package/dist/sample-contract.js.map +1 -0
- package/dist/sample-read.d.ts +8 -0
- package/dist/sample-read.d.ts.map +1 -0
- package/dist/sample-read.js +89 -0
- package/dist/sample-read.js.map +1 -0
- package/dist/samples.d.ts +33 -0
- package/dist/samples.d.ts.map +1 -0
- package/dist/samples.js +108 -0
- package/dist/samples.js.map +1 -0
- package/dist/scaffold.d.ts +34 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +106 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +39 -0
- package/dist/schemas.js.map +1 -0
- package/dist/sources.d.ts +17 -0
- package/dist/sources.d.ts.map +1 -0
- package/dist/sources.js +103 -0
- package/dist/sources.js.map +1 -0
- package/dist/templates.d.ts +64 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +88 -0
- package/dist/templates.js.map +1 -0
- package/package.json +68 -3
- package/project-templates/kafka/README.md +12 -0
- package/project-templates/kafka/connections/events.yaml +8 -0
- package/project-templates/kafka/contracts/page_views.yaml +13 -0
- package/project-templates/kafka/contracts/purchases.yaml +13 -0
- package/project-templates/kafka/flows/events.yaml +10 -0
- package/project-templates/kafka/project.yaml +10 -0
- package/project-templates/kafka/standard.yaml +4 -0
- package/project-templates/kafka/template.yaml +3 -0
- package/project-templates/minimal/README.md +10 -0
- package/project-templates/minimal/connections/shop.yaml +7 -0
- package/project-templates/minimal/contracts/orders.yaml +14 -0
- package/project-templates/minimal/flows/orders.yaml +6 -0
- package/project-templates/minimal/project.yaml +11 -0
- package/project-templates/minimal/standard.yaml +4 -0
- package/project-templates/minimal/template.yaml +7 -0
- package/project-templates/private-files/README.md +29 -0
- package/project-templates/private-files/connections/share.yaml +10 -0
- package/project-templates/private-files/contracts/sales.yaml +14 -0
- package/project-templates/private-files/contracts/stock.yaml +14 -0
- package/project-templates/private-files/flows/stores.yaml +9 -0
- package/project-templates/private-files/platform.yaml +11 -0
- package/project-templates/private-files/project.yaml +19 -0
- package/project-templates/private-files/standard.yaml +11 -0
- package/project-templates/private-files/template.yaml +10 -0
- package/project-templates/private-sql-server/README.md +25 -0
- package/project-templates/private-sql-server/connections/erp.yaml +10 -0
- package/project-templates/private-sql-server/contracts/customers.yaml +14 -0
- package/project-templates/private-sql-server/contracts/invoices.yaml +15 -0
- package/project-templates/private-sql-server/flows/erp.yaml +13 -0
- package/project-templates/private-sql-server/platform.yaml +11 -0
- package/project-templates/private-sql-server/project.yaml +19 -0
- package/project-templates/private-sql-server/standard.yaml +17 -0
- package/project-templates/private-sql-server/template.yaml +10 -0
- package/project-templates/pyspark/README.md +12 -0
- package/project-templates/pyspark/connections/billing.yaml +7 -0
- package/project-templates/pyspark/contracts/invoices.yaml +16 -0
- package/project-templates/pyspark/contracts/monthly_revenue.yaml +14 -0
- package/project-templates/pyspark/flows/billing.yaml +6 -0
- package/project-templates/pyspark/project.yaml +11 -0
- package/project-templates/pyspark/standard.yaml +4 -0
- package/project-templates/pyspark/template.yaml +3 -0
- package/project-templates/pyspark/transforms/monthly_revenue.py +10 -0
- package/project-templates/pyspark/transforms/monthly_revenue.yaml +10 -0
- package/project-templates/retail/README.md +22 -0
- package/project-templates/retail/connections/shop.yaml +8 -0
- package/project-templates/retail/contracts/customers.yaml +12 -0
- package/project-templates/retail/contracts/daily_sales.yaml +19 -0
- package/project-templates/retail/contracts/orders.yaml +16 -0
- package/project-templates/retail/flows/shop.yaml +18 -0
- package/project-templates/retail/platform.yaml +21 -0
- package/project-templates/retail/project.yaml +18 -0
- package/project-templates/retail/standard.yaml +15 -0
- package/project-templates/retail/template.yaml +10 -0
- package/project-templates/retail/transforms/daily_sales.sql +7 -0
- package/project-templates/retail/transforms/daily_sales.yaml +7 -0
- package/project-templates/salesforce/README.md +6 -0
- package/project-templates/salesforce/connections/salesforce.yaml +7 -0
- package/project-templates/salesforce/contracts/accounts.yaml +13 -0
- package/project-templates/salesforce/contracts/opportunities.yaml +14 -0
- package/project-templates/salesforce/flows/salesforce.yaml +7 -0
- package/project-templates/salesforce/project.yaml +9 -0
- package/project-templates/salesforce/standard.yaml +4 -0
- package/project-templates/salesforce/template.yaml +3 -0
- package/project-templates/sql-server/README.md +17 -0
- package/project-templates/sql-server/connections/erp.yaml +8 -0
- package/project-templates/sql-server/contracts/customer_balances.yaml +13 -0
- package/project-templates/sql-server/contracts/customers.yaml +13 -0
- package/project-templates/sql-server/contracts/invoices.yaml +14 -0
- package/project-templates/sql-server/flows/erp.yaml +13 -0
- package/project-templates/sql-server/platform.yaml +6 -0
- package/project-templates/sql-server/project.yaml +13 -0
- package/project-templates/sql-server/standard.yaml +9 -0
- package/project-templates/sql-server/template.yaml +3 -0
- package/project-templates/sql-server/transforms/customer_balances.sql +5 -0
- 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,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,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,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,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,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,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,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,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
|