stoobly-agent 0.34.7__py3-none-any.whl → 0.34.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/app/api/requests_controller.py +1 -1
- stoobly_agent/app/cli/helpers/endpoints_import_service.py +70 -11
- stoobly_agent/app/cli/helpers/openapi_endpoint_adapter.py +108 -207
- stoobly_agent/app/cli/helpers/schema_builder.py +11 -88
- stoobly_agent/app/proxy/replay/body_parser_service.py +1 -1
- stoobly_agent/app/proxy/run.py +0 -1
- stoobly_agent/app/proxy/utils/allowed_request_service.py +10 -0
- stoobly_agent/cli.py +11 -2
- stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_missing_info_test.py +25 -15
- stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_missing_oauth2_scopes_test.py +26 -16
- stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_missing_servers_test.py +25 -15
- stoobly_agent/test/app/cli/helpers/openapi_endpoint_adapter_test.py +79 -9
- stoobly_agent/test/app/cli/helpers/schema_builder_test.py +22 -7
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- stoobly_agent/test/app/proxy/replay/body_parser_service_test.py +3 -3
- {stoobly_agent-0.34.7.dist-info → stoobly_agent-0.34.9.dist-info}/METADATA +1 -1
- {stoobly_agent-0.34.7.dist-info → stoobly_agent-0.34.9.dist-info}/RECORD +21 -21
- {stoobly_agent-0.34.7.dist-info → stoobly_agent-0.34.9.dist-info}/LICENSE +0 -0
- {stoobly_agent-0.34.7.dist-info → stoobly_agent-0.34.9.dist-info}/WHEEL +0 -0
- {stoobly_agent-0.34.7.dist-info → stoobly_agent-0.34.9.dist-info}/entry_points.txt +0 -0
@@ -16,97 +16,20 @@ class SchemaBuilder:
|
|
16
16
|
self.param_column_name: str = param_column_name
|
17
17
|
|
18
18
|
def build(self, params):
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
###
|
24
|
-
#
|
25
|
-
# @param name [String] name of current param
|
26
|
-
# @param value [Object] value of current param
|
27
|
-
# @param param [QueryParamName, BodyParamName, ResponseParamName] parent param record
|
28
|
-
#
|
29
|
-
def __traverse(self, name: str, value, param: RequestComponentName):
|
30
|
-
if type(value) is list:
|
31
|
-
self.__traverse_array(name, value, param)
|
32
|
-
elif type(value) is dict:
|
33
|
-
self.__traverse_hash(name, value, param)
|
34
|
-
|
35
|
-
def __traverse_array(self, name: str, value, parent_param: RequestComponentName):
|
36
|
-
columns = {
|
37
|
-
'endpoint_id': self.endpoint_id,
|
38
|
-
'name': f"{name.capitalize()}Element",
|
39
|
-
'query': f"{parent_param.get('query')}[*]" if parent_param else '[*]'
|
40
|
-
}
|
41
|
-
columns[self.param_column_name + '_id'] = parent_param['id'] if parent_param else None
|
42
|
-
|
43
|
-
# Iterate
|
44
|
-
types = {}
|
45
|
-
|
46
|
-
for e in value:
|
47
|
-
# Example of e = {'id': {'value': 0, 'required': False}}
|
48
|
-
type_value = None
|
49
|
-
if e.get('value') is not None:
|
50
|
-
type_value = e.get('value')
|
51
|
-
else:
|
52
|
-
type_value = e
|
53
|
-
|
54
|
-
_type = self.__infer_type(type_value)
|
55
|
-
|
56
|
-
if types.get(_type) is None:
|
57
|
-
columns['inferred_type'] = convert(_type)
|
58
|
-
types[_type] = self.__find_or_create_by(columns)
|
59
|
-
|
60
|
-
self.__traverse('', type_value, types[_type])
|
61
|
-
|
62
|
-
def __traverse_hash(self, name, value, parent_param: RequestComponentName):
|
63
|
-
# Iterate
|
64
|
-
for k, v in value.items():
|
65
|
-
columns = {
|
19
|
+
params_list = []
|
20
|
+
for literal_param in params:
|
21
|
+
param: RequestComponentName = {
|
66
22
|
'endpoint_id': self.endpoint_id,
|
67
|
-
'
|
68
|
-
'
|
69
|
-
|
23
|
+
'name': literal_param['name'],
|
24
|
+
'query': literal_param['query'],
|
25
|
+
'is_required': literal_param['required'],
|
26
|
+
'inferred_type': convert(self.__infer_type(literal_param['value'])),
|
70
27
|
'is_deterministic': True,
|
71
|
-
'
|
72
|
-
|
28
|
+
'id': literal_param['id'],
|
29
|
+
f"{self.param_column_name}_id": literal_param['parent_id']
|
73
30
|
}
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
self.__traverse(k, v['value'], param)
|
78
|
-
|
79
|
-
def __find_or_create_by(self, columns):
|
80
|
-
param = self.__find_by(columns)
|
81
|
-
|
82
|
-
if param is None:
|
83
|
-
param = self.__create(columns)
|
84
|
-
|
85
|
-
return param
|
86
|
-
|
87
|
-
def __find_by(self, columns) -> Union[RequestComponentName, None]:
|
88
|
-
for id, param_name in self.param_names_created.items():
|
89
|
-
matches = True
|
90
|
-
|
91
|
-
for key in columns:
|
92
|
-
if key not in param_name:
|
93
|
-
matches = False
|
94
|
-
break
|
95
|
-
|
96
|
-
if param_name[key] != columns[key]:
|
97
|
-
matches = False
|
98
|
-
break
|
99
|
-
|
100
|
-
if matches:
|
101
|
-
return param_name
|
102
|
-
|
103
|
-
def __create(self, columns):
|
104
|
-
param: RequestComponentName = columns.copy()
|
105
|
-
param['id'] = len(self.param_names_created.keys()) + 1
|
106
|
-
|
107
|
-
self.param_names_created[param['id']] = param
|
108
|
-
|
109
|
-
return param
|
31
|
+
params_list.append(param)
|
32
|
+
return params_list
|
110
33
|
|
111
34
|
def __infer_type(self, val) -> str:
|
112
35
|
return str(val.__class__)
|
@@ -76,7 +76,7 @@ def parse_multipart_form_data(content, content_type) -> Dict[bytes, bytes]:
|
|
76
76
|
|
77
77
|
params_array = []
|
78
78
|
for ele in decoded_multipart:
|
79
|
-
params_array.append((decode(ele[0]),
|
79
|
+
params_array.append((decode(ele[0]), ele[1]))
|
80
80
|
|
81
81
|
return MultiDict(params_array)
|
82
82
|
|
stoobly_agent/app/proxy/run.py
CHANGED
@@ -45,8 +45,18 @@ def __request_excluded(request: MitmproxyRequest, exclude_rules: List[FirewallRu
|
|
45
45
|
return False
|
46
46
|
|
47
47
|
def __request_included(request: MitmproxyRequest, include_rules: List[FirewallRule]):
|
48
|
+
if not include_rules:
|
49
|
+
return True
|
50
|
+
|
48
51
|
method = request.method.upper()
|
49
52
|
rules = list(filter(lambda rule: method in rule.methods, include_rules))
|
53
|
+
|
54
|
+
# If there are include rules, but none that match the request's method,
|
55
|
+
# then we know that none of the include rules will match the request
|
56
|
+
if len(include_rules) > 0 and len(rules) == 0:
|
57
|
+
Logger.instance().info(f"{bcolors.OKBLUE}{request.method} {request.url} not included by firewall rule{bcolors.ENDC}")
|
58
|
+
return False
|
59
|
+
|
50
60
|
patterns = list(map(lambda rule: rule.pattern, rules))
|
51
61
|
if not __include(request, patterns):
|
52
62
|
Logger.instance().info(f"{bcolors.OKBLUE}{request.method} {request.url} not included by firewall rule{bcolors.ENDC}")
|
stoobly_agent/cli.py
CHANGED
@@ -86,7 +86,16 @@ def init(**kwargs):
|
|
86
86
|
Passphrase for decrypting the private key provided in the --cert option. Note that passing cert_passphrase on the command line makes your passphrase visible in your system's process list. Specify it in
|
87
87
|
config.yaml to avoid this.
|
88
88
|
''')
|
89
|
+
@click.option('--confdir', default=os.path.join(os.path.expanduser('~'), '.mitmproxy'), help='Location of the default mitmproxy configuration files.')
|
89
90
|
@click.option('--connection-strategy', help=', '.join(CONNECTION_STRATEGIES), type=click.Choice(CONNECTION_STRATEGIES))
|
91
|
+
@click.option('--flow-detail', default='1', type=click.Choice(['1', '2', '3', '4']), help='''
|
92
|
+
The display detail level for flows in mitmdump: 0 (quiet) to 4 (very verbose).
|
93
|
+
0: no output
|
94
|
+
1: shortened request URL with response status code
|
95
|
+
2: full request URL with response status code and HTTP headers
|
96
|
+
3: 2 + truncated response content, content of WebSocket and TCP messages (content_view_lines_cutoff: 512)
|
97
|
+
4: 3 + nothing is truncated
|
98
|
+
''')
|
90
99
|
@click.option('--headless', is_flag=True, default=False, help='Disable starting UI.')
|
91
100
|
@click.option('--intercept', is_flag=True, default=False, help='Enable intercept on run.')
|
92
101
|
@click.option('--log-level', default=logger.INFO, type=click.Choice([logger.DEBUG, logger.INFO, logger.WARNING, logger.ERROR]), help='''
|
@@ -218,7 +227,7 @@ def __build_request_from_curl(**kwargs):
|
|
218
227
|
|
219
228
|
if len(toks) != 2:
|
220
229
|
continue
|
221
|
-
|
230
|
+
|
222
231
|
headers[toks[0].strip()] = toks[1].strip()
|
223
232
|
|
224
233
|
return requests.Request(
|
@@ -226,4 +235,4 @@ def __build_request_from_curl(**kwargs):
|
|
226
235
|
headers=headers,
|
227
236
|
method=kwargs['request'],
|
228
237
|
url=kwargs['url']
|
229
|
-
)
|
238
|
+
)
|
@@ -61,39 +61,49 @@ class TestOpenApiEndpointAdapterMissingInfo():
|
|
61
61
|
"response_param_names": [
|
62
62
|
{
|
63
63
|
"endpoint_id": 1,
|
64
|
-
"
|
65
|
-
"
|
64
|
+
"name": "Element",
|
65
|
+
"query": "[*]",
|
66
|
+
"is_required": False,
|
67
|
+
"inferred_type": "Hash",
|
66
68
|
"is_deterministic": True,
|
67
|
-
"name": "id",
|
68
|
-
"query": "id",
|
69
|
-
"response_param_name_id": None,
|
70
69
|
"id": 1,
|
70
|
+
"response_param_name_id": None
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"endpoint_id": 1,
|
74
|
+
"name": "id",
|
75
|
+
"query": "[*].id",
|
76
|
+
"is_required": True,
|
77
|
+
"inferred_type": "Integer",
|
78
|
+
"is_deterministic": True,
|
79
|
+
"id": 2,
|
80
|
+
"response_param_name_id": 1,
|
71
81
|
"values": [
|
72
82
|
0
|
73
83
|
]
|
74
84
|
},
|
75
85
|
{
|
76
86
|
"endpoint_id": 1,
|
77
|
-
"
|
87
|
+
"name": "name",
|
88
|
+
"query": "[*].name",
|
78
89
|
"is_required": True,
|
90
|
+
"inferred_type": "String",
|
79
91
|
"is_deterministic": True,
|
80
|
-
"
|
81
|
-
"
|
82
|
-
"response_param_name_id": None,
|
83
|
-
"id": 2,
|
92
|
+
"id": 3,
|
93
|
+
"response_param_name_id": 1,
|
84
94
|
"values": [
|
85
95
|
""
|
86
96
|
]
|
87
97
|
},
|
88
98
|
{
|
89
99
|
"endpoint_id": 1,
|
90
|
-
"
|
100
|
+
"name": "tag",
|
101
|
+
"query": "[*].tag",
|
91
102
|
"is_required": False,
|
103
|
+
"inferred_type": "String",
|
92
104
|
"is_deterministic": True,
|
93
|
-
"
|
94
|
-
"
|
95
|
-
"response_param_name_id": None,
|
96
|
-
"id": 3
|
105
|
+
"id": 4,
|
106
|
+
"response_param_name_id": 1
|
97
107
|
}
|
98
108
|
],
|
99
109
|
}
|
@@ -62,42 +62,52 @@ class TestOpenApiEndpointAdapterMissingOauthScopes():
|
|
62
62
|
'name': 'x-next',
|
63
63
|
},
|
64
64
|
],
|
65
|
-
|
65
|
+
"response_param_names": [
|
66
66
|
{
|
67
67
|
"endpoint_id": 1,
|
68
|
-
"
|
69
|
-
"
|
68
|
+
"name": "Element",
|
69
|
+
"query": "[*]",
|
70
|
+
"is_required": False,
|
71
|
+
"inferred_type": "Hash",
|
70
72
|
"is_deterministic": True,
|
71
|
-
"name": "id",
|
72
|
-
"query": "id",
|
73
|
-
"response_param_name_id": None,
|
74
73
|
"id": 1,
|
74
|
+
"response_param_name_id": None
|
75
|
+
},
|
76
|
+
{
|
77
|
+
"endpoint_id": 1,
|
78
|
+
"name": "id",
|
79
|
+
"query": "[*].id",
|
80
|
+
"is_required": True,
|
81
|
+
"inferred_type": "Integer",
|
82
|
+
"is_deterministic": True,
|
83
|
+
"id": 2,
|
84
|
+
"response_param_name_id": 1,
|
75
85
|
"values": [
|
76
86
|
0
|
77
87
|
]
|
78
88
|
},
|
79
89
|
{
|
80
90
|
"endpoint_id": 1,
|
81
|
-
"
|
91
|
+
"name": "name",
|
92
|
+
"query": "[*].name",
|
82
93
|
"is_required": True,
|
94
|
+
"inferred_type": "String",
|
83
95
|
"is_deterministic": True,
|
84
|
-
"
|
85
|
-
"
|
86
|
-
"response_param_name_id": None,
|
87
|
-
"id": 2,
|
96
|
+
"id": 3,
|
97
|
+
"response_param_name_id": 1,
|
88
98
|
"values": [
|
89
99
|
""
|
90
100
|
]
|
91
101
|
},
|
92
102
|
{
|
93
103
|
"endpoint_id": 1,
|
94
|
-
"
|
104
|
+
"name": "tag",
|
105
|
+
"query": "[*].tag",
|
95
106
|
"is_required": False,
|
107
|
+
"inferred_type": "String",
|
96
108
|
"is_deterministic": True,
|
97
|
-
"
|
98
|
-
"
|
99
|
-
"response_param_name_id": None,
|
100
|
-
"id": 3
|
109
|
+
"id": 4,
|
110
|
+
"response_param_name_id": 1
|
101
111
|
}
|
102
112
|
],
|
103
113
|
}
|
@@ -61,39 +61,49 @@ class TestOpenApiEndpointAdapterMissingServers():
|
|
61
61
|
"response_param_names": [
|
62
62
|
{
|
63
63
|
"endpoint_id": 1,
|
64
|
-
"
|
65
|
-
"
|
64
|
+
"name": "Element",
|
65
|
+
"query": "[*]",
|
66
|
+
"is_required": False,
|
67
|
+
"inferred_type": "Hash",
|
66
68
|
"is_deterministic": True,
|
67
|
-
"name": "id",
|
68
|
-
"query": "id",
|
69
|
-
"response_param_name_id": None,
|
70
69
|
"id": 1,
|
70
|
+
"response_param_name_id": None
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"endpoint_id": 1,
|
74
|
+
"name": "id",
|
75
|
+
"query": "[*].id",
|
76
|
+
"is_required": True,
|
77
|
+
"inferred_type": "Integer",
|
78
|
+
"is_deterministic": True,
|
79
|
+
"id": 2,
|
80
|
+
"response_param_name_id": 1,
|
71
81
|
"values": [
|
72
82
|
0
|
73
83
|
]
|
74
84
|
},
|
75
85
|
{
|
76
86
|
"endpoint_id": 1,
|
77
|
-
"
|
87
|
+
"name": "name",
|
88
|
+
"query": "[*].name",
|
78
89
|
"is_required": True,
|
90
|
+
"inferred_type": "String",
|
79
91
|
"is_deterministic": True,
|
80
|
-
"
|
81
|
-
"
|
82
|
-
"response_param_name_id": None,
|
83
|
-
"id": 2,
|
92
|
+
"id": 3,
|
93
|
+
"response_param_name_id": 1,
|
84
94
|
"values": [
|
85
95
|
""
|
86
96
|
]
|
87
97
|
},
|
88
98
|
{
|
89
99
|
"endpoint_id": 1,
|
90
|
-
"
|
100
|
+
"name": "tag",
|
101
|
+
"query": "[*].tag",
|
91
102
|
"is_required": False,
|
103
|
+
"inferred_type": "String",
|
92
104
|
"is_deterministic": True,
|
93
|
-
"
|
94
|
-
"
|
95
|
-
"response_param_name_id": None,
|
96
|
-
"id": 3
|
105
|
+
"id": 4,
|
106
|
+
"response_param_name_id": 1
|
97
107
|
}
|
98
108
|
],
|
99
109
|
}
|
@@ -73,8 +73,9 @@ class TestOpenApiEndpointAdapter():
|
|
73
73
|
'endpoint_id': 1,
|
74
74
|
'id': 2,
|
75
75
|
'inferred_type': 'String',
|
76
|
+
'is_deterministic': True,
|
76
77
|
'is_required': False,
|
77
|
-
'name': '
|
78
|
+
'name': 'tagsElement',
|
78
79
|
'query': 'tags[*]',
|
79
80
|
'query_param_name_id': 1,
|
80
81
|
},
|
@@ -104,6 +105,7 @@ class TestOpenApiEndpointAdapter():
|
|
104
105
|
'endpoint_id': 1,
|
105
106
|
'id': 1,
|
106
107
|
'inferred_type': 'Hash',
|
108
|
+
'is_deterministic': True,
|
107
109
|
'is_required': False,
|
108
110
|
'name': 'Element',
|
109
111
|
'query': '[*]',
|
@@ -365,6 +367,56 @@ class TestOpenApiEndpointAdapter():
|
|
365
367
|
'query': 'apis',
|
366
368
|
'response_param_name_id': None,
|
367
369
|
},
|
370
|
+
{
|
371
|
+
'endpoint_id': 1,
|
372
|
+
'id': 3,
|
373
|
+
'inferred_type': 'Hash',
|
374
|
+
'is_deterministic': True,
|
375
|
+
'is_required': False,
|
376
|
+
'name': 'apisElement',
|
377
|
+
'query': 'apis[*]',
|
378
|
+
'response_param_name_id': 2,
|
379
|
+
},
|
380
|
+
{
|
381
|
+
'endpoint_id': 1,
|
382
|
+
'id': 4,
|
383
|
+
'inferred_type': 'String',
|
384
|
+
'is_deterministic': True,
|
385
|
+
'is_required': False,
|
386
|
+
'name': 'apiKey',
|
387
|
+
'query': 'apis[*].apiKey',
|
388
|
+
'response_param_name_id': 3,
|
389
|
+
},
|
390
|
+
{
|
391
|
+
'endpoint_id': 1,
|
392
|
+
'id': 5,
|
393
|
+
'inferred_type': 'String',
|
394
|
+
'is_deterministic': True,
|
395
|
+
'is_required': False,
|
396
|
+
'name': 'apiVersionNumber',
|
397
|
+
'query': 'apis[*].apiVersionNumber',
|
398
|
+
'response_param_name_id': 3,
|
399
|
+
},
|
400
|
+
{
|
401
|
+
'endpoint_id': 1,
|
402
|
+
'id': 6,
|
403
|
+
'inferred_type': 'String',
|
404
|
+
'is_deterministic': True,
|
405
|
+
'is_required': False,
|
406
|
+
'name': 'apiUrl',
|
407
|
+
'query': 'apis[*].apiUrl',
|
408
|
+
'response_param_name_id': 3,
|
409
|
+
},
|
410
|
+
{
|
411
|
+
'endpoint_id': 1,
|
412
|
+
'id': 7,
|
413
|
+
'inferred_type': 'String',
|
414
|
+
'is_deterministic': True,
|
415
|
+
'is_required': False,
|
416
|
+
'name': 'apiDocumentationUrl',
|
417
|
+
'query': 'apis[*].apiDocumentationUrl',
|
418
|
+
'response_param_name_id': 3,
|
419
|
+
},
|
368
420
|
],
|
369
421
|
}
|
370
422
|
|
@@ -475,6 +527,18 @@ class TestOpenApiEndpointAdapter():
|
|
475
527
|
'type': 'static',
|
476
528
|
},
|
477
529
|
],
|
530
|
+
'response_param_names': [
|
531
|
+
{
|
532
|
+
'endpoint_id': 3,
|
533
|
+
'id': 1,
|
534
|
+
'inferred_type': 'Hash',
|
535
|
+
'is_deterministic': True,
|
536
|
+
'is_required': False,
|
537
|
+
'name': 'Element',
|
538
|
+
'query': '[*]',
|
539
|
+
'response_param_name_id': None,
|
540
|
+
},
|
541
|
+
],
|
478
542
|
}
|
479
543
|
|
480
544
|
@pytest.fixture(scope='class')
|
@@ -485,6 +549,7 @@ class TestOpenApiEndpointAdapter():
|
|
485
549
|
http_endpoint_version['body_param_names'][0]['endpoint_id'] = 6
|
486
550
|
http_endpoint_version['body_param_names'][1]['endpoint_id'] = 6
|
487
551
|
http_endpoint_version['body_param_names'][2]['endpoint_id'] = 6
|
552
|
+
http_endpoint_version['response_param_names'][0]['endpoint_id'] = 6
|
488
553
|
return http_endpoint_version
|
489
554
|
|
490
555
|
def test_adapt_from_file(self, open_api_endpoint_adapter, uspto_file_path, expected_get_root_https, expected_get_root_http, expected_get_dataset_version_fields_https, expected_get_dataset_version_fields_http, expected_post_dataset_version_records_https, expected_post_dataset_version_records_http):
|
@@ -694,14 +759,14 @@ class TestOpenApiEndpointAdapter():
|
|
694
759
|
'values': [0]
|
695
760
|
},
|
696
761
|
{
|
697
|
-
'body_param_name_id':
|
762
|
+
'body_param_name_id': 4,
|
698
763
|
'endpoint_id': 2,
|
699
764
|
'id': 7,
|
700
765
|
'inferred_type': 'Hash',
|
701
766
|
'is_deterministic': True,
|
702
767
|
'is_required': False,
|
703
768
|
'name': 'adoption',
|
704
|
-
'query': 'adoption'
|
769
|
+
'query': 'extra.adoption'
|
705
770
|
},
|
706
771
|
{
|
707
772
|
'body_param_name_id': 7,
|
@@ -711,7 +776,7 @@ class TestOpenApiEndpointAdapter():
|
|
711
776
|
'is_deterministic': True,
|
712
777
|
'is_required': True,
|
713
778
|
'name': 'adopted',
|
714
|
-
'query': 'adoption.adopted',
|
779
|
+
'query': 'extra.adoption.adopted',
|
715
780
|
'values': [False]
|
716
781
|
},
|
717
782
|
{
|
@@ -722,7 +787,7 @@ class TestOpenApiEndpointAdapter():
|
|
722
787
|
'is_deterministic': True,
|
723
788
|
'is_required': False,
|
724
789
|
'name': 'shelter',
|
725
|
-
'query': 'adoption.shelter'
|
790
|
+
'query': 'extra.adoption.shelter'
|
726
791
|
},
|
727
792
|
{
|
728
793
|
'body_param_name_id': None,
|
@@ -840,8 +905,9 @@ class TestOpenApiEndpointAdapter():
|
|
840
905
|
'endpoint_id': 1,
|
841
906
|
'id': 7,
|
842
907
|
'inferred_type': 'String',
|
908
|
+
'is_deterministic': True,
|
843
909
|
'is_required': False,
|
844
|
-
'name': '
|
910
|
+
'name': 'photoUrlsElement',
|
845
911
|
'query': 'photoUrls[*]'
|
846
912
|
},
|
847
913
|
{
|
@@ -859,8 +925,9 @@ class TestOpenApiEndpointAdapter():
|
|
859
925
|
'endpoint_id': 1,
|
860
926
|
'id': 9,
|
861
927
|
'inferred_type': 'Hash',
|
928
|
+
'is_deterministic': True,
|
862
929
|
'is_required': False,
|
863
|
-
'name': '
|
930
|
+
'name': 'tagsElement',
|
864
931
|
'query': 'tags[*]'
|
865
932
|
},
|
866
933
|
{
|
@@ -971,8 +1038,9 @@ class TestOpenApiEndpointAdapter():
|
|
971
1038
|
'endpoint_id': 1,
|
972
1039
|
'id': 7,
|
973
1040
|
'inferred_type': 'String',
|
1041
|
+
'is_deterministic': True,
|
974
1042
|
'is_required': False,
|
975
|
-
'name': '
|
1043
|
+
'name': 'photoUrlsElement',
|
976
1044
|
'query': 'photoUrls[*]',
|
977
1045
|
'response_param_name_id': 6,
|
978
1046
|
},
|
@@ -990,8 +1058,9 @@ class TestOpenApiEndpointAdapter():
|
|
990
1058
|
'endpoint_id': 1,
|
991
1059
|
'id': 9,
|
992
1060
|
'inferred_type': 'Hash',
|
1061
|
+
'is_deterministic': True,
|
993
1062
|
'is_required': False,
|
994
|
-
'name': '
|
1063
|
+
'name': 'tagsElement',
|
995
1064
|
'query': 'tags[*]',
|
996
1065
|
'response_param_name_id': 8,
|
997
1066
|
},
|
@@ -1043,6 +1112,7 @@ class TestOpenApiEndpointAdapter():
|
|
1043
1112
|
'endpoint_id': 14,
|
1044
1113
|
'id': 1,
|
1045
1114
|
'inferred_type': 'Hash',
|
1115
|
+
'is_deterministic': True,
|
1046
1116
|
'is_required': False,
|
1047
1117
|
'name': 'Element',
|
1048
1118
|
'query': '[*]'
|
@@ -7,7 +7,10 @@ class TestSchemaBuilder():
|
|
7
7
|
|
8
8
|
def test_it_builds_single_level_dict(self):
|
9
9
|
builder = SchemaBuilder(-1, 'query_param_name')
|
10
|
-
param_names = builder.build(
|
10
|
+
param_names = builder.build([
|
11
|
+
{'name': 'id', 'value': 0, 'query': 'id', 'required': False, 'id': 1, "parent_id": None},
|
12
|
+
{'name': 'name', 'value': '', 'query': 'name', 'required': False, 'id': 2, "parent_id": None}
|
13
|
+
])
|
11
14
|
|
12
15
|
param_names_index = self.__index(param_names)
|
13
16
|
expected_queries = ['id.Integer', 'name.String']
|
@@ -17,11 +20,17 @@ class TestSchemaBuilder():
|
|
17
20
|
|
18
21
|
def test_it_builds_two_level_dict(self):
|
19
22
|
builder = SchemaBuilder(-1, 'query_param_name')
|
20
|
-
param_names = builder.build(
|
23
|
+
param_names = builder.build([
|
24
|
+
{'name': 'list', 'value': [], 'query': 'list', 'required': False, 'id': 1, "parent_id": None},
|
25
|
+
{'name': 'ListElement', 'value': {}, 'query': 'list[*]', 'required': False, 'id': 2, "parent_id": 1},
|
26
|
+
{'name': 'id', 'value': 0, 'query': 'list[*].id', 'required': False, 'id': 3, "parent_id": 2},
|
27
|
+
{'name': 'name', 'value': '', 'query': 'list[*].name', 'required': False, 'id': 4, "parent_id": 2},
|
28
|
+
{'name': 'total', 'value': 0, 'query': 'total', 'required': False, 'id': 5, "parent_id": None}
|
29
|
+
])
|
21
30
|
|
22
31
|
param_names_index = self.__index(param_names)
|
23
32
|
expected_queries = [
|
24
|
-
'list.Array', '
|
33
|
+
'list.Array', 'list[*].Hash', 'list[*].id.Integer', 'list[*].name.String', 'total.Integer'
|
25
34
|
]
|
26
35
|
|
27
36
|
for query in expected_queries:
|
@@ -29,11 +38,13 @@ class TestSchemaBuilder():
|
|
29
38
|
|
30
39
|
def test_it_builds_single_level_array(self):
|
31
40
|
builder = SchemaBuilder(-1, 'body_param_name')
|
32
|
-
param_names = builder.build([
|
41
|
+
param_names = builder.build([
|
42
|
+
{'name': 'Element', 'value': 0, 'query': '[*]', 'required': False, 'id': 1, "parent_id": None},
|
43
|
+
])
|
33
44
|
|
34
45
|
param_names_index = self.__index(param_names)
|
35
46
|
expected_queries = [
|
36
|
-
'[*].Integer'
|
47
|
+
'[*].Integer'
|
37
48
|
]
|
38
49
|
|
39
50
|
for query in expected_queries:
|
@@ -41,11 +52,15 @@ class TestSchemaBuilder():
|
|
41
52
|
|
42
53
|
def test_it_builds_item_and_single_level_array(self):
|
43
54
|
builder = SchemaBuilder(-1, 'query_param_name')
|
44
|
-
param_names = builder.build(
|
55
|
+
param_names = builder.build([
|
56
|
+
{'name': 'tags', 'value': [], 'query': 'tags', 'required': False, 'id': 1, "parent_id": None},
|
57
|
+
{'name': 'TagsElement', 'value': '', 'query': 'tags[*]', 'required': False, 'id': 2, "parent_id": 1},
|
58
|
+
{'name': 'limit', 'value': 0, 'query': 'limit', 'required': False, 'id': 3, "parent_id": None}
|
59
|
+
])
|
45
60
|
|
46
61
|
param_names_index = self.__index(param_names)
|
47
62
|
expected_queries = [
|
48
|
-
'
|
63
|
+
'tags.Array', 'tags[*].String', 'limit.Integer'
|
49
64
|
]
|
50
65
|
|
51
66
|
for query in expected_queries:
|
@@ -1 +1 @@
|
|
1
|
-
0.34.
|
1
|
+
0.34.8
|
@@ -16,11 +16,11 @@ class TestMultipart():
|
|
16
16
|
def test_decodes_response(self, multipart_string: bytes, content_type: str):
|
17
17
|
multidict = decode_response(multipart_string, content_type)
|
18
18
|
|
19
|
-
assert multidict.get('author') == 'John Smith'
|
20
|
-
assert multidict.get('file') == 'Hello World'
|
19
|
+
assert multidict.get('author') == b'John Smith'
|
20
|
+
assert multidict.get('file') == b'Hello World'
|
21
21
|
|
22
22
|
def test_encodes_response(self, content_type: str):
|
23
|
-
expected_params = { 'author': 'John Smith', 'file': 'Hello World'}
|
23
|
+
expected_params = { 'author': b'John Smith', 'file': b'Hello World'}
|
24
24
|
multipart_string = encode_response(expected_params, content_type)
|
25
25
|
|
26
26
|
multidict = decode_response(multipart_string, content_type)
|