Skip to content

Commit

Permalink
fix(cts): improve type inference APIC-467 (#619)
Browse files Browse the repository at this point in the history
  • Loading branch information
millotp authored Jun 1, 2022
1 parent 6f65cd7 commit 9b55b7e
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,59 +26,20 @@ public ParametersWithDataType(Map<String, CodegenModel> models, String language)
this.language = language;
}

public Map<String, Object> buildJSONForRequest(String operationId, Request req, CodegenOperation ope, int testIndex)
public void enhanceParameters(Map<String, Object> parameters, Map<String, Object> bundle, CodegenOperation operation)
throws CTSException, JsonMappingException, JsonProcessingException {
Map<String, Object> test = new HashMap<>();
test.put("method", operationId);
test.put("testName", req.testName == null ? operationId : req.testName);
test.put("testIndex", testIndex);
test.put("request", req.request);
test.put("hasParameters", req.parameters.size() != 0);

if (req.requestOptions != null) {
test.put("hasRequestOptions", true);
test.put("requestOptions", Json.mapper().writeValueAsString(req.requestOptions));
Map<String, Object> requestOptions = new HashMap<>();

if (req.requestOptions.queryParameters != null) {
CodegenParameter objSpec = new CodegenParameter();
objSpec.dataType = inferDataType(req.requestOptions.queryParameters, objSpec, null);
requestOptions.put("queryParameters", traverseParams("queryParameters", req.requestOptions.queryParameters, objSpec, "", 0));
}

if (req.requestOptions.headers != null) {
List<Map<String, String>> headers = new ArrayList<Map<String, String>>();

for (Entry<String, String> entry : req.requestOptions.headers.entrySet()) {
Map<String, String> parameter = new HashMap<>();

parameter.put("key", entry.getKey());
parameter.put("value", entry.getValue());

headers.add(parameter);
}

requestOptions.put("headers", headers);
}

test.put("requestOptionsWithDataType", requestOptions);
}

if (req.parameters.size() == 0) {
return test;
if (parameters == null || parameters.size() == 0) {
return;
}
// Give the stringified version to mustache
test.put("parameters", Json.mapper().writeValueAsString(req.parameters));
// Give the stringified version to mustache, for js
bundle.put("parameters", Json.mapper().writeValueAsString(parameters));

List<Object> parametersWithDataType = new ArrayList<>();

// special case if there is only bodyParam which is not an array
if (ope.allParams.size() == 1 && ope.bodyParams.size() == 1 && !ope.bodyParam.isArray) {
parametersWithDataType.add(traverseParams(ope.bodyParam.paramName, req.parameters, ope.bodyParam, "", 0));
} else {
for (Entry<String, Object> param : req.parameters.entrySet()) {
CodegenParameter specParam = null;
for (CodegenParameter sp : ope.allParams) {
for (Entry<String, Object> param : parameters.entrySet()) {
CodegenParameter specParam = null;
if (operation != null) {
for (CodegenParameter sp : operation.allParams) {
if (sp.paramName.equals(param.getKey())) {
specParam = sp;
break;
Expand All @@ -87,12 +48,29 @@ public Map<String, Object> buildJSONForRequest(String operationId, Request req,
if (specParam == null) {
throw new CTSException("Parameter " + param.getKey() + " not found in the root parameter");
}
parametersWithDataType.add(traverseParams(param.getKey(), param.getValue(), specParam, "", 0));
}
parametersWithDataType.add(traverseParams(param.getKey(), param.getValue(), specParam, "", 0));
}

test.put("parametersWithDataType", parametersWithDataType);
return test;
bundle.put("parametersWithDataType", parametersWithDataType);
}

public void enhanceRootParameters(
Map<String, Object> parameters,
String paramName,
IJsonSchemaValidationProperties spec,
Map<String, Object> bundle
) throws CTSException, JsonMappingException, JsonProcessingException {
if (parameters == null || parameters.size() == 0) {
return;
}
// Give the stringified version to mustache, for js
bundle.put("parameters", Json.mapper().writeValueAsString(parameters));

List<Object> parametersWithDataType = new ArrayList<>();
parametersWithDataType.add(traverseParams(paramName, parameters, spec, "", 0));

bundle.put("parametersWithDataType", parametersWithDataType);
}

private Map<String, Object> traverseParams(
Expand All @@ -102,6 +80,9 @@ private Map<String, Object> traverseParams(
String parent,
int suffix
) throws CTSException {
if (spec == null) {
return traverseParams(paramName, param, parent, suffix);
}
String baseType = getTypeName(spec);
if (baseType == null) {
throw new CTSException("Cannot determine type of " + paramName + " (value: " + param + ")");
Expand Down Expand Up @@ -145,7 +126,7 @@ private Map<String, Object> traverseParams(
handleModel(paramName, param, testOutput, spec, baseType, parent, suffix);
} else if (baseType.equals("Object")) {
// not var, no item, pure free form
handleObject(paramName, param, testOutput, spec, suffix);
handleObject(paramName, param, testOutput, suffix);
} else if (spec.getIsMap()) {
// free key but only one type
handleMap(paramName, param, testOutput, spec, suffix);
Expand All @@ -155,6 +136,34 @@ private Map<String, Object> traverseParams(
return testOutput;
}

/** Same method but with inference only */
private Map<String, Object> traverseParams(String paramName, Object param, String parent, int suffix) throws CTSException {
String finalParamName = paramName;
if (language.equals("java") && paramName.startsWith("_")) {
finalParamName = paramName.substring(1);
}
Boolean isFirstLevel = suffix == 0;

Map<String, Object> testOutput = createDefaultOutput();
testOutput.put("key", finalParamName);
testOutput.put("parentSuffix", suffix - 1);
testOutput.put("isFirstLevel", isFirstLevel);
testOutput.put("hasGeneratedKey", finalParamName.matches("(.*)_[0-9]$"));
testOutput.put("suffix", suffix);
testOutput.put("parent", parent);
// cannot determine objectName with inference
// testOutput.put("objectName", Utils.capitalize(baseType));

if (param instanceof List) {
handleArray(paramName, param, testOutput, null, suffix);
} else if (param instanceof Map) {
handleObject(paramName, param, testOutput, suffix);
} else {
handlePrimitive(param, testOutput, null);
}
return testOutput;
}

private Map<String, Object> createDefaultOutput() {
Map<String, Object> testOutput = new HashMap<>();

Expand Down Expand Up @@ -187,7 +196,7 @@ private void handleArray(

List<Object> values = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
values.add(traverseParams(paramName + "_" + i, items.get(i), spec.getItems(), paramName, suffix + 1));
values.add(traverseParams(paramName + "_" + i, items.get(i), spec == null ? null : spec.getItems(), paramName, suffix + 1));
}

testOutput.put("isArray", true);
Expand Down Expand Up @@ -281,21 +290,7 @@ private void handleModel(
testOutput.put("value", values);
}

private void handleObject(
String paramName,
Object param,
Map<String, Object> testOutput,
IJsonSchemaValidationProperties spec,
int suffix
) throws CTSException {
if (spec.getHasVars()) {
throw new CTSException("Spec has vars.");
}

if (spec.getItems() != null) {
throw new CTSException("Spec has items.");
}

private void handleObject(String paramName, Object param, Map<String, Object> testOutput, int suffix) throws CTSException {
Map<String, Object> vars = (Map<String, Object>) param;

List<Object> values = new ArrayList<>();
Expand All @@ -305,7 +300,7 @@ private void handleObject(
values.add(traverseParams(entry.getKey(), entry.getValue(), objSpec, paramName, suffix + 1));
}
// sometimes it's really just an object
if (testOutput.get("objectName").equals("Object")) {
if (testOutput.getOrDefault("objectName", "").equals("Object")) {
testOutput.put("isSimpleObject", true);
}

Expand Down Expand Up @@ -348,7 +343,7 @@ private void handleMap(String paramName, Object param, Map<String, Object> testO
}

private void handlePrimitive(Object param, Map<String, Object> testOutput, IJsonSchemaValidationProperties spec) throws CTSException {
if (isPrimitiveType(spec)) {
if (spec != null && isPrimitiveType(spec)) {
transferPrimitiveData(spec, testOutput);
} else {
inferDataType(param, null, testOutput);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,45 @@ public void run(Map<String, CodegenModel> models, Map<String, CodegenOperation>

List<Object> tests = new ArrayList<>();
for (int i = 0; i < op.length; i++) {
Map<String, Object> test = paramsType.buildJSONForRequest(operationId, op[i], entry.getValue(), i);
Map<String, Object> test = new HashMap<>();
Request req = op[i];
test.put("method", operationId);
test.put("testName", req.testName == null ? operationId : req.testName);
test.put("testIndex", i);
test.put("request", req.request);
test.put("hasParameters", req.parameters.size() != 0);

if (req.requestOptions != null) {
test.put("hasRequestOptions", true);
Map<String, Object> requestOptions = new HashMap<>();
if (req.requestOptions.queryParameters != null) {
Map<String, Object> queryParameters = new HashMap<>();
paramsType.enhanceParameters(req.requestOptions.queryParameters, queryParameters, null);
requestOptions.put("queryParameters", queryParameters);
}
if (req.requestOptions.headers != null) {
Map<String, Object> headers = new HashMap<>();
// convert the headers to an acceptable type
paramsType.enhanceParameters(new HashMap<String, Object>(req.requestOptions.headers), headers, null);
requestOptions.put("headers", headers);
}
test.put("requestOptions", requestOptions);
}

CodegenOperation ope = entry.getValue();
// special case if there is only bodyParam which is not an array
if (ope.allParams.size() == 1 && ope.bodyParams.size() == 1 && !ope.bodyParam.isArray) {
paramsType.enhanceRootParameters(req.parameters, ope.bodyParam.paramName, ope.bodyParam, test);
} else {
paramsType.enhanceParameters(req.parameters, test, ope);
}
tests.add(test);
}
Map<String, Object> testObj = new HashMap<>();
testObj.put("tests", tests);
testObj.put("operationId", operationId);
blocks.add(testObj);
}
bundle.put("blocks", blocks);
bundle.put("blocksRequests", blocks);
}
}
3 changes: 3 additions & 0 deletions templates/java/tests/requests/requestOptionsParams.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
{{#isBoolean}}
{{{value}}}
{{/isBoolean}}
{{#isArray}}
Arrays.asList({{#value}}{{> requests/requestOptionsParams}}{{^-last}},{{/-last}}{{/value}})
{{/isArray}}
21 changes: 7 additions & 14 deletions templates/java/tests/requests/requests.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class {{client}}Tests {
client = new {{client}}("appId", "apiKey", requester);
}

{{#blocks}}
{{#blocksRequests}}
{{#tests}}
@Test
@DisplayName("{{testName}}")
Expand All @@ -42,19 +42,12 @@ class {{client}}Tests {

{{#hasRequestOptions}}
RequestOptions requestOptions = new RequestOptions();
{{#requestOptionsWithDataType.queryParameters.value.0}}
{{#isArray}}
List<Object> requestOptionsQueryParameters = new ArrayList<>();
{{#value}}requestOptionsQueryParameters.add({{> requests/requestOptionsParams}});{{/value}}
requestOptions.addExtraQueryParameters("{{{key}}}", requestOptionsQueryParameters);
{{/isArray}}
{{^isArray}}
requestOptions.addExtraQueryParameters("{{{key}}}", {{{#value}}}{{> requests/requestOptionsParams}}{{{/value}}});
{{/isArray}}
{{/requestOptionsWithDataType.queryParameters.value.0}}
{{#requestOptionsWithDataType.headers}}
{{#requestOptions.queryParameters.parametersWithDataType}}
requestOptions.addExtraQueryParameters("{{{key}}}", {{> requests/requestOptionsParams}});
{{/requestOptions.queryParameters.parametersWithDataType}}
{{#requestOptions.headers.parametersWithDataType}}
requestOptions.addExtraHeader("{{{key}}}", "{{{value}}}");
{{/requestOptionsWithDataType.headers}}
{{/requestOptions.headers.parametersWithDataType}}
{{/hasRequestOptions}}

assertDoesNotThrow(() -> {
Expand Down Expand Up @@ -91,5 +84,5 @@ class {{client}}Tests {
{{/request.headers}}
}
{{/tests}}
{{/blocks}}
{{/blocksRequests}}
}
9 changes: 6 additions & 3 deletions templates/javascript/tests/requests/requests.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ const apiKey = process.env.ALGOLIA_SEARCH_KEY || 'test_api_key';

const client = {{client}}(appId, apiKey, {{#hasRegionalHost}}'{{{defaultRegion}}}', {{/hasRegionalHost}}{ requester: echoRequester() });

{{#blocks}}
{{#blocksRequests}}
describe('{{operationId}}', () => {
{{#tests}}
test('{{testName}}', async () => {
{{#hasRequestOptions}}const requestOptions: RequestOptions = {{{requestOptions}}}{{/hasRequestOptions}}
{{#hasRequestOptions}}const requestOptions: RequestOptions = {
{{#requestOptions.queryParameters.parameters}}queryParameters: {{{.}}},{{/requestOptions.queryParameters.parameters}}
{{#requestOptions.headers.parameters}}headers: {{{.}}}{{/requestOptions.headers.parameters}}
};{{/hasRequestOptions}}

const req = (await client.{{method}}({{#hasParameters}}{{{parameters}}}{{/hasParameters}}{{#hasRequestOptions}}, requestOptions{{/hasRequestOptions}})) as unknown as EchoResponse;

Expand All @@ -30,4 +33,4 @@ describe('{{operationId}}', () => {
{{/tests}}
})

{{/blocks}}
{{/blocksRequests}}
3 changes: 3 additions & 0 deletions templates/php/tests/requests/requestOptionsParams.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
{{#isBoolean}}
{{{value}}}
{{/isBoolean}}
{{#isArray}}
[{{#value}}{{> requests/requestOptionsParams}}{{^-last}},{{/-last}}{{/value}}]
{{/isArray}}
16 changes: 7 additions & 9 deletions templates/php/tests/requests/requests.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class {{clientPrefix}}Test extends TestCase implements HttpClientInterface
return new {{client}}($api, $config);
}

{{#blocks}}
{{#blocksRequests}}
{{#tests}}

/**
Expand All @@ -94,16 +94,14 @@ class {{clientPrefix}}Test extends TestCase implements HttpClientInterface
{{#hasRequestOptions}}
$requestOptions = [
'queryParameters' => [
{{#requestOptionsWithDataType.queryParameters.value.0}}
'{{{key}}}' =>
{{#isArray}}[{{#value}}{{> requests/requestOptionsParams}}{{^-last}},{{/-last}}{{/value}}]{{/isArray}}
{{^isArray}}{{{#value}}}{{> requests/requestOptionsParams}}{{{/value}}}{{/isArray}},
{{/requestOptionsWithDataType.queryParameters.value.0}}
{{#requestOptions.queryParameters.parametersWithDataType}}
'{{{key}}}' => {{> requests/requestOptionsParams}},
{{/requestOptions.queryParameters.parametersWithDataType}}
],
'headers' => [
{{#requestOptionsWithDataType.headers}}
{{#requestOptions.headers.parametersWithDataType}}
'{{{key}}}' => '{{{value}}}',
{{/requestOptionsWithDataType.headers}}
{{/requestOptions.headers.parametersWithDataType}}
]
];
{{/hasRequestOptions}}
Expand All @@ -129,5 +127,5 @@ class {{clientPrefix}}Test extends TestCase implements HttpClientInterface
]);
}
{{/tests}}
{{/blocks}}
{{/blocksRequests}}
}

0 comments on commit 9b55b7e

Please sign in to comment.