contextbase-plugin-asana 0.2.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.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.3
2
+ Name: contextbase-plugin-asana
3
+ Version: 0.2.0
4
+ Summary: Asana plugin for ContextBase (PyAirbyte-backed)
5
+ Author: Alizain Feerasta
6
+ Author-email: Alizain Feerasta <alizain.feerasta@gmail.com>
7
+ Requires-Dist: contextbase-shared-plugins==0.2.0
8
+ Requires-Dist: dagster==1.12.14
9
+ Requires-Python: >=3.14, <3.15
@@ -0,0 +1,9 @@
1
+ plugin_asana/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ plugin_asana/binding_config.py,sha256=dOQIGFcLDliHucOKh58LaXc_U0e8PrukpD4yjAmJmzU,303
3
+ plugin_asana/component.py,sha256=0Apc22xPukpTtyMDWQOaokyeqa09QfkHVE_wref2OwQ,4199
4
+ plugin_asana/defs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ plugin_asana/defs/defs.yaml,sha256=3ZBC-MkD3er3uhI-c-LIovSeUgjTG9pElxcXsrEzfA0,48
6
+ plugin_asana/plugin.json,sha256=4xaJc0VRV9uAFnEK4JU73IJEN8u9-alJlFfovYgl4BY,81
7
+ contextbase_plugin_asana-0.2.0.dist-info/WHEEL,sha256=fWriCkzqm-pffF5af4gJC9iI5FMFaJTuN9UxxxzOmdY,81
8
+ contextbase_plugin_asana-0.2.0.dist-info/METADATA,sha256=MNGhp0ADcomriT94bDZoU_eyC9rxDoT1tC3Hlft1hO8,322
9
+ contextbase_plugin_asana-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.14
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
File without changes
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import Field
4
+
5
+ from shared_plugins.bindings import BaseBindingConfigModel, NonEmptyText
6
+
7
+
8
+ class AsanaBindingConfig(BaseBindingConfigModel):
9
+ organization_export_ids: list[NonEmptyText] | None = Field(
10
+ default=None,
11
+ min_length=1,
12
+ )
@@ -0,0 +1,124 @@
1
+ import dagster as dg
2
+ from dagster import AssetExecutionContext
3
+ from shared_plugins.automation import non_overlapping_automation_condition
4
+ from shared_plugins.bindings import (
5
+ parse_binding_config,
6
+ require_api_key,
7
+ )
8
+ from shared_plugins.control_plane import ControlPlaneClient
9
+ from shared_plugins.dlt import resolve_partition_binding
10
+ from shared_plugins.exceptions import PluginConfigurationError
11
+ from shared_plugins.naming import (
12
+ dagster_airbyte_sync_asset_key,
13
+ dagster_asset_group_name,
14
+ dagster_asset_tags,
15
+ dagster_partition_def_name,
16
+ dagster_pool_name,
17
+ plugin_id_from_module,
18
+ )
19
+ from shared_plugins.pyairbyte import (
20
+ build_pyairbyte_source,
21
+ require_pyairbyte_selected_stream_names,
22
+ run_pyairbyte_sync,
23
+ )
24
+ from shared_types.api_key_auth import ApiKeyAuth
25
+ from shared_types.dagster_binding_plan import DagsterAllPlanBinding
26
+
27
+ from .binding_config import AsanaBindingConfig
28
+
29
+ PLUGIN_ID = plugin_id_from_module(__file__)
30
+ DOCKER_IMAGE = "airbyte/source-asana:1.5.2"
31
+ SOURCE_UPDATED_AT_FIELDS = {
32
+ "tasks": "modified_at",
33
+ "projects": "modified_at",
34
+ }
35
+
36
+
37
+ def _build_connector_config(
38
+ cfg: AsanaBindingConfig,
39
+ auth: ApiKeyAuth,
40
+ ) -> dict[str, object]:
41
+ return {
42
+ **cfg.model_dump(mode="json", exclude_none=True),
43
+ "credentials": {
44
+ "option_title": "PAT Credentials",
45
+ "personal_access_token": auth.api_key,
46
+ },
47
+ }
48
+
49
+
50
+ def _resolve_stream_selection(
51
+ binding: DagsterAllPlanBinding,
52
+ cfg: AsanaBindingConfig,
53
+ ) -> tuple[str, ...]:
54
+ selected_stream_names = require_pyairbyte_selected_stream_names(binding)
55
+
56
+ if (
57
+ "organization_exports" in selected_stream_names
58
+ and cfg.organization_export_ids is None
59
+ ):
60
+ raise PluginConfigurationError(
61
+ "asana binding.config.organization_export_ids is required when "
62
+ "models.active includes organization_exports."
63
+ )
64
+
65
+ return selected_stream_names
66
+
67
+
68
+ class AsanaSyncComponent(dg.Component):
69
+ def build_defs(self, context: dg.ComponentLoadContext) -> dg.Definitions:
70
+ partitions_def = dg.DynamicPartitionsDefinition(
71
+ name=dagster_partition_def_name(PLUGIN_ID)
72
+ )
73
+
74
+ @dg.asset(
75
+ key=dagster_airbyte_sync_asset_key(PLUGIN_ID),
76
+ group_name=dagster_asset_group_name(PLUGIN_ID),
77
+ tags=dagster_asset_tags(PLUGIN_ID),
78
+ automation_condition=non_overlapping_automation_condition(
79
+ dg.AutomationCondition.on_missing()
80
+ | dg.AutomationCondition.on_cron("*/15 * * * *")
81
+ ),
82
+ partitions_def=partitions_def,
83
+ pool=dagster_pool_name(PLUGIN_ID),
84
+ )
85
+ def asana_sync_asset(
86
+ context: AssetExecutionContext,
87
+ control_plane: dg.ResourceParam[ControlPlaneClient],
88
+ ):
89
+ binding = resolve_partition_binding(
90
+ context=context,
91
+ control_plane=control_plane,
92
+ plugin_id=PLUGIN_ID,
93
+ )
94
+ binding_id = str(binding.binding_id)
95
+ cfg = parse_binding_config(binding, AsanaBindingConfig)
96
+ auth = require_api_key(binding)
97
+ selected_stream_names = _resolve_stream_selection(binding, cfg)
98
+
99
+ connector_config = _build_connector_config(cfg, auth)
100
+ source = build_pyairbyte_source(
101
+ docker_image=DOCKER_IMAGE,
102
+ connector_config=connector_config,
103
+ )
104
+
105
+ return run_pyairbyte_sync(
106
+ context=context,
107
+ plugin_id=PLUGIN_ID,
108
+ binding_id=binding_id,
109
+ selected_stream_names=selected_stream_names,
110
+ source_updated_at_fields=SOURCE_UPDATED_AT_FIELDS,
111
+ source=source,
112
+ )
113
+
114
+ automation_sensor = dg.AutomationConditionSensorDefinition(
115
+ name="asana_automation_sensor",
116
+ target=dg.AssetSelection.assets(asana_sync_asset),
117
+ default_status=dg.DefaultSensorStatus.RUNNING,
118
+ minimum_interval_seconds=30,
119
+ )
120
+
121
+ return dg.Definitions(
122
+ assets=[asana_sync_asset],
123
+ sensors=[automation_sensor],
124
+ )
File without changes
@@ -0,0 +1 @@
1
+ type: plugin_asana.component.AsanaSyncComponent
@@ -0,0 +1,7 @@
1
+ {
2
+ "auth": {
3
+ "type": "api_key"
4
+ },
5
+ "mode": "dagster",
6
+ "plugin_id": "asana"
7
+ }