dagster-dingtalk 0.1.4__py3-none-any.whl → 0.1.5b1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|