Skip to content

Commit 798d51f

Browse files
authored
Merge pull request #74 from TencentBlueKing/develop
v1.2.2
2 parents 230161f + 2f2da75 commit 798d51f

File tree

8 files changed

+237
-5
lines changed

8 files changed

+237
-5
lines changed

docs/usage.md

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,37 @@ Resource API Framework 中有两个核心概念:
543543
- `page.limit(int)`:查询数量
544544
- `page.offset(int)`:查询偏移
545545
- 返回值:返回 `iam.resource.provider.ListResult` 的实例,其中 `results` 应满足 IAM search_instance 响应协议
546+
7. `fetch_instance_list(filter, page, **options)`:处理来自 IAM 的 fetch_instance_list 请求,在审计中心生成静态资源快照时,需要实现此方法
547+
- 参数:
548+
- `filter`:过滤器对象
549+
- `filter.start_time(int)`:资源实例变更时间的开始时间(包含start_time)
550+
- `filter.end_time(int)`:资源实例变更时间的结束时间(包含end_time)
551+
- `page`:分页对象
552+
- `page.limit(int)`:查询数量
553+
- `page.offset(int)`:查询偏移
554+
- 返回值:返回 `iam.resource.provider.ListResult` 的实例,其中 `results` 应满足 IAM fetch_instance_list 响应协议
555+
8. `fetch_resource_type_schema(**options)`:处理来自 IAM 的 fetch_resource_type_schema 请求,在审计中心显示静态资源时,需要实现此方法
556+
- 返回值:返回 `iam.resource.provider.SchemaResult` 的实例,输出结果可以通过 [JSON Schema Validator](https://www.jsonschemavalidator.net/) 校验
557+
- `注意`:为满足`审计中心`需求,字段描述新增`description_en``code` 两个 Key,`description_en`指字段英文描述,`code(可选)`指该字段为`代码`内容,在`审计中心`将按代码格式显示
558+
- DEMO(Response.data)
559+
```
560+
{
561+
"type": "object",
562+
"properties": {
563+
"id": {
564+
"type": "string",
565+
"description": "ID",
566+
"description_en": "ID",
567+
},
568+
"script": {
569+
"type": "string",
570+
"description": "脚本",
571+
"description_en": "Script",
572+
"code": "shell"
573+
}
574+
}
575+
}
576+
```
546577

547578
除此之外,如果 Provider 中定义了 `pre_{method}` 方法(`method` 可选值(`list_attr`, `list_attr_value`, `list_instance`, `fetch_instance_info`, `list_instance_by_policy`),Dispatcher 会在调用对应的 `{method}` 方法前调用其对应的 `pre` 方法进行预处理,下面的例子检测 `list_instance` 中传入的 page 对象,如果 limit 过大,则拒绝该请求:
548579

@@ -557,7 +588,7 @@ class TaskResourceProvider(ResourceProvider):
557588
下面是一种资源类型的 Provider 定义示例:
558589

559590
```python
560-
from iam.resource.provider import ResourceProvider, ListResult
591+
from iam.resource.provider import ResourceProvider, ListResult, SchemaResult
561592
from task.models import Tasks
562593

563594

@@ -614,6 +645,18 @@ class TaskResourceProvider(ResourceProvider):
614645
注意, 有翻页; 需要返回count
615646
"""
616647
return ListResult(results=[], count=0)
648+
649+
def fetch_instance_list(self, filter, page, **options):
650+
"""根据过滤条件搜索实例
651+
注意, 有翻页; 需要返回count
652+
"""
653+
return ListResult(results=[], count=0)
654+
655+
def fetch_resource_type_schema(self, **options):
656+
"""获取资源类型 schema 定义
657+
schema定义
658+
"""
659+
return SchemaResult(properties={})
617660
```
618661

619662
### 3.2 Dispatcher
@@ -648,7 +691,7 @@ class TaskResourceProvider(ResourceProvider):
648691
from blueapps.account.decorators import login_exempt
649692
from iam import IAM
650693
from iam.contrib.django.dispatcher import DjangoBasicResourceApiDispatcher
651-
from iam.resource.provider import ResourceProvider, ListResult
694+
from iam.resource.provider import ResourceProvider, ListResult, SchemaResult
652695
from iam.contrib.converter.queryset import PathEqDjangoQuerySetConverter
653696
from django.conf.urls import url, include
654697

@@ -763,6 +806,42 @@ class FlowResourceProvider(ResourceProvider):
763806
# TODO
764807
return ListResult(results=[], count=0)
765808

809+
def fetch_instance_list(self, filter, page, **options):
810+
"""
811+
flow
812+
"""
813+
queryset = TaskTemplate.objects.filter(updated_at__gte=filter.start_time).filter(updated_at__lte=filter.end_time)
814+
results = []
815+
for flow in queryset[page.slice_from : page.slice_to]:
816+
results.append(
817+
{
818+
"id": str(flow.id),
819+
"display_name": flow.name,
820+
"creator": flow.creator,
821+
"created_at": flow.created_at,
822+
"updater": flow.updater,
823+
"updated_at": flow.updated_at,
824+
"data": flow.to_json()
825+
}
826+
)
827+
return ListResult(results=results, count=queryset.count())
828+
829+
def fetch_resource_type_schema(self, **options):
830+
properties = {
831+
"id": {
832+
"type": "string",
833+
"description": "ID",
834+
"description_en": "ID",
835+
},
836+
"script": {
837+
"type": "string",
838+
"description": "脚本",
839+
"description_en": "Script",
840+
"code": "shell"
841+
}
842+
}
843+
return SchemaResult(properties=properties)
844+
766845
dispatcher = DjangoBasicResourceApiDispatcher(iam, "my_system")
767846
dispatcher.register("flow", FlowResourceProvider())
768847

iam/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# -*- coding: utf-8 -*-
22

3-
__version__ = "1.2.1"
3+
__version__ = "1.2.2"

iam/contrib/django/dispatcher/dispatchers.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,32 @@ def _dispatch_search_instance(self, request, data, request_id):
218218
result = provider.search_instance(filter_obj, page_obj, **options)
219219

220220
return success_response(result.to_dict(), request_id)
221+
222+
def _dispatch_fetch_instance_list(self, request, data, request_id):
223+
options = self._get_options(request)
224+
225+
filter_obj = get_filter_obj(data.get("filter"), ["start_time", "end_time"])
226+
page_obj = get_page_obj(data.get("page"))
227+
228+
provider = self._provider[data["type"]]
229+
230+
pre_process = getattr(provider, "pre_fetch_instance_list", None)
231+
if pre_process and callable(pre_process):
232+
pre_process(filter_obj, page_obj, **options)
233+
234+
result = provider.fetch_instance_list(filter_obj, page_obj, **options)
235+
236+
return success_response(result.to_dict(), request_id)
237+
238+
def _dispatch_fetch_resource_type_schema(self, request, data, request_id):
239+
options = self._get_options(request)
240+
241+
provider = self._provider[data["type"]]
242+
243+
pre_process = getattr(provider, "pre_fetch_resource_type_schema", None)
244+
if pre_process and callable(pre_process):
245+
pre_process(**options)
246+
247+
result = provider.fetch_resource_type_schema(**options)
248+
249+
return success_response(result.to_dict(), request_id)

iam/resource/constants.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
TencentBlueKing is pleased to support the open source community by making
4+
蓝鲸智云-权限中心Python SDK(iam-python-sdk) available.
5+
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
6+
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at http://opensource.org/licenses/MIT
8+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
9+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10+
specific language governing permissions and limitations under the License.
11+
"""
12+
13+
14+
class SchemaSpecificType(object):
15+
"""
16+
NOTE: don't want to use Enum
17+
"""
18+
19+
ARRAY = "array"
20+
BOOLEAN = "boolean"
21+
INTEGER = "integer"
22+
NULL = "null"
23+
NUMBER = "number"
24+
OBJECT = "object"
25+
STRING = "string"

iam/resource/provider.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import six
1616

17+
from iam.resource.constants import SchemaSpecificType
18+
1719

1820
class ListResult(object):
1921
def __init__(self, results, count):
@@ -31,6 +33,18 @@ def to_list(self):
3133
return self.results
3234

3335

36+
class SchemaResult(object):
37+
def __init__(self, properties):
38+
"""
39+
:param properties: 资源类型 schema 定义
40+
"""
41+
self.type = SchemaSpecificType.OBJECT
42+
self.properties = properties
43+
44+
def to_dict(self):
45+
return {"type": self.type, "properties": self.properties}
46+
47+
3448
@six.add_metaclass(abc.ABCMeta)
3549
class ResourceProvider(object):
3650
@abc.abstractmethod
@@ -80,3 +94,19 @@ def search_instance(self, filter, page, **options):
8094
return: ListResult
8195
"""
8296
raise NotImplementedError()
97+
98+
def fetch_instance_list(self, filter, page, **options):
99+
"""
100+
处理来自 iam 的 fetch_instance_list 请求
101+
在审计中心生成静态资源快照时,需要实现此方法
102+
return: ListResult
103+
"""
104+
raise NotImplementedError()
105+
106+
def fetch_resource_type_schema(self, **options):
107+
"""
108+
处理来自 iam 的 fetch_resource_type_schema 请求
109+
在审计中心显示静态资源时,需要实现此方法
110+
return: SchemaResult
111+
"""
112+
raise NotImplementedError()

iam/resource/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ def slice_to(self):
3838

3939

4040
def get_page_obj(page_data):
41-
return Page(limit=page_data.get("limit", 0), offset=page_data.get("offset", 0))
41+
return Page(limit=int(page_data.get("limit") or 0), offset=int(page_data.get("offset") or 0))

release.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
版本日志
22
===============
33

4+
# v1.2.2
5+
6+
- add: fetch_instance_list/fetch_resource_type_schema in ResourceProvider
7+
48
# v1.2.1
59

610
- add: operator `string_contains` #68

tests/contrib/django/dispatcher/test_dispatchers.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
from iam.contrib.django.dispatcher import DjangoBasicResourceApiDispatcher, InvalidPageException
2525
from iam.exceptions import AuthInvalidOperation
26-
from iam.resource.provider import ListResult, ResourceProvider
26+
from iam.resource.provider import ListResult, SchemaResult, ResourceProvider
2727

2828

2929
def test_basic_resource_api_dispatcher_register():
@@ -53,6 +53,12 @@ def list_instance_by_policy(self, filter, page, **options):
5353
def search_instance(self, filter, page, **options):
5454
return ListResult(results=[filter, page], count=100)
5555

56+
def fetch_instance_list(self, filter, page, **options):
57+
return ListResult(results=[filter, page], count=100)
58+
59+
def fetch_resource_type_schema(self, **options):
60+
return SchemaResult(properties={"id": {"type": "string"}})
61+
5662
with pytest.raises(AuthInvalidOperation):
5763
dispatcher.register("type", "provider")
5864

@@ -257,12 +263,16 @@ def __init__(self):
257263
self.fetch_instance_info_spy = {}
258264
self.list_instance_by_policy_spy = {}
259265
self.search_instance_spy = {}
266+
self.fetch_instance_list_spy = {}
267+
self.fetch_resource_type_schema_spy = {}
260268
self.pre_list_attr = MagicMock()
261269
self.pre_list_attr_value = MagicMock()
262270
self.pre_list_instance = MagicMock()
263271
self.pre_fetch_instance_info = MagicMock()
264272
self.pre_list_instance_by_policy = MagicMock()
265273
self.pre_search_instance = MagicMock()
274+
self.pre_fetch_instance_list = MagicMock()
275+
self.pre_fetch_resource_type_schema = MagicMock()
266276

267277
def list_attr(self, **options):
268278
self.list_attr_spy["options"] = options
@@ -297,6 +307,16 @@ def search_instance(self, filter, page, **options):
297307
self.search_instance_spy["options"] = options
298308
return ListResult(results=["search_instance_token"], count=100)
299309

310+
def fetch_instance_list(self, filter, page, **options):
311+
self.fetch_instance_list_spy["filter"] = filter
312+
self.fetch_instance_list_spy["page"] = page
313+
self.fetch_instance_list_spy["options"] = options
314+
return ListResult(results=["fetch_instance_list_token"], count=100)
315+
316+
def fetch_resource_type_schema(self, **options):
317+
self.fetch_resource_type_schema_spy["options"] = options
318+
return SchemaResult(properties={"fetch_resource_type_schema_token"})
319+
300320
provider = SpyResourceProvider()
301321
dispatcher.register("spy", provider)
302322

@@ -450,3 +470,48 @@ def search_instance(self, filter, page, **options):
450470
"filter": {"expression": "expression"},
451471
"page": {"limit": "limit", "offset": "offset"},
452472
}
473+
474+
# test fetch_instance_list
475+
fetch_instance_list_req = MagicMock()
476+
fetch_instance_list_req.body = json.dumps(
477+
{
478+
"method": "fetch_instance_list",
479+
"type": "spy",
480+
"filter": {"start_time": 1654012800, "end_time": 1654099199},
481+
"page": {"limit": "limit", "offset": "offset"},
482+
}
483+
)
484+
fetch_instance_list_req.META = {"HTTP_X_REQUEST_ID": "rid", "HTTP_BLUEKING_LANGUAGE": "en"}
485+
486+
resp = dispatcher._dispatch(fetch_instance_list_req)
487+
488+
provider.pre_fetch_instance_list.assert_called_once_with(
489+
{"start_time": 1654012800, "end_time": 1654099199},
490+
{"limit": "limit", "offset": "offset"},
491+
language="en",
492+
)
493+
assert resp["code"] == 0
494+
assert resp["result"] is True
495+
assert resp["data"] == {"count": 100, "results": ["fetch_instance_list_token"]}
496+
assert resp["X-Request-Id"] == "rid"
497+
assert "message" in resp
498+
assert provider.fetch_instance_list_spy == {
499+
"options": {"language": "en"},
500+
"filter": {"start_time": 1654012800, "end_time": 1654099199},
501+
"page": {"limit": "limit", "offset": "offset"},
502+
}
503+
504+
# test fetch_resource_type_schema
505+
fetch_resource_type_schema_req = MagicMock()
506+
fetch_resource_type_schema_req.body = json.dumps({"method": "fetch_resource_type_schema", "type": "spy"})
507+
fetch_resource_type_schema_req.META = {"HTTP_X_REQUEST_ID": "rid", "HTTP_BLUEKING_LANGUAGE": "en"}
508+
509+
resp = dispatcher._dispatch(fetch_resource_type_schema_req)
510+
511+
provider.pre_fetch_resource_type_schema.assert_called_once_with(language="en")
512+
assert resp["code"] == 0
513+
assert resp["result"] is True
514+
assert resp["data"] == SchemaResult(properties={"fetch_resource_type_schema_token"}).to_dict()
515+
assert resp["X-Request-Id"] == "rid"
516+
assert "message" in resp
517+
assert provider.fetch_resource_type_schema_spy == {"options": {"language": "en"}}

0 commit comments

Comments
 (0)