pytest_ui_api_fw/template/generate_yaml.py

289 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
# 设置utf-8 显示中文
"""
@Create Time: 2021/3/12 23:48
@Author: guo
@Filegenerate_yaml.py
"""
import json
import mitmproxy
from mitmproxy import http
import yaml
import copy
import re
import sys
import os
sys.path.append("../")
from config.get_conf_data import GetConfData
class GenYamlTemplate:
def __init__(self):
self.__yaml_case_template_path = "case_yml_template.yml"
self.__yaml_page_template_path = "page_yml_template.yml"
# 设置目标host
self.__host_target = ["192.168.9.105", "105.xxx.xxxx", "xxx.xxx.xxx"]
# 静态资源
self.__static_ext = ["js", "css", "ico", "jpg", "png", "gif", "jpeg", "bmp", "ttf"]
# 静态文件,在家校pc端查看,其中 "application/javascript" js 文件也是无效的。
self.__static_files = ["text/css", "image/jpeg", "image/png", "image/gif", "text/html",
"application/octet-stream",
"application/x-protobuf"]
# 图片
self.__media_types = ["image", "video", "audio"]
# 上面的几种静态资源需要通过resonse来进行校验。存在于 response.headers("Content-Type")中
# 其中response为 flow.response
# 本次由于只需要生成操作的页面的接口对应的yaml文件。所以需要拿 request.headers("Content-Type") 是否为None进行判断即可。
self.__type = "Content-Type"
# 环境
self.__env_web = "web"
self.__env_app = "app"
self.__env = self.__env_web
# 读取yaml模板
self.__case_yaml_temp = yaml.safe_load(open(self.__yaml_case_template_path, encoding="utf8"))
self.__page_yaml_temp = yaml.safe_load(open(self.__yaml_page_template_path, encoding="utf-8"))
# remove 字符串。因为公司的有些remove接口 的request 的headers里没有Content-Type。 有些是有值。
self.__remove_str = "remove"
self.__post_str = "post"
self.__get_str = "get"
self.__json_str = "json"
self.__content_json = "application/json"
self.__data_str = "data"
self.__params_str = "params"
self.__case_tmp_str = "case_template"
self.__page_tmp_str = "page_template"
self.__web_str = "web"
self.__zhjx_gate = "zhjxgate"
self.__web_flag = f"/{self.__web_str}/"
self.__zhjx_flag= f"/{self.__zhjx_gate}/"
self.__env_web_flag = "web"
self.__env_app_flag = "app"
# 环境切换
self.__env_flag = self.__env_web_flag
self.__conf = GetConfData()
self.__yaml_dir = self.__conf.get_ymlfile_abspath()
self.__init_key()
self.__count=0
def __init_key(self):
"""需要用到的一些key,先不考虑把这些key写入到配置文件或独立出去."""
self.__app_login_key="app_login"
self.__web_login_key = "web_login"
self.__web_str_key = "web_str"
self.__app_str_key = "app_str"
self.__test_init_key = "test_init"
self.__login_type_key = "login_type"
self.__page_template_key = "page_template"
self.__case_template_key = "case_template"
self.__login_data_key = "login_data"
self.__api_data_key = "api_data"
self.__path_key = "path"
self.__data_type_key = "data_type"
self.__request_data_key = "request_data"
self.__method_key = "method"
self.__timestamp_key = "_"
def write_data_to_yaml(self, yaml_path: str, data: dict) -> None:
yml_path = yaml_path
# 先检查是否以.yaml 结尾,如果是统一修改为 .yml
yaml_suffix=".yaml"
yml_suffix=".yml"
exp=f"({yaml_suffix})$"
yml_path=re.sub(exp,yml_suffix,yml_path)
# 如果不是以.yml结尾就更改为 .yml
if yml_path:
yml_path = yml_path if yml_path.endswith(".yml") else yml_path + ".yml"
# 拼接路径
target_path = os.path.join(self.__yaml_dir, yml_path)
# 获取目录
target_dir = os.path.dirname(target_path)
# 当目录不存在时,则进行创建操作
if not os.path.exists(target_dir):
# 当已经存在的目录,则不创建
os.makedirs(target_dir,exist_ok=True)
datas={}
# 检测文件是否存在
if os.path.exists(target_path):
data_tmp = yaml.safe_load(open(target_path,encoding="utf-8"))
# 当不为None时赋值给变量
if data_tmp !=None:
datas= data_tmp
# 写入文件
with open(target_path,"w",encoding="utf-8") as fp :
datas.update(data)
# 以原来的顺序写入到文件中,保存原有顺序不变,同时中文不能乱码。
yaml.safe_dump(datas,fp,allow_unicode="utf-8",default_flow_style=False,sort_keys=False)
def request(self, flow: mitmproxy.http.HTTPFlow):
self.__count+=1
request = flow.request
host = request.host
# 获取不带参数的path返回的tuple类型
path = request.path_components
# 获取url path路径
url_path = "/" + "/".join(path)
# 用于获取,并校验是否为删除接口
latest_path = path[-1]
# 获取请求方法类型
method = str.lower(request.method)
# 获取提交的参数,可按字典的操作方式进行操作。
# 在获取数据之前,要先判断是get请求还是post请求,两者存的位置不一样
if method == self.__get_str:
# 目前知道的,当为get请求时,请求的参数是存放于 request.query 中
params = request.query
# post 请求(非get请求)
else:
params = request.urlencoded_form
# 获取各项值
data={}
for key ,item in params.items():
# 有些字段是时间戳,可以不用录入,这个要根据各个公司的接口规范要求
# 目前时间戳是非必填字段
if self.__timestamp_key != key :
data[key] = item
# 获取请求的 Content-Type
content_type = request.headers.get(self.__type)
# 过滤请求,只保留目标数据
if (host in self.__host_target) and \
((content_type != None) or ((content_type == None) and latest_path.startswith(self.__remove_str))):
# 先拷贝yaml模板的数据,深度拷贝
case_yaml_datas = copy.deepcopy(self.__case_yaml_temp)
page_yaml_datas = copy.deepcopy(self.__page_yaml_temp)
# 至于用什么标识进行切割,要根据实际的项目进行确定,找出共同点
# 检测 "/web/" 字符串是否存在于path路径中
if self.__web_flag in url_path:
# 获取切割后的功能模块的路径.
tmp_path = url_path.split(self.__web_flag)[-1]
# 检测 "/zhjxgate/" 字符串是否存在于 path路径中
elif self.__zhjx_flag in url_path:
tmp_path = url_path.split(self.__zhjx_flag)[-1]
# 暂时不考虑其他情况.
else:
pass
# 获取当前的接口名称(如:add,remove,query,audit),在写入page 和 case层yml文件时,需要用到
key = os.path.basename(tmp_path)
page_key = key
case_key = "test_"+key
# 生成各自的key的数据,要使用deepcopy,防止出现引用现象.因为在使用浅拷贝时,只要是容器类型(字典,列表,元组,集合)时,
# 使用copy.copy时,是直接引用的对象的id,也就是内存中的地址.所以一改,就全部都改了.并且最后在赋值的时候,也是进行引用赋值
# 使用deepcopy 是相当于重新新建了一份数据,是完全独立的.
case_templ_data = copy.deepcopy(case_yaml_datas[self.__case_template_key])
page_templ_data = copy.deepcopy(page_yaml_datas[self.__page_template_key])
# 先生成各自层的数据
case_yaml_datas[case_key] = case_templ_data
page_yaml_datas[page_key] = page_templ_data
# 判断是web端 还是app端
if self.__env_flag == self.__env_web_flag:
# 下面进行web yaml模板的配置
case_login_key = self.__web_login_key
page_login_key = self.__web_login_key
# 终端标识
type_flag_key = self.__web_str_key
elif self.__env_flag == self.__env_app_flag:
case_login_key = self.__app_login_key
page_login_key = self.__app_login_key
type_flag_key = self.__app_str_key
case_login = copy.deepcopy(case_yaml_datas[case_login_key])
page_login = copy.deepcopy(page_yaml_datas[page_login_key])
# 获取登录类型
login_type = page_yaml_datas[type_flag_key]
# 将case模板中的 test_init 下的login_type 和 login_data 的值全部替换掉
case_yaml_datas[self.__test_init_key][self.__login_type_key] = login_type
# 必须再次进行deepcopy,否则会以引用的方式出来.
# 也就是说 deepcopy后的值,必须只能赋值一次,否则
case_login_data = copy.deepcopy(case_yaml_datas[case_login_key])
case_yaml_datas[self.__test_init_key][self.__login_data_key] = case_login_data
# 获取各自api_data数据
case_apidata = case_yaml_datas[case_key][self.__api_data_key]
page_apidata = page_yaml_datas[page_key][self.__api_data_key]
#替换page层的path
page_apidata[self.__path_key] = url_path
data_type=""
# 下面开始进行method判断
if method == self.__get_str :
# 当为get请求时data_type 为params
data_type = self.__params_str
elif method == self.__post_str:
# 当为post请求时要判断content-type 是否为application/json若为则表示为 data_type=json
if self.__content_json in content_type:
data_type = self.__json_str
else:
data_type = self.__data_str
# 当为其他类型的请求方法时暂时不考虑或将设置为data
else:
data_type = self.__data_str
# 对各自的data_type 进行赋值
case_apidata[self.__data_type_key] = data_type
page_apidata[self.__data_type_key] = data_type
# 对page层的request_data下的method 进行赋值
page_apidata[self.__request_data_key][self.__method_key] = method
# 再对各自的request_data中的data 进行赋值 ,注意顺序
request_data = {}
request_data[data_type] = data
case_apidata[self.__request_data_key] = request_data
page_apidata[self.__request_data_key][data_type] = data
# 对各login_data 下的 login_data 进行赋值
case_apidata[self.__login_data_key][self.__login_data_key] = case_login
page_apidata[self.__login_data_key][self.__login_data_key] = page_login
# 对page层下的 login_data下的login_type 进行赋值
page_apidata[self.__login_data_key][self.__login_type_key] = login_type
#******************** 下面 开始进行 yml 文件的生成了 ××××××××××××××××××××××
'''
在进行yaml文件生成时需要注意的是 一个功能模块下的接口应该在同个yaml文件中且各自接口的名称(如addquery作为各自的key
目前在进行分割时是优先拿web进行侵害当不存在web时再拿zhjxgate进行分割。
需要注意的是app的接口ymlfile目录下的 app目录下web接口在ymlfile目录下的 web目录下。
'''
# 获取路径
# 这块的写法是不严谨的,有可能会出现 key为 空的情况.即 /web/action,这种情况,此时的 tmp_path = "action"
# 当这情况时,此时的dir为空,也就是""
dir = os.path.dirname(tmp_path)
if dir == "":
dir = os.path.basename(tmp_path)
# 获取文件名称
filename = os.path.basename(dir)+".yml"
# 设置case层和 page层各自的yml文件
page_file = self.__web_str+"/"+dir+"/"+filename
case_file = self.__web_str+"/"+dir+"/"+"test_"+filename
self.write_data_to_yaml(case_file,case_yaml_datas)
self.write_data_to_yaml(page_file,page_yaml_datas)
addons = [GenYamlTemplate()]