dataspring-cli 0.3.0__py3-none-any.whl

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.
cli/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ """DataSpring CLI - Command-line interface for DataSpring.
2
+
3
+ This package provides a terminal-based interface to DataSpring's
4
+ metric query and dashboard capabilities.
5
+
6
+ Usage:
7
+ dataspring login # Authenticate via Google OAuth
8
+ dataspring whoami # Show current user and org
9
+ dataspring metrics list # List available metrics
10
+ dataspring query -m revenue -g month # Query metrics
11
+ """
12
+
13
+ from .main import app
14
+
15
+ __all__ = ["app"]
@@ -0,0 +1,401 @@
1
+ ---
2
+ name: dataspring-author
3
+ description: Conventions for writing dbt YAML descriptions and the org's business_context doc. Use when editing dbt/models/**.yml, dbt/context/business_context.md, or any file that feeds DataSpring's semantic layer or org context.
4
+ ---
5
+
6
+ # Authoring DataSpring's data-layer context
7
+
8
+ You are editing files that an analytical agent will read at query time.
9
+ Lazy descriptions silently degrade every query that touches the
10
+ affected entity. This skill exists so the conventions stay tight.
11
+
12
+ ## 1. The contract
13
+
14
+ The descriptions on metrics, dimensions, and semantic models are
15
+ **authoritative**. Consumer agents trust them as the source of truth
16
+ for unit, computation, source rollup, applicable filter rules, and
17
+ known caveats. They are not commentary.
18
+
19
+ Two surfaces matter:
20
+
21
+ - **Per-entity descriptions** (dbt YAML) — ride directly in the tool
22
+ responses to `list_metrics` / `list_dimensions`. No length cap.
23
+ This is where 80% of "knowledge" should live.
24
+ - **Per-org `business_context`** — a single markdown document that
25
+ covers cross-cutting facts. Hard cap: 2000 chars. Wired into the
26
+ MCP `instructions` blob so external agents see it from turn 1.
27
+
28
+ If it attaches to one entity, put it on that entity. If it doesn't,
29
+ put it in `business_context`.
30
+
31
+ ## 2. Metric description anatomy
32
+
33
+ A complete metric description has six ordered elements. Skip an
34
+ element only if it genuinely doesn't apply.
35
+
36
+ 1. **Canonical sentence** — what this metric measures, in one line.
37
+ 2. **Unit** — DKK, count, %, hours, customers, etc. Always explicit.
38
+ 3. **Source rollup** — which systems / tables contribute.
39
+ 4. **Caveats** — known bugs, temporal discontinuities, methodology
40
+ changes. Date them.
41
+ 5. **Standing filter rules** — "for B2B, pair with..."; "exclude
42
+ internal test orders by setting `status != 'test'`".
43
+ 6. **Derived-field pointers** — "use `event_segment`, not raw
44
+ `event_type`".
45
+
46
+ ### Good example
47
+
48
+ ```yaml
49
+ - name: total_revenue
50
+ description: |
51
+ Total invoiced revenue from paid Stripe invoices.
52
+ Unit: DKK (gross, before VAT).
53
+ Source: stg_stripe__invoices, joined to dim_customer.
54
+ Caveat: pre-2024-04-01 numbers exclude refunds (ETL bug
55
+ fixed 2024-04-15; segment if comparing across that boundary).
56
+ Filter rule: pair with status='paid' to exclude voided invoices.
57
+ Use the derived `customer_segment`, not raw `customer.tier`.
58
+ ```
59
+
60
+ ### Bad example
61
+
62
+ ```yaml
63
+ - name: total_revenue
64
+ description: Total revenue.
65
+ ```
66
+
67
+ The bad version forces every consumer agent to guess unit, source,
68
+ caveats, and rules. It also makes the metric look interchangeable
69
+ with similarly-named metrics in other systems.
70
+
71
+ ## 3. Dimension description rules
72
+
73
+ For each dimension, document:
74
+
75
+ - **Allowed values** if it's an enum (`["smb", "midmarket", "enterprise"]`).
76
+ - **Derivation pointer** if it's computed (`derived from
77
+ customer.tier in stg_customers`).
78
+ - **Use-this-not-that steering** when a raw field is more naive than
79
+ the derived one (`prefer event_segment over event_type — segment
80
+ layers business logic the raw field doesn't`).
81
+
82
+ ```yaml
83
+ - name: customer_segment
84
+ description: |
85
+ SMB / Midmarket / Enterprise segmentation. Allowed values:
86
+ smb, midmarket, enterprise.
87
+ Derived in stg_customers from ARR + employee count, not the
88
+ raw customer.tier field (tier hasn't been kept current).
89
+ ```
90
+
91
+ ## 4. Source / model description rules
92
+
93
+ For sources and semantic models, document:
94
+
95
+ - **Refresh cadence** (hourly, daily 06:00 UTC, etc.).
96
+ - **Format quirks** ("amount stored as cents in JSON payload, not
97
+ dollars").
98
+ - **Coverage gaps** ("`customer_id` is 100% NULL pre-2023-06-01 —
99
+ signups before then aren't tracked").
100
+
101
+ ```yaml
102
+ sources:
103
+ - name: stripe_events
104
+ description: |
105
+ Refreshed hourly from Stripe webhook ingest.
106
+ `payload` is JSON; amount is in cents.
107
+ Pre-2023-06-01: `customer_id` is NULL (signup tracking
108
+ added then). Filter out NULL or use the post-2023 window
109
+ for customer-level aggregations.
110
+ ```
111
+
112
+ ## 5. `business_context` anatomy
113
+
114
+ A single markdown document, ≤2000 chars (the MCP instructions cap).
115
+ Cover the cross-cutting facts that don't attach to any specific
116
+ entity.
117
+
118
+ Recommended structure:
119
+
120
+ ```markdown
121
+ # <Org name>
122
+
123
+ ## What we sell
124
+ - One sentence on the business model.
125
+ - Currency, fiscal calendar.
126
+
127
+ ## Systems we sync from
128
+ - Stripe (billing) — refreshed hourly.
129
+ - HubSpot (CRM, contacts).
130
+ - Zendesk (support tickets).
131
+ - We do NOT sync personal staff mailboxes; email-correspondence
132
+ questions cannot be answered.
133
+
134
+ ## Cross-cutting derivations
135
+ - B2B vs B2C is `event_segment`, NOT raw `event_type`.
136
+ - `customer_segment` (smb/midmarket/enterprise) is derived in
137
+ `stg_customers` — use it instead of `customer.tier`.
138
+
139
+ ## Operational pointers
140
+ - Region: EU.
141
+ - dbt project lives in `dbt/` of the analytics repo.
142
+ - Per-metric specifics live in metric YAML descriptions.
143
+ ```
144
+
145
+ What does NOT belong in `business_context`:
146
+ - Per-metric meaning, unit, formula — goes on the metric.
147
+ - Per-dimension derivation — goes on the dimension.
148
+ - Findings / analysis results — those aren't context.
149
+
150
+ ## 6. The filing decision rule
151
+
152
+ | Type of fact | Goes in… |
153
+ |---|---|
154
+ | One metric's meaning, computation, unit | That metric's description |
155
+ | One metric's caveat, filter rule, derived-field pointer | Same |
156
+ | One dimension's allowed values, derivation, steering | That dimension's description |
157
+ | One source's refresh cadence, format quirk, NULL coverage | That source's description |
158
+ | Cross-cutting derivation rule | `business_context` |
159
+ | Org-level system inventory / coverage gap | `business_context` |
160
+ | Region, dbt layout, operational pointer | `business_context` |
161
+ | Findings, analysis, history | NOT in the context layer |
162
+ | Definition logic | dbt model SQL, not descriptions |
163
+
164
+ When unsure: put it in YAML, accept some duplication.
165
+
166
+ ## 7. Edit surfaces for `business_context`
167
+
168
+ `business_context` is **DataSpring-owned**: the document lives in
169
+ DataSpring, and an edit made there is live immediately and survives
170
+ manifest uploads. dbt owns computation; it does not own this text.
171
+
172
+ Edit it on whichever surface you are already on — they are the same
173
+ document:
174
+
175
+ - Web UI: Settings → Data → Business Context. Has a live byte
176
+ counter against the 2000-char cap.
177
+ - Tool: `business_context_edit` (admin/owner), actions `get`,
178
+ `show_size`, `set`, `append`. For agent-authored edits.
179
+ - CLI: `dataspring business-context set` (stdin) or
180
+ `dataspring business-context edit` ($EDITOR).
181
+
182
+ `dataspring business-context import FILE` remains available as a
183
+ one-time bootstrap: it is how you seed the document from a file you
184
+ already have, not a sync loop to maintain afterwards.
185
+
186
+ ## 8. Maintenance discipline
187
+
188
+ - **Reference rot.** When you rename a metric or dimension, search
189
+ `business_context` and other entity descriptions for stale
190
+ references. CI doesn't lint this yet.
191
+ - **Caveat sunset.** When a temporal-discontinuity bug gets fixed,
192
+ remove the caveat — but only after enough time has passed that
193
+ no useful query window starts before the fix.
194
+ - **Cross-PR review.** A description rewrite is a behavior change
195
+ to every agent. Treat it like an API change in review.
196
+
197
+ That's the contract. Boring, dense, useful.
198
+
199
+ ## 9. Parameter reference
200
+
201
+ <!-- dispatch-params: semantic_model_edit metric_edit import_manifest business_context_edit warehouse_edit datacore_edit datacore_run -->
202
+ _Generated from the dispatch registry (`GET /api/dispatch`); do not edit by hand. Field descriptions are the models' own, verbatim on every surface._
203
+
204
+ #### `semantic_model_edit`
205
+
206
+ MCP tool `semantic_model_edit`; `POST /api/dispatch/semantic_model_edit`. Edit semantic models in the org manifest. Admin/owner only for writes. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
207
+
208
+ **`create`** —
209
+
210
+ | Parameter | Type | Required | Description |
211
+ |---|---|---|---|
212
+ | `model_data` | object | yes | Full semantic-model definition (name, measures, dimensions, entities) |
213
+ | `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
214
+
215
+ **`update`** —
216
+
217
+ | Parameter | Type | Required | Description |
218
+ |---|---|---|---|
219
+ | `name` | string | yes | |
220
+ | `updates` | object | yes | |
221
+ | `confirmed` | boolean | no, default `False` | set true only after the user confirmed a redefinition of a NATIVE entity whose impact you showed them; has no effect on imported entities |
222
+ | `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
223
+
224
+ **`delete`** —
225
+
226
+ | Parameter | Type | Required | Description |
227
+ |---|---|---|---|
228
+ | `name` | string | yes | |
229
+ | `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
230
+
231
+ **`preview`** — Validate a semantic model without writing it or touching the warehouse.
232
+
233
+ | Parameter | Type | Required | Description |
234
+ |---|---|---|---|
235
+ | `model_data` | object | yes | Full semantic-model definition to validate (name, measures, dimensions, entities) |
236
+
237
+ #### `metric_edit`
238
+
239
+ MCP tool `metric_edit`; `POST /api/dispatch/metric_edit`. Edit metrics in the org manifest. Admin/owner only for writes. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
240
+
241
+ **`create`** —
242
+
243
+ | Parameter | Type | Required | Description |
244
+ |---|---|---|---|
245
+ | `metric_data` | object | yes | Metric definition (name, type, type_params) |
246
+ | `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
247
+
248
+ **`update`** —
249
+
250
+ | Parameter | Type | Required | Description |
251
+ |---|---|---|---|
252
+ | `name` | string | yes | |
253
+ | `updates` | object | yes | |
254
+ | `confirmed` | boolean | no, default `False` | set true only after the user confirmed a redefinition of a NATIVE entity whose impact you showed them; has no effect on imported entities |
255
+ | `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
256
+
257
+ **`delete`** —
258
+
259
+ | Parameter | Type | Required | Description |
260
+ |---|---|---|---|
261
+ | `name` | string | yes | |
262
+ | `expect_version` | string | no | Managed tenants only: the datacore version (draft snapshot id) this edit was written against, from the previous edit's `version` or dataspring://datacore/files; refused with the current one when stale |
263
+
264
+ **`preview`** —
265
+
266
+ | Parameter | Type | Required | Description |
267
+ |---|---|---|---|
268
+ | `metric_data` | object | yes | Metric definition to preview |
269
+ | `sample_query` | object | no | Optional query params (start_date, end_date, grain, limit) |
270
+
271
+ **`impact`** — What applying ``updates`` to this metric would touch, without writing.
272
+
273
+ | Parameter | Type | Required | Description |
274
+ |---|---|---|---|
275
+ | `name` | string | yes | |
276
+ | `updates` | object | yes | The updates whose impact to assess |
277
+
278
+ #### `import_manifest`
279
+
280
+ MCP tool `import_manifest`; `POST /api/dispatch/import_manifest`. Import a semantic manifest using replace semantics.
281
+
282
+ | Parameter | Type | Required | Description |
283
+ |---|---|---|---|
284
+ | `manifest_data` | object | yes | The semantic manifest to import (semantic models, metrics), as a JSON object. |
285
+ | `warehouse` | string | no | Which of the org's warehouses the manifest belongs to (see dataspring://warehouses); omit for the active one. |
286
+
287
+ #### `business_context_edit`
288
+
289
+ MCP tool `business_context_edit`; `POST /api/dispatch/business_context_edit`. Edit the org's cross-cutting business_context document. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
290
+
291
+ **`get`** — Read the current org business_context document.
292
+
293
+ No parameters.
294
+
295
+ **`show_size`** — Report current size and remaining room within the 2000-byte UTF-8 cap.
296
+
297
+ No parameters.
298
+
299
+ **`set`** — Replace the org's business_context with ``content`` (admin/owner).
300
+
301
+ | Parameter | Type | Required | Description |
302
+ |---|---|---|---|
303
+ | `content` | string | yes | Full document content to save |
304
+
305
+ **`append`** — Append ``content`` to the existing business_context (admin/owner).
306
+
307
+ | Parameter | Type | Required | Description |
308
+ |---|---|---|---|
309
+ | `content` | string | yes | Text to append to the current document |
310
+
311
+ #### `warehouse_edit`
312
+
313
+ MCP tool `warehouse_edit`; `POST /api/dispatch/warehouse_edit`. Manage the org's warehouses (admin/owner). Read them with ``dataspring://warehouses`` / ``read(kind="warehouses")``: each entry has id, kind (external \| managed), label, active flag, manifest counts and import time. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
314
+
315
+ **`activate`** — Make one warehouse the org's default: every query that names no ``warehouse`` runs against it.
316
+
317
+ | Parameter | Type | Required | Description |
318
+ |---|---|---|---|
319
+ | `id` | string | yes | Warehouse id, e.g. 'managed' or 'external' |
320
+
321
+ **`add_external`** — Register a customer-owned BigQuery project as an external warehouse.
322
+
323
+ | Parameter | Type | Required | Description |
324
+ |---|---|---|---|
325
+ | `label` | string | yes | Display name, e.g. "Noon's BigQuery" |
326
+ | `project` | string | yes | The customer's GCP project id |
327
+ | `dataset` | string | yes | The dataset the semantic manifest points at |
328
+ | `location` | string | yes | BigQuery location of that dataset, e.g. 'europe-north2' |
329
+ | `key_secret` | string | yes | NAME of the Secret Manager secret holding the customer's service-account key (t-<org>-external-bigquery-key). Never the key itself. |
330
+ | `id` | string | no, default `external` | Warehouse id to create (default 'external') |
331
+
332
+ **`remove`** — Delete a warehouse and its manifest. Refused for the active warehouse and for the managed one.
333
+
334
+ | Parameter | Type | Required | Description |
335
+ |---|---|---|---|
336
+ | `id` | string | yes | Warehouse id to remove |
337
+
338
+ #### `datacore_edit`
339
+
340
+ MCP tool `datacore_edit`; `POST /api/dispatch/datacore_edit`. Edit the datacore workspace: connections, pipelines and the dbt project of a managed tenant, as files. ``kind`` picks the family: The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
341
+
342
+ **`apply`** — Write one workspace file whole (create or replace): a connection or pipeline as a ``document`` (validated against its schema and the catalog), a dbt project file as ``content``.
343
+
344
+ | Parameter | Type | Required | Description |
345
+ |---|---|---|---|
346
+ | `kind` | one of `connection`, `pipeline`, `model` | yes | connection (admin), pipeline (admin) or model (member) |
347
+ | `name` | string | yes | For kind connection or pipeline: the name, i.e. the file name without .yaml (connections/<name>.yaml, pipelines/<name>.yaml). For kind model: the path under models/, the dbt project (models/models/marts/orders.sql, models/models/staging/schema.yml, models/seeds/x.csv, models/dbt_project.yml); no traversal, .sql .yml .yaml .csv .md .txt .json, never target/ or dbt_packages/ |
348
+ | `document` | object | no | kind connection: the whole document (connection.schema.json: kind, base_url, auth with secret NAMES). kind pipeline: the whole document (pipeline.schema.json: a catalog `connector` with config, secrets, overrides; or a `connection` reference, an inline `source` or named `sources`, with `resources`; schedule, checks, then). Secrets are named, never valued; every name must exist |
349
+ | `content` | string | no | kind model: the whole file content |
350
+ | `expect_version` | string | no | The datacore version this edit was written against: the `version` the previous edit returned, or dataspring://datacore/files. An edit against a stale version is refused with the current one. Omit to skip the check. |
351
+
352
+ **`patch`** — Apply a JSON merge patch (RFC 7386: a key set to null is removed) to an existing YAML file: a connection, a pipeline (a schedule, a window, a check), or a YAML file under models/ (not SQL).
353
+
354
+ | Parameter | Type | Required | Description |
355
+ |---|---|---|---|
356
+ | `kind` | one of `connection`, `pipeline`, `model` | yes | connection (admin), pipeline (admin) or model (member) |
357
+ | `name` | string | yes | For kind connection or pipeline: the name, i.e. the file name without .yaml (connections/<name>.yaml, pipelines/<name>.yaml). For kind model: the path under models/, the dbt project (models/models/marts/orders.sql, models/models/staging/schema.yml, models/seeds/x.csv, models/dbt_project.yml); no traversal, .sql .yml .yaml .csv .md .txt .json, never target/ or dbt_packages/ |
358
+ | `patch` | object | yes | JSON merge patch over the current document |
359
+ | `expect_version` | string | no | The datacore version this edit was written against: the `version` the previous edit returned, or dataspring://datacore/files. An edit against a stale version is refused with the current one. Omit to skip the check. |
360
+
361
+ **`delete`** — Remove one workspace file.
362
+
363
+ | Parameter | Type | Required | Description |
364
+ |---|---|---|---|
365
+ | `kind` | one of `connection`, `pipeline`, `model` | yes | connection (admin), pipeline (admin) or model (member) |
366
+ | `name` | string | yes | For kind connection or pipeline: the name, i.e. the file name without .yaml (connections/<name>.yaml, pipelines/<name>.yaml). For kind model: the path under models/, the dbt project (models/models/marts/orders.sql, models/models/staging/schema.yml, models/seeds/x.csv, models/dbt_project.yml); no traversal, .sql .yml .yaml .csv .md .txt .json, never target/ or dbt_packages/ |
367
+ | `expect_version` | string | no | The datacore version this edit was written against: the `version` the previous edit returned, or dataspring://datacore/files. An edit against a stale version is refused with the current one. Omit to skip the check. |
368
+
369
+ #### `datacore_run`
370
+
371
+ MCP tool `datacore_run`; `POST /api/dispatch/datacore_run`. The datacore's check-then-deploy loop and its operations. The body is `{"action": {"action": "<verb>", ...}}`, one verb per table below.
372
+
373
+ **`check`** — Check the current draft; no arguments beyond an optional note.
374
+
375
+ | Parameter | Type | Required | Description |
376
+ |---|---|---|---|
377
+ | `note` | string | no | Optional note, recorded on the check run |
378
+
379
+ **`deploy`** —
380
+
381
+ | Parameter | Type | Required | Description |
382
+ |---|---|---|---|
383
+ | `snapshot` | string | no | Omit to promote the current draft (needs a green check bound to it). A previously deployed snapshot id rolls back to it |
384
+ | `note` | string | no | Why, recorded on the learned trail |
385
+
386
+ **`run`** —
387
+
388
+ | Parameter | Type | Required | Description |
389
+ |---|---|---|---|
390
+ | `pipeline` | string | yes | Pipeline name (a deployed pipelines/<name>.yaml) |
391
+ | `window` | RunWindowParams | no | A date window to load instead of the pipeline's own; wider than backfill_chunk becomes a chunked backfill |
392
+ | `cursor` | RunCursorParams | no | {from: <value>}: a cursor to re-pull from |
393
+ | `backfill_chunk` | string | no | Chunk size for a windowed run: 7d, 2w or 1M (calendar months, the default) |
394
+
395
+ **`reset`** —
396
+
397
+ | Parameter | Type | Required | Description |
398
+ |---|---|---|---|
399
+ | `pipeline` | string | yes | The pipeline whose dlt state and raw tables to remove |
400
+ | `approve` | boolean | no, default `False` | Must be true: the reset is irreversible. Ask the user first |
401
+ <!-- /dispatch-params -->