http-content-parser 0.0.17__tar.gz → 0.0.19__tar.gz
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.
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/.gitignore +4 -1
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/PKG-INFO +4 -4
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/README.md +1 -1
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/pyproject.toml +2 -2
- http_content_parser-0.0.19/src/http_content_parser/api_parser.py +165 -0
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/curl_parser.py +7 -4
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/generate_api_file.py +9 -30
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/req_data.py +1 -1
- http_content_parser-0.0.19/tests/test_api_model_parser.py +10 -0
- http_content_parser-0.0.19/tests/test_curl.py +20 -0
- http_content_parser-0.0.17/tests/test_curl.py +0 -20
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/LICENSE +0 -0
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/requirements.txt +0 -0
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/__init__.py +0 -0
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/openapi_parser.py +0 -0
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/param_util.py +0 -0
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/postman_parser.py +0 -0
- {http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/swagger2_parser.py +0 -0
|
@@ -158,4 +158,7 @@ cython_debug/
|
|
|
158
158
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
159
159
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
160
160
|
#.idea/
|
|
161
|
-
.vscode/
|
|
161
|
+
.vscode/
|
|
162
|
+
|
|
163
|
+
#
|
|
164
|
+
tmp
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: http_content_parser
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.19
|
|
4
4
|
Summary: parse http's payload and response
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: leo <suleiabc@gmail.com>
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Classifier: License :: OSI Approved :: MIT License
|
|
8
8
|
Classifier: Operating System :: OS Independent
|
|
@@ -24,5 +24,5 @@ python3 -m build
|
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
rm -f dist/*
|
|
27
|
-
|
|
27
|
+
python3 -m twine upload dist/*
|
|
28
28
|
```
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "http_content_parser"
|
|
7
|
-
version = "0.0.
|
|
8
|
-
authors = [{ name = "
|
|
7
|
+
version = "0.0.19"
|
|
8
|
+
authors = [{ name = "leo", email = "suleiabc@gmail.com" }]
|
|
9
9
|
description = "parse http's payload and response"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.8"
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
import copy
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
from http_content_parser.curl_parser import CurlParser
|
|
6
|
+
from http_content_parser.openapi_parser import OpenApiParser
|
|
7
|
+
from http_content_parser.postman_parser import parse_postman
|
|
8
|
+
from http_content_parser.req_data import ReqData
|
|
9
|
+
from http_content_parser.swagger2_parser import Swagger2Parser
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ApiModelParser:
|
|
13
|
+
def get_api_model_for_curl(self, curl_file, curl_filter=None) -> list[ReqData]:
|
|
14
|
+
# convert curl
|
|
15
|
+
payload_list = self.convert_curl_data_to_model(
|
|
16
|
+
curl_file_path=curl_file, url_filter=curl_filter
|
|
17
|
+
)
|
|
18
|
+
# handle duplicate key
|
|
19
|
+
new_payload_list = self.__handle_duplicate_api_label(payload_list)
|
|
20
|
+
return new_payload_list
|
|
21
|
+
|
|
22
|
+
def get_api_model_for_postman(self, json_dict: dict) -> list[ReqData]:
|
|
23
|
+
payload_list = self.convert_postman_to_model(postman_dict=json_dict)
|
|
24
|
+
# handle duplicate key
|
|
25
|
+
new_payload_list = self.__handle_duplicate_api_label(payload_list)
|
|
26
|
+
return new_payload_list
|
|
27
|
+
|
|
28
|
+
def get_api_model_for_swagger(self, json_dict: dict) -> list[ReqData]:
|
|
29
|
+
payload_list = self.convert_swagger_to_model(swagger2_dict=json_dict)
|
|
30
|
+
# handle duplicate key
|
|
31
|
+
new_payload_list = self.__handle_duplicate_api_label(payload_list)
|
|
32
|
+
return new_payload_list
|
|
33
|
+
|
|
34
|
+
def get_api_model_for_openapi(self, json_dict: dict) -> list[ReqData]:
|
|
35
|
+
payload_list = self.convert_openapi_to_model(openapi_dict=json_dict)
|
|
36
|
+
# handle duplicate key
|
|
37
|
+
new_payload_list = self.__handle_duplicate_api_label(payload_list)
|
|
38
|
+
return new_payload_list
|
|
39
|
+
|
|
40
|
+
def convert_curl_data_to_model(
|
|
41
|
+
self, curl_file_path: str, url_filter=None
|
|
42
|
+
) -> list[ReqData]:
|
|
43
|
+
curl_parser = CurlParser()
|
|
44
|
+
payload_list = []
|
|
45
|
+
with open(curl_file_path, "rt") as f:
|
|
46
|
+
lines = f.readlines()
|
|
47
|
+
line_num_array = curl_parser.get_curl_line_num_scope(lines=lines)
|
|
48
|
+
for r in line_num_array:
|
|
49
|
+
res_dict = curl_parser.split_curl_to_struct(
|
|
50
|
+
lines, r[0], r[1], url_filter
|
|
51
|
+
)
|
|
52
|
+
req_model = ReqData(dd=res_dict)
|
|
53
|
+
url_content = curl_parser.parse_url(req_model.original_url)
|
|
54
|
+
req_model.temp_api_label = (
|
|
55
|
+
self.__replace_api_label_chars(url_content["path"][1:])
|
|
56
|
+
+ "_"
|
|
57
|
+
+ req_model.method
|
|
58
|
+
)
|
|
59
|
+
req_model.header = json.dumps(req_model.header)
|
|
60
|
+
req_model.query_param = json.dumps(url_content["query_params"])
|
|
61
|
+
req_model.path = url_content["path"][1:]
|
|
62
|
+
payload_list.append(req_model)
|
|
63
|
+
return payload_list
|
|
64
|
+
|
|
65
|
+
def convert_postman_to_model(self, postman_dict: dict) -> list[ReqData]:
|
|
66
|
+
api_infos = parse_postman(postman_dict)
|
|
67
|
+
payload_list = []
|
|
68
|
+
for api_info in api_infos:
|
|
69
|
+
req_data = ReqData()
|
|
70
|
+
req_data.path = api_info["path"]
|
|
71
|
+
req_data.header = json.dumps(api_info["header"])
|
|
72
|
+
req_data.body = api_info["body"]
|
|
73
|
+
req_data.query_param = json.dumps(api_info["query_param"])
|
|
74
|
+
req_data.original_url = api_info["url"]
|
|
75
|
+
req_data.method = api_info["method"].lower()
|
|
76
|
+
req_data.temp_api_label = (
|
|
77
|
+
self.__handle_http_path(api_info["path"].split("/"), "_")
|
|
78
|
+
.replace("{", "")
|
|
79
|
+
.replace("}", "")
|
|
80
|
+
+ "_"
|
|
81
|
+
+ api_info["method"].lower()
|
|
82
|
+
)
|
|
83
|
+
payload_list.append(req_data)
|
|
84
|
+
return payload_list
|
|
85
|
+
|
|
86
|
+
def convert_swagger_to_model(self, swagger2_dict: dict) -> list[ReqData]:
|
|
87
|
+
swagger_parser = Swagger2Parser(swagger2_dict)
|
|
88
|
+
api_dict = swagger_parser.get_swagger_api_info()
|
|
89
|
+
if not api_dict:
|
|
90
|
+
print("check your swagger json")
|
|
91
|
+
return
|
|
92
|
+
payload_list = []
|
|
93
|
+
for path, path_info in api_dict.items():
|
|
94
|
+
req_data = ReqData()
|
|
95
|
+
req_data.path = self.__handle_http_path(path.split("/")[:-1], "/")
|
|
96
|
+
req_data.temp_api_label = (
|
|
97
|
+
self.__handle_http_path(path.split("/"), "_")
|
|
98
|
+
.replace("{", "")
|
|
99
|
+
.replace("}", "")
|
|
100
|
+
)
|
|
101
|
+
req_data.method = path.split("/")[-1]
|
|
102
|
+
req_data.query_param = json.dumps(path_info["query_param"])
|
|
103
|
+
req_data.path_param = json.dumps(path_info["path_param"])
|
|
104
|
+
req_data.response = json.dumps(path_info.get("response", {}))
|
|
105
|
+
# swagger中body第一层和第二层key重复,只取第二层后的数据
|
|
106
|
+
for _, v in path_info["body_param"].items():
|
|
107
|
+
req_data.body = json.dumps(v)
|
|
108
|
+
payload_list.append(req_data)
|
|
109
|
+
return payload_list
|
|
110
|
+
|
|
111
|
+
def convert_openapi_to_model(self, openapi_dict: dict) -> list[ReqData]:
|
|
112
|
+
if not openapi_dict:
|
|
113
|
+
print("openapi dict is null")
|
|
114
|
+
return []
|
|
115
|
+
payload_list = []
|
|
116
|
+
parser = OpenApiParser(openapi_dict)
|
|
117
|
+
api_dict = parser.get_open_api_info()
|
|
118
|
+
if not api_dict:
|
|
119
|
+
print("check your swagger json")
|
|
120
|
+
return
|
|
121
|
+
for path, path_info in api_dict.items():
|
|
122
|
+
req_data = ReqData()
|
|
123
|
+
req_data.path = self.__handle_http_path(path.split("/")[:-1], "/")
|
|
124
|
+
req_data.temp_api_label = (
|
|
125
|
+
self.__handle_http_path(path.split("/"), "_")
|
|
126
|
+
.replace("{", "")
|
|
127
|
+
.replace("}", "")
|
|
128
|
+
)
|
|
129
|
+
req_data.method = path.split("/")[-1]
|
|
130
|
+
req_data.query_param = json.dumps(path_info["query_param"])
|
|
131
|
+
req_data.path_param = json.dumps(path_info["path_param"])
|
|
132
|
+
req_data.response = json.dumps(path_info.get("response", {}))
|
|
133
|
+
req_data.body = json.dumps(path_info["body_param"])
|
|
134
|
+
payload_list.append(req_data)
|
|
135
|
+
return payload_list
|
|
136
|
+
|
|
137
|
+
def __handle_http_path(self, path_list: list, split_char: str) -> str:
|
|
138
|
+
if not path_list:
|
|
139
|
+
return ""
|
|
140
|
+
url_path = ""
|
|
141
|
+
for u in path_list:
|
|
142
|
+
if u:
|
|
143
|
+
url_path += u + split_char
|
|
144
|
+
return url_path[:-1]
|
|
145
|
+
|
|
146
|
+
def __replace_api_label_chars(self, string: str) -> str:
|
|
147
|
+
pattern = r"[+/@?=.]" # 定义要匹配的特殊字符模式
|
|
148
|
+
replacement = "_" # 替换为的字符串
|
|
149
|
+
|
|
150
|
+
new_string = re.sub(pattern, replacement, string)
|
|
151
|
+
return new_string
|
|
152
|
+
|
|
153
|
+
def __handle_duplicate_api_label(
|
|
154
|
+
self, payload_list: list[ReqData]
|
|
155
|
+
) -> list[ReqData]:
|
|
156
|
+
key_filter = {}
|
|
157
|
+
new_payload_list = copy.deepcopy(payload_list)
|
|
158
|
+
for payload, p_copy in zip(payload_list, new_payload_list):
|
|
159
|
+
k = payload.temp_api_label
|
|
160
|
+
if k in key_filter.keys():
|
|
161
|
+
p_copy.temp_api_label = k + "_" + str(key_filter[k])
|
|
162
|
+
key_filter[k] += 1
|
|
163
|
+
else:
|
|
164
|
+
key_filter[k] = 2
|
|
165
|
+
return new_payload_list
|
{http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/curl_parser.py
RENAMED
|
@@ -5,6 +5,8 @@ from urllib.parse import urlparse, parse_qs
|
|
|
5
5
|
|
|
6
6
|
class CurlParser(object):
|
|
7
7
|
def parse_url(self, url: str) -> dict:
|
|
8
|
+
if "://" not in url:
|
|
9
|
+
url = "http://" + url
|
|
8
10
|
# 解析 URL
|
|
9
11
|
parsed_url = urlparse(url)
|
|
10
12
|
# 获取各个组成部分
|
|
@@ -62,7 +64,7 @@ class CurlParser(object):
|
|
|
62
64
|
line_num_array.append([start_num, num])
|
|
63
65
|
return line_num_array
|
|
64
66
|
|
|
65
|
-
def split_curl_to_struct(self, lines, s, e,
|
|
67
|
+
def split_curl_to_struct(self, lines, s, e, url_filter=None) -> dict:
|
|
66
68
|
req_data = {}
|
|
67
69
|
header = {}
|
|
68
70
|
reduced_lines = self.reduce_curl_data_part(lines=lines, s=s, e=e)
|
|
@@ -70,8 +72,8 @@ class CurlParser(object):
|
|
|
70
72
|
lines_i_str = str(reduced_lines[i])
|
|
71
73
|
line_i_list = lines_i_str.split(" ")
|
|
72
74
|
if "curl" in lines_i_str:
|
|
73
|
-
if
|
|
74
|
-
if
|
|
75
|
+
if url_filter:
|
|
76
|
+
if url_filter not in lines_i_str:
|
|
75
77
|
# 如果curl_filter 不存在于当前Url中,则跳过本次循环
|
|
76
78
|
continue
|
|
77
79
|
for line_sub in line_i_list:
|
|
@@ -106,7 +108,8 @@ class CurlParser(object):
|
|
|
106
108
|
curl_data = line_i_list[1]
|
|
107
109
|
else:
|
|
108
110
|
curl_data = line_i_list[0]
|
|
109
|
-
body = re.sub(
|
|
111
|
+
body = re.sub(r"\n\s*", "", curl_data.replace(" \\\n", ""))
|
|
112
|
+
# body = re.sub(r"\s*", "", curl_data.replace(" \\\n", ""))
|
|
110
113
|
req_data["body"] = body[:-1]
|
|
111
114
|
|
|
112
115
|
if not req_data.get("method"):
|
|
@@ -11,6 +11,7 @@ from http_content_parser.openapi_parser import OpenApiParser
|
|
|
11
11
|
from http_content_parser.postman_parser import parse_postman
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
# TODO 不直接生成yaml文件,只生成dict,方便后续扩展
|
|
14
15
|
class GenerateApiFile:
|
|
15
16
|
def __init__(self) -> None:
|
|
16
17
|
pass
|
|
@@ -39,7 +40,7 @@ class GenerateApiFile:
|
|
|
39
40
|
def produce_api_yaml_for_curl(self, curl_file, yaml_file, curl_filter=None):
|
|
40
41
|
# convert curl
|
|
41
42
|
payload_list = self.convert_curl_data_to_model(
|
|
42
|
-
curl_file_path=curl_file,
|
|
43
|
+
curl_file_path=curl_file, url_filter=curl_filter
|
|
43
44
|
)
|
|
44
45
|
# handle duplicate key
|
|
45
46
|
new_payload_list = self.handle_duplicate_yaml_key(payload_list)
|
|
@@ -56,11 +57,11 @@ class GenerateApiFile:
|
|
|
56
57
|
p_copy.temp_api_label = k + "_" + str(key_filter[k])
|
|
57
58
|
key_filter[k] += 1
|
|
58
59
|
else:
|
|
59
|
-
key_filter[k] =
|
|
60
|
+
key_filter[k] = 2
|
|
60
61
|
return new_payload_list
|
|
61
62
|
|
|
62
63
|
def convert_curl_data_to_model(
|
|
63
|
-
self, curl_file_path,
|
|
64
|
+
self, curl_file_path, url_filter=None
|
|
64
65
|
) -> list[ReqData]:
|
|
65
66
|
curl_parser = CurlParser()
|
|
66
67
|
payload_list = []
|
|
@@ -69,7 +70,7 @@ class GenerateApiFile:
|
|
|
69
70
|
line_num_array = curl_parser.get_curl_line_num_scope(lines=lines)
|
|
70
71
|
for r in line_num_array:
|
|
71
72
|
res_dict = curl_parser.split_curl_to_struct(
|
|
72
|
-
lines, r[0], r[1],
|
|
73
|
+
lines, r[0], r[1], url_filter
|
|
73
74
|
)
|
|
74
75
|
req_model = ReqData(dd=res_dict)
|
|
75
76
|
url_content = curl_parser.parse_url(req_model.original_url)
|
|
@@ -79,36 +80,14 @@ class GenerateApiFile:
|
|
|
79
80
|
+ req_model.method
|
|
80
81
|
)
|
|
81
82
|
req_model.header = json.dumps(req_model.header)
|
|
82
|
-
|
|
83
|
+
if url_content["query_params"]:
|
|
84
|
+
req_model.query_param = json.dumps(url_content["query_params"])
|
|
85
|
+
else:
|
|
86
|
+
req_model.query_param = {}
|
|
83
87
|
req_model.path = url_content["path"][1:]
|
|
84
88
|
payload_list.append(req_model)
|
|
85
89
|
return payload_list
|
|
86
90
|
|
|
87
|
-
def convert_curl_data_to_model_old(
|
|
88
|
-
self, curl_file_path, curl_filter=None
|
|
89
|
-
) -> list[ReqData]:
|
|
90
|
-
curl_parser = CurlParser()
|
|
91
|
-
payload_list = []
|
|
92
|
-
with open(curl_file_path, "rt") as f:
|
|
93
|
-
lines = f.readlines()
|
|
94
|
-
line_num_array = curl_parser.get_curl_line_num_scope(lines=lines)
|
|
95
|
-
for r in line_num_array:
|
|
96
|
-
res_dict = curl_parser.split_curl_to_struct(
|
|
97
|
-
lines, r[0], r[1], curl_filter
|
|
98
|
-
)
|
|
99
|
-
template = ReqData(dd=res_dict)
|
|
100
|
-
split_url = curl_parser.split_url(template.original_url, "_")
|
|
101
|
-
template.temp_api_label = (
|
|
102
|
-
self.replace_api_label_chars(split_url["url_path"])
|
|
103
|
-
+ template.method
|
|
104
|
-
)
|
|
105
|
-
template.header = json.dumps(template.header)
|
|
106
|
-
template.query_param = json.dumps(split_url["url_params"])
|
|
107
|
-
split_url_origin = curl_parser.split_url(template.original_url, "/")
|
|
108
|
-
template.path = split_url_origin["url_path"][:-1]
|
|
109
|
-
payload_list.append(template)
|
|
110
|
-
return payload_list
|
|
111
|
-
|
|
112
91
|
def produce_api_yaml_for_swagger2(self, swagger2_dict, yaml_file):
|
|
113
92
|
swagger_parser = Swagger2Parser(swagger2_dict)
|
|
114
93
|
api_dict = swagger_parser.get_swagger_api_info()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from http_content_parser.generate_api_file import GenerateApiFile
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_curl():
|
|
7
|
+
gaf = GenerateApiFile()
|
|
8
|
+
# with open("./postman.json", "r") as f:
|
|
9
|
+
# json_dict = json.load(f)
|
|
10
|
+
# gaf.produce_api_yaml_for_postman(json_dict, "./test.yaml")
|
|
11
|
+
curl_file = (
|
|
12
|
+
"./tmp"
|
|
13
|
+
)
|
|
14
|
+
gaf.produce_api_yaml_for_curl(curl_file=curl_file, yaml_file="api.yaml")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_for():
|
|
18
|
+
a = "#!/usr/bin/env bash \t\n \n echo \\n 66 >> /root/pre23"
|
|
19
|
+
b = a.replace("\n", "")
|
|
20
|
+
print(b)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
from http_content_parser.generate_api_file import GenerateApiFile
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_curl():
|
|
7
|
-
gaf = GenerateApiFile()
|
|
8
|
-
# with open("./postman.json", "r") as f:
|
|
9
|
-
# json_dict = json.load(f)
|
|
10
|
-
# gaf.produce_api_yaml_for_postman(json_dict, "./test.yaml")
|
|
11
|
-
curl_file = ""
|
|
12
|
-
res = gaf.produce_api_yaml_for_curl(curl_file=curl_file, yaml_file="api.yaml")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_for():
|
|
16
|
-
data = {"name": "John", "age": 30, "city": "New York"}
|
|
17
|
-
temp = '{ "job_id":{{job_id}}, "product_id": 4, "local_user_id": 0}'
|
|
18
|
-
temp = json.dumps(data)
|
|
19
|
-
temp = json.loads(temp)
|
|
20
|
-
print(temp)
|
|
File without changes
|
|
File without changes
|
{http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/__init__.py
RENAMED
|
File without changes
|
{http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/openapi_parser.py
RENAMED
|
File without changes
|
{http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/param_util.py
RENAMED
|
File without changes
|
{http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/postman_parser.py
RENAMED
|
File without changes
|
{http_content_parser-0.0.17 → http_content_parser-0.0.19}/src/http_content_parser/swagger2_parser.py
RENAMED
|
File without changes
|