dagster-dingtalk 0.1.14__tar.gz → 0.1.15__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {dagster_dingtalk-0.1.14 → dagster_dingtalk-0.1.15}/PKG-INFO +55 -38
- {dagster_dingtalk-0.1.14 → dagster_dingtalk-0.1.15}/README.md +54 -37
- {dagster_dingtalk-0.1.14 → dagster_dingtalk-0.1.15}/dagster_dingtalk/__init__.py +3 -4
- {dagster_dingtalk-0.1.14 → dagster_dingtalk-0.1.15}/dagster_dingtalk/app_client.py +251 -0
- dagster_dingtalk-0.1.15/dagster_dingtalk/operations.py +86 -0
- {dagster_dingtalk-0.1.14 → dagster_dingtalk-0.1.15}/dagster_dingtalk/resources.py +1 -3
- dagster_dingtalk-0.1.15/dagster_dingtalk/version.py +1 -0
- {dagster_dingtalk-0.1.14 → dagster_dingtalk-0.1.15}/pyproject.toml +1 -1
- dagster_dingtalk-0.1.14/dagster_dingtalk/operations.py +0 -20
- dagster_dingtalk-0.1.14/dagster_dingtalk/version.py +0 -1
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dagster-dingtalk
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.15
|
4
4
|
Summary: A dagster plugin for the DingTalk
|
5
5
|
Author: YiZixuan
|
6
6
|
Author-email: sqkkyzx@qq.com
|
@@ -16,32 +16,37 @@ Description-Content-Type: text/markdown
|
|
16
16
|
|
17
17
|
# 钉钉与 Dagster 集成
|
18
18
|
|
19
|
-
|
19
|
+
该 Dagster 集成是为了更便捷的调用钉钉(DingTalk)的API,集成提供了两个 Dagster Resource。
|
20
20
|
|
21
|
-
##
|
21
|
+
## 安装
|
22
|
+
要安装库,请在现有的Dagster环境中使用pip。
|
22
23
|
|
23
|
-
|
24
|
+
```bash
|
25
|
+
pip install dagster-dingtalk
|
26
|
+
```
|
27
|
+
|
28
|
+
## 资源
|
24
29
|
|
30
|
+
### DingTalkWebhookResource
|
25
31
|
|
26
|
-
|
32
|
+
---
|
27
33
|
|
28
34
|
该资源允许定义单个钉钉自定义机器人的 Webhook 端点,以便于发送文本、Markdown、Link、 ActionCard、FeedCard 消息,消息具体样式可参考
|
29
35
|
[钉钉开放平台 | 自定义机器人发送消息的消息类型](https://open.dingtalk.com/document/orgapp/custom-bot-send-message-type)。
|
30
36
|
|
31
|
-
|
37
|
+
#### 配置:
|
32
38
|
|
33
39
|
- **access_token** (str):
|
34
40
|
机器人 Webhook 地址中的 access_token 值。
|
35
41
|
- **secret** (str, optional):
|
36
42
|
如使用加签安全配置,则需传签名密钥。默认值为 None。
|
37
43
|
- **alias** (str, optional):
|
38
|
-
|
44
|
+
别名,仅用作标记。默认值为 None。
|
39
45
|
- **base_url** (str, optional):
|
40
46
|
通用地址,一般无需更改。默认值为 “https://oapi.dingtalk.com/robot/send”。
|
41
47
|
|
42
|
-
|
48
|
+
#### 用例 1:使用单个资源
|
43
49
|
|
44
|
-
##### 1. 使用单个资源:
|
45
50
|
```python
|
46
51
|
from dagster_dingtalk import DingTalkWebhookResource
|
47
52
|
from dagster import op, In, OpExecutionContext, job, Definitions
|
@@ -61,7 +66,10 @@ defs = Definitions(
|
|
61
66
|
)
|
62
67
|
```
|
63
68
|
|
64
|
-
|
69
|
+
#### 用例 2:启动时动态构建 Webhook 资源
|
70
|
+
|
71
|
+
如果你事先不确定会用到哪个 webhook 机器人,或是需要根据代码逻辑动态选择 webhook ,dagster 提供了一种 [在运行时配置资源](https://docs.dagster.io/concepts/resources#configuring-resources-at-launch-time)
|
72
|
+
的原生支持。以下是示例:
|
65
73
|
|
66
74
|
```python
|
67
75
|
from dagster_dingtalk import DingTalkWebhookResource
|
@@ -94,30 +102,13 @@ def schedule_user_info():
|
|
94
102
|
))
|
95
103
|
```
|
96
104
|
|
97
|
-
### 注意:
|
98
|
-
|
99
|
-
应该永远避免直接将密钥字符串直接配置给资源,这会导致在 dagster 前端用户界面暴露密钥。
|
100
|
-
应当从环境变量中读取密钥。你可以在代码中注册临时的环境变量,或从系统中引入环境变量。
|
101
|
-
|
102
|
-
```python
|
103
|
-
import os
|
104
|
-
from dagster import EnvVar
|
105
|
-
from dagster_dingtalk import DingTalkWebhookResource
|
106
|
-
|
107
|
-
# 直接在代码中注册临时的环境变量
|
108
|
-
os.environ.update({'access_token_name': "<your-access_token>"})
|
109
|
-
os.environ.update({'secret_name': "<your-secret>"})
|
110
|
-
|
111
|
-
webhook = DingTalkWebhookResource(access_token=EnvVar("access_token_name"), secret=EnvVar("secret_name"))
|
112
|
-
```
|
113
|
-
|
114
|
-
## DingTalkAppResource
|
115
105
|
|
116
|
-
|
106
|
+
### DingTalkAppResource
|
117
107
|
|
118
|
-
|
108
|
+
---
|
119
109
|
|
120
|
-
|
110
|
+
该 Dagster 资源允许定义一个可以调用 [钉钉服务端 API](https://open.dingtalk.com/document/orgapp/api-overview) 的 Client,
|
111
|
+
具有一些常用 HTTP API 的封装。你可以在 IDE 中通过引入 `DingTalkAppClient` 类来查看 IDE 提示:
|
121
112
|
|
122
113
|
```python
|
123
114
|
from dagster_dingtalk import DingTalkAppClient
|
@@ -125,7 +116,18 @@ from dagster_dingtalk import DingTalkAppClient
|
|
125
116
|
dingtalk: DingTalkAppClient
|
126
117
|
```
|
127
118
|
|
128
|
-
|
119
|
+
**请注意:`DingTalkAppClient` 未使用钉钉官方 SDK 实现,并采用了 ASCII 字符来命名实例方法。**
|
120
|
+
|
121
|
+
> 这是为了与
|
122
|
+
> [钉钉服务端 API 文档](https://open.dingtalk.com/document/orgapp/api-overview) 里的中文 API
|
123
|
+
> 保持完全一致命名,以便于更符合直觉地进行调用和快速查阅文档。因此,可以按
|
124
|
+
> [钉钉服务端 API 文档](https://open.dingtalk.com/document/orgapp/api-overview)
|
125
|
+
> 中的层级,通过链式调用来发起 API 请求。例如:
|
126
|
+
>
|
127
|
+
> `dingtalk.智能人事.花名册.获取花名册元数据()`
|
128
|
+
|
129
|
+
|
130
|
+
#### 配置:
|
129
131
|
|
130
132
|
- **AppID** (str):
|
131
133
|
应用应用唯一标识 AppID,作为缓存标识符使用。不传入则不缓存鉴权。
|
@@ -138,9 +140,7 @@ dingtalk: DingTalkAppClient
|
|
138
140
|
- **ClientSecret** (str):
|
139
141
|
应用的 Client Secret ,原 AppSecret 和 SuiteSecret
|
140
142
|
|
141
|
-
|
142
|
-
|
143
|
-
##### 1. 使用单一的企业内部应用资源。
|
143
|
+
#### 用例 1:使用确定的企业内部应用配置资源
|
144
144
|
|
145
145
|
```python
|
146
146
|
from dagster_dingtalk import DingTalkAppResource, DingTalkAppClient
|
@@ -165,7 +165,9 @@ defs = Definitions(
|
|
165
165
|
)})
|
166
166
|
```
|
167
167
|
|
168
|
-
|
168
|
+
#### 用例 2:运行时动态构建企业内部应用资源
|
169
|
+
|
170
|
+
可参考 [Dagster文档 | 在启动时配置资源](https://docs.dagster.io/concepts/resources#configuring-resources-at-launch-time)
|
169
171
|
|
170
172
|
```python
|
171
173
|
from dagster_dingtalk import DingTalkAppResource, DingTalkAppClient
|
@@ -204,7 +206,22 @@ def schedule_user_info():
|
|
204
206
|
))
|
205
207
|
```
|
206
208
|
|
207
|
-
### 注意:
|
208
209
|
|
209
|
-
|
210
|
+
## 提醒:
|
211
|
+
|
212
|
+
应该永远避免直接将密钥字符串直接配置给资源,这会导致在 dagster 前端用户界面暴露密钥。
|
213
|
+
应当从环境变量中读取密钥。你可以在代码中注册临时的环境变量,或从系统中引入环境变量。
|
214
|
+
|
215
|
+
```python
|
216
|
+
import os
|
217
|
+
from dagster import EnvVar
|
218
|
+
from dagster_dingtalk import DingTalkWebhookResource
|
219
|
+
|
220
|
+
# 直接在代码中注册临时的环境变量
|
221
|
+
os.environ.update({'access_token_name': "<your-access_token>"})
|
222
|
+
os.environ.update({'secret_name': "<your-secret>"})
|
223
|
+
|
224
|
+
webhook = DingTalkWebhookResource(access_token=EnvVar("access_token_name"), secret=EnvVar("secret_name"))
|
225
|
+
```
|
226
|
+
|
210
227
|
|
@@ -1,31 +1,36 @@
|
|
1
1
|
# 钉钉与 Dagster 集成
|
2
2
|
|
3
|
-
|
3
|
+
该 Dagster 集成是为了更便捷的调用钉钉(DingTalk)的API,集成提供了两个 Dagster Resource。
|
4
4
|
|
5
|
-
##
|
5
|
+
## 安装
|
6
|
+
要安装库,请在现有的Dagster环境中使用pip。
|
6
7
|
|
7
|
-
|
8
|
+
```bash
|
9
|
+
pip install dagster-dingtalk
|
10
|
+
```
|
11
|
+
|
12
|
+
## 资源
|
8
13
|
|
14
|
+
### DingTalkWebhookResource
|
9
15
|
|
10
|
-
|
16
|
+
---
|
11
17
|
|
12
18
|
该资源允许定义单个钉钉自定义机器人的 Webhook 端点,以便于发送文本、Markdown、Link、 ActionCard、FeedCard 消息,消息具体样式可参考
|
13
19
|
[钉钉开放平台 | 自定义机器人发送消息的消息类型](https://open.dingtalk.com/document/orgapp/custom-bot-send-message-type)。
|
14
20
|
|
15
|
-
|
21
|
+
#### 配置:
|
16
22
|
|
17
23
|
- **access_token** (str):
|
18
24
|
机器人 Webhook 地址中的 access_token 值。
|
19
25
|
- **secret** (str, optional):
|
20
26
|
如使用加签安全配置,则需传签名密钥。默认值为 None。
|
21
27
|
- **alias** (str, optional):
|
22
|
-
|
28
|
+
别名,仅用作标记。默认值为 None。
|
23
29
|
- **base_url** (str, optional):
|
24
30
|
通用地址,一般无需更改。默认值为 “https://oapi.dingtalk.com/robot/send”。
|
25
31
|
|
26
|
-
|
32
|
+
#### 用例 1:使用单个资源
|
27
33
|
|
28
|
-
##### 1. 使用单个资源:
|
29
34
|
```python
|
30
35
|
from dagster_dingtalk import DingTalkWebhookResource
|
31
36
|
from dagster import op, In, OpExecutionContext, job, Definitions
|
@@ -45,7 +50,10 @@ defs = Definitions(
|
|
45
50
|
)
|
46
51
|
```
|
47
52
|
|
48
|
-
|
53
|
+
#### 用例 2:启动时动态构建 Webhook 资源
|
54
|
+
|
55
|
+
如果你事先不确定会用到哪个 webhook 机器人,或是需要根据代码逻辑动态选择 webhook ,dagster 提供了一种 [在运行时配置资源](https://docs.dagster.io/concepts/resources#configuring-resources-at-launch-time)
|
56
|
+
的原生支持。以下是示例:
|
49
57
|
|
50
58
|
```python
|
51
59
|
from dagster_dingtalk import DingTalkWebhookResource
|
@@ -78,30 +86,13 @@ def schedule_user_info():
|
|
78
86
|
))
|
79
87
|
```
|
80
88
|
|
81
|
-
### 注意:
|
82
|
-
|
83
|
-
应该永远避免直接将密钥字符串直接配置给资源,这会导致在 dagster 前端用户界面暴露密钥。
|
84
|
-
应当从环境变量中读取密钥。你可以在代码中注册临时的环境变量,或从系统中引入环境变量。
|
85
|
-
|
86
|
-
```python
|
87
|
-
import os
|
88
|
-
from dagster import EnvVar
|
89
|
-
from dagster_dingtalk import DingTalkWebhookResource
|
90
|
-
|
91
|
-
# 直接在代码中注册临时的环境变量
|
92
|
-
os.environ.update({'access_token_name': "<your-access_token>"})
|
93
|
-
os.environ.update({'secret_name': "<your-secret>"})
|
94
|
-
|
95
|
-
webhook = DingTalkWebhookResource(access_token=EnvVar("access_token_name"), secret=EnvVar("secret_name"))
|
96
|
-
```
|
97
|
-
|
98
|
-
## DingTalkAppResource
|
99
89
|
|
100
|
-
|
90
|
+
### DingTalkAppResource
|
101
91
|
|
102
|
-
|
92
|
+
---
|
103
93
|
|
104
|
-
|
94
|
+
该 Dagster 资源允许定义一个可以调用 [钉钉服务端 API](https://open.dingtalk.com/document/orgapp/api-overview) 的 Client,
|
95
|
+
具有一些常用 HTTP API 的封装。你可以在 IDE 中通过引入 `DingTalkAppClient` 类来查看 IDE 提示:
|
105
96
|
|
106
97
|
```python
|
107
98
|
from dagster_dingtalk import DingTalkAppClient
|
@@ -109,7 +100,18 @@ from dagster_dingtalk import DingTalkAppClient
|
|
109
100
|
dingtalk: DingTalkAppClient
|
110
101
|
```
|
111
102
|
|
112
|
-
|
103
|
+
**请注意:`DingTalkAppClient` 未使用钉钉官方 SDK 实现,并采用了 ASCII 字符来命名实例方法。**
|
104
|
+
|
105
|
+
> 这是为了与
|
106
|
+
> [钉钉服务端 API 文档](https://open.dingtalk.com/document/orgapp/api-overview) 里的中文 API
|
107
|
+
> 保持完全一致命名,以便于更符合直觉地进行调用和快速查阅文档。因此,可以按
|
108
|
+
> [钉钉服务端 API 文档](https://open.dingtalk.com/document/orgapp/api-overview)
|
109
|
+
> 中的层级,通过链式调用来发起 API 请求。例如:
|
110
|
+
>
|
111
|
+
> `dingtalk.智能人事.花名册.获取花名册元数据()`
|
112
|
+
|
113
|
+
|
114
|
+
#### 配置:
|
113
115
|
|
114
116
|
- **AppID** (str):
|
115
117
|
应用应用唯一标识 AppID,作为缓存标识符使用。不传入则不缓存鉴权。
|
@@ -122,9 +124,7 @@ dingtalk: DingTalkAppClient
|
|
122
124
|
- **ClientSecret** (str):
|
123
125
|
应用的 Client Secret ,原 AppSecret 和 SuiteSecret
|
124
126
|
|
125
|
-
|
126
|
-
|
127
|
-
##### 1. 使用单一的企业内部应用资源。
|
127
|
+
#### 用例 1:使用确定的企业内部应用配置资源
|
128
128
|
|
129
129
|
```python
|
130
130
|
from dagster_dingtalk import DingTalkAppResource, DingTalkAppClient
|
@@ -149,7 +149,9 @@ defs = Definitions(
|
|
149
149
|
)})
|
150
150
|
```
|
151
151
|
|
152
|
-
|
152
|
+
#### 用例 2:运行时动态构建企业内部应用资源
|
153
|
+
|
154
|
+
可参考 [Dagster文档 | 在启动时配置资源](https://docs.dagster.io/concepts/resources#configuring-resources-at-launch-time)
|
153
155
|
|
154
156
|
```python
|
155
157
|
from dagster_dingtalk import DingTalkAppResource, DingTalkAppClient
|
@@ -188,6 +190,21 @@ def schedule_user_info():
|
|
188
190
|
))
|
189
191
|
```
|
190
192
|
|
191
|
-
### 注意:
|
192
193
|
|
193
|
-
|
194
|
+
## 提醒:
|
195
|
+
|
196
|
+
应该永远避免直接将密钥字符串直接配置给资源,这会导致在 dagster 前端用户界面暴露密钥。
|
197
|
+
应当从环境变量中读取密钥。你可以在代码中注册临时的环境变量,或从系统中引入环境变量。
|
198
|
+
|
199
|
+
```python
|
200
|
+
import os
|
201
|
+
from dagster import EnvVar
|
202
|
+
from dagster_dingtalk import DingTalkWebhookResource
|
203
|
+
|
204
|
+
# 直接在代码中注册临时的环境变量
|
205
|
+
os.environ.update({'access_token_name': "<your-access_token>"})
|
206
|
+
os.environ.update({'secret_name': "<your-secret>"})
|
207
|
+
|
208
|
+
webhook = DingTalkWebhookResource(access_token=EnvVar("access_token_name"), secret=EnvVar("secret_name"))
|
209
|
+
```
|
210
|
+
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# noinspection PyProtectedMember
|
2
2
|
from dagster._core.libraries import DagsterLibraryRegistry
|
3
|
+
from dagster_dingtalk.version import __version__
|
3
4
|
|
4
|
-
from dagster_dingtalk.resources import DingTalkAppResource
|
5
|
-
from dagster_dingtalk.resources import DingTalkWebhookResource
|
5
|
+
from dagster_dingtalk.resources import DingTalkAppResource, DingTalkWebhookResource
|
6
6
|
from dagster_dingtalk.app_client import DingTalkClient as DingTalkAppClient
|
7
|
-
|
8
|
-
from dagster_dingtalk.version import __version__
|
7
|
+
import dagster_dingtalk.operations as DingTalkOp
|
9
8
|
|
10
9
|
DagsterLibraryRegistry.register("dagster-dingtalk", __version__)
|
@@ -5,7 +5,10 @@ from datetime import datetime
|
|
5
5
|
from enum import Enum
|
6
6
|
from pathlib import Path
|
7
7
|
from typing import List, Literal, Dict, Tuple
|
8
|
+
|
9
|
+
import httpx
|
8
10
|
from httpx import Client
|
11
|
+
from pydantic import BaseModel, Field
|
9
12
|
|
10
13
|
|
11
14
|
# noinspection NonAsciiCharacters
|
@@ -26,6 +29,7 @@ class DingTalkClient:
|
|
26
29
|
self.通讯录管理 = 通讯录管理_API(self)
|
27
30
|
self.文档文件 = 文档文件_API(self)
|
28
31
|
self.互动卡片 = 互动卡片_API(self)
|
32
|
+
self.OA审批 = OA审批_API(self)
|
29
33
|
|
30
34
|
def __get_access_token(self) -> str:
|
31
35
|
access_token_cache = Path("/tmp/.dingtalk_cache")
|
@@ -210,6 +214,7 @@ class 文档文件_API:
|
|
210
214
|
def __init__(self, _client:DingTalkClient):
|
211
215
|
self.__client:DingTalkClient = _client
|
212
216
|
self.媒体文件 = 文档文件_媒体文件_API(_client)
|
217
|
+
self.文件传输 = 文档文件_文件传输_API(_client)
|
213
218
|
|
214
219
|
# noinspection NonAsciiCharacters
|
215
220
|
class 文档文件_媒体文件_API:
|
@@ -238,6 +243,84 @@ class 文档文件_媒体文件_API:
|
|
238
243
|
response = self.__client.oapi.post(url=f"/media/upload?type={media_type}", files={'media': f})
|
239
244
|
return response.json()
|
240
245
|
|
246
|
+
# noinspection NonAsciiCharacters
|
247
|
+
class 文档文件_文件传输_API:
|
248
|
+
def __init__(self, _client:DingTalkClient):
|
249
|
+
self.__client:DingTalkClient = _client
|
250
|
+
|
251
|
+
def 获取文件上传信息(self, space_id:int, union_id:str, multi_part:bool = False) -> dict:
|
252
|
+
"""
|
253
|
+
调用本接口,上传图片、语音媒体资源文件以及普通文件,接口返回媒体资源标识 media_id。
|
254
|
+
|
255
|
+
https://open.dingtalk.com/document/orgapp/upload-media-files
|
256
|
+
|
257
|
+
:param space_id: 空间Id。
|
258
|
+
:param union_id: 操作者unionId。
|
259
|
+
:param multi_part: 是否需要分片上传。默认值为 False
|
260
|
+
|
261
|
+
:return:
|
262
|
+
{
|
263
|
+
"uploadKey": str,
|
264
|
+
"storageDriver": str,
|
265
|
+
"protocol": str,
|
266
|
+
"headerSignatureInfo": {
|
267
|
+
"resourceUrls" : ["resourceUrl"],
|
268
|
+
"headers" : {
|
269
|
+
"key" : "header_value"
|
270
|
+
},
|
271
|
+
}
|
272
|
+
}
|
273
|
+
"""
|
274
|
+
response = self.__client.api.post(
|
275
|
+
url=f"/v1.0/storage/spaces/{space_id}/files/uploadInfos/query",
|
276
|
+
params={'unionId': union_id},
|
277
|
+
json={
|
278
|
+
"protocol": "HEADER_SIGNATURE",
|
279
|
+
"multipart": multi_part
|
280
|
+
}
|
281
|
+
)
|
282
|
+
return response.json()
|
283
|
+
|
284
|
+
def 提交文件(self, url:str, headers:dict, file_path:Path|str, space_id:int, union_id:str,
|
285
|
+
upload_key:str, convert_to_online_doc:bool = False) -> dict:
|
286
|
+
"""
|
287
|
+
调用本接口,上传图片、语音媒体资源文件以及普通文件,接口返回媒体资源标识 media_id。
|
288
|
+
|
289
|
+
https://open.dingtalk.com/document/orgapp/upload-media-files
|
290
|
+
|
291
|
+
:param url: 获取文件上传信息得到的 resourceUrl。
|
292
|
+
:param headers: 获取文件上传信息得到的 headers。
|
293
|
+
:param file_path: 文件路径
|
294
|
+
:param space_id: 空间Id。
|
295
|
+
:param union_id: 操作者unionId。
|
296
|
+
:param upload_key: 添加文件唯一标识。
|
297
|
+
:param convert_to_online_doc: 是否转换成在线文档。默认值 False
|
298
|
+
|
299
|
+
:return:
|
300
|
+
{
|
301
|
+
"uploadKey": str,
|
302
|
+
"storageDriver": str,
|
303
|
+
"protocol": str,
|
304
|
+
"headerSignatureInfo": dict,
|
305
|
+
}
|
306
|
+
"""
|
307
|
+
with open(file_path, 'rb') as f:
|
308
|
+
httpx.put(
|
309
|
+
url=url,
|
310
|
+
files={"file":f},
|
311
|
+
headers=headers
|
312
|
+
)
|
313
|
+
|
314
|
+
response = self.__client.api.post(
|
315
|
+
url = f"/v2.0/storage/spaces/files/{space_id}/commit?unionId={union_id}",
|
316
|
+
json = {
|
317
|
+
"uploadKey": upload_key,
|
318
|
+
"name": file_path.split("/")[-1],
|
319
|
+
"convertToOnlineDoc": convert_to_online_doc
|
320
|
+
}
|
321
|
+
)
|
322
|
+
return response.json()
|
323
|
+
|
241
324
|
# noinspection NonAsciiCharacters
|
242
325
|
class 互动卡片_API:
|
243
326
|
def __init__(self, _client:DingTalkClient):
|
@@ -325,3 +408,171 @@ class 互动卡片_API:
|
|
325
408
|
)
|
326
409
|
|
327
410
|
return response.json()
|
411
|
+
|
412
|
+
# noinspection NonAsciiCharacters
|
413
|
+
class OA审批_API:
|
414
|
+
def __init__(self, _client:DingTalkClient):
|
415
|
+
self.__client:DingTalkClient = _client
|
416
|
+
self.审批实例 = OA审批_审批实例_API(_client)
|
417
|
+
self.审批钉盘 = OA审批_审批钉盘_API(_client)
|
418
|
+
|
419
|
+
# noinspection NonAsciiCharacters
|
420
|
+
class OA审批_审批实例_API:
|
421
|
+
def __init__(self, _client:DingTalkClient):
|
422
|
+
self.__client:DingTalkClient = _client
|
423
|
+
|
424
|
+
class CommentAttachment(BaseModel):
|
425
|
+
spaceId: str = Field(description="钉盘空间ID")
|
426
|
+
fileSize: str = Field(description="文件大小")
|
427
|
+
fileId: str = Field(description="文件ID")
|
428
|
+
fileName: str = Field(description="文件名称")
|
429
|
+
fileType: str = Field(description="文件类型")
|
430
|
+
|
431
|
+
def 获取单个审批实例详情(self, instance_id:str) -> dict:
|
432
|
+
"""
|
433
|
+
调用本接口可以获取审批实例详情数据,根据审批实例ID,获取审批实例详情,包括审批实例标题、发起人的userId、审批人userId、操作记录列表等内容。
|
434
|
+
|
435
|
+
https://open.dingtalk.com/document/orgapp/obtains-the-details-of-a-single-approval-instance-pop
|
436
|
+
|
437
|
+
:param instance_id: 审批实例ID。
|
438
|
+
|
439
|
+
:return:
|
440
|
+
{
|
441
|
+
"success": boolean,
|
442
|
+
"result": {}
|
443
|
+
}
|
444
|
+
"""
|
445
|
+
response = self.__client.api.get(url="/v1.0/workflow/processInstances", params={'processInstanceId': instance_id})
|
446
|
+
return response.json()
|
447
|
+
|
448
|
+
def 撤销审批实例(self, instance_id:str, is_system:bool = True, remark:str|None = None, operating_user_id:str = None) -> dict:
|
449
|
+
"""
|
450
|
+
撤销发起的处于流程中的审批实例。审批发起15秒内不能撤销审批流程。本接口只能撤销流程中的审批实例,不能撤销已审批完成的审批实例。
|
451
|
+
|
452
|
+
https://open.dingtalk.com/document/orgapp/revoke-an-approval-instance
|
453
|
+
|
454
|
+
:param instance_id: 审批实例ID。
|
455
|
+
:param is_system: 是否通过系统操作。默认为 True。当为 false 时,需要传发起人才能撤销。
|
456
|
+
:param remark: 终止说明。
|
457
|
+
:param operating_user_id: 操作人的userId。is_system 为 false 时必填。
|
458
|
+
|
459
|
+
:return:
|
460
|
+
{
|
461
|
+
"success": boolean,
|
462
|
+
"result": {}
|
463
|
+
}
|
464
|
+
"""
|
465
|
+
response = self.__client.api.post(
|
466
|
+
url="/v1.0/workflow/processInstances",
|
467
|
+
json={
|
468
|
+
"processInstanceId" : instance_id,
|
469
|
+
"isSystem" : is_system,
|
470
|
+
"remark" : remark,
|
471
|
+
"operatingUserId" : operating_user_id
|
472
|
+
}
|
473
|
+
)
|
474
|
+
return response.json()
|
475
|
+
|
476
|
+
def 添加审批评论(
|
477
|
+
self, instance_id:str, text:str, comment_user_id: str,
|
478
|
+
photos: List[str]|None = None, attachments: List[CommentAttachment]|None = None
|
479
|
+
) -> dict:
|
480
|
+
"""
|
481
|
+
调用本接口可以获取审批实例详情数据,根据审批实例ID,获取审批实例详情,包括审批实例标题、发起人的userId、审批人userId、操作记录列表等内容。
|
482
|
+
|
483
|
+
其中,添加审批评论附件需调用获取审批钉盘空间信息接口,获取钉盘空间的上传权限,并获取审批钉盘空间spaceId。
|
484
|
+
|
485
|
+
https://open.dingtalk.com/document/orgapp/obtains-the-details-of-a-single-approval-instance-pop
|
486
|
+
|
487
|
+
:param instance_id: 审批实例 ID。
|
488
|
+
:param text: 评论的内容。
|
489
|
+
:param comment_user_id: 评论人的 UserId
|
490
|
+
:param photos: 图片的 URL 链接的列表,默认为 None。
|
491
|
+
:param attachments: 附件列表,默认为 None。添加审批评论附件需将文件上传至审批钉盘空间,可以获取到相关接口参数。
|
492
|
+
|
493
|
+
:return:
|
494
|
+
{
|
495
|
+
"success": boolean,
|
496
|
+
"result": boolean
|
497
|
+
}
|
498
|
+
"""
|
499
|
+
|
500
|
+
data = {
|
501
|
+
'processInstanceId': instance_id,
|
502
|
+
'text': text,
|
503
|
+
'commentUserId': comment_user_id,
|
504
|
+
}
|
505
|
+
|
506
|
+
if photos or attachments:
|
507
|
+
data.update({'file': {"photos": photos, "attachments": attachments}})
|
508
|
+
|
509
|
+
response = self.__client.api.post(
|
510
|
+
url="/v1.0/workflow/processInstances/comments",
|
511
|
+
json=data
|
512
|
+
)
|
513
|
+
return response.json()
|
514
|
+
|
515
|
+
def 获取审批实例ID列表(
|
516
|
+
self, process_code:str, start_time:datetime, end_time:datetime, next_token: int = 0, max_results: int = 20,
|
517
|
+
statuses: Literal["RUNNING", "TERMINATED", "COMPLETED"]|None = None, user_ids = List[str]
|
518
|
+
) -> dict:
|
519
|
+
"""
|
520
|
+
获取权限范围内的相关部门审批实例ID列表。
|
521
|
+
|
522
|
+
https://open.dingtalk.com/document/orgapp/obtain-an-approval-list-of-instance-ids
|
523
|
+
|
524
|
+
:param user_ids:
|
525
|
+
:param process_code: 审批流模板的 code。
|
526
|
+
:param start_time: 审批实例开始时间。
|
527
|
+
:param end_time: 审批实例结束时间。
|
528
|
+
:param next_token: 分页游标, 首次调用传 0, 默认值为 0
|
529
|
+
:param max_results: 分页小,最多传20,默认值为 20
|
530
|
+
:param statuses: 筛选流程实例状态,默认为 None,表示不筛选。 RUNNING-审批中 TERMINATED-已撤销 COMPLETED-审批完成
|
531
|
+
|
532
|
+
:return:
|
533
|
+
{
|
534
|
+
"success": boolean,
|
535
|
+
"result": {}
|
536
|
+
}
|
537
|
+
"""
|
538
|
+
response = self.__client.api.post(
|
539
|
+
url="/v1.0/workflow/processes/instanceIds/query",
|
540
|
+
json={
|
541
|
+
"processCode" : process_code,
|
542
|
+
"startTime" : int(start_time.timestamp()*1000),
|
543
|
+
"endTime" : int(end_time.timestamp()*1000),
|
544
|
+
"nextToken" : next_token,
|
545
|
+
"maxResults" : max_results,
|
546
|
+
"userIds" : user_ids,
|
547
|
+
"statuses" : statuses
|
548
|
+
})
|
549
|
+
return response.json()
|
550
|
+
|
551
|
+
# noinspection NonAsciiCharacters
|
552
|
+
class OA审批_审批钉盘_API:
|
553
|
+
def __init__(self, _client:DingTalkClient):
|
554
|
+
self.__client:DingTalkClient = _client
|
555
|
+
|
556
|
+
def 获取审批钉盘空间信息(self, user_id:str) -> dict:
|
557
|
+
"""
|
558
|
+
获取审批钉盘空间的ID并授予当前用户上传附件的权限。
|
559
|
+
|
560
|
+
https://open.dingtalk.com/document/orgapp/obtains-the-information-about-approval-nail-disk
|
561
|
+
|
562
|
+
:param user_id: 用户的userId。
|
563
|
+
|
564
|
+
:return:
|
565
|
+
{
|
566
|
+
"success": bool,
|
567
|
+
"result": {
|
568
|
+
"spaceId": int
|
569
|
+
}
|
570
|
+
}
|
571
|
+
"""
|
572
|
+
response = self.__client.api.post(
|
573
|
+
url="/v1.0/workflow/processInstances/spaces/infos/query",
|
574
|
+
json={
|
575
|
+
"userId" : user_id,
|
576
|
+
"agentId" : self.__client.agent_id
|
577
|
+
})
|
578
|
+
return response.json()
|
@@ -0,0 +1,86 @@
|
|
1
|
+
from dagster import In, OpExecutionContext, op, Out
|
2
|
+
|
3
|
+
# noinspection PyProtectedMember
|
4
|
+
from dagster._annotations import experimental
|
5
|
+
|
6
|
+
from .app_client import DingTalkClient
|
7
|
+
from .resources import DingTalkWebhookResource
|
8
|
+
|
9
|
+
@experimental
|
10
|
+
@op(description="钉钉Webhook发送文本消息",
|
11
|
+
required_resource_keys={'dingtalk_webhook'},
|
12
|
+
ins={
|
13
|
+
"text": In(str),
|
14
|
+
"at": In(default_value=None, description="@列表,传List[str]解析为userId,传List[int]解析为phone,传ALL解析为全部。")
|
15
|
+
})
|
16
|
+
def op_send_simple_text(context: OpExecutionContext, text, at):
|
17
|
+
webhook:DingTalkWebhookResource = context.resources.dingtalk_webhook
|
18
|
+
if isinstance(at, str) and at == 'ALL':
|
19
|
+
webhook.send_text(text=text, at_all=True)
|
20
|
+
if isinstance(at, list) and isinstance(at[0], str):
|
21
|
+
webhook.send_text(text=text, at_user_ids=at)
|
22
|
+
if isinstance(at, list) and isinstance(at[0], int):
|
23
|
+
at = [str(mobile) for mobile in at]
|
24
|
+
webhook.send_text(text=text, at_mobiles=at)
|
25
|
+
if not at:
|
26
|
+
webhook.send_text(text=text)
|
27
|
+
|
28
|
+
|
29
|
+
@op(description="钉钉Webhook发送Markdown消息",
|
30
|
+
required_resource_keys={'dingtalk_webhook'},
|
31
|
+
ins={
|
32
|
+
"text": In(str),
|
33
|
+
"title": In(str, default_value=''),
|
34
|
+
"at": In(default_value=None, description="@列表,传List[str]解析为userId,传List[int]解析为phone,传ALL解析为全部。")
|
35
|
+
})
|
36
|
+
def op_send_markdown(context: OpExecutionContext, text, title, at):
|
37
|
+
webhook:DingTalkWebhookResource = context.resources.dingtalk_webhook
|
38
|
+
if isinstance(at, str) and at == 'ALL':
|
39
|
+
webhook.send_markdown(text=text, title=title, at_all=True)
|
40
|
+
if isinstance(at, list) and isinstance(at[0], str):
|
41
|
+
webhook.send_markdown(text=text, title=title, at_user_ids=at)
|
42
|
+
if isinstance(at, list) and isinstance(at[0], int):
|
43
|
+
at = [str(mobile) for mobile in at]
|
44
|
+
webhook.send_markdown(text=text, title=title, at_mobiles=at)
|
45
|
+
if not at:
|
46
|
+
webhook.send_markdown(text=text, title=title)
|
47
|
+
|
48
|
+
|
49
|
+
@op(description="更新钉钉互动卡片",
|
50
|
+
required_resource_keys={"dingtalk"},
|
51
|
+
ins={"card_param_map":In(), "out_track_id":In(str), "update_by_key":In(bool, default_value=True)},
|
52
|
+
out={"is_success":Out(bool, is_required=False)})
|
53
|
+
def renew_card(context:OpExecutionContext, card_param_map, out_track_id, update_by_key):
|
54
|
+
try:
|
55
|
+
dingtalk_yunxiao: DingTalkClient = context.resources.dingtalk_yunxiao
|
56
|
+
res = dingtalk_yunxiao.互动卡片.更新卡片(out_track_id=out_track_id, card_param_map=card_param_map, update_card_data_by_key=update_by_key)
|
57
|
+
context.log.info(res)
|
58
|
+
if res.get('success') and res.get('result'):
|
59
|
+
return True
|
60
|
+
else:
|
61
|
+
return False
|
62
|
+
except Exception as e:
|
63
|
+
context.log.error(e)
|
64
|
+
return False
|
65
|
+
|
66
|
+
|
67
|
+
@op(description="创建并发送钉钉互动卡片",
|
68
|
+
ins={"search_type_name":In(str), "alert_content":In(str), "card_template_id":In(str), "card_param_map":In(), "out_track_id":In(str), "open_space_id":In(str)},
|
69
|
+
out={"is_success":Out(bool, is_required=False)}, required_resource_keys={"dingtalk"})
|
70
|
+
def send_revenue_dt_card(context:OpExecutionContext, search_type_name, alert_content,card_template_id, card_param_map, out_track_id, open_space_id):
|
71
|
+
try:
|
72
|
+
dingtalk_yunxiao: DingTalkClient = context.resources.dingtalk_yunxiao
|
73
|
+
res = dingtalk_yunxiao.互动卡片.创建并投放卡片(
|
74
|
+
search_type_name=search_type_name,
|
75
|
+
search_desc=alert_content,
|
76
|
+
card_template_id=card_template_id,
|
77
|
+
card_param_map=card_param_map,
|
78
|
+
alert_content=alert_content,
|
79
|
+
out_track_id=out_track_id,
|
80
|
+
open_space_ids=[open_space_id]
|
81
|
+
)
|
82
|
+
context.log.info(res)
|
83
|
+
return res.get("success")
|
84
|
+
except Exception as e:
|
85
|
+
context.log.error(e)
|
86
|
+
return False
|
@@ -48,7 +48,7 @@ class DingTalkWebhookResource(ConfigurableResource):
|
|
48
48
|
- **secret** (str, optional):
|
49
49
|
如使用加签安全配置,则需传签名密钥。默认值为 None。
|
50
50
|
- **alias** (str, optional):
|
51
|
-
|
51
|
+
别名,仅用作标记。默认值为 None。
|
52
52
|
- **base_url** (str, optional):
|
53
53
|
通用地址,一般无需更改。默认值为 “https://oapi.dingtalk.com/robot/send”。
|
54
54
|
|
@@ -303,8 +303,6 @@ class DingTalkWebhookResource(ConfigurableResource):
|
|
303
303
|
|
304
304
|
class DingTalkAppResource(ConfigurableResource):
|
305
305
|
"""
|
306
|
-
该 Dagster 资源允许定义一个钉钉的 API Client,更加便捷地调用钉钉服务端企业内部应用 API
|
307
|
-
|
308
306
|
[钉钉服务端 API](https://open.dingtalk.com/document/orgapp/api-overview) 企业内部应用部分的第三方封装。
|
309
307
|
|
310
308
|
通过此资源,可以调用部分钉钉服务端 API。具体封装的 API 可以在 IDE 中通过引入 `DingTalkAppClient` 类来查看 IDE 提示:
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.1.15"
|
@@ -1,20 +0,0 @@
|
|
1
|
-
from dagster import In, OpExecutionContext, op
|
2
|
-
|
3
|
-
# noinspection PyProtectedMember
|
4
|
-
from dagster._annotations import experimental
|
5
|
-
|
6
|
-
|
7
|
-
@experimental
|
8
|
-
@op(description="使用钉钉 Webhook 发送文本消息",
|
9
|
-
required_resource_keys={'dingtalk_webhook'},
|
10
|
-
ins={"text": In(str)})
|
11
|
-
def op_send_simple_text(context: OpExecutionContext, text):
|
12
|
-
webhook = context.resources.dingtalk_webhook
|
13
|
-
webhook.send_text(text)
|
14
|
-
|
15
|
-
@op(description="使用钉钉 Webhook 发送 Markdown 消息",
|
16
|
-
required_resource_keys={'dingtalk_webhook'},
|
17
|
-
ins={"text": In(str), "title": In(str, default_value='')})
|
18
|
-
def op_simple_markdown(context: OpExecutionContext, text, title):
|
19
|
-
webhook = context.resources.dingtalk_webhook
|
20
|
-
webhook.send_text(text, title)
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "0.1.14"
|