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.
@@ -1,8 +1,9 @@
1
1
  # noinspection PyProtectedMember
2
2
  from dagster._core.libraries import DagsterLibraryRegistry
3
3
 
4
- from dagster_dingtalk.resources import DingTalkAPIResource, DingTalkWebhookResource
5
- from dagster_dingtalk.operations import DingTalkWebhookOp
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__)
@@ -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
- config_schema={"dingtalk_webhook_key": str},
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 = getattr(context.resources, context.op_config["dingtalk_webhook_key"])
11
+ webhook = context.resources.dingtalk_webhook
13
12
  webhook.send_text(text)
14
13
 
15
14
  @staticmethod
16
15
  @op(description="使用钉钉 Webhook 发送 Markdown 消息",
17
- config_schema={"dingtalk_webhook_key": str},
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 = getattr(context.resources, context.op_config["dingtalk_webhook_key"])
20
+ webhook = context.resources.dingtalk_webhook
22
21
  webhook.send_text(text, title)
@@ -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
- class DingTalkMultiClient:
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 DingTalkAPIResource(ConfigurableResource):
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: DingTalkMultiClient = PrivateAttr()
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, context: InitResourceContext) -> str:
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
- context.log.info(f"应用{self.AppName}<{self.AppID}> 鉴权缓存过期或不存在,正在重新获取...")
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 = DingTalkMultiClient(
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:DingTalkMultiClient):
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:DingTalkMultiClient):
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:DingTalkMultiClient):
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']):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dagster-dingtalk
3
- Version: 0.1.4
3
+ Version: 0.1.5b1
4
4
  Summary: A dagster plugin for the DingTalk
5
5
  Author: YiZixuan
6
6
  Author-email: sqkkyzx@qq.com
@@ -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,,