Merge branch 'zf_dependency_opt' into test_newcore
Showing
6 changed files
with
812 additions
and
17 deletions
1 | +package com.yoho.search.service.restapi; | ||
2 | + | ||
3 | +import org.junit.Assert; | ||
4 | +import org.yaml.snakeyaml.Yaml; | ||
5 | + | ||
6 | +import java.io.FileInputStream; | ||
7 | +import java.net.URL; | ||
8 | +import java.util.HashMap; | ||
9 | +import java.util.List; | ||
10 | +import java.util.Map; | ||
11 | + | ||
12 | +/** | ||
13 | + * Created by ginozhang on 2016/11/17. | ||
14 | + */ | ||
15 | +public class ApiDefUtils { | ||
16 | + | ||
17 | + private static Map<String, Map<String, Object>> apiDefs = new HashMap<>(); | ||
18 | + | ||
19 | + public static Map<String, Object> getApiDef(String api) { | ||
20 | + | ||
21 | + if(apiDefs.containsKey(api)) | ||
22 | + { | ||
23 | + return apiDefs.get(api); | ||
24 | + } | ||
25 | + | ||
26 | + synchronized (ApiDefUtils.class) { | ||
27 | + if(apiDefs.containsKey(api)) | ||
28 | + { | ||
29 | + return apiDefs.get(api); | ||
30 | + } | ||
31 | + | ||
32 | + String fileName = api.substring(1).replace("/", "_"); | ||
33 | + Yaml yaml = new Yaml(); | ||
34 | + URL ymlUrl = SearchNewControllerTest.class.getClassLoader().getResource("api_def/" + fileName + ".yml"); | ||
35 | + Map<String, Object> def = null; | ||
36 | + try { | ||
37 | + def = (Map<String, Object>) yaml.load(new FileInputStream(ymlUrl.getFile())); | ||
38 | + } catch (Exception e) { | ||
39 | + throw new RuntimeException("cannot find file " + fileName, e); | ||
40 | + } | ||
41 | + | ||
42 | + Assert.assertNotNull(fileName, def); | ||
43 | + apiDefs.put(api, def); | ||
44 | + return def; | ||
45 | + } | ||
46 | + } | ||
47 | + | ||
48 | + public static List<String> getEnumItems(String api, String enumKey) | ||
49 | + { | ||
50 | + List<String> enums = ((Map<String, List<String>>)getApiDef(api).get("_enums")).get(enumKey); | ||
51 | + return enums; | ||
52 | + } | ||
53 | + | ||
54 | + public static Map<String, Object> getRequestDef(String api) | ||
55 | + { | ||
56 | + return (Map<String, Object>)(getApiDef(api).get("_request")); | ||
57 | + } | ||
58 | + | ||
59 | + public static Map<String, Object> getResponseDef(String api) | ||
60 | + { | ||
61 | + return (Map<String, Object>)(getApiDef(api).get("_response")); | ||
62 | + } | ||
63 | +} |
1 | +package com.yoho.search.service.restapi; | ||
2 | + | ||
3 | +import com.alibaba.fastjson.JSONArray; | ||
4 | +import com.alibaba.fastjson.JSONObject; | ||
5 | +import org.junit.Assert; | ||
6 | + | ||
7 | +import java.util.*; | ||
8 | + | ||
9 | +/** | ||
10 | + * Created by ginozhang on 2016/11/15. | ||
11 | + */ | ||
12 | +public class CheckStructureUtils { | ||
13 | + | ||
14 | + private static final List<String> BASIC_TYPES = Arrays.asList("int", "long", "double", "string"); | ||
15 | + | ||
16 | + private static final List<String> COMPLEX_TYPES = Arrays.asList("array", "object", "enum_*", "connected_enum_*", "connected_<BASIC>", "ranged_int", "ranged_double", "optional(*), date(*)"); | ||
17 | + | ||
18 | + public static void checkRequest(String api, String url) { | ||
19 | + // 1. 获取参数列表 | ||
20 | + Map<String, String> map = getParamMap(url); | ||
21 | + | ||
22 | + // 2. 获取接口请求定义 | ||
23 | + Map<String, Object> requestDef = ApiDefUtils.getRequestDef(api); | ||
24 | + | ||
25 | + // 3. 校验请求参数 | ||
26 | + for (Map.Entry<String, String> entry : map.entrySet()) { | ||
27 | + checkSingleFiled(api, entry.getKey(), entry.getValue(), (Map<String, String>) requestDef.get(entry.getKey())); | ||
28 | + } | ||
29 | + } | ||
30 | + | ||
31 | + private static Map<String, String> getParamMap(String api) { | ||
32 | + Map<String, String> map = new LinkedHashMap<>(); | ||
33 | + String[] parts = api.split("\\?"); | ||
34 | + if (parts != null && parts.length == 2) { | ||
35 | + String paramStr = parts[1]; | ||
36 | + String[] paramPairs = paramStr.split("\\&"); | ||
37 | + for (String paramItem : paramPairs) { | ||
38 | + String[] keyValueArray = paramItem.split("="); | ||
39 | + if (keyValueArray != null && keyValueArray.length == 2) { | ||
40 | + map.put(keyValueArray[0], keyValueArray[1]); | ||
41 | + } | ||
42 | + } | ||
43 | + } | ||
44 | + | ||
45 | + System.out.println("[INFO]Get request url param map: " + map); | ||
46 | + return map; | ||
47 | + } | ||
48 | + | ||
49 | + public static void main(String[] args) { | ||
50 | + System.out.println(getParamMap("http://192.168.102.216:8080/yohosearch/productindex/productList?query=vans&brand=144&gender=2,3")); | ||
51 | + } | ||
52 | + | ||
53 | + public static void check(String api, JSONObject response) { | ||
54 | + Assert.assertTrue("The response should contain data.", response.containsKey("data") && response.get("data") != null); | ||
55 | + Map<String, Object> responseDef = ApiDefUtils.getResponseDef(api); | ||
56 | + doCheck(api, responseDef, response); | ||
57 | + } | ||
58 | + | ||
59 | + private static void doCheck(String api, Map<String, Object> responseDef, JSONObject response) { | ||
60 | + if (responseDef == null || response.isEmpty()) { | ||
61 | + return; | ||
62 | + } | ||
63 | + | ||
64 | + System.out.println("Start to process. responseDef: " + responseDef + ", response: " + response); | ||
65 | + for (Map.Entry<String, Object> entry : responseDef.entrySet()) { | ||
66 | + String property = entry.getKey(); | ||
67 | + Map<String, Object> pDef = (Map<String, Object>) entry.getValue(); | ||
68 | + Object pResponse = response.get(property); | ||
69 | + System.out.println("Begin to process. property: " + property + ", pDef: " + pDef + ", pResponse: " + pResponse); | ||
70 | + Assert.assertNotNull("interface api_def for property " + property + " cannot be null", pDef); | ||
71 | + boolean required = pDef.containsKey("_required") ? (Boolean) pDef.get("_required") : false; | ||
72 | + String type = pDef.containsKey("_type") ? (String) pDef.get("_type") : "string"; | ||
73 | + | ||
74 | + if (pResponse == null || pResponse.toString() == null || pResponse.toString().isEmpty()) { | ||
75 | + if (required) { | ||
76 | + Assert.fail("The property " + property + " is required!"); | ||
77 | + } else { | ||
78 | + continue; | ||
79 | + } | ||
80 | + } | ||
81 | + | ||
82 | + if ("object".equalsIgnoreCase(type)) { | ||
83 | + doCheck(api, (Map<String, Object>) pDef.get("_content"), (JSONObject) pResponse); | ||
84 | + } else if ("array".equalsIgnoreCase(type)) { | ||
85 | + JSONArray jsonArray = (JSONArray) pResponse; | ||
86 | + for (int i = 0; i < jsonArray.size(); i++) { | ||
87 | + doCheck(api, (Map<String, Object>) pDef.get("_content"), jsonArray.getJSONObject(i)); | ||
88 | + } | ||
89 | + } else { | ||
90 | + checkSingleFiled(api, property, pResponse.toString(), (Map<String, String>) entry.getValue()); | ||
91 | + } | ||
92 | + } | ||
93 | + | ||
94 | + // 检查是否有字段未定义 | ||
95 | + Set<String> defKeys = responseDef.keySet(); | ||
96 | + Set<String> resKeys = response.keySet(); | ||
97 | + if (!defKeys.containsAll(resKeys)) { | ||
98 | + resKeys.removeAll(defKeys); | ||
99 | + System.out.println("[WARN]NOT SAME KEYS. There has new propertys: " + resKeys); | ||
100 | + } else if (!resKeys.containsAll(defKeys)) { | ||
101 | + defKeys.removeAll(resKeys); | ||
102 | + System.out.println("[WARN]NOT SAME KEYS. There has propertys not set value: " + defKeys); | ||
103 | + } | ||
104 | + } | ||
105 | + | ||
106 | + private static void checkSingleFiled(String api, String fieldName, String sValue, Map<String, String> fieldDef) { | ||
107 | + System.out.println("[INFO]Begin to check field " + fieldName); | ||
108 | + Assert.assertNotNull("field not defined for " + fieldName, fieldDef); | ||
109 | + String type = fieldDef.containsKey("_type") ? (String) fieldDef.get("_type") : "string"; | ||
110 | + if (BASIC_TYPES.contains(type)) { | ||
111 | + checkBasicType(type, sValue); | ||
112 | + } else if (type.startsWith("optional")) { | ||
113 | + // skip to check | ||
114 | + } else if (type.startsWith("date")) { | ||
115 | + // skip to check | ||
116 | + } else if ("ranged_int".equals(type)) { | ||
117 | + String[] limits = sValue.split(","); | ||
118 | + Assert.assertTrue("The value for " + fieldName + " should contains comma", limits != null && limits.length == 2); | ||
119 | + Assert.assertTrue("The upper limit should larger than the lower limit", Integer.valueOf(limits[1]) >= Integer.valueOf(limits[0])); | ||
120 | + } else if ("ranged_double".equals(type)) { | ||
121 | + String[] limits = sValue.split(","); | ||
122 | + Assert.assertTrue("The value for " + fieldName + " should contains comma", limits != null && limits.length == 2); | ||
123 | + Assert.assertTrue("The upper limit should larger than the lower limit", Double.valueOf(limits[1]) >= Double.valueOf(limits[0])); | ||
124 | + } else if (type.startsWith("enum_")) { | ||
125 | + // 校验枚举类型 | ||
126 | + String enumKey = type.substring("enum_".length()); | ||
127 | + Assert.assertTrue("Invalid value " + sValue + " for enum " + enumKey, ApiDefUtils.getEnumItems(api, enumKey).contains(sValue)); | ||
128 | + } else if (type.startsWith("connected_enum_")) { | ||
129 | + // 校验枚举类型 | ||
130 | + String enumKey = type.substring("connected_enum_".length()); | ||
131 | + Assert.assertTrue("Invalid value " + sValue + " for enum " + enumKey, ApiDefUtils.getEnumItems(api, enumKey).containsAll(Arrays.asList(sValue.split(",")))); | ||
132 | + | ||
133 | + } else if (type.startsWith("connected_")) { | ||
134 | + String type4Connected = type.substring("connected_".length()); | ||
135 | + for (String item : sValue.split(",")) { | ||
136 | + checkBasicType(type4Connected, item); | ||
137 | + } | ||
138 | + } else { | ||
139 | + Assert.fail("Unknown property type " + type + " for property " + fieldName); | ||
140 | + } | ||
141 | + } | ||
142 | + | ||
143 | + private static void checkBasicType(String type, String sValue) { | ||
144 | + switch (type) { | ||
145 | + case "int": | ||
146 | + Integer.valueOf(sValue); | ||
147 | + break; | ||
148 | + case "long": | ||
149 | + Long.valueOf(sValue); | ||
150 | + break; | ||
151 | + case "double": | ||
152 | + Double.valueOf(sValue); | ||
153 | + break; | ||
154 | + default: | ||
155 | + break; | ||
156 | + } | ||
157 | + } | ||
158 | + | ||
159 | +} |
1 | +package com.yoho.search.service.restapi; | ||
2 | + | ||
3 | +import com.yoho.search.base.utils.FileUtils; | ||
4 | +import org.apache.commons.lang3.StringUtils; | ||
5 | + | ||
6 | +import java.util.HashMap; | ||
7 | +import java.util.Map; | ||
8 | + | ||
9 | +/** | ||
10 | + * Created by ginozhang on 2016/11/17. | ||
11 | + */ | ||
12 | +public class MarkDownUtils { | ||
13 | + | ||
14 | + private final String SEPARATOR = System.getProperty("line.separator"); | ||
15 | + | ||
16 | + private Map<String, String> typeDescptions = new HashMap<>(); | ||
17 | + | ||
18 | + private String api; | ||
19 | + | ||
20 | + private MarkDownUtils(String api) { | ||
21 | + this.api = api; | ||
22 | + } | ||
23 | + | ||
24 | + public static MarkDownUtils getInstance(String api) { | ||
25 | + return new MarkDownUtils(api); | ||
26 | + } | ||
27 | + | ||
28 | + { | ||
29 | + typeDescptions.put("connected_int", "int多值参数类型,多个值用逗号分隔。"); | ||
30 | + typeDescptions.put("ranged_double", "double范围类型,使用逗号分隔下限和上限"); | ||
31 | + typeDescptions.put("ranged_int", "int范围类型,使用逗号分隔下限和上限"); | ||
32 | + } | ||
33 | + | ||
34 | + | ||
35 | + /** | ||
36 | + * 根据接口描述文件生成接口描述的MD文件 | ||
37 | + */ | ||
38 | + public void generateApiMd() { | ||
39 | + Map<String, Object> apiDef = ApiDefUtils.getApiDef(api); | ||
40 | + StringBuffer sb = new StringBuffer(500); | ||
41 | + sb.append("# ").append(api).append(" 接口说明").append(SEPARATOR); | ||
42 | + | ||
43 | + // 1. 生成header 秒杀接口的功能和使用场景 | ||
44 | + sb.append(apiDef.get("_name")).append(SEPARATOR); | ||
45 | + | ||
46 | + // 2. 生成请求参数说明 | ||
47 | + sb.append(getRequestMdTable(apiDef)); | ||
48 | + | ||
49 | + // 3. 生成响应参数说明 | ||
50 | + sb.append(generateResponseMdTable(apiDef)); | ||
51 | + | ||
52 | + FileUtils.writeFile(api.replace("/", "_") + ".md", sb.toString()); | ||
53 | + } | ||
54 | + | ||
55 | + private StringBuffer generateResponseMdTable(Map<String, Object> apiDef) { | ||
56 | + return generateMdTable4SingleObject(api + " 响应参数说明", (Map<String, Object>) apiDef.get("_response"), 2); | ||
57 | + } | ||
58 | + | ||
59 | + private StringBuffer generateMdTable4SingleObject(String tableTitle, Map<String, Object> respnseDef, int level) { | ||
60 | + StringBuffer sb = new StringBuffer(100); | ||
61 | + sb.append(getLevelChars(level)).append(tableTitle).append(SEPARATOR); | ||
62 | + sb.append("|参数名 |参数类型 |参数说明 |").append(SEPARATOR); | ||
63 | + sb.append("|------|--------|---------|").append(SEPARATOR); | ||
64 | + for (Map.Entry<String, Object> entry : respnseDef.entrySet()) { | ||
65 | + String paramName = entry.getKey(); | ||
66 | + Map<String, Object> details = ((Map<String, Object>) entry.getValue()); | ||
67 | + String type = getTypeDescription((String) details.get("_type")); | ||
68 | + String desc = (String) details.get("_desc"); | ||
69 | + sb.append("|").append(paramName).append("|").append(type).append("|").append(desc).append("|").append(SEPARATOR); | ||
70 | + | ||
71 | + if ("object".equals(type) || "array".equals(type)) { | ||
72 | + // 如果是数组或者对象类型 就新搞一个表格来说明里面的对象 | ||
73 | + sb.append(SEPARATOR).append(SEPARATOR); | ||
74 | + sb.append(generateMdTable4SingleObject("参数" + paramName + "的属性说明", (Map<String, Object>) details.get("_content"), level + 1)); | ||
75 | + } | ||
76 | + | ||
77 | + } | ||
78 | + sb.append(SEPARATOR).append(SEPARATOR); | ||
79 | + return sb; | ||
80 | + } | ||
81 | + | ||
82 | + private StringBuffer getRequestMdTable(Map<String, Object> apiDef) { | ||
83 | + StringBuffer sb = new StringBuffer(100); | ||
84 | + sb.append("## ").append(api).append(" 请求参数说明").append(SEPARATOR); | ||
85 | + sb.append("|参数名 |参数类型 |参数说明 |").append(SEPARATOR); | ||
86 | + sb.append("|------|--------|---------|").append(SEPARATOR); | ||
87 | + | ||
88 | + Map<String, Object> requestDef = (Map<String, Object>) apiDef.get("_request"); | ||
89 | + for (Map.Entry<String, Object> entry : requestDef.entrySet()) { | ||
90 | + String paramName = entry.getKey(); | ||
91 | + Map<String, String> details = ((Map<String, String>) entry.getValue()); | ||
92 | + String type = getTypeDescription(details.get("_type")); | ||
93 | + String desc = details.get("_desc"); | ||
94 | + sb.append("|").append(paramName).append("|").append(type).append("|").append(desc).append("|").append(SEPARATOR); | ||
95 | + } | ||
96 | + sb.append(SEPARATOR).append(SEPARATOR); | ||
97 | + return sb; | ||
98 | + } | ||
99 | + | ||
100 | + private StringBuffer getLevelChars(int level) { | ||
101 | + StringBuffer sb = new StringBuffer(10); | ||
102 | + level = level <= 3 ? level : 3; | ||
103 | + for (int i = 1; i <= level; i++) { | ||
104 | + sb.append("#"); | ||
105 | + } | ||
106 | + | ||
107 | + sb.append(" "); | ||
108 | + return sb; | ||
109 | + } | ||
110 | + | ||
111 | + private String getTypeDescription(String type) { | ||
112 | + | ||
113 | + if (StringUtils.isEmpty(type)) { | ||
114 | + type = "string"; | ||
115 | + } | ||
116 | + | ||
117 | + if (typeDescptions.containsKey(type)) { | ||
118 | + return typeDescptions.get(type); | ||
119 | + } | ||
120 | + | ||
121 | + if (type.startsWith("enum_")) { | ||
122 | + String enumKey = type.substring("enum_".length()); | ||
123 | + return "枚举类型,可选范围:" + ApiDefUtils.getEnumItems(api, enumKey); | ||
124 | + } else if (type.startsWith("connected_enum_")) { | ||
125 | + String enumKey = type.substring("connected_enum_".length()); | ||
126 | + return "多值枚举类型,枚举可选范围:" + ApiDefUtils.getEnumItems(api, enumKey) + ", 多个值用逗号分隔。"; | ||
127 | + } else if (type.startsWith("optional")) { | ||
128 | + String specialValue = type.substring("optional(".length(), "optional(".length() + 1); | ||
129 | + return "特定值{" + specialValue + "}或不传值"; | ||
130 | + } | ||
131 | + | ||
132 | + return type; | ||
133 | + } | ||
134 | + | ||
135 | +} |
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; | @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; | ||
4 | import com.alibaba.fastjson.JSONObject; | 4 | import com.alibaba.fastjson.JSONObject; |
5 | import com.yoho.search.base.utils.HttpClientUtils; | 5 | import com.yoho.search.base.utils.HttpClientUtils; |
6 | import com.yoho.search.service.TestConstants; | 6 | import com.yoho.search.service.TestConstants; |
7 | +import org.apache.commons.lang3.StringUtils; | ||
7 | import org.junit.Assert; | 8 | import org.junit.Assert; |
8 | import org.junit.Test; | 9 | import org.junit.Test; |
9 | import org.springframework.web.bind.annotation.RequestMapping; | 10 | import org.springframework.web.bind.annotation.RequestMapping; |
@@ -23,22 +24,22 @@ public class SearchNewControllerTest { | @@ -23,22 +24,22 @@ public class SearchNewControllerTest { | ||
23 | .append(TestConstants.SERVICE_PORT).append("/").append(TestConstants.SERVICE_CONTEXT) | 24 | .append(TestConstants.SERVICE_PORT).append("/").append(TestConstants.SERVICE_CONTEXT) |
24 | .toString(); | 25 | .toString(); |
25 | 26 | ||
26 | - static Map<String,String> appendParams = new HashMap<>(); | 27 | + static Map<String, String> appendParams = new HashMap<>(); |
27 | 28 | ||
28 | static final List<String> skippedApis = Arrays.asList("/count"); | 29 | static final List<String> skippedApis = Arrays.asList("/count"); |
29 | 30 | ||
30 | - static | ||
31 | - { | ||
32 | - appendParams.put("/product_pool","filter_poolId=1"); | ||
33 | - appendParams.put("/new_product","brand=144"); | ||
34 | - appendParams.put("/group_brands","brand=144"); | ||
35 | - appendParams.put("/group_shops","shop=1"); | ||
36 | - appendParams.put("/suggest","query=vans"); | ||
37 | - appendParams.put("/shops","keyword=vans"); | 31 | + static { |
32 | + appendParams.put("/product_pool", "filter_poolId=1"); | ||
33 | + appendParams.put("/new_product", "brand=144"); | ||
34 | + appendParams.put("/group_brands", "brand=144"); | ||
35 | + appendParams.put("/group_shops", "shop=1"); | ||
36 | + appendParams.put("/suggest", "query=vans"); | ||
37 | + appendParams.put("/shops", "keyword=vans"); | ||
38 | + appendParams.put("/productindex/productList", "query=vans&brand=144&gender=2,3&attribute_not=2&status=1"); | ||
38 | } | 39 | } |
39 | 40 | ||
40 | @Test | 41 | @Test |
41 | - public void testAPI() { | 42 | + public void testAPIs() { |
42 | SearchNewController c = new SearchNewController(); | 43 | SearchNewController c = new SearchNewController(); |
43 | Method[] methods = c.getClass().getMethods(); | 44 | Method[] methods = c.getClass().getMethods(); |
44 | if (methods == null || methods.length == 0) { | 45 | if (methods == null || methods.length == 0) { |
@@ -53,21 +54,66 @@ public class SearchNewControllerTest { | @@ -53,21 +54,66 @@ public class SearchNewControllerTest { | ||
53 | String methodName = m.getName(); | 54 | String methodName = m.getName(); |
54 | String api = m.getAnnotation(RequestMapping.class).value()[0]; | 55 | String api = m.getAnnotation(RequestMapping.class).value()[0]; |
55 | System.out.println("Begin to execute mothod " + methodName + " for api: " + api); | 56 | System.out.println("Begin to execute mothod " + methodName + " for api: " + api); |
56 | - if(skippedApis.contains(api)) | ||
57 | - { | 57 | + if (skippedApis.contains(api)) { |
58 | continue; | 58 | continue; |
59 | } | 59 | } |
60 | 60 | ||
61 | + testSingle(api); | ||
62 | + } | ||
63 | + } | ||
64 | + | ||
65 | + @Test | ||
66 | + public void generateAPIMd() { | ||
67 | + SearchNewController c = new SearchNewController(); | ||
68 | + Method[] methods = c.getClass().getMethods(); | ||
69 | + if (methods == null || methods.length == 0) { | ||
70 | + return; | ||
71 | + } | ||
72 | + | ||
73 | + for (Method m : methods) { | ||
74 | + if (!m.isAnnotationPresent(RequestMapping.class)) { | ||
75 | + continue; | ||
76 | + } | ||
77 | + | ||
78 | + String api = m.getAnnotation(RequestMapping.class).value()[0]; | ||
79 | + if (skippedApis.contains(api)) { | ||
80 | + continue; | ||
81 | + } | ||
82 | + | ||
83 | + // 生成markdown接口说明文件 | ||
84 | + MarkDownUtils.getInstance(api).generateApiMd(); | ||
85 | + } | ||
86 | + } | ||
87 | + | ||
88 | + @Test | ||
89 | + public void testShops() { | ||
90 | + testSingle("/shops"); | ||
91 | + } | ||
92 | + | ||
93 | + @Test | ||
94 | + public void testProductList() { | ||
95 | + testSingle("/productindex/productList"); | ||
96 | + } | ||
97 | + | ||
98 | + private void testSingle(String api) { | ||
99 | + testSingle(api, appendParams.get(api)); | ||
100 | + } | ||
101 | + | ||
102 | + private void testSingle(String api, String paramStr) { | ||
61 | String url = urlBase + api; | 103 | String url = urlBase + api; |
62 | - if(appendParams.containsKey(api)) | ||
63 | - { | ||
64 | - url = url + "?" + appendParams.get(api); | 104 | + if (StringUtils.isNotEmpty(paramStr)) { |
105 | + url = url + "?" + paramStr; | ||
65 | } | 106 | } |
107 | + | ||
108 | + CheckStructureUtils.checkRequest(api, url); | ||
66 | String jsonResult = HttpClientUtils.getMethod(url); | 109 | String jsonResult = HttpClientUtils.getMethod(url); |
67 | System.out.println("Request url is: " + url + ". jsonResult: \n" + jsonResult); | 110 | System.out.println("Request url is: " + url + ". jsonResult: \n" + jsonResult); |
68 | JSONObject jsonObject = JSON.parseObject(jsonResult); | 111 | JSONObject jsonObject = JSON.parseObject(jsonResult); |
112 | + | ||
113 | + // 检查响应是否OK | ||
69 | Assert.assertEquals(api, "200", jsonObject.getString("code")); | 114 | Assert.assertEquals(api, "200", jsonObject.getString("code")); |
70 | - } | ||
71 | - } | ||
72 | 115 | ||
116 | + // 检查响应结果是否OK | ||
117 | + CheckStructureUtils.check(api, jsonObject); | ||
118 | + } | ||
73 | } | 119 | } |
1 | +# _enums: 定义使用到的枚举类型 | ||
2 | +# _request: 请求 | ||
3 | +# _response: 响应 | ||
4 | +# _desc: 字段描述 | ||
5 | +# _required: 字段是否必须,默认为false | ||
6 | +# _type: 字段类型,可选为int、long、string、double、object、array、enum_<ENUM_NAME>,默认为string | ||
7 | +_name: 商品列表搜索接口。 | ||
8 | +_enums: | ||
9 | + YesOrNo: ['Y','N'] | ||
10 | + Status: ['0','1'] | ||
11 | + Gender: ['1','2','3'] | ||
12 | + AgeLevel: ['1','2','3'] | ||
13 | + Outlet: ['1', '2'] | ||
14 | + VIPDiscountType: ['0', '1', '2'] | ||
15 | + Attribute: ['1', '2'] | ||
16 | + AppType: ['0', '1'] | ||
17 | + SellChannel: ['0', '1', '2'] | ||
18 | +_request: | ||
19 | + query: | ||
20 | + _desc: '搜索关键词' | ||
21 | + _type: 'string' | ||
22 | + viewNum: | ||
23 | + _desc: '分页每页显示商品数量' | ||
24 | + _type: 'int' | ||
25 | + page: | ||
26 | + _desc: '分页页数' | ||
27 | + _type: 'int' | ||
28 | + product_skn: | ||
29 | + _desc: '指定的商品SKN列表' | ||
30 | + _type: 'connected_int' | ||
31 | + brand: | ||
32 | + _desc: '指定的品牌列表' | ||
33 | + _type: 'connected_int' | ||
34 | + shop: | ||
35 | + _desc: '指定的店铺列表' | ||
36 | + _type: 'connected_int' | ||
37 | + msort: | ||
38 | + _desc: '指定的大分类列表' | ||
39 | + _type: 'connected_int' | ||
40 | + misort: | ||
41 | + _desc: '指定的中分类列表' | ||
42 | + _type: 'connected_int' | ||
43 | + sort: | ||
44 | + _desc: '指定的小分类列表' | ||
45 | + _type: 'connected_int' | ||
46 | + color: | ||
47 | + _desc: '指定的颜色列表' | ||
48 | + _type: 'connected_int' | ||
49 | + style: | ||
50 | + _desc: '指定的风格列表' | ||
51 | + _type: 'connected_int' | ||
52 | + size: | ||
53 | + _desc: '指定的尺寸列表' | ||
54 | + _type: 'connected_int' | ||
55 | + gender: | ||
56 | + _desc: '性别(1:男,2:女,3:通用)' | ||
57 | + _type: 'connected_enum_Gender' | ||
58 | + ageLevel: | ||
59 | + _desc: '年龄段(1成人 2大童 3小童)' | ||
60 | + _type: 'connected_enum_AgeLevel' | ||
61 | + price: | ||
62 | + _desc: '价格区间' | ||
63 | + _type: 'ranged_int' | ||
64 | + specialoffer: | ||
65 | + _desc: '是否为促销品[5折以下]' | ||
66 | + _type: 'enum_YesOrNo' | ||
67 | + isdiscount: | ||
68 | + _desc: '是否打折' | ||
69 | + _type: 'enum_YesOrNo' | ||
70 | + vdt: | ||
71 | + _desc: 'VIP折扣类型' | ||
72 | + _type: 'enum_VIPDiscountType' | ||
73 | + p_d: | ||
74 | + _desc: '折扣范围,浮点型,如【p_d_int=0.1,0.3】' | ||
75 | + _type: 'ranged_double' | ||
76 | + p_d_int: | ||
77 | + _desc: '折扣范围,整形,如【p_d_int=1,3】' | ||
78 | + _type: 'ranged_int' | ||
79 | + isStudentPrice: | ||
80 | + _desc: '是否有学生价优惠' | ||
81 | + _type: 'enum_YesOrNo' | ||
82 | + isStudentRebate: | ||
83 | + _desc: '是否有学生返币' | ||
84 | + _type: 'enum_YesOrNo' | ||
85 | + isInstalment: | ||
86 | + _desc: '是否分期' | ||
87 | + _type: 'enum_Status' | ||
88 | + sales: | ||
89 | + _desc: '是否在售' | ||
90 | + _type: 'enum_Status' | ||
91 | + promotion: | ||
92 | + _desc: '是否促销/推广商品 TODO' | ||
93 | + _type: 'int' | ||
94 | + attribute: | ||
95 | + _desc: '包含指定的商品属性(1:正常商品,2:赠品)' | ||
96 | + _type: 'enum_Attribute' | ||
97 | + attribute_not: | ||
98 | + _desc: '过滤指定的商品属性(1:正常商品,2:赠品)' | ||
99 | + _type: 'enum_Attribute' | ||
100 | + limited: | ||
101 | + _desc: '是否限量商品' | ||
102 | + _type: 'enum_YesOrNo' | ||
103 | + new: | ||
104 | + _desc: '是否新品' | ||
105 | + _type: 'enum_YesOrNo' | ||
106 | + outlets: | ||
107 | + _desc: '是否奥莱商品' | ||
108 | + _type: 'enum_Outlet' | ||
109 | + status: | ||
110 | + _desc: '是否上架' | ||
111 | + _type: 'enum_Status' | ||
112 | + breaking: | ||
113 | + _desc: '传入1只查询断码商品,不传值或其他值该参数不起作用' | ||
114 | + _type: 'optional(1)' | ||
115 | + app_type: | ||
116 | + _desc: 'APP类型' | ||
117 | + _type: 'connected_enum_AppType' | ||
118 | + sell_channels: | ||
119 | + _desc: '销售平台 (网站、APP、现场 TODO)' | ||
120 | + _type: 'enum_SellChannel' | ||
121 | + stocknumber: | ||
122 | + _desc: '商品库存,当传入0时查询库存为0的商品,当传入大于0的数值时,查询库存不小于改数值的商品' | ||
123 | + _type: 'int' | ||
124 | + folder_id: | ||
125 | + _desc: '商品目录' | ||
126 | + _type: 'int' | ||
127 | + series_id: | ||
128 | + _desc: '商品系列' | ||
129 | + _type: 'int' | ||
130 | + first_shelve_time: | ||
131 | + _desc: '首次上架时间间隔' | ||
132 | + _type: 'ranged_double' | ||
133 | + shelve_time: | ||
134 | + _desc: '最近上架日期间隔' | ||
135 | + _type: 'ranged_double' | ||
136 | + day: | ||
137 | + _desc: '上架时间' | ||
138 | + _type: 'date(yyyy-MM-dd)' | ||
139 | + contain_global: | ||
140 | + _desc: '传入Y查询结果包含全球购商品,不传值或其他值该参数不起作用' | ||
141 | + _type: 'optional(Y)' | ||
142 | + contain_seckill: | ||
143 | + _desc: '传入Y查询结果包含秒杀商品,不传值或其他值该参数不起作用' | ||
144 | + _type: 'optional(Y)' | ||
145 | + act_temp: | ||
146 | + _desc: '活动属性-模板id' | ||
147 | + _type: 'int' | ||
148 | + act_rec: | ||
149 | + _desc: '活动属性-是否推荐' | ||
150 | + _type: 'enum_Status' | ||
151 | + act_status: | ||
152 | + _desc: '活动属性-状态' | ||
153 | + _type: 'enum_Status' | ||
154 | +_response: | ||
155 | + code: | ||
156 | + _desc: '响应状态码,200为成功,其他为失败' | ||
157 | + _required: true | ||
158 | + _type: 'int' | ||
159 | + message: | ||
160 | + _desc: '响应描述信息' | ||
161 | + data: | ||
162 | + _desc: '响应具体数据' | ||
163 | + _required: true | ||
164 | + _type: 'object' | ||
165 | + _content: | ||
166 | + total: | ||
167 | + _desc: '搜索出的商品总数' | ||
168 | + _required: true | ||
169 | + _type: 'int' | ||
170 | + page: | ||
171 | + _desc: '当前页数' | ||
172 | + _required: true | ||
173 | + _type: 'int' | ||
174 | + page_size: | ||
175 | + _desc: '每页显示商品数' | ||
176 | + _required: true | ||
177 | + _type: 'int' | ||
178 | + page_total: | ||
179 | + _desc: '总页数' | ||
180 | + _required: true | ||
181 | + _type: 'int' | ||
182 | + product_list: | ||
183 | + _desc: '当前页的商品列表' | ||
184 | + _required: true | ||
185 | + _type: 'array' | ||
186 | + _content: | ||
187 | + product_id: | ||
188 | + _desc: '商品ID' | ||
189 | + _type: 'int' | ||
190 | + _required: true | ||
191 | + product_skn: | ||
192 | + _desc: '商品SKN' | ||
193 | + _required: true | ||
194 | + product_name: | ||
195 | + _desc: '商品名称' | ||
196 | + product_name_highlight: | ||
197 | + _desc: '高亮的商品名称' | ||
198 | + sales_phrase: | ||
199 | + _desc: 'TODO' | ||
200 | + cn_alphabet: | ||
201 | + _desc: 'TODO' | ||
202 | + brand_id: | ||
203 | + _desc: '品牌ID' | ||
204 | + _type: 'int' | ||
205 | + brand_name: | ||
206 | + _desc: '品牌名称' | ||
207 | + brand_domain: | ||
208 | + _desc: '品牌DOMAIN' | ||
209 | + market_price: | ||
210 | + _desc: '市场售价' | ||
211 | + _type: 'double' | ||
212 | + sales_price: | ||
213 | + _desc: '售价' | ||
214 | + _type: 'double' | ||
215 | + student_price: | ||
216 | + _desc: '学生售价' | ||
217 | + _type: 'double' | ||
218 | + vip_price: | ||
219 | + _desc: 'VIP售价' | ||
220 | + _type: 'double' | ||
221 | + vip1_price: | ||
222 | + _desc: 'VIP1售价' | ||
223 | + _type: 'double' | ||
224 | + vip2_price: | ||
225 | + _desc: 'VIP2售价' | ||
226 | + _type: 'double' | ||
227 | + vip3_price: | ||
228 | + _desc: 'VIP3售价' | ||
229 | + _type: 'double' | ||
230 | + vip_discount_type: | ||
231 | + _desc: 'VIP折扣类型TODO' | ||
232 | + _type: 'int' | ||
233 | + is_new: | ||
234 | + _desc: '是否新品' | ||
235 | + _type: 'enum_YesOrNo' | ||
236 | + is_advance: | ||
237 | + _desc: 'TODO' | ||
238 | + _type: 'enum_YesOrNo' | ||
239 | + is_limited: | ||
240 | + _desc: 'TODO' | ||
241 | + _type: 'enum_YesOrNo' | ||
242 | + is_special: | ||
243 | + _desc: 'TODO' | ||
244 | + _type: 'enum_YesOrNo' | ||
245 | + is_discount: | ||
246 | + _desc: '是否打折' | ||
247 | + _type: 'enum_YesOrNo' | ||
248 | + is_soon_sold_out: | ||
249 | + _desc: '是否快要销售完' | ||
250 | + _type: 'enum_YesOrNo' | ||
251 | + status: | ||
252 | + _desc: '商品状态' | ||
253 | + _type: 'enum_Status' | ||
254 | + is_promotion: | ||
255 | + _desc: '优惠码' | ||
256 | + _type: 'int' | ||
257 | + is_student_price: | ||
258 | + _desc: '是否有学生优惠' | ||
259 | + _type: 'enum_YesOrNo' | ||
260 | + is_student_rebate: | ||
261 | + _desc: '是否有学生返币' | ||
262 | + _type: 'enum_YesOrNo' | ||
263 | + is_global: | ||
264 | + _desc: '是否是全球购商品' | ||
265 | + _type: 'enum_YesOrNo' | ||
266 | + max_sort_id: | ||
267 | + _desc: '商品大分类' | ||
268 | + _type: 'int' | ||
269 | + _required: true | ||
270 | + middle_sort_id: | ||
271 | + _desc: '商品中分类' | ||
272 | + _type: 'int' | ||
273 | + _required: true | ||
274 | + small_sort_id: | ||
275 | + _desc: '商品小分类' | ||
276 | + _type: 'int' | ||
277 | + _required: true | ||
278 | + stock_number: | ||
279 | + _desc: 'TODO' | ||
280 | + _type: 'int' | ||
281 | + storage_num: | ||
282 | + _desc: '库存量' | ||
283 | + _type: 'int' | ||
284 | + sales_num: | ||
285 | + _desc: '销量' | ||
286 | + _type: 'int' | ||
287 | + shelve_time: | ||
288 | + _desc: '上架时间' | ||
289 | + _type: 'long' | ||
290 | + edit_time: | ||
291 | + _desc: '编辑时间' | ||
292 | + _type: 'long' | ||
293 | + sales_num: | ||
294 | + _desc: '销量' | ||
295 | + _type: 'int' | ||
296 | + is_outlets: | ||
297 | + _desc: '是否是奥特莱斯商品' | ||
298 | + _type: 'enum_Outlet' | ||
299 | + gender: | ||
300 | + _desc: '性别(1:男,2:女,3:通用)' | ||
301 | + _type: 'enum_Gender' | ||
302 | + age_level: | ||
303 | + _desc: '年龄段(1成人 2大童 3小童)' | ||
304 | + _type: 'connected_enum_AgeLevel' | ||
305 | + default_images: | ||
306 | + _desc: '默认的商品图片' | ||
307 | + yohood_id: | ||
308 | + _desc: 'TODO' | ||
309 | + country_id: | ||
310 | + _desc: 'TODO' | ||
311 | + goods_list: | ||
312 | + _desc: '商品的GOOD列表' | ||
313 | + _type: 'array' | ||
314 | + _content: | ||
315 | + goods_id: | ||
316 | + _desc: '商品的GOOD ID' | ||
317 | + _type: 'int' | ||
318 | + _required: true | ||
319 | + color_id: | ||
320 | + _desc: '商品的GOOD的颜色标识' | ||
321 | + _type: 'int' | ||
322 | + _required: true | ||
323 | + product_skc: | ||
324 | + _desc: '商品的GOOD的SKC' | ||
325 | + _type: 'int' | ||
326 | + _required: true | ||
327 | + status: | ||
328 | + _desc: '商品的GOOD状态' | ||
329 | + _type: 'enum_Status' | ||
330 | + _required: true | ||
331 | + is_default: | ||
332 | + _desc: '是否是默认GOOD' | ||
333 | + _type: 'enum_YesOrNo' | ||
334 | + color_name: | ||
335 | + _desc: '商品的GOOD的颜色' | ||
336 | + _type: 'string' | ||
337 | + _required: true | ||
338 | + color_code: | ||
339 | + _desc: '商品的GOOD的颜色取值' | ||
340 | + _type: 'string' | ||
341 | + _required: true | ||
342 | + color_value: | ||
343 | + _desc: '商品的GOOD颜色值 TODO' | ||
344 | + _type: 'string' | ||
345 | + is_default: | ||
346 | + _desc: '是否是默认GOOD' | ||
347 | + _type: 'enum_YesOrNo' | ||
348 | + images_url: | ||
349 | + _desc: '商品的GOOD的图片链接' | ||
350 | + _type: 'string' | ||
351 | + cover_1: | ||
352 | + _desc: '商品的GOOD的封面1' | ||
353 | + _type: 'string' | ||
354 | + cover_2: | ||
355 | + _desc: '商品的GOOD的封面2' | ||
356 | + _type: 'string' |
service/src/test/resources/api_def/shops.yml
0 → 100644
1 | +_name: '根据关键词搜索品牌店铺接口。' | ||
2 | +_request: | ||
3 | + keyword: | ||
4 | + _desc: '搜索店铺的关键词,不能为空' | ||
5 | + _required: true | ||
6 | + _type: 'string' | ||
7 | +_response: | ||
8 | + code: | ||
9 | + _desc: '响应状态码,200为成功,其他为失败' | ||
10 | + _required: true | ||
11 | + _type: 'int' | ||
12 | + message: | ||
13 | + _desc: '响应描述信息' | ||
14 | + _required: false | ||
15 | + _type: 'string' | ||
16 | + data: | ||
17 | + _desc: '根据关键字搜索出的关联的品牌' | ||
18 | + _required: false | ||
19 | + _type: 'object' | ||
20 | + _content: | ||
21 | + id: | ||
22 | + _desc: '品牌ID' | ||
23 | + _required: true | ||
24 | + _type: 'int' | ||
25 | + brand_name: | ||
26 | + _desc: '品牌名称' | ||
27 | + _required: true | ||
28 | + _type: 'string' | ||
29 | + brand_ico: | ||
30 | + _desc: '品牌ICO' | ||
31 | + _required: true | ||
32 | + _type: 'string' | ||
33 | + brand_domain: | ||
34 | + _desc: '品牌DOMAIN' | ||
35 | + _required: true | ||
36 | + _type: 'string' |
-
Please register or login to post a comment