dagster-dingtalk 0.1.4__py3-none-any.whl → 0.1.5b1__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_dingtalk/__init__.py +3 -2
- dagster_dingtalk/operations.py +4 -5
- dagster_dingtalk/resources.py +124 -19
- {dagster_dingtalk-0.1.4.dist-info → dagster_dingtalk-0.1.5b1.dist-info}/METADATA +1 -1
- dagster_dingtalk-0.1.5b1.dist-info/RECORD +7 -0
- dagster_dingtalk-0.1.4.dist-info/RECORD +0 -7
- {dagster_dingtalk-0.1.4.dist-info → dagster_dingtalk-0.1.5b1.dist-info}/WHEEL +0 -0
dagster_dingtalk/__init__.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# noinspection PyProtectedMember
|
2
2
|
from dagster._core.libraries import DagsterLibraryRegistry
|
3
3
|
|
4
|
-
from dagster_dingtalk.resources import
|
5
|
-
from dagster_dingtalk.
|
4
|
+
from dagster_dingtalk.resources import DingTalkResource, MultiDingTalkResource
|
5
|
+
from dagster_dingtalk.resources import DingTalkWebhookResource, MultiDingTalkWebhookResource
|
6
|
+
# from dagster_dingtalk.operations import DingTalkWebhookOp
|
6
7
|
from dagster_dingtalk.version import __version__
|
7
8
|
|
8
9
|
DagsterLibraryRegistry.register("dagster-dingtalk", __version__)
|
dagster_dingtalk/operations.py
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
from dagster import In, OpExecutionContext, op
|
2
|
-
from pydantic import Field
|
3
2
|
|
4
3
|
|
5
4
|
class DingTalkWebhookOp:
|
6
5
|
@staticmethod
|
7
6
|
@op(description="使用钉钉 Webhook 发送文本消息",
|
8
|
-
|
7
|
+
required_resource_keys={'dingtalk_webhook'},
|
9
8
|
ins={"text": In(str)},
|
10
9
|
)
|
11
10
|
def send_simple_text(context: OpExecutionContext, text):
|
12
|
-
webhook =
|
11
|
+
webhook = context.resources.dingtalk_webhook
|
13
12
|
webhook.send_text(text)
|
14
13
|
|
15
14
|
@staticmethod
|
16
15
|
@op(description="使用钉钉 Webhook 发送 Markdown 消息",
|
17
|
-
|
16
|
+
required_resource_keys={'dingtalk_webhook'},
|
18
17
|
ins={"text": In(str), "title": In(str, default_value='')},
|
19
18
|
)
|
20
19
|
def send_simple_markdown(context: OpExecutionContext, text, title):
|
21
|
-
webhook =
|
20
|
+
webhook = context.resources.dingtalk_webhook
|
22
21
|
webhook.send_text(text, title)
|
dagster_dingtalk/resources.py
CHANGED
@@ -12,19 +12,19 @@ import httpx
|
|
12
12
|
|
13
13
|
from dagster import (
|
14
14
|
ConfigurableResource,
|
15
|
-
InitResourceContext,
|
15
|
+
InitResourceContext, ResourceDependency,
|
16
16
|
)
|
17
17
|
from httpx import Client
|
18
|
-
from pydantic import Field, PrivateAttr
|
18
|
+
from pydantic import Field, PrivateAttr, BaseModel
|
19
19
|
|
20
20
|
|
21
21
|
class DingTalkWebhookResource(ConfigurableResource):
|
22
|
-
base_url: str = Field(default="https://oapi.dingtalk.com/robot/send", description="Webhook的通用地址,无需更改")
|
23
22
|
access_token: str = Field(description="Webhook地址中的 access_token 部分")
|
24
23
|
secret: Optional[str] = Field(default=None, description="如使用加签安全配置,需传签名密钥")
|
24
|
+
alias: Optional[str] = Field(default=None, description="如提供别名,将来可以使用别名进行选择")
|
25
|
+
base_url: str = Field(default="https://oapi.dingtalk.com/robot/send", description="Webhook的通用地址,无需更改")
|
25
26
|
|
26
27
|
def _sign_webhook_url(self):
|
27
|
-
|
28
28
|
if self.secret is None:
|
29
29
|
return self.webhook_url
|
30
30
|
else:
|
@@ -88,7 +88,60 @@ class DingTalkWebhookResource(ConfigurableResource):
|
|
88
88
|
httpx.post(url=self.webhook_url, json={"msgtype": "feedCard", "feedCard": {"links": links_data}})
|
89
89
|
|
90
90
|
|
91
|
-
|
91
|
+
# noinspection NonAsciiCharacters
|
92
|
+
class MultiDingTalkWebhookResource(ConfigurableResource):
|
93
|
+
"""
|
94
|
+
该资源提供了预先定义多个 webhook 资源,并在运行时动态选择的方法。
|
95
|
+
|
96
|
+
使用示例:
|
97
|
+
|
98
|
+
```
|
99
|
+
from dagster_dingtalk import DingTalkResource, MultiDingTalkResource
|
100
|
+
|
101
|
+
app_apple = DingTalkResource(AppID="apple", ClientId="", ClientSecret="")
|
102
|
+
app_book = DingTalkResource(AppID="book", ClientId="", ClientSecret="")
|
103
|
+
|
104
|
+
@op(required_resource_keys={"dingtalk"}, ins={"app_id":In(str)})
|
105
|
+
def print_app_id(context:OpExecutionContext, app_id):
|
106
|
+
dingtalk:DingTalkResource = context.resources.dingtalk
|
107
|
+
select_app = dingtalk.select_app(app_id)
|
108
|
+
context.log.info(dingtalk_app.AppName)
|
109
|
+
|
110
|
+
@job
|
111
|
+
def print_app_id_job():
|
112
|
+
print_app_id()
|
113
|
+
|
114
|
+
defs = Definitions(
|
115
|
+
jobs=[print_app_id_job],
|
116
|
+
resources={
|
117
|
+
"dingtalk": MultiDingTalkResource(
|
118
|
+
Apps=[app_apple, app_book]
|
119
|
+
)
|
120
|
+
},
|
121
|
+
)
|
122
|
+
```
|
123
|
+
|
124
|
+
"""
|
125
|
+
|
126
|
+
Webhooks: ResourceDependency[List[DingTalkWebhookResource]] = Field(description="多个 Webhook 资源的列表")
|
127
|
+
|
128
|
+
_webhooks = PrivateAttr()
|
129
|
+
|
130
|
+
def setup_for_execution(self, context: InitResourceContext) -> None:
|
131
|
+
_webhooks_token_key = {webhook.access_token:webhook for webhook in self.Webhooks}
|
132
|
+
_webhooks_alias_key = {webhook.alias:webhook for webhook in self.Webhooks if webhook.alias}
|
133
|
+
self._webhooks = _webhooks_token_key | _webhooks_alias_key
|
134
|
+
|
135
|
+
def select(self, key:str = "_FIRST_"):
|
136
|
+
try:
|
137
|
+
if key == "_FIRST_":
|
138
|
+
return self.Webhooks[0]
|
139
|
+
return self._webhooks[key]
|
140
|
+
except KeyError:
|
141
|
+
raise f"该 AccessToken 或 别名 <{key}> 不存在于提供的 Webhooks 中。请使用 DingTalkWebhookResource 定义单个 Webhook 后,将其加入 Webhooks 。"
|
142
|
+
|
143
|
+
|
144
|
+
class DingTalkClient:
|
92
145
|
def __init__(self, access_token: str, app_id: str, agent_id: int, robot_code: str) -> None:
|
93
146
|
self.access_token: str = access_token
|
94
147
|
self.app_id: str = app_id
|
@@ -99,7 +152,7 @@ class DingTalkMultiClient:
|
|
99
152
|
|
100
153
|
|
101
154
|
# noinspection NonAsciiCharacters
|
102
|
-
class
|
155
|
+
class DingTalkResource(ConfigurableResource):
|
103
156
|
"""
|
104
157
|
[钉钉服务端 API](https://open.dingtalk.com/document/orgapp/api-overview) 企业内部应用部分的第三方封装。
|
105
158
|
通过此资源,可以调用部分钉钉服务端API。
|
@@ -112,15 +165,15 @@ class DingTalkAPIResource(ConfigurableResource):
|
|
112
165
|
AppName: Optional[str] = Field(default=None, description="应用名。")
|
113
166
|
ClientId: str = Field(description="应用的 Client ID (原 AppKey 和 SuiteKey)")
|
114
167
|
ClientSecret: str = Field(description="应用的 Client Secret (原 AppSecret 和 SuiteSecret)")
|
115
|
-
RobotCode: Optional[str] = Field(default=None, description="应用的机器人 RobotCode")
|
168
|
+
RobotCode: Optional[str] = Field(default=None, description="应用的机器人 RobotCode,不传时使用 self.ClientId ")
|
116
169
|
|
117
|
-
_client:
|
170
|
+
_client: DingTalkClient = PrivateAttr()
|
118
171
|
|
119
172
|
@classmethod
|
120
173
|
def _is_dagster_maintained(cls) -> bool:
|
121
174
|
return False
|
122
175
|
|
123
|
-
def _get_access_token(self
|
176
|
+
def _get_access_token(self) -> str:
|
124
177
|
access_token_cache = Path("~/.dingtalk_cache")
|
125
178
|
|
126
179
|
if access_token_cache.exists():
|
@@ -134,7 +187,7 @@ class DingTalkAPIResource(ConfigurableResource):
|
|
134
187
|
if access_token and expire_in < int(time.time()):
|
135
188
|
return access_token
|
136
189
|
else:
|
137
|
-
|
190
|
+
print(f"应用{self.AppName}<{self.AppID}> 鉴权缓存过期或不存在,正在重新获取...")
|
138
191
|
response = httpx.post(
|
139
192
|
url="https://api.dingtalk.com/v1.0/oauth2/accessToken",
|
140
193
|
json={"appKey": self.ClientId, "appSecret": self.ClientSecret},
|
@@ -147,30 +200,82 @@ class DingTalkAPIResource(ConfigurableResource):
|
|
147
200
|
return access_token
|
148
201
|
|
149
202
|
def setup_for_execution(self, context: InitResourceContext) -> None:
|
150
|
-
self._client =
|
151
|
-
self._get_access_token(context),
|
152
|
-
self.AppID,
|
153
|
-
self.AgentID,
|
154
|
-
self.RobotCode,
|
155
|
-
)
|
203
|
+
self._client = DingTalkClient(self._get_access_token(), self.AppID, self.AgentID, self.RobotCode or self.ClientId)
|
156
204
|
|
157
205
|
def teardown_after_execution(self, context: InitResourceContext) -> None:
|
158
206
|
self._client.api.close()
|
159
207
|
self._client.oapi.close()
|
160
208
|
|
161
209
|
def 智能人事(self):
|
210
|
+
if not hasattr(self, '_client'):
|
211
|
+
self._client = DingTalkClient(self._get_access_token(), self.AppID, self.AgentID, self.RobotCode or self.ClientId)
|
162
212
|
return API_智能人事(self._client)
|
163
213
|
|
164
214
|
def 通讯录管理(self):
|
215
|
+
if not hasattr(self, '_client'):
|
216
|
+
self._client = DingTalkClient(self._get_access_token(), self.AppID, self.AgentID, self.RobotCode or self.ClientId)
|
165
217
|
return API_通讯录管理(self._client)
|
166
218
|
|
167
219
|
def 文档文件(self):
|
220
|
+
if not hasattr(self, '_client'):
|
221
|
+
self._client = DingTalkClient(self._get_access_token(), self.AppID, self.AgentID, self.RobotCode or self.ClientId)
|
168
222
|
return API_文档文件(self._client)
|
169
223
|
|
170
224
|
|
225
|
+
# noinspection NonAsciiCharacters
|
226
|
+
class MultiDingTalkResource(ConfigurableResource):
|
227
|
+
"""
|
228
|
+
该资源提供了预先定义多个应用资源,并在运行时动态选择的方法。
|
229
|
+
|
230
|
+
使用示例:
|
231
|
+
|
232
|
+
```
|
233
|
+
from dagster_dingtalk import DingTalkResource, MultiDingTalkResource
|
234
|
+
|
235
|
+
app_apple = DingTalkResource(AppID="apple", ClientId="", ClientSecret="")
|
236
|
+
app_book = DingTalkResource(AppID="book", ClientId="", ClientSecret="")
|
237
|
+
|
238
|
+
@op(required_resource_keys={"dingtalk"}, ins={"app_id":In(str)})
|
239
|
+
def print_app_id(context:OpExecutionContext, app_id):
|
240
|
+
dingtalk:DingTalkResource = context.resources.dingtalk
|
241
|
+
select_app = dingtalk.select_app(app_id)
|
242
|
+
context.log.info(dingtalk_app.AppName)
|
243
|
+
|
244
|
+
@job
|
245
|
+
def print_app_id_job():
|
246
|
+
print_app_id()
|
247
|
+
|
248
|
+
defs = Definitions(
|
249
|
+
jobs=[print_app_id_job],
|
250
|
+
resources={
|
251
|
+
"dingtalk": MultiDingTalkResource(
|
252
|
+
Apps=[app_apple, app_book]
|
253
|
+
)
|
254
|
+
},
|
255
|
+
)
|
256
|
+
```
|
257
|
+
|
258
|
+
"""
|
259
|
+
|
260
|
+
Apps: ResourceDependency[List[DingTalkResource]] = Field(description="多个单应用资源的列表")
|
261
|
+
|
262
|
+
_apps = PrivateAttr()
|
263
|
+
|
264
|
+
def setup_for_execution(self, context: InitResourceContext) -> None:
|
265
|
+
self._apps = {app.AppID:app for app in self.Apps}
|
266
|
+
|
267
|
+
def select(self, app_id:str = "_FIRST_"):
|
268
|
+
try:
|
269
|
+
if app_id == "_FIRST_":
|
270
|
+
return self.Apps[0]
|
271
|
+
return self._apps[app_id]
|
272
|
+
except KeyError:
|
273
|
+
raise f"该 AppID <{app_id}> 不存在于提供的 AppLists 中。请使用 DingTalkResource 定义单个 App 后,将其加入 AppLists 。"
|
274
|
+
|
275
|
+
|
171
276
|
# noinspection NonAsciiCharacters
|
172
277
|
class API_智能人事:
|
173
|
-
def __init__(self, _client:
|
278
|
+
def __init__(self, _client:DingTalkClient):
|
174
279
|
self._client = _client
|
175
280
|
|
176
281
|
def 花名册_获取花名册元数据(self):
|
@@ -228,7 +333,7 @@ class API_智能人事:
|
|
228
333
|
|
229
334
|
# noinspection NonAsciiCharacters
|
230
335
|
class API_通讯录管理:
|
231
|
-
def __init__(self, _client:
|
336
|
+
def __init__(self, _client:DingTalkClient):
|
232
337
|
self._client = _client
|
233
338
|
|
234
339
|
def 查询用户详情(self, user_id:str, language:str = "zh_CN"):
|
@@ -245,7 +350,7 @@ class API_通讯录管理:
|
|
245
350
|
|
246
351
|
# noinspection NonAsciiCharacters
|
247
352
|
class API_文档文件:
|
248
|
-
def __init__(self, _client:
|
353
|
+
def __init__(self, _client:DingTalkClient):
|
249
354
|
self._client = _client
|
250
355
|
|
251
356
|
def 媒体文件_上传媒体文件(self, file_path:Path|str, media_type:Literal['image', 'voice', 'video', 'file']):
|
@@ -0,0 +1,7 @@
|
|
1
|
+
dagster_dingtalk/__init__.py,sha256=ktvoURpkJwIzcyQfUvnel1KA4DukRgavAgLl7f0Cy_0,440
|
2
|
+
dagster_dingtalk/operations.py,sha256=xJJlOVmFjpaDTMkHZXxj5LbXqRtIQwREl9ZJdXIMOyE,788
|
3
|
+
dagster_dingtalk/resources.py,sha256=6S8pVSz0ZMumaibfsJ_ZTxJVUZOleotg1c4hzt_VPDk,15156
|
4
|
+
dagster_dingtalk/version.py,sha256=sXLh7g3KC4QCFxcZGBTpG2scR7hmmBsMjq6LqRptkRg,22
|
5
|
+
dagster_dingtalk-0.1.5b1.dist-info/METADATA,sha256=JeVAne_YvTbPe_Av_NPbuu5KpsKCsO0uJzNvarEfvLo,1661
|
6
|
+
dagster_dingtalk-0.1.5b1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
7
|
+
dagster_dingtalk-0.1.5b1.dist-info/RECORD,,
|
@@ -1,7 +0,0 @@
|
|
1
|
-
dagster_dingtalk/__init__.py,sha256=o6bZAaJFfoQEphn9ZaxGBYwq45EsDEBCeTRNUDCWLz0,350
|
2
|
-
dagster_dingtalk/operations.py,sha256=MOgt-a1k2OKxEaICm7-hq_JD2IpqxnI8-K9GUCfprTI,886
|
3
|
-
dagster_dingtalk/resources.py,sha256=uZ-DV8_7RswiT9t0zv8IOiu5nkQcc1yPwNESRTlMDnY,11089
|
4
|
-
dagster_dingtalk/version.py,sha256=sXLh7g3KC4QCFxcZGBTpG2scR7hmmBsMjq6LqRptkRg,22
|
5
|
-
dagster_dingtalk-0.1.4.dist-info/METADATA,sha256=o1hdiDJpcjOzt9tFNat7zRuzbKNDKNSxbV4eLk6psWs,1659
|
6
|
-
dagster_dingtalk-0.1.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
7
|
-
dagster_dingtalk-0.1.4.dist-info/RECORD,,
|
File without changes
|