dq-export-connector 0.1.0.dev1__tar.gz
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.
- dq_export_connector-0.1.0.dev1/.gitignore +63 -0
- dq_export_connector-0.1.0.dev1/PKG-INFO +47 -0
- dq_export_connector-0.1.0.dev1/README.md +37 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/__init__.py +0 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/_models/__init__.py +0 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/_models/bucket_validation_result.py +63 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/_models/export_connector_credentials.py +84 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/_models/export_receipt.py +152 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/_models/export_worker_config.py +183 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/bucket/__init__.py +0 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/bucket/datasets.py +71 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/bucket/export.py +215 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/bucket/paths.py +83 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/bucket/store.py +179 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/bucket/upload.py +46 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/bucket/validate.py +71 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/log.py +45 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/redact.py +51 -0
- dq_export_connector-0.1.0.dev1/dq_export_connector/runner.py +87 -0
- dq_export_connector-0.1.0.dev1/pyproject.toml +38 -0
- dq_export_connector-0.1.0.dev1/scripts/codegen-models.sh +22 -0
- dq_export_connector-0.1.0.dev1/tests/test_live_buckets.py +135 -0
- dq_export_connector-0.1.0.dev1/tests/test_models_are_current.py +15 -0
- dq_export_connector-0.1.0.dev1/tests/test_paths.py +60 -0
- dq_export_connector-0.1.0.dev1/tests/test_redact.py +28 -0
- dq_export_connector-0.1.0.dev1/tests/test_runner.py +194 -0
- dq_export_connector-0.1.0.dev1/tests/test_upload.py +22 -0
- dq_export_connector-0.1.0.dev1/uv.lock +1189 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
**/build/
|
|
3
|
+
.idea/
|
|
4
|
+
.vscode/*
|
|
5
|
+
!.vscode/tasks.json
|
|
6
|
+
target/
|
|
7
|
+
node_modules/
|
|
8
|
+
result
|
|
9
|
+
/result-*
|
|
10
|
+
**/*.o
|
|
11
|
+
env
|
|
12
|
+
venv
|
|
13
|
+
.venv
|
|
14
|
+
.direnv
|
|
15
|
+
**/perf.data*
|
|
16
|
+
infra/packages/hadoop/binary.tar.gz
|
|
17
|
+
.terraform
|
|
18
|
+
config.tf.json
|
|
19
|
+
*.tfstate.*
|
|
20
|
+
*.tfstate
|
|
21
|
+
**/.deploy-gc
|
|
22
|
+
.vagrant
|
|
23
|
+
.pnpm-debug.log
|
|
24
|
+
.yarn
|
|
25
|
+
__pycache__
|
|
26
|
+
trusted/dcr-compiler/ddc/src/data_lab/**/*.png
|
|
27
|
+
trusted/dcr-compiler/ddc/src/data_lab/**/*.json
|
|
28
|
+
e2e-tests/frontend/confidence-check-config.json
|
|
29
|
+
.terraform.lock.hcl
|
|
30
|
+
dmp/analysis-scripts/results/*
|
|
31
|
+
.env
|
|
32
|
+
!tools/dqdev/internal/target/
|
|
33
|
+
docusaurus/style-guide.md
|
|
34
|
+
**/.claude/settings.local.json
|
|
35
|
+
**/.claude/agent-memory-local/
|
|
36
|
+
.claude/*.lock
|
|
37
|
+
.claude/worktrees/
|
|
38
|
+
**/CLAUDE.local.md
|
|
39
|
+
**/*.parquet/**
|
|
40
|
+
.pnpm-store/
|
|
41
|
+
.turbo/
|
|
42
|
+
|
|
43
|
+
# Agentic-coding workflow (local, not part of this repo)
|
|
44
|
+
.workflow/
|
|
45
|
+
.workflow-backup-*
|
|
46
|
+
|
|
47
|
+
# Local-only Google Ads/DV360 test scripts (kept out of the PR)
|
|
48
|
+
dmp/dqdmp/scripts/
|
|
49
|
+
|
|
50
|
+
# Playwright MCP screenshots (local only)
|
|
51
|
+
.playwright-mcp/
|
|
52
|
+
|
|
53
|
+
# Claude Design sync build artifacts (local only).
|
|
54
|
+
**/.design-sync/sb-reference/
|
|
55
|
+
**/.design-sync/learnings/
|
|
56
|
+
**/.design-sync/.cache/
|
|
57
|
+
**/.design-sync/node_modules
|
|
58
|
+
.ds-sync/
|
|
59
|
+
ds-bundle/
|
|
60
|
+
|
|
61
|
+
# DMP creation-prefill tests (local only)
|
|
62
|
+
avato-backend/frontend/decentriq-platform/src/features/dmp/models/creationPrefill.test.ts
|
|
63
|
+
avato-backend/frontend/decentriq-platform/src/features/dmp/utils/creationPrefill/*.test.ts
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dq-export-connector
|
|
3
|
+
Version: 0.1.0.dev1
|
|
4
|
+
Summary: Worker logic for the Export Connector DCR.
|
|
5
|
+
Author: Decentriq
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: obstore<0.12,>=0.11.1
|
|
8
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# dq-export-connector
|
|
12
|
+
|
|
13
|
+
Worker logic for the **Export Connector DCR**: one package for every
|
|
14
|
+
Connector Kind, each in its own sub-package. The **bucket** kind uploads each
|
|
15
|
+
Input's dataset to S3, Azure Blob or GCS through
|
|
16
|
+
[`obstore`](https://developmentseed.org/obstore/).
|
|
17
|
+
|
|
18
|
+
ddc pins this package by hash in a fully locked `requirements.txt` and runs
|
|
19
|
+
`dq_export_connector.runner.main` in the task's Python environment. It is
|
|
20
|
+
published to PyPI, since the compiled graph installs it from there.
|
|
21
|
+
|
|
22
|
+
- `dq_export_connector.runner`: the entry point. Reads `/input/config.json`
|
|
23
|
+
and `/input/credentials.json`, sets up logging, and dispatches on the
|
|
24
|
+
Connector Kind and the action.
|
|
25
|
+
- `dq_export_connector.redact`: keeps every value from the sealed half, and
|
|
26
|
+
URL query strings, out of log lines and result messages.
|
|
27
|
+
- `dq_export_connector.bucket`: the bucket kind (`ValidateConnection` and
|
|
28
|
+
`Export`).
|
|
29
|
+
- `dq_export_connector._models`: pydantic models generated from ddc's JSON
|
|
30
|
+
schemas, so the config the worker reads and the receipt and validation
|
|
31
|
+
result it writes match ddc's Rust types. Regenerate them with
|
|
32
|
+
`scripts/codegen-models.sh` after the types change and
|
|
33
|
+
`decentriq_dcr_compiler` has been rebuilt; a test fails while they are
|
|
34
|
+
stale.
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
uv run pytest # unit tests, against obstore's in-memory store
|
|
38
|
+
uv run pytest -m live # against real buckets, credentials from the environment
|
|
39
|
+
uv run mypy dq_export_connector tests
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Releasing
|
|
43
|
+
|
|
44
|
+
Bump `version` in `pyproject.toml`, then push a `dq-export-connector-v<version>`
|
|
45
|
+
tag; the `Publish dq-export-connector to PyPI` workflow tests, builds and uploads
|
|
46
|
+
it. Use `.devN` versions while developing; PyPI releases can't be replaced.
|
|
47
|
+
ddc then pins the new version in a new `export_connector/v0/worker/<version>/`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# dq-export-connector
|
|
2
|
+
|
|
3
|
+
Worker logic for the **Export Connector DCR**: one package for every
|
|
4
|
+
Connector Kind, each in its own sub-package. The **bucket** kind uploads each
|
|
5
|
+
Input's dataset to S3, Azure Blob or GCS through
|
|
6
|
+
[`obstore`](https://developmentseed.org/obstore/).
|
|
7
|
+
|
|
8
|
+
ddc pins this package by hash in a fully locked `requirements.txt` and runs
|
|
9
|
+
`dq_export_connector.runner.main` in the task's Python environment. It is
|
|
10
|
+
published to PyPI, since the compiled graph installs it from there.
|
|
11
|
+
|
|
12
|
+
- `dq_export_connector.runner`: the entry point. Reads `/input/config.json`
|
|
13
|
+
and `/input/credentials.json`, sets up logging, and dispatches on the
|
|
14
|
+
Connector Kind and the action.
|
|
15
|
+
- `dq_export_connector.redact`: keeps every value from the sealed half, and
|
|
16
|
+
URL query strings, out of log lines and result messages.
|
|
17
|
+
- `dq_export_connector.bucket`: the bucket kind (`ValidateConnection` and
|
|
18
|
+
`Export`).
|
|
19
|
+
- `dq_export_connector._models`: pydantic models generated from ddc's JSON
|
|
20
|
+
schemas, so the config the worker reads and the receipt and validation
|
|
21
|
+
result it writes match ddc's Rust types. Regenerate them with
|
|
22
|
+
`scripts/codegen-models.sh` after the types change and
|
|
23
|
+
`decentriq_dcr_compiler` has been rebuilt; a test fails while they are
|
|
24
|
+
stale.
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
uv run pytest # unit tests, against obstore's in-memory store
|
|
28
|
+
uv run pytest -m live # against real buckets, credentials from the environment
|
|
29
|
+
uv run mypy dq_export_connector tests
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Releasing
|
|
33
|
+
|
|
34
|
+
Bump `version` in `pyproject.toml`, then push a `dq-export-connector-v<version>`
|
|
35
|
+
tag; the `Publish dq-export-connector to PyPI` workflow tests, builds and uploads
|
|
36
|
+
it. Use `.devN` versions while developing; PyPI releases can't be replaced.
|
|
37
|
+
ddc then pins the new version in a new `export_connector/v0/worker/<version>/`.
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: bucket_validation_result.json
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Annotated, Literal
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, RootModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BucketValidationResult1(BaseModel):
|
|
12
|
+
model_config = ConfigDict(
|
|
13
|
+
populate_by_name=True,
|
|
14
|
+
)
|
|
15
|
+
status: Literal['OK']
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExportMessage(BaseModel):
|
|
19
|
+
model_config = ConfigDict(
|
|
20
|
+
populate_by_name=True,
|
|
21
|
+
)
|
|
22
|
+
code: Annotated[
|
|
23
|
+
str,
|
|
24
|
+
Field(
|
|
25
|
+
description='SCREAMING_SNAKE_CASE, such as `ACCESS_DENIED`. A string rather than an\nenum, so a worker release can add codes that readers built against an\nolder ddc still accept.'
|
|
26
|
+
),
|
|
27
|
+
]
|
|
28
|
+
message: Annotated[
|
|
29
|
+
str,
|
|
30
|
+
Field(
|
|
31
|
+
description='Safe to show to the user: no credentials and no dataset values.'
|
|
32
|
+
),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class BucketValidationResult2(BaseModel):
|
|
37
|
+
model_config = ConfigDict(
|
|
38
|
+
populate_by_name=True,
|
|
39
|
+
)
|
|
40
|
+
status: Literal['OK_WITH_WARNINGS']
|
|
41
|
+
warnings: list[ExportMessage]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BucketValidationResult3(BaseModel):
|
|
45
|
+
model_config = ConfigDict(
|
|
46
|
+
populate_by_name=True,
|
|
47
|
+
)
|
|
48
|
+
errors: list[ExportMessage]
|
|
49
|
+
status: Literal['ERROR']
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BucketValidationResult(
|
|
53
|
+
RootModel[
|
|
54
|
+
BucketValidationResult1 | BucketValidationResult2 | BucketValidationResult3
|
|
55
|
+
]
|
|
56
|
+
):
|
|
57
|
+
root: Annotated[
|
|
58
|
+
BucketValidationResult1 | BucketValidationResult2 | BucketValidationResult3,
|
|
59
|
+
Field(
|
|
60
|
+
description='The result of a Bucket `ValidateConnection`. Connection problems are\nreported here, and the task still succeeds.',
|
|
61
|
+
title='BucketValidationResult',
|
|
62
|
+
),
|
|
63
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: export_connector_credentials.json
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, RootModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class S3(BaseModel):
|
|
12
|
+
model_config = ConfigDict(
|
|
13
|
+
populate_by_name=True,
|
|
14
|
+
)
|
|
15
|
+
access_key: Annotated[str, Field(alias='accessKey')]
|
|
16
|
+
secret_key: Annotated[str, Field(alias='secretKey')]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BucketCredentials1(BaseModel):
|
|
20
|
+
model_config = ConfigDict(
|
|
21
|
+
extra='forbid',
|
|
22
|
+
populate_by_name=True,
|
|
23
|
+
)
|
|
24
|
+
s3: S3
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AzureBlob(BaseModel):
|
|
28
|
+
model_config = ConfigDict(
|
|
29
|
+
populate_by_name=True,
|
|
30
|
+
)
|
|
31
|
+
sas_token: Annotated[
|
|
32
|
+
str,
|
|
33
|
+
Field(
|
|
34
|
+
alias='sasToken',
|
|
35
|
+
description='A container SAS token — the scoped, expiring grant Azure recommends\nover an account key. Only the grant: the account and container it\napplies to are pinned by the signed `BucketConnection`, so rotating\nthe token can never repoint the connector.',
|
|
36
|
+
),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class BucketCredentials2(BaseModel):
|
|
41
|
+
model_config = ConfigDict(
|
|
42
|
+
extra='forbid',
|
|
43
|
+
populate_by_name=True,
|
|
44
|
+
)
|
|
45
|
+
azure_blob: Annotated[AzureBlob, Field(alias='azureBlob')]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Gcs(BaseModel):
|
|
49
|
+
model_config = ConfigDict(
|
|
50
|
+
populate_by_name=True,
|
|
51
|
+
)
|
|
52
|
+
service_account_json: Annotated[str, Field(alias='serviceAccountJson')]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class BucketCredentials3(BaseModel):
|
|
56
|
+
model_config = ConfigDict(
|
|
57
|
+
extra='forbid',
|
|
58
|
+
populate_by_name=True,
|
|
59
|
+
)
|
|
60
|
+
gcs: Gcs
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class BucketCredentials(
|
|
64
|
+
RootModel[BucketCredentials1 | BucketCredentials2 | BucketCredentials3]
|
|
65
|
+
):
|
|
66
|
+
root: BucketCredentials1 | BucketCredentials2 | BucketCredentials3
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ExportConnectorCredentials1(BaseModel):
|
|
70
|
+
model_config = ConfigDict(
|
|
71
|
+
extra='forbid',
|
|
72
|
+
populate_by_name=True,
|
|
73
|
+
)
|
|
74
|
+
bucket: BucketCredentials
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ExportConnectorCredentials(RootModel[ExportConnectorCredentials1]):
|
|
78
|
+
root: Annotated[
|
|
79
|
+
ExportConnectorCredentials1,
|
|
80
|
+
Field(
|
|
81
|
+
description='Keyed by Connector Kind, in step with [`ExportConnectorKind`]. The worker\nreads it at `/input/credentials.json`.',
|
|
82
|
+
title='ExportConnectorCredentials',
|
|
83
|
+
),
|
|
84
|
+
]
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: export_receipt.json
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Annotated, Literal
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field, RootModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExportMessage(BaseModel):
|
|
13
|
+
model_config = ConfigDict(
|
|
14
|
+
populate_by_name=True,
|
|
15
|
+
)
|
|
16
|
+
code: Annotated[
|
|
17
|
+
str,
|
|
18
|
+
Field(
|
|
19
|
+
description='SCREAMING_SNAKE_CASE, such as `ACCESS_DENIED`. A string rather than an\nenum, so a worker release can add codes that readers built against an\nolder ddc still accept.'
|
|
20
|
+
),
|
|
21
|
+
]
|
|
22
|
+
message: Annotated[
|
|
23
|
+
str,
|
|
24
|
+
Field(
|
|
25
|
+
description='Safe to show to the user: no credentials and no dataset values.'
|
|
26
|
+
),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ExportTotals(BaseModel):
|
|
31
|
+
model_config = ConfigDict(
|
|
32
|
+
populate_by_name=True,
|
|
33
|
+
)
|
|
34
|
+
bytes: Annotated[int, Field(ge=0)]
|
|
35
|
+
files: Annotated[int, Field(ge=0)]
|
|
36
|
+
rows: Annotated[
|
|
37
|
+
int | None,
|
|
38
|
+
Field(
|
|
39
|
+
description='`None` unless the worker counted the rows of every file.', ge=0
|
|
40
|
+
),
|
|
41
|
+
] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ExportedFile(BaseModel):
|
|
45
|
+
model_config = ConfigDict(
|
|
46
|
+
populate_by_name=True,
|
|
47
|
+
)
|
|
48
|
+
bytes: Annotated[int, Field(ge=0)]
|
|
49
|
+
path: Annotated[str, Field(description='The object key in the bucket.')]
|
|
50
|
+
rows: Annotated[
|
|
51
|
+
int | None, Field(description="`None` where the worker can't count rows.", ge=0)
|
|
52
|
+
] = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class InputStatus(Enum):
|
|
56
|
+
success = 'SUCCESS'
|
|
57
|
+
failed = 'FAILED'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BucketInputReceipt(BaseModel):
|
|
61
|
+
model_config = ConfigDict(
|
|
62
|
+
populate_by_name=True,
|
|
63
|
+
)
|
|
64
|
+
errors: list[ExportMessage]
|
|
65
|
+
files: list[ExportedFile]
|
|
66
|
+
leaf_id: Annotated[str, Field(alias='leafId')]
|
|
67
|
+
status: InputStatus
|
|
68
|
+
totals: ExportTotals
|
|
69
|
+
warnings: list[ExportMessage]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ExportReceiptV01(BaseModel):
|
|
73
|
+
model_config = ConfigDict(
|
|
74
|
+
populate_by_name=True,
|
|
75
|
+
)
|
|
76
|
+
errors: Annotated[
|
|
77
|
+
list[ExportMessage],
|
|
78
|
+
Field(
|
|
79
|
+
description='Problems tied to no single Input, such as credentials rejected before\nany upload.'
|
|
80
|
+
),
|
|
81
|
+
]
|
|
82
|
+
export_prefix: Annotated[
|
|
83
|
+
str | None,
|
|
84
|
+
Field(
|
|
85
|
+
alias='exportPrefix',
|
|
86
|
+
description='The export prefix with its date tokens filled; `None` without one.',
|
|
87
|
+
),
|
|
88
|
+
] = None
|
|
89
|
+
inputs: list[BucketInputReceipt]
|
|
90
|
+
totals: ExportTotals
|
|
91
|
+
warnings: list[ExportMessage]
|
|
92
|
+
type: Literal['BUCKET']
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ExportReceiptV0(RootModel[ExportReceiptV01]):
|
|
96
|
+
root: ExportReceiptV01
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ExportReceipt1(BaseModel):
|
|
100
|
+
model_config = ConfigDict(
|
|
101
|
+
populate_by_name=True,
|
|
102
|
+
)
|
|
103
|
+
version: Literal['V0']
|
|
104
|
+
errors: Annotated[
|
|
105
|
+
list[ExportMessage],
|
|
106
|
+
Field(
|
|
107
|
+
description='Problems tied to no single Input, such as credentials rejected before\nany upload.'
|
|
108
|
+
),
|
|
109
|
+
]
|
|
110
|
+
export_prefix: Annotated[
|
|
111
|
+
str | None,
|
|
112
|
+
Field(
|
|
113
|
+
alias='exportPrefix',
|
|
114
|
+
description='The export prefix with its date tokens filled; `None` without one.',
|
|
115
|
+
),
|
|
116
|
+
] = None
|
|
117
|
+
inputs: list[BucketInputReceipt]
|
|
118
|
+
totals: ExportTotals
|
|
119
|
+
warnings: list[ExportMessage]
|
|
120
|
+
type: Literal['BUCKET']
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class ExportReceipt(RootModel[ExportReceipt1]):
|
|
124
|
+
root: Annotated[
|
|
125
|
+
ExportReceipt1,
|
|
126
|
+
Field(
|
|
127
|
+
description='A released version never changes: a change is a new version, and readers\nkeep reading every version ever written.',
|
|
128
|
+
title='ExportReceipt',
|
|
129
|
+
),
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class BucketExportReceiptV0(BaseModel):
|
|
134
|
+
model_config = ConfigDict(
|
|
135
|
+
populate_by_name=True,
|
|
136
|
+
)
|
|
137
|
+
errors: Annotated[
|
|
138
|
+
list[ExportMessage],
|
|
139
|
+
Field(
|
|
140
|
+
description='Problems tied to no single Input, such as credentials rejected before\nany upload.'
|
|
141
|
+
),
|
|
142
|
+
]
|
|
143
|
+
export_prefix: Annotated[
|
|
144
|
+
str | None,
|
|
145
|
+
Field(
|
|
146
|
+
alias='exportPrefix',
|
|
147
|
+
description='The export prefix with its date tokens filled; `None` without one.',
|
|
148
|
+
),
|
|
149
|
+
] = None
|
|
150
|
+
inputs: list[BucketInputReceipt]
|
|
151
|
+
totals: ExportTotals
|
|
152
|
+
warnings: list[ExportMessage]
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: export_worker_config.json
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Annotated, Literal
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field, RootModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BucketConnection1(BaseModel):
|
|
12
|
+
model_config = ConfigDict(
|
|
13
|
+
populate_by_name=True,
|
|
14
|
+
)
|
|
15
|
+
bucket: str
|
|
16
|
+
path_prefix: Annotated[str | None, Field(alias='pathPrefix')] = None
|
|
17
|
+
region: str
|
|
18
|
+
type: Literal['s3']
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BucketConnection2(BaseModel):
|
|
22
|
+
model_config = ConfigDict(
|
|
23
|
+
populate_by_name=True,
|
|
24
|
+
)
|
|
25
|
+
account_name: Annotated[str, Field(alias='accountName')]
|
|
26
|
+
container: str
|
|
27
|
+
path_prefix: Annotated[str | None, Field(alias='pathPrefix')] = None
|
|
28
|
+
type: Literal['azureBlob']
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BucketConnection3(BaseModel):
|
|
32
|
+
model_config = ConfigDict(
|
|
33
|
+
populate_by_name=True,
|
|
34
|
+
)
|
|
35
|
+
bucket: str
|
|
36
|
+
path_prefix: Annotated[str | None, Field(alias='pathPrefix')] = None
|
|
37
|
+
type: Literal['gcs']
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class BucketConnection(
|
|
41
|
+
RootModel[BucketConnection1 | BucketConnection2 | BucketConnection3]
|
|
42
|
+
):
|
|
43
|
+
root: Annotated[
|
|
44
|
+
BucketConnection1 | BucketConnection2 | BucketConnection3,
|
|
45
|
+
Field(
|
|
46
|
+
description='Cloud-bucket addressing (no credentials). Provider-structurally paired with\nthe matching [`BucketCredentials`] variant in the confidential half.'
|
|
47
|
+
),
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class BucketInput(BaseModel):
|
|
52
|
+
model_config = ConfigDict(
|
|
53
|
+
populate_by_name=True,
|
|
54
|
+
)
|
|
55
|
+
destination_directory: Annotated[str, Field(alias='destinationDirectory')]
|
|
56
|
+
leaf_id: Annotated[str, Field(alias='leafId')]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ExportConnectorKind1(BaseModel):
|
|
60
|
+
model_config = ConfigDict(
|
|
61
|
+
populate_by_name=True,
|
|
62
|
+
)
|
|
63
|
+
connection: BucketConnection
|
|
64
|
+
export_prefix: Annotated[
|
|
65
|
+
str | None,
|
|
66
|
+
Field(
|
|
67
|
+
alias='exportPrefix',
|
|
68
|
+
description='May hold `{yyyy}`, `{mm}`, `{dd}` and `{hh}`, which the worker fills in\nUTC when a run starts.',
|
|
69
|
+
),
|
|
70
|
+
] = None
|
|
71
|
+
inputs: list[BucketInput]
|
|
72
|
+
type: Literal['bucket']
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ExportConnectorKind(RootModel[ExportConnectorKind1]):
|
|
76
|
+
root: ExportConnectorKind1
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ExportParams1(BaseModel):
|
|
80
|
+
model_config = ConfigDict(
|
|
81
|
+
populate_by_name=True,
|
|
82
|
+
)
|
|
83
|
+
type: Literal['bucket']
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ExportParams(RootModel[ExportParams1]):
|
|
87
|
+
root: ExportParams1
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ProvisionedPolicy(BaseModel):
|
|
91
|
+
model_config = ConfigDict(
|
|
92
|
+
populate_by_name=True,
|
|
93
|
+
)
|
|
94
|
+
leaf_id: Annotated[str, Field(alias='leafId')]
|
|
95
|
+
policy_id: Annotated[str, Field(alias='policyId')]
|
|
96
|
+
provisioned_at: Annotated[
|
|
97
|
+
str, Field(alias='provisionedAt', description='RFC 3339.')
|
|
98
|
+
]
|
|
99
|
+
provisioned_by_email: Annotated[
|
|
100
|
+
str,
|
|
101
|
+
Field(
|
|
102
|
+
alias='provisionedByEmail',
|
|
103
|
+
description='Whose READ on the dataset every run re-checks.',
|
|
104
|
+
),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ValidateConnectionParams1(BaseModel):
|
|
109
|
+
model_config = ConfigDict(
|
|
110
|
+
populate_by_name=True,
|
|
111
|
+
)
|
|
112
|
+
type: Literal['bucket']
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ValidateConnectionParams(RootModel[ValidateConnectionParams1]):
|
|
116
|
+
root: ValidateConnectionParams1
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class BucketExportConfig(BaseModel):
|
|
120
|
+
model_config = ConfigDict(
|
|
121
|
+
populate_by_name=True,
|
|
122
|
+
)
|
|
123
|
+
connection: BucketConnection
|
|
124
|
+
export_prefix: Annotated[
|
|
125
|
+
str | None,
|
|
126
|
+
Field(
|
|
127
|
+
alias='exportPrefix',
|
|
128
|
+
description='May hold `{yyyy}`, `{mm}`, `{dd}` and `{hh}`, which the worker fills in\nUTC when a run starts.',
|
|
129
|
+
),
|
|
130
|
+
] = None
|
|
131
|
+
inputs: list[BucketInput]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class ValidateConnection(BaseModel):
|
|
135
|
+
model_config = ConfigDict(
|
|
136
|
+
populate_by_name=True,
|
|
137
|
+
)
|
|
138
|
+
params: ValidateConnectionParams
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ExportConnectorDcrComputeAction1(BaseModel):
|
|
142
|
+
model_config = ConfigDict(
|
|
143
|
+
extra='forbid',
|
|
144
|
+
populate_by_name=True,
|
|
145
|
+
)
|
|
146
|
+
validate_connection: Annotated[
|
|
147
|
+
ValidateConnection, Field(alias='validateConnection')
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class Export(BaseModel):
|
|
152
|
+
model_config = ConfigDict(
|
|
153
|
+
populate_by_name=True,
|
|
154
|
+
)
|
|
155
|
+
params: ExportParams
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class ExportConnectorDcrComputeAction2(BaseModel):
|
|
159
|
+
model_config = ConfigDict(
|
|
160
|
+
extra='forbid',
|
|
161
|
+
populate_by_name=True,
|
|
162
|
+
)
|
|
163
|
+
export: Export
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class ExportConnectorDcrComputeAction(
|
|
167
|
+
RootModel[ExportConnectorDcrComputeAction1 | ExportConnectorDcrComputeAction2]
|
|
168
|
+
):
|
|
169
|
+
root: Annotated[
|
|
170
|
+
ExportConnectorDcrComputeAction1 | ExportConnectorDcrComputeAction2,
|
|
171
|
+
Field(
|
|
172
|
+
description='Compute actions for an Export Connector DCR, started through `POST /jobs`.'
|
|
173
|
+
),
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class ExportWorkerConfig(BaseModel):
|
|
178
|
+
model_config = ConfigDict(
|
|
179
|
+
populate_by_name=True,
|
|
180
|
+
)
|
|
181
|
+
action: ExportConnectorDcrComputeAction
|
|
182
|
+
connector: ExportConnectorKind
|
|
183
|
+
policies: list[ProvisionedPolicy]
|
|
File without changes
|