dagster-dingtalk 0.1.5b2__tar.gz → 0.1.8__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dagster-dingtalk
3
- Version: 0.1.5b2
3
+ Version: 0.1.8
4
4
  Summary: A dagster plugin for the DingTalk
5
5
  Author: YiZixuan
6
6
  Author-email: sqkkyzx@qq.com
@@ -15,7 +15,7 @@ from dagster import (
15
15
  InitResourceContext, ResourceDependency,
16
16
  )
17
17
  from httpx import Client
18
- from pydantic import Field, PrivateAttr, BaseModel
18
+ from pydantic import Field, PrivateAttr
19
19
 
20
20
 
21
21
  class DingTalkWebhookResource(ConfigurableResource):
@@ -24,9 +24,9 @@ class DingTalkWebhookResource(ConfigurableResource):
24
24
  alias: Optional[str] = Field(default=None, description="如提供别名,将来可以使用别名进行选择")
25
25
  base_url: str = Field(default="https://oapi.dingtalk.com/robot/send", description="Webhook的通用地址,无需更改")
26
26
 
27
- def _sign_webhook_url(self):
27
+ def webhook_url(self):
28
28
  if self.secret is None:
29
- return self.webhook_url
29
+ return f"{self.base_url}?access_token={self.access_token}"
30
30
  else:
31
31
  timestamp = round(time.time() * 1000)
32
32
  hmac_code = hmac.new(
@@ -46,12 +46,12 @@ class DingTalkWebhookResource(ConfigurableResource):
46
46
  at["atUserIds"] = at_user_ids
47
47
  if at_mobiles:
48
48
  at["atMobiles"] = at_mobiles
49
- httpx.post(url=self.webhook_url, json={"msgtype": "text", "text": {"content": text}, "at": at})
49
+ httpx.post(url=self.webhook_url(), json={"msgtype": "text", "text": {"content": text}, "at": at})
50
50
 
51
51
  def send_link(self, text: str, message_url:str, title:str|None = None, pic_url:str = ""):
52
52
  title = title or self._gen_title(text)
53
53
  httpx.post(
54
- url=self.webhook_url,
54
+ url=self.webhook_url(),
55
55
  json={"msgtype": "link", "link": {"title": title, "text": text, "picUrl": pic_url, "messageUrl": message_url}}
56
56
  )
57
57
 
@@ -64,7 +64,7 @@ class DingTalkWebhookResource(ConfigurableResource):
64
64
  at["atUserIds"] = at_user_ids
65
65
  if at_mobiles:
66
66
  at["atMobiles"] = at_mobiles
67
- httpx.post(url=self.webhook_url,json={"msgtype": "markdown", "markdown": {"title": title, "text": text}, "at": at})
67
+ httpx.post(url=self.webhook_url(),json={"msgtype": "markdown", "markdown": {"title": title, "text": text}, "at": at})
68
68
 
69
69
  def send_action_card(self, text: List[str]|str, title:str|None = None, btn_orientation:Literal["0","1"] = "0",
70
70
  single_jump:Tuple[str,str]|None = None, btns_jump:List[Tuple[str,str]]|None = None):
@@ -73,10 +73,10 @@ class DingTalkWebhookResource(ConfigurableResource):
73
73
  action_card = {"title": title, "text": text, "btnOrientation": btn_orientation}
74
74
  if btns_jump:
75
75
  action_card["btns"] = [{"title": action_title, "actionURL": action_url} for action_title, action_url in btns_jump]
76
- httpx.post(url=self.webhook_url, json={"msgtype": "actionCard", "actionCard": action_card})
76
+ httpx.post(url=self.webhook_url(), json={"msgtype": "actionCard", "actionCard": action_card})
77
77
  elif single_jump:
78
78
  action_card["singleTitle"], action_card["singleURL"] = single_jump
79
- httpx.post(url=self.webhook_url, json={"msgtype": "actionCard", "actionCard": action_card})
79
+ httpx.post(url=self.webhook_url(), json={"msgtype": "actionCard", "actionCard": action_card})
80
80
  else:
81
81
  pass
82
82
 
@@ -85,7 +85,7 @@ class DingTalkWebhookResource(ConfigurableResource):
85
85
  {"title": title, "messageURL": message_url, "picURL": pic_url}
86
86
  for title, message_url, pic_url in links
87
87
  ]
88
- httpx.post(url=self.webhook_url, json={"msgtype": "feedCard", "feedCard": {"links": links_data}})
88
+ httpx.post(url=self.webhook_url(), json={"msgtype": "feedCard", "feedCard": {"links": links_data}})
89
89
 
90
90
 
91
91
  # noinspection NonAsciiCharacters
@@ -134,9 +134,12 @@ class MultiDingTalkWebhookResource(ConfigurableResource):
134
134
 
135
135
  def select(self, key:str = "_FIRST_"):
136
136
  try:
137
- if key == "_FIRST_":
138
- return self.Webhooks[0]
139
- return self._webhooks[key]
137
+ if key == "_FIRST_" or key is None:
138
+ webhook = self.Webhooks[0]
139
+ else:
140
+ webhook = self._webhooks[key]
141
+ webhook.init_webhook_url()
142
+ return webhook
140
143
  except KeyError:
141
144
  raise f"该 AccessToken 或 别名 <{key}> 不存在于提供的 Webhooks 中。请使用 DingTalkWebhookResource 定义单个 Webhook 后,将其加入 Webhooks 。"
142
145
 
@@ -176,13 +179,14 @@ class DingTalkResource(ConfigurableResource):
176
179
  def _get_access_token(self) -> str:
177
180
  access_token_cache = Path("/tmp/.dingtalk_cache")
178
181
 
179
- if access_token_cache.exists():
182
+ try:
180
183
  with open(access_token_cache, 'rb') as f:
181
184
  all_access_token: Dict[str, Tuple[str, int]] = pickle.loads(f.read())
182
- else:
185
+ access_token, expire_in = all_access_token.get(self.AppID, ('', 0))
186
+ except Exception as e:
187
+ print(e)
183
188
  all_access_token = {}
184
-
185
- access_token, expire_in = all_access_token.get(self.AppID, ('', 0))
189
+ access_token, expire_in = all_access_token.get(self.AppID, ('', 0))
186
190
 
187
191
  if access_token and expire_in < int(time.time()):
188
192
  return access_token
@@ -196,29 +200,32 @@ class DingTalkResource(ConfigurableResource):
196
200
  expire_in:int = response.json().get("expireIn") + int(time.time()) - 60
197
201
  with open(access_token_cache, 'wb') as f:
198
202
  all_access_token[self.AppID] = (access_token, expire_in)
199
- f.write(pickle.dumps(access_token_cache))
203
+ f.write(pickle.dumps(all_access_token))
200
204
  return access_token
201
205
 
206
+ def init_client(self):
207
+ if not hasattr(self, '_client'):
208
+ self._client = DingTalkClient(
209
+ self._get_access_token(),
210
+ self.AppID,
211
+ self.AgentID,
212
+ self.RobotCode or self.ClientId
213
+ )
214
+
202
215
  def setup_for_execution(self, context: InitResourceContext) -> None:
203
- self._client = DingTalkClient(self._get_access_token(), self.AppID, self.AgentID, self.RobotCode or self.ClientId)
216
+ self.init_client()
204
217
 
205
218
  def teardown_after_execution(self, context: InitResourceContext) -> None:
206
219
  self._client.api.close()
207
220
  self._client.oapi.close()
208
221
 
209
222
  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)
212
223
  return API_智能人事(self._client)
213
224
 
214
225
  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)
217
226
  return API_通讯录管理(self._client)
218
227
 
219
228
  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)
222
229
  return API_文档文件(self._client)
223
230
 
224
231
 
@@ -266,9 +273,12 @@ class MultiDingTalkResource(ConfigurableResource):
266
273
 
267
274
  def select(self, app_id:str = "_FIRST_"):
268
275
  try:
269
- if app_id == "_FIRST_":
270
- return self.Apps[0]
271
- return self._apps[app_id]
276
+ if app_id == "_FIRST_" or app_id is None:
277
+ app = self.Apps[0]
278
+ else:
279
+ app = self._apps[app_id]
280
+ app.init_client()
281
+ return app
272
282
  except KeyError:
273
283
  raise f"该 AppID <{app_id}> 不存在于提供的 AppLists 中。请使用 DingTalkResource 定义单个 App 后,将其加入 AppLists 。"
274
284
 
@@ -278,14 +288,14 @@ class API_智能人事:
278
288
  def __init__(self, _client:DingTalkClient):
279
289
  self._client = _client
280
290
 
281
- def 花名册_获取花名册元数据(self):
291
+ def 花名册_获取花名册元数据(self) -> dict:
282
292
  response = self._client.oapi.post(
283
293
  url="/topapi/smartwork/hrm/roster/meta/get",
284
294
  json={"agentid": self._client.agent_id},
285
295
  )
286
296
  return response.json()
287
297
 
288
- def 花名册_获取员工花名册字段信息(self, user_id_list:List[str], field_filter_list:List[str]|None = None, text_to_select_convert:bool|None = None):
298
+ def 花名册_获取员工花名册字段信息(self, user_id_list:List[str], field_filter_list:List[str]|None = None, text_to_select_convert:bool|None = None) -> dict:
289
299
  body_dict = {"userIdList": user_id_list, "appAgentId": self._client.agent_id}
290
300
  if field_filter_list is not None:
291
301
  body_dict["fieldFilterList"] = field_filter_list
@@ -302,28 +312,28 @@ class API_智能人事:
302
312
  待离职: '5'
303
313
  无状态: '-1'
304
314
 
305
- def 员工管理_获取待入职员工列表(self, offset:int, size:int):
315
+ def 员工管理_获取待入职员工列表(self, offset:int, size:int) -> dict:
306
316
  response = self._client.oapi.post(
307
317
  "/topapi/smartwork/hrm/employee/querypreentry",
308
318
  json={"offset": offset, "size": size},
309
319
  )
310
320
  return response.json()
311
321
 
312
- def 员工管理_获取在职员工列表(self, status_list:List[在职员工状态], offset:int, size:int):
322
+ def 员工管理_获取在职员工列表(self, status_list:List[在职员工状态], offset:int, size:int) -> dict:
313
323
  response = self._client.oapi.post(
314
324
  "/topapi/smartwork/hrm/employee/querypreentry",
315
325
  json={"status_list": status_list, "offset": offset, "size": size},
316
326
  )
317
327
  return response.json()
318
328
 
319
- def 员工管理_获取离职员工列表(self, next_token:int, max_results:int):
329
+ def 员工管理_获取离职员工列表(self, next_token:int, max_results:int) -> dict:
320
330
  response = self._client.api.get(
321
331
  "/v1.0/hrm/employees/dismissions",
322
332
  params={"nextToken": next_token, "maxResults": max_results},
323
333
  )
324
334
  return response.json()
325
335
 
326
- def 员工管理_批量获取员工离职信息(self, user_id_list:List[str]):
336
+ def 员工管理_批量获取员工离职信息(self, user_id_list:List[str]) -> dict:
327
337
  response = self._client.api.get(
328
338
  "/v1.0/hrm/employees/dimissionInfo",
329
339
  params={"userIdList": user_id_list},
@@ -336,11 +346,11 @@ class API_通讯录管理:
336
346
  def __init__(self, _client:DingTalkClient):
337
347
  self._client = _client
338
348
 
339
- def 查询用户详情(self, user_id:str, language:str = "zh_CN"):
349
+ def 查询用户详情(self, user_id:str, language:str = "zh_CN") -> dict:
340
350
  response = self._client.oapi.post(url="/topapi/v2/user/get", json={"language": language, "userid": user_id})
341
351
  return response.json()
342
352
 
343
- def 查询离职记录列表(self, start_time:datetime, end_time:datetime|None, next_token:str, max_results:int):
353
+ def 查询离职记录列表(self, start_time:datetime, end_time:datetime|None, next_token:str, max_results:int) -> dict:
344
354
  params = {"startTime": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"), "nextToken": next_token, "maxResults": max_results}
345
355
  if end_time is not None:
346
356
  params["endTime"] = end_time.strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -353,7 +363,7 @@ class API_文档文件:
353
363
  def __init__(self, _client:DingTalkClient):
354
364
  self._client = _client
355
365
 
356
- def 媒体文件_上传媒体文件(self, file_path:Path|str, media_type:Literal['image', 'voice', 'video', 'file']):
366
+ def 媒体文件_上传媒体文件(self, file_path:Path|str, media_type:Literal['image', 'voice', 'video', 'file']) -> dict:
357
367
  with open(file_path, 'rb') as f:
358
368
  response = self._client.oapi.post(url=f"/media/upload?type={media_type}", files={'media': f})
359
369
  return response.json()
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dagster-dingtalk"
3
- version = "0.1.5.b2"
3
+ version = "0.1.8"
4
4
  description = "A dagster plugin for the DingTalk"
5
5
  authors = ["YiZixuan <sqkkyzx@qq.com>"]
6
6
  readme = "README.md"