dagster-census 0.28.1__py3-none-any.whl → 0.28.3__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.
- dagster_census/__init__.py +2 -2
- dagster_census/components/__init__.py +0 -0
- dagster_census/components/census_component.py +219 -0
- dagster_census/components/census_scaffolder.py +26 -0
- dagster_census/resources.py +93 -77
- dagster_census/translator.py +57 -0
- dagster_census/version.py +1 -1
- {dagster_census-0.28.1.dist-info → dagster_census-0.28.3.dist-info}/METADATA +3 -4
- dagster_census-0.28.3.dist-info/RECORD +16 -0
- dagster_census-0.28.1.dist-info/RECORD +0 -12
- {dagster_census-0.28.1.dist-info → dagster_census-0.28.3.dist-info}/WHEEL +0 -0
- {dagster_census-0.28.1.dist-info → dagster_census-0.28.3.dist-info}/licenses/LICENSE +0 -0
- {dagster_census-0.28.1.dist-info → dagster_census-0.28.3.dist-info}/top_level.txt +0 -0
dagster_census/__init__.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from dagster_shared.libraries import DagsterLibraryRegistry
|
|
2
2
|
|
|
3
|
+
from dagster_census.components.census_component import CensusComponent as CensusComponent
|
|
3
4
|
from dagster_census.ops import census_trigger_sync_op
|
|
4
|
-
from dagster_census.resources import CensusResource
|
|
5
|
+
from dagster_census.resources import CensusResource
|
|
5
6
|
from dagster_census.types import CensusOutput
|
|
6
7
|
from dagster_census.version import __version__
|
|
7
8
|
|
|
@@ -10,6 +11,5 @@ DagsterLibraryRegistry.register("dagster-census", __version__)
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"CensusOutput",
|
|
12
13
|
"CensusResource",
|
|
13
|
-
"census_resource",
|
|
14
14
|
"census_trigger_sync_op",
|
|
15
15
|
]
|
|
File without changes
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated, Optional, Union
|
|
5
|
+
|
|
6
|
+
import dagster as dg
|
|
7
|
+
import pydantic
|
|
8
|
+
from dagster._annotations import public
|
|
9
|
+
from dagster._core.definitions.metadata import TableMetadataSet
|
|
10
|
+
from dagster._utils.names import clean_name
|
|
11
|
+
from dagster.components.component.state_backed_component import StateBackedComponent
|
|
12
|
+
from dagster.components.resolved.base import resolve_fields
|
|
13
|
+
from dagster.components.utils.defs_state import (
|
|
14
|
+
DefsStateConfig,
|
|
15
|
+
DefsStateConfigArgs,
|
|
16
|
+
ResolvedDefsStateConfig,
|
|
17
|
+
)
|
|
18
|
+
from dagster_shared import check
|
|
19
|
+
from dagster_shared.serdes.serdes import deserialize_value
|
|
20
|
+
|
|
21
|
+
from dagster_census.components.census_scaffolder import CensusComponentScaffolder
|
|
22
|
+
from dagster_census.resources import CensusResource
|
|
23
|
+
from dagster_census.translator import (
|
|
24
|
+
CensusMetadataSet,
|
|
25
|
+
CensusSync,
|
|
26
|
+
CensusWorkspaceData,
|
|
27
|
+
generate_table_schema,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CensusSyncSelectorByName(dg.Resolvable, dg.Model):
|
|
32
|
+
by_name: Annotated[
|
|
33
|
+
Sequence[str],
|
|
34
|
+
pydantic.Field(..., description="A list of sync names to include in the collection."),
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CensusSyncSelectorById(dg.Resolvable, dg.Model):
|
|
39
|
+
by_id: Annotated[
|
|
40
|
+
Sequence[Union[int, str]], # Allow strings for use-cases like '{{ env.ENV_VAR }}'
|
|
41
|
+
pydantic.Field(..., description="A list of sync IDs to include in the collection."),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def resolve_sync_selector(
|
|
46
|
+
context: dg.ResolutionContext, model
|
|
47
|
+
) -> Optional[Callable[[CensusSync], bool]]:
|
|
48
|
+
if isinstance(model, str):
|
|
49
|
+
resolved = context.resolve_value(model)
|
|
50
|
+
resolved = check.callable_param(resolved, "unknown") # pyright: ignore[reportArgumentType]
|
|
51
|
+
return resolved
|
|
52
|
+
if isinstance(model, CensusSyncSelectorByName.model()):
|
|
53
|
+
resolved = resolve_fields(model, CensusSyncSelectorByName.model(), context)
|
|
54
|
+
return lambda sync: sync.name in resolved["by_name"]
|
|
55
|
+
elif isinstance(model, CensusSyncSelectorById.model()):
|
|
56
|
+
resolved = resolve_fields(model, CensusSyncSelectorById.model(), context)
|
|
57
|
+
return lambda sync: sync.id in resolved["by_id"]
|
|
58
|
+
else:
|
|
59
|
+
check.failed(f"Unknown sync target type: {type(model)}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class CensusResourceArgs(dg.Resolvable, dg.Model):
|
|
64
|
+
"""The fields are analogous to the fields at `CensusResource`."""
|
|
65
|
+
|
|
66
|
+
api_key: Annotated[str, pydantic.Field(...)]
|
|
67
|
+
request_max_retries: Annotated[int, pydantic.Field(None)]
|
|
68
|
+
request_retry_delay: Annotated[float, pydantic.Field(None)]
|
|
69
|
+
request_timeout: Annotated[int, pydantic.Field(None)]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def resolve_census_workspace(
|
|
73
|
+
context: dg.ResolutionContext, model: CensusResourceArgs
|
|
74
|
+
) -> CensusResource:
|
|
75
|
+
return CensusResource(**resolve_fields(model, CensusResourceArgs, context))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@public
|
|
79
|
+
@dataclass
|
|
80
|
+
@dg.scaffold_with(CensusComponentScaffolder)
|
|
81
|
+
class CensusComponent(StateBackedComponent, dg.Resolvable):
|
|
82
|
+
"""Loads Census syncs from a Census workspace as Dagster assets.
|
|
83
|
+
Materializing these assets will trigger the Census sync, enabling
|
|
84
|
+
you to schedule Census syncs using Dagster.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
|
|
88
|
+
.. code-block:: yaml
|
|
89
|
+
|
|
90
|
+
# defs.yaml
|
|
91
|
+
|
|
92
|
+
type: dagster_census.CensusComponent
|
|
93
|
+
attributes:
|
|
94
|
+
workspace:
|
|
95
|
+
api_key: "{{ env.CENSUS_API_KEY }}"
|
|
96
|
+
sync_selector:
|
|
97
|
+
by_name:
|
|
98
|
+
- my_first_sync
|
|
99
|
+
- my_second_sync
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
workspace: Annotated[
|
|
103
|
+
CensusResource,
|
|
104
|
+
dg.Resolver(resolve_census_workspace, model_field_type=CensusResourceArgs),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
sync_selector: Annotated[
|
|
108
|
+
Optional[Callable[[CensusSync], bool]],
|
|
109
|
+
dg.Resolver(
|
|
110
|
+
resolve_sync_selector,
|
|
111
|
+
model_field_type=Union[
|
|
112
|
+
str, CensusSyncSelectorByName.model(), CensusSyncSelectorById.model()
|
|
113
|
+
],
|
|
114
|
+
description="Function used to select Census syncs to pull into Dagster.",
|
|
115
|
+
),
|
|
116
|
+
] = None
|
|
117
|
+
|
|
118
|
+
defs_state: ResolvedDefsStateConfig = field(
|
|
119
|
+
default_factory=DefsStateConfigArgs.local_filesystem
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def defs_state_config(self) -> DefsStateConfig:
|
|
124
|
+
default_key = f"{self.__class__.__name__}"
|
|
125
|
+
return DefsStateConfig.from_args(self.defs_state, default_key=default_key)
|
|
126
|
+
|
|
127
|
+
def write_state_to_path(self, state_path: Path) -> None:
|
|
128
|
+
state = self.workspace.fetch_census_workspace_data()
|
|
129
|
+
state_path.write_text(dg.serialize_value(state))
|
|
130
|
+
|
|
131
|
+
@public
|
|
132
|
+
def get_asset_spec(self, sync: CensusSync) -> dg.AssetSpec:
|
|
133
|
+
metadata = {
|
|
134
|
+
**TableMetadataSet(
|
|
135
|
+
column_schema=generate_table_schema(sync.mappings),
|
|
136
|
+
table_name=sync.name,
|
|
137
|
+
),
|
|
138
|
+
**CensusMetadataSet(
|
|
139
|
+
sync_id=sync.id,
|
|
140
|
+
sync_name=sync.name,
|
|
141
|
+
source_id=sync.source_id,
|
|
142
|
+
destination_id=sync.destination_id,
|
|
143
|
+
),
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return dg.AssetSpec(
|
|
147
|
+
key=dg.AssetKey(clean_name(sync.name)),
|
|
148
|
+
metadata=metadata,
|
|
149
|
+
description=f"Asset generated from Census sync {sync.id}",
|
|
150
|
+
kinds={"census"},
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
@public
|
|
154
|
+
def execute(
|
|
155
|
+
self, context: dg.AssetExecutionContext, census: CensusResource
|
|
156
|
+
) -> Iterable[Union[dg.AssetMaterialization, dg.MaterializeResult]]:
|
|
157
|
+
"""Executes a Census sync for the selected sync.
|
|
158
|
+
|
|
159
|
+
This method can be overridden in a subclass to customize the sync execution behavior,
|
|
160
|
+
such as adding custom logging or handling sync results differently.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
context: The asset execution context provided by Dagster
|
|
164
|
+
census: The CensusResource used to trigger and monitor syncs
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
MaterializeResult event from the Census sync
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
Override this method to add custom logging during sync execution:
|
|
171
|
+
|
|
172
|
+
.. code-block:: python
|
|
173
|
+
|
|
174
|
+
from dagster_census import CensusComponent
|
|
175
|
+
import dagster as dg
|
|
176
|
+
|
|
177
|
+
class CustomCensusComponent(CensusComponent):
|
|
178
|
+
def execute(self, context, census):
|
|
179
|
+
context.log.info(f"Starting Census sync for {context.asset_key}")
|
|
180
|
+
result = super().execute(context, census)
|
|
181
|
+
context.log.info("Census sync completed successfully")
|
|
182
|
+
return result
|
|
183
|
+
"""
|
|
184
|
+
# Select the first asset-spec (since there is only one)
|
|
185
|
+
spec = next(iter(context.assets_def.specs))
|
|
186
|
+
|
|
187
|
+
census_metadataset = CensusMetadataSet.extract(spec.metadata)
|
|
188
|
+
census.trigger_sync_and_poll(sync_id=census_metadataset.sync_id)
|
|
189
|
+
yield dg.AssetMaterialization(asset_key=clean_name(census_metadataset.sync_name))
|
|
190
|
+
|
|
191
|
+
def _load_asset_specs(self, state: CensusWorkspaceData) -> Sequence[dg.AssetSpec]:
|
|
192
|
+
connection_selector_fn = self.sync_selector or (lambda sync: True)
|
|
193
|
+
return [self.get_asset_spec(sync) for sync in state.syncs if connection_selector_fn(sync)]
|
|
194
|
+
|
|
195
|
+
def _get_census_assets_def(self, sync_name: str, spec: dg.AssetSpec) -> dg.AssetsDefinition:
|
|
196
|
+
@dg.multi_asset(
|
|
197
|
+
name=f"census_{clean_name(sync_name)}",
|
|
198
|
+
can_subset=True,
|
|
199
|
+
specs=[spec],
|
|
200
|
+
)
|
|
201
|
+
def _asset(context: dg.AssetExecutionContext):
|
|
202
|
+
yield from self.execute(context, self.workspace)
|
|
203
|
+
|
|
204
|
+
return _asset
|
|
205
|
+
|
|
206
|
+
def build_defs_from_state(
|
|
207
|
+
self, context: dg.ComponentLoadContext, state_path: Optional[Path]
|
|
208
|
+
) -> dg.Definitions:
|
|
209
|
+
if state_path is None:
|
|
210
|
+
return dg.Definitions()
|
|
211
|
+
|
|
212
|
+
state = deserialize_value(state_path.read_text(), CensusWorkspaceData)
|
|
213
|
+
|
|
214
|
+
assets = [
|
|
215
|
+
self._get_census_assets_def(CensusMetadataSet.extract(spec.metadata).sync_name, spec)
|
|
216
|
+
for spec in self._load_asset_specs(state)
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
return dg.Definitions(assets=assets)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from dagster.components.component.component_scaffolder import Scaffolder
|
|
4
|
+
from dagster.components.component_scaffolding import scaffold_component
|
|
5
|
+
from dagster.components.scaffold.scaffold import ScaffoldRequest
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CensusScaffolderParams(BaseModel):
|
|
10
|
+
api_key: Optional[str] = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CensusComponentScaffolder(Scaffolder[CensusScaffolderParams]):
|
|
14
|
+
@classmethod
|
|
15
|
+
def get_scaffold_params(cls) -> type[CensusScaffolderParams]:
|
|
16
|
+
return CensusScaffolderParams
|
|
17
|
+
|
|
18
|
+
def scaffold(self, request: ScaffoldRequest[CensusScaffolderParams]) -> None:
|
|
19
|
+
scaffold_component(
|
|
20
|
+
request,
|
|
21
|
+
{
|
|
22
|
+
"workspace": {
|
|
23
|
+
"api_key": "{{ env.CENSUS_API_KEY }}",
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
)
|
dagster_census/resources.py
CHANGED
|
@@ -5,12 +5,14 @@ import time
|
|
|
5
5
|
from collections.abc import Mapping
|
|
6
6
|
from typing import Any, Optional
|
|
7
7
|
|
|
8
|
+
import pydantic
|
|
8
9
|
import requests
|
|
9
|
-
from dagster import
|
|
10
|
-
from
|
|
10
|
+
from dagster import ConfigurableResource, Failure, __version__, get_dagster_logger
|
|
11
|
+
from dagster_shared.utils.cached_method import cached_method
|
|
11
12
|
from requests.auth import HTTPBasicAuth
|
|
12
13
|
from requests.exceptions import RequestException
|
|
13
14
|
|
|
15
|
+
from dagster_census.translator import CensusSync, CensusWorkspaceData
|
|
14
16
|
from dagster_census.types import CensusOutput
|
|
15
17
|
|
|
16
18
|
CENSUS_API_BASE = "app.getcensus.com/api"
|
|
@@ -21,35 +23,68 @@ DEFAULT_POLL_INTERVAL = 10
|
|
|
21
23
|
SYNC_RUN_STATUSES = {"completed", "failed", "queued", "skipped", "working"}
|
|
22
24
|
|
|
23
25
|
|
|
24
|
-
class CensusResource:
|
|
25
|
-
"""This
|
|
26
|
+
class CensusResource(ConfigurableResource):
|
|
27
|
+
"""This resource allows users to programatically interface with the Census REST API to launch
|
|
28
|
+
syncs and monitor their progress. This currently implements only a subset of the functionality
|
|
29
|
+
exposed by the API.
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
self,
|
|
29
|
-
api_key: str,
|
|
30
|
-
request_max_retries: int = 3,
|
|
31
|
-
request_retry_delay: float = 0.25,
|
|
32
|
-
log: logging.Logger = get_dagster_logger(),
|
|
33
|
-
):
|
|
34
|
-
self.api_key = api_key
|
|
31
|
+
**Examples:**
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
self._request_retry_delay = request_retry_delay
|
|
33
|
+
.. code-block:: python
|
|
38
34
|
|
|
39
|
-
|
|
35
|
+
import dagster as dg
|
|
36
|
+
from dagster_census import CensusResource
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
census_resource = CensusResource(
|
|
39
|
+
api_key=dg.EnvVar("CENSUS_API_KEY")
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
@dg.asset
|
|
43
|
+
def census_sync_asset(census: CensusResource):
|
|
44
|
+
census.trigger_sync_and_poll(sync_id=123456)
|
|
45
|
+
|
|
46
|
+
defs = dg.Definitions(
|
|
47
|
+
assets=[census_sync_asset],
|
|
48
|
+
resources={"census": census_resource}
|
|
49
|
+
)
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
api_key: str = pydantic.Field(..., description="The Census API key")
|
|
53
|
+
request_max_retries: int = pydantic.Field(
|
|
54
|
+
default=3,
|
|
55
|
+
description=(
|
|
56
|
+
"The maximum number of times requests to the Census API should be retried "
|
|
57
|
+
"before failing."
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
request_retry_delay: float = pydantic.Field(
|
|
61
|
+
default=0.25,
|
|
62
|
+
description="Time (in seconds) to wait between each request retry.",
|
|
63
|
+
)
|
|
64
|
+
request_timeout: int = pydantic.Field(
|
|
65
|
+
default=15,
|
|
66
|
+
description="Time (in seconds) after which the requests to Census are declared timed out.",
|
|
67
|
+
)
|
|
46
68
|
|
|
47
69
|
@property
|
|
48
70
|
def api_base_url(self) -> str:
|
|
49
71
|
return f"https://{CENSUS_API_BASE}/{CENSUS_VERSION}"
|
|
50
72
|
|
|
73
|
+
@classmethod
|
|
74
|
+
def _is_dagster_maintained(cls) -> bool:
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
@cached_method
|
|
79
|
+
def _log(self) -> logging.Logger:
|
|
80
|
+
return get_dagster_logger()
|
|
81
|
+
|
|
51
82
|
def make_request(
|
|
52
|
-
self,
|
|
83
|
+
self,
|
|
84
|
+
method: str,
|
|
85
|
+
endpoint: str,
|
|
86
|
+
data: Optional[str] = None,
|
|
87
|
+
page_number: Optional[int] = None,
|
|
53
88
|
) -> Mapping[str, Any]:
|
|
54
89
|
"""Creates and sends a request to the desired Census API endpoint.
|
|
55
90
|
|
|
@@ -67,6 +102,11 @@ class CensusResource:
|
|
|
67
102
|
"Content-Type": "application/json;version=2",
|
|
68
103
|
}
|
|
69
104
|
|
|
105
|
+
if page_number is not None:
|
|
106
|
+
params = {"page": page_number}
|
|
107
|
+
else:
|
|
108
|
+
params = {}
|
|
109
|
+
|
|
70
110
|
num_retries = 0
|
|
71
111
|
while True:
|
|
72
112
|
try:
|
|
@@ -74,19 +114,20 @@ class CensusResource:
|
|
|
74
114
|
method=method,
|
|
75
115
|
url=url,
|
|
76
116
|
headers=headers,
|
|
77
|
-
auth=HTTPBasicAuth("bearer", self.
|
|
117
|
+
auth=HTTPBasicAuth("bearer", self.api_key),
|
|
78
118
|
data=data,
|
|
119
|
+
params=params,
|
|
79
120
|
)
|
|
80
121
|
response.raise_for_status()
|
|
81
122
|
return response.json()
|
|
82
123
|
except RequestException as e:
|
|
83
124
|
self._log.error("Request to Census API failed: %s", e)
|
|
84
|
-
if num_retries == self.
|
|
125
|
+
if num_retries == self.request_max_retries:
|
|
85
126
|
break
|
|
86
127
|
num_retries += 1
|
|
87
|
-
time.sleep(self.
|
|
128
|
+
time.sleep(self.request_retry_delay)
|
|
88
129
|
|
|
89
|
-
raise Failure(f"Max retries ({self.
|
|
130
|
+
raise Failure(f"Max retries ({self.request_max_retries}) exceeded with url: {url}.")
|
|
90
131
|
|
|
91
132
|
def get_sync(self, sync_id: int) -> Mapping[str, Any]:
|
|
92
133
|
"""Gets details about a given sync from the Census API.
|
|
@@ -250,57 +291,32 @@ class CensusResource:
|
|
|
250
291
|
destination=destination_details,
|
|
251
292
|
)
|
|
252
293
|
|
|
294
|
+
@cached_method
|
|
295
|
+
def fetch_census_workspace_data(self) -> CensusWorkspaceData:
|
|
296
|
+
"""Retrieves all Census syncs from the workspace and returns it as a CensusWorkspaceData object.
|
|
253
297
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
def census_resource(context) -> CensusResource:
|
|
279
|
-
"""This resource allows users to programatically interface with the Census REST API to launch
|
|
280
|
-
syncs and monitor their progress. This currently implements only a subset of the functionality
|
|
281
|
-
exposed by the API.
|
|
282
|
-
|
|
283
|
-
**Examples:**
|
|
284
|
-
|
|
285
|
-
.. code-block:: python
|
|
286
|
-
|
|
287
|
-
from dagster import job
|
|
288
|
-
from dagster_census import census_resource
|
|
289
|
-
|
|
290
|
-
my_census_resource = census_resource.configured(
|
|
291
|
-
{
|
|
292
|
-
"api_key": {"env": "CENSUS_API_KEY"},
|
|
293
|
-
}
|
|
298
|
+
Returns:
|
|
299
|
+
CensusWorkspaceData: A snapshot of the Census workspace's syncs.
|
|
300
|
+
"""
|
|
301
|
+
all_syncs = []
|
|
302
|
+
page = self.make_request(method="GET", endpoint="syncs")
|
|
303
|
+
|
|
304
|
+
last_page_number = page["pagination"]["last_page"]
|
|
305
|
+
for sync in page["data"]:
|
|
306
|
+
all_syncs.append(self._census_sync_struct_from_json(sync))
|
|
307
|
+
|
|
308
|
+
for i in range(2, last_page_number + 1):
|
|
309
|
+
page = self.make_request(method="GET", endpoint="syncs", page_number=i)
|
|
310
|
+
for sync in page["data"]:
|
|
311
|
+
all_syncs.append(self._census_sync_struct_from_json(sync))
|
|
312
|
+
|
|
313
|
+
return CensusWorkspaceData(syncs=all_syncs)
|
|
314
|
+
|
|
315
|
+
def _census_sync_struct_from_json(self, sync: dict[str, Any]) -> CensusSync:
|
|
316
|
+
return CensusSync(
|
|
317
|
+
id=sync["id"],
|
|
318
|
+
name=sync["label"] or sync["resource_identifier"],
|
|
319
|
+
source_id=sync["source_attributes"]["connection_id"],
|
|
320
|
+
destination_id=sync["destination_attributes"]["connection_id"],
|
|
321
|
+
mappings=sync["mappings"],
|
|
294
322
|
)
|
|
295
|
-
|
|
296
|
-
@job(resource_defs={"census":my_census_resource})
|
|
297
|
-
def my_census_job():
|
|
298
|
-
...
|
|
299
|
-
|
|
300
|
-
"""
|
|
301
|
-
return CensusResource(
|
|
302
|
-
api_key=context.resource_config["api_key"],
|
|
303
|
-
request_max_retries=context.resource_config["request_max_retries"],
|
|
304
|
-
request_retry_delay=context.resource_config["request_retry_delay"],
|
|
305
|
-
log=context.log,
|
|
306
|
-
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import dagster as dg
|
|
5
|
+
from dagster._core.definitions.metadata.metadata_set import NamespacedMetadataSet
|
|
6
|
+
from dagster._record import record
|
|
7
|
+
from dagster_shared.serdes import whitelist_for_serdes
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@whitelist_for_serdes
|
|
11
|
+
@record
|
|
12
|
+
class CensusSync:
|
|
13
|
+
"""Represents a Census sync, based on data as returned from the API."""
|
|
14
|
+
|
|
15
|
+
id: int
|
|
16
|
+
name: str
|
|
17
|
+
source_id: int
|
|
18
|
+
destination_id: int
|
|
19
|
+
mappings: list[dict[str, Any]]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@whitelist_for_serdes
|
|
23
|
+
@record
|
|
24
|
+
class CensusWorkspaceData:
|
|
25
|
+
"""A record representing all content in a Census workspace.
|
|
26
|
+
Provided as context for the translator so that it can resolve dependencies between content.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
syncs: list[CensusSync]
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def syncs_by_id(self) -> Mapping[int, CensusSync]:
|
|
33
|
+
"""Returns a mapping of sync IDs to CensusSync objects."""
|
|
34
|
+
return {sync.id: sync for sync in self.syncs}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def generate_table_schema(sync_mappings_props: list[dict[str, Any]]) -> dg.TableSchema:
|
|
38
|
+
return dg.TableSchema(
|
|
39
|
+
columns=sorted(
|
|
40
|
+
[
|
|
41
|
+
dg.TableColumn(name=mapping["to"], type=mapping.get("field_type", "unknown"))
|
|
42
|
+
for mapping in sync_mappings_props
|
|
43
|
+
],
|
|
44
|
+
key=lambda col: col.name,
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class CensusMetadataSet(NamespacedMetadataSet):
|
|
50
|
+
sync_id: int
|
|
51
|
+
sync_name: str
|
|
52
|
+
source_id: int
|
|
53
|
+
destination_id: int
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def namespace(cls) -> str:
|
|
57
|
+
return "dagster-census"
|
dagster_census/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.28.
|
|
1
|
+
__version__ = "0.28.3"
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dagster-census
|
|
3
|
-
Version: 0.28.
|
|
3
|
+
Version: 0.28.3
|
|
4
4
|
Summary: Package for integrating Census with Dagster.
|
|
5
5
|
Home-page: https://github.com/dagster-io/dagster/tree/master/python_modules/libraries/dagster-census
|
|
6
6
|
Author: Dagster Labs
|
|
7
7
|
Author-email: hello@dagsterlabs.com
|
|
8
8
|
License: Apache-2.0
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
10
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
14
|
Classifier: Operating System :: OS Independent
|
|
16
|
-
Requires-Python: >=3.
|
|
15
|
+
Requires-Python: >=3.10,<3.14
|
|
17
16
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: dagster==1.12.
|
|
17
|
+
Requires-Dist: dagster==1.12.3
|
|
19
18
|
Dynamic: author
|
|
20
19
|
Dynamic: author-email
|
|
21
20
|
Dynamic: classifier
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
dagster_census/__init__.py,sha256=eIDWjz13trVGLVMDjAdq0LeUkbEiWex1ho2NX8cDFOY,501
|
|
2
|
+
dagster_census/ops.py,sha256=4N53-w0u3PG5kn-q1tW-7Ac-i5SCDFq9ku--g7gDig4,3659
|
|
3
|
+
dagster_census/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
dagster_census/resources.py,sha256=R-McSQLQTOImDVAQ3Tr-ZbvbUMQnbKpW7hkvtuwtS7I,11661
|
|
5
|
+
dagster_census/translator.py,sha256=caSshQ3ZfSsl_ASNlN6p24t-15WDRN29Z1xBg7Utbp4,1510
|
|
6
|
+
dagster_census/types.py,sha256=on1CIKWZMsSuw5h-HkzQPVctMQzQu2L9HgkX5gc9X54,698
|
|
7
|
+
dagster_census/utils.py,sha256=fFsKjn6xlLEel1-nvAwfAaemjSTg2gYxJHY8sK2e1no,1348
|
|
8
|
+
dagster_census/version.py,sha256=PFj1PlFYtLhN7k4TLEHMneuQ4Ep8o7DCrl69TL8I0SY,23
|
|
9
|
+
dagster_census/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
dagster_census/components/census_component.py,sha256=vgaVfT0pFVoC8ddhJJQEGJwjfZGsjLiTlXDOeNYW4q0,7970
|
|
11
|
+
dagster_census/components/census_scaffolder.py,sha256=n0YT0Rov6yJsPHAVpbtUrkxxF59z0WLCnRhYEpYtF4A,818
|
|
12
|
+
dagster_census-0.28.3.dist-info/licenses/LICENSE,sha256=4lsMW-RCvfVD4_F57wrmpe3vX1xwUk_OAKKmV_XT7Z0,11348
|
|
13
|
+
dagster_census-0.28.3.dist-info/METADATA,sha256=Ev9xMQZWqpRwda9sF_o9Xz4VEAEJ-ggIY1GjvRW5g3k,869
|
|
14
|
+
dagster_census-0.28.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
dagster_census-0.28.3.dist-info/top_level.txt,sha256=Lz6T6gBn89ilLGOLr8YIrBTRcWPCjeOtdNk8sdqmUnA,15
|
|
16
|
+
dagster_census-0.28.3.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
dagster_census/__init__.py,sha256=MsOV0XOueouBCn1FUYSN36fyswIK4pfwaWD2qceNPy4,451
|
|
2
|
-
dagster_census/ops.py,sha256=4N53-w0u3PG5kn-q1tW-7Ac-i5SCDFq9ku--g7gDig4,3659
|
|
3
|
-
dagster_census/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
dagster_census/resources.py,sha256=2kktbxyzgZOCo0VB8rUDmvK3-D9IpanYQyPTkTRlsg4,10839
|
|
5
|
-
dagster_census/types.py,sha256=on1CIKWZMsSuw5h-HkzQPVctMQzQu2L9HgkX5gc9X54,698
|
|
6
|
-
dagster_census/utils.py,sha256=fFsKjn6xlLEel1-nvAwfAaemjSTg2gYxJHY8sK2e1no,1348
|
|
7
|
-
dagster_census/version.py,sha256=ZRQKbgDaGz_yuLk-cUKuk6ZBKCSRKZC8nQd041NRNXk,23
|
|
8
|
-
dagster_census-0.28.1.dist-info/licenses/LICENSE,sha256=4lsMW-RCvfVD4_F57wrmpe3vX1xwUk_OAKKmV_XT7Z0,11348
|
|
9
|
-
dagster_census-0.28.1.dist-info/METADATA,sha256=ORN8dO12w1fe1k1EkdZiExK4oZOPW_HqhTaqUXPykTU,918
|
|
10
|
-
dagster_census-0.28.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
-
dagster_census-0.28.1.dist-info/top_level.txt,sha256=Lz6T6gBn89ilLGOLr8YIrBTRcWPCjeOtdNk8sdqmUnA,15
|
|
12
|
-
dagster_census-0.28.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|