diff --git a/connectors/citrus-openapi/pom.xml b/connectors/citrus-openapi/pom.xml index bf85267d78..83d9212ec5 100644 --- a/connectors/citrus-openapi/pom.xml +++ b/connectors/citrus-openapi/pom.xml @@ -72,6 +72,12 @@ ${project.version} test + + org.assertj + assertj-core + ${assertj.version} + test + diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java index c240fe80c2..059127d516 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/OpenApiTestDataGenerator.java @@ -26,6 +26,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import static org.citrusframework.openapi.model.OasModelHelper.*; + /** * Generates proper payloads and validation expressions based on Open API specification rules. Creates outbound payloads * with generated random test data according to specification and creates inbound payloads with proper validation expressions to @@ -37,19 +39,23 @@ public class OpenApiTestDataGenerator { /** * Creates payload from schema for outbound message. + * * @param schema * @param definitions * @return */ - public static String createOutboundPayload(OasSchema schema, Map definitions, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); + public static String createOutboundPayload( + OasSchema schema, + Map definitions, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); return createOutboundPayload(resolved, definitions, specification); } StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema)) { + if (isObjectType(schema)) { payload.append("{"); if (schema.properties != null) { @@ -80,15 +86,72 @@ public static String createOutboundPayload(OasSchema schema, Map definitions, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); + return createInboundPayload(resolved, definitions, specification); + } + + StringBuilder payload = new StringBuilder(); + if (isObjectType(schema)) { + payload.append("{"); + + if (schema.properties != null) { + for (Map.Entry entry : schema.properties.entrySet()) { + if (specification.isValidateOptionalFields() || isRequired(schema, entry.getKey())) { + payload.append("\"") + .append(entry.getKey()) + .append("\": ") + .append(createValidationExpression(entry.getValue(), definitions, true, specification)) + .append(","); + } + } + } + + if (payload.toString().endsWith(",")) { + payload.replace(payload.length() - 1, payload.length(), ""); + } + + payload.append("}"); + } else if (OasModelHelper.isArrayType(schema)) { + payload.append("["); + payload.append(createValidationExpression((OasSchema) schema.items, definitions, true, specification)); + payload.append("]"); + } else { + payload.append(createValidationExpression(schema, definitions, false, specification)); + } + + return payload.toString(); + } + /** * Use test variable with given name if present or create value from schema with random values + * * @param schema * @param definitions * @param quotes * @return */ - public static String createRandomValueExpression(String name, OasSchema schema, Map definitions, - boolean quotes, OpenApiSpecification specification, TestContext context) { + @Deprecated(forRemoval = true) + public static String createRandomValueExpression( + String name, + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification, + TestContext context + ) { if (context.getVariables().containsKey(name)) { return CitrusSettings.VARIABLE_PREFIX + name + CitrusSettings.VARIABLE_SUFFIX; } @@ -98,20 +161,25 @@ public static String createRandomValueExpression(String name, OasSchema schema, /** * Create payload from schema with random values. + * * @param schema * @param definitions * @param quotes * @return */ - public static String createRandomValueExpression(OasSchema schema, Map definitions, boolean quotes, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); + public static String createRandomValueExpression( + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); return createRandomValueExpression(resolved, definitions, quotes, specification); } StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema) || OasModelHelper.isArrayType(schema)) { + if (isObjectType(schema) || OasModelHelper.isArrayType(schema)) { payload.append(createOutboundPayload(schema, definitions, specification)); } else if ("string".equals(schema.type)) { if (quotes) { @@ -147,57 +215,71 @@ public static String createRandomValueExpression(OasSchema schema, Map definitions, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); - return createInboundPayload(resolved, definitions, specification); + public static String createRandomValueExpression( + String name, + OasSchema schema, + TestContext context + ) { + if (context.getVariables().containsKey(name)) { + return CitrusSettings.VARIABLE_PREFIX + name + CitrusSettings.VARIABLE_SUFFIX; } - StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema)) { - payload.append("{"); + return createRandomValueExpression(schema); + } - if (schema.properties != null) { - for (Map.Entry entry : schema.properties.entrySet()) { - if (specification.isValidateOptionalFields() || isRequired(schema, entry.getKey())) { - payload.append("\"") - .append(entry.getKey()) - .append("\": ") - .append(createValidationExpression(entry.getValue(), definitions, true, specification)) - .append(","); - } + /** + * Create random value expression using functions according to schema type and format. + * + * @param schema + * @return + */ + public static String createRandomValueExpression( + OasSchema schema + ) { + switch (schema.type) { + case "string": + if (schema.format != null && schema.format.equals("date")) { + return "\"citrus:currentDate('yyyy-MM-dd')\""; + } else if (schema.format != null && schema.format.equals("date-time")) { + return "\"citrus:currentDate('yyyy-MM-dd'T'hh:mm:ss')\""; + } else if (StringUtils.hasText(schema.pattern)) { + return "\"citrus:randomValue(" + schema.pattern + ")\""; + } else if (!CollectionUtils.isEmpty(schema.enum_)) { + return "\"citrus:randomEnumValue(" + (String.join(",", schema.enum_)) + ")\""; + } else if (schema.format != null && schema.format.equals("uuid")) { + return "citrus:randomUUID()"; + } else { + return "citrus:randomString(10)"; } - } - - if (payload.toString().endsWith(",")) { - payload.replace(payload.length() - 1, payload.length(), ""); - } - - payload.append("}"); - } else if (OasModelHelper.isArrayType(schema)) { - payload.append("["); - payload.append(createValidationExpression((OasSchema) schema.items, definitions, true, specification)); - payload.append("]"); - } else { - payload.append(createValidationExpression(schema, definitions, false, specification)); + case "number": + case "integer": + return "citrus:randomNumber(8)"; + case "boolean": + return "citrus:randomEnumValue('true', 'false')"; + default: + return ""; } - - return payload.toString(); } /** * Checks if given field name is in list of required fields for this schema. + * * @param schema * @param field * @return */ - private static boolean isRequired(OasSchema schema, String field) { + private static boolean isRequired( + OasSchema schema, + String field + ) { if (schema.required == null) { return true; } @@ -207,6 +289,7 @@ private static boolean isRequired(OasSchema schema, String field) { /** * Use test variable with given name if present or create validation expression using functions according to schema type and format. + * * @param name * @param schema * @param definitions @@ -214,9 +297,14 @@ private static boolean isRequired(OasSchema schema, String field) { * @param context * @return */ - public static String createValidationExpression(String name, OasSchema schema, Map definitions, - boolean quotes, OpenApiSpecification specification, - TestContext context) { + public static String createValidationExpression( + String name, + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification, + TestContext context + ) { if (context.getVariables().containsKey(name)) { return CitrusSettings.VARIABLE_PREFIX + name + CitrusSettings.VARIABLE_SUFFIX; } @@ -226,20 +314,25 @@ public static String createValidationExpression(String name, OasSchema schema, M /** * Create validation expression using functions according to schema type and format. + * * @param schema * @param definitions * @param quotes * @return */ - public static String createValidationExpression(OasSchema schema, Map definitions, boolean quotes, - OpenApiSpecification specification) { - if (OasModelHelper.isReferenceType(schema)) { - OasSchema resolved = definitions.get(OasModelHelper.getReferenceName(schema.$ref)); + public static String createValidationExpression( + OasSchema schema, + Map definitions, + boolean quotes, + OpenApiSpecification specification + ) { + if (isReferenceType(schema)) { + OasSchema resolved = definitions.get(getReferenceName(schema.$ref)); return createValidationExpression(resolved, definitions, quotes, specification); } StringBuilder payload = new StringBuilder(); - if (OasModelHelper.isObjectType(schema)) { + if (isObjectType(schema)) { payload.append("{"); if (schema.properties != null) { @@ -276,10 +369,13 @@ public static String createValidationExpression(OasSchema schema, Map parameters = new HashMap<>(); + + public OpenApiOperationBuilder(String operationId) { + this.operationId = operationId; + } + + public static OpenApiOperationBuilder operation(String operationId) { + return new OpenApiOperationBuilder(operationId); + } + + public OpenApiOperationBuilder withParameter(String name, Object value) { + parameters.put(name, value); + return this; + } + + public OpenApiOperationBuilder withParameters(Map parameters) { + this.parameters.putAll(parameters); + return this; + } + } + + public OpenApiClientRequestActionBuilder send(OpenApiOperationBuilder openApiOperationBuilder) { + var builder = OpenApiClientRequestActionBuilder.create( + specification, + openApiOperationBuilder.operationId + ); if (httpClient != null) { builder.endpoint(httpClient); } else { @@ -73,11 +99,19 @@ public OpenApiClientRequestActionBuilder send(String operationId) { builder.name("openapi:send-request"); builder.withReferenceResolver(referenceResolver); + openApiOperationBuilder.parameters.forEach(builder::withParameter); this.delegate = builder; return builder; } + /** + * Sends Http requests as client. + */ + public OpenApiClientRequestActionBuilder send(String operationId) { + return send(OpenApiOperationBuilder.operation(operationId)); + } + /** * Receives Http response messages as client. * Uses default Http status 200 OK. diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java index d646202c4d..8d306a7761 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientRequestActionBuilder.java @@ -16,17 +16,7 @@ package org.citrusframework.openapi.actions; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; - -import io.apicurio.datamodels.openapi.models.OasDocument; -import io.apicurio.datamodels.openapi.models.OasOperation; -import io.apicurio.datamodels.openapi.models.OasParameter; -import io.apicurio.datamodels.openapi.models.OasPathItem; -import io.apicurio.datamodels.openapi.models.OasSchema; +import io.apicurio.datamodels.openapi.models.*; import org.citrusframework.CitrusSettings; import org.citrusframework.context.TestContext; import org.citrusframework.exceptions.CitrusRuntimeException; @@ -35,126 +25,163 @@ import org.citrusframework.http.message.HttpMessageBuilder; import org.citrusframework.message.Message; import org.citrusframework.openapi.OpenApiSpecification; -import org.citrusframework.openapi.OpenApiTestDataGenerator; import org.citrusframework.openapi.model.OasModelHelper; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import java.util.*; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static java.lang.Boolean.TRUE; +import static org.citrusframework.CitrusSettings.VARIABLE_PREFIX; +import static org.citrusframework.CitrusSettings.VARIABLE_SUFFIX; +import static org.citrusframework.openapi.OpenApiTestDataGenerator.createOutboundPayload; +import static org.citrusframework.openapi.OpenApiTestDataGenerator.createRandomValueExpression; +import static org.citrusframework.openapi.model.OasModelHelper.*; + /** * @author Christoph Deppisch * @since 4.1 */ public class OpenApiClientRequestActionBuilder extends HttpClientRequestActionBuilder { - /** - * Default constructor initializes http request message builder. - */ - public OpenApiClientRequestActionBuilder(OpenApiSpecification openApiSpec, String operationId) { - this(new HttpMessage(), openApiSpec, operationId); + private final OpenApiClientRequestMessageBuilder messageBuilder; + + protected OpenApiClientRequestActionBuilder(OpenApiClientRequestMessageBuilder messageBuilder) { + super(messageBuilder, messageBuilder.httpMessage); + this.messageBuilder = messageBuilder; + } + + public static OpenApiClientRequestActionBuilder create(OpenApiSpecification openApiSpec, String operationId) { + var messageBuilder = new OpenApiClientRequestMessageBuilder(new HttpMessage(), openApiSpec, operationId); + return new OpenApiClientRequestActionBuilder(messageBuilder); } - public OpenApiClientRequestActionBuilder(HttpMessage httpMessage, OpenApiSpecification openApiSpec, - String operationId) { - super(new OpenApiClientRequestMessageBuilder(httpMessage, openApiSpec, operationId), httpMessage); + + public OpenApiClientRequestActionBuilder withParameter(String name, Object number) { + messageBuilder.parameters.put(name, number); + return this; } - private static class OpenApiClientRequestMessageBuilder extends HttpMessageBuilder { + protected static class OpenApiClientRequestMessageBuilder extends HttpMessageBuilder { + + private final Map parameters = new HashMap<>(); private final OpenApiSpecification openApiSpec; private final String operationId; private final HttpMessage httpMessage; - public OpenApiClientRequestMessageBuilder(HttpMessage httpMessage, OpenApiSpecification openApiSpec, - String operationId) { + public OpenApiClientRequestMessageBuilder( + HttpMessage httpMessage, + OpenApiSpecification openApiSpec, + String operationId + ) { super(httpMessage); this.openApiSpec = openApiSpec; this.operationId = operationId; this.httpMessage = httpMessage; } - @Override - public Message build(TestContext context, String messageType) { - OasDocument oasDocument = openApiSpec.getOpenApiDoc(context); - OasOperation operation = null; - OasPathItem pathItem = null; - HttpMethod method = null; - - for (OasPathItem path : OasModelHelper.getPathItems(oasDocument.paths)) { - Optional> operationEntry = OasModelHelper.getOperationMap(path).entrySet().stream() - .filter(op -> operationId.equals(op.getValue().operationId)) - .findFirst(); - - if (operationEntry.isPresent()) { - method = HttpMethod.valueOf(operationEntry.get().getKey().toUpperCase(Locale.US)); - operation = operationEntry.get().getValue(); - pathItem = path; - break; + private record OasItem( + OasOperation operation, + OasPathItem pathItem, + HttpMethod method + ) { + public static OasItem create(String operationId, OasDocument oasDocument) { + OasItem item = null; + + for (OasPathItem path : OasModelHelper.getPathItems(oasDocument.paths)) { + Optional> operationEntry = OasModelHelper.getOperationMap(path).entrySet().stream() + .filter(op -> operationId.equals(op.getValue().operationId)) + .findFirst(); + + if (operationEntry.isPresent()) { + item = new OasItem( + operationEntry.get().getValue(), + path, + HttpMethod.valueOf(operationEntry.get().getKey().toUpperCase(Locale.US)) + ); + break; + } } - } - if (operation == null) { - throw new CitrusRuntimeException("Unable to locate operation with id '%s' in OpenAPI specification %s".formatted(operationId, openApiSpec.getSpecUrl())); + if (item == null) { + throw new CitrusRuntimeException( + "Unable to locate operation with id '%s' in OpenAPI specification %s" + .formatted(operationId, oasDocument.getAttributeNames()) + ); + } + return item; } + } - if (operation.parameters != null) { - List configuredHeaders = getHeaderBuilders() - .stream() - .flatMap(b -> b.builderHeaders(context).keySet().stream()) - .toList(); - operation.parameters.stream() - .filter(param -> "header".equals(param.in)) - .filter(param -> (param.required != null && param.required) || context.getVariables().containsKey(param.getName())) - .forEach(param -> { - if(httpMessage.getHeader(param.getName()) == null && !configuredHeaders.contains(param.getName())) { - httpMessage.setHeader(param.getName(), - OpenApiTestDataGenerator.createRandomValueExpression(param.getName(), (OasSchema) param.schema, - OasModelHelper.getSchemaDefinitions(oasDocument), false, openApiSpec, context)); - } - }); - - operation.parameters.stream() - .filter(param -> "query".equals(param.in)) - .filter(param -> (param.required != null && param.required) || context.getVariables().containsKey(param.getName())) - .forEach(param -> { - if(!httpMessage.getQueryParams().containsKey(param.getName())) { - httpMessage.queryParam(param.getName(), - OpenApiTestDataGenerator.createRandomValueExpression(param.getName(), (OasSchema) param.schema, context)); - } - }); - } + @Override + public Message build(TestContext context, String messageType) { + // TODO TAT-1291 - make parameter substitution more explicit + context.addVariables(parameters); + OasDocument oasDocument = openApiSpec.getOpenApiDoc(context); + var item = OasItem.create(operationId, oasDocument); + + getRequiredOrPresentParametersIn("header", item, context).forEach(param -> + httpMessage.setHeader( + param.getName(), + context.getVariables().containsKey(param.getName()) + ? CitrusSettings.VARIABLE_PREFIX + param.getName() + CitrusSettings.VARIABLE_SUFFIX + : + createRandomValueExpression( + (OasSchema) param.schema, + getSchemaDefinitions(oasDocument), + false, + openApiSpec + ) + ) + ); + + getRequiredOrPresentParametersIn("query", item, context) + .forEach(param -> httpMessage.queryParam( + param.getName(), + createRandomValueExpression(param.getName(), (OasSchema) param.schema, context) + )); + + getRequestBodySchema(oasDocument, item.operation) + .ifPresent(oasSchema -> httpMessage.setPayload( + createOutboundPayload(oasSchema, getSchemaDefinitions(oasDocument), openApiSpec) + )); + + getRequestContentType(item.operation).ifPresent(httpMessage::contentType); + httpMessage.path(getSubstitutedPath(context, item)); + httpMessage.method(item.method); - if(httpMessage.getPayload() == null || (httpMessage.getPayload() instanceof String p && p.isEmpty())) { - Optional body = OasModelHelper.getRequestBodySchema(oasDocument, operation); - body.ifPresent(oasSchema -> httpMessage.setPayload(OpenApiTestDataGenerator.createOutboundPayload(oasSchema, - OasModelHelper.getSchemaDefinitions(oasDocument), openApiSpec))); - } + return super.build(context, messageType); + } - String randomizedPath = pathItem.getPath(); - if (operation.parameters != null) { - List pathParams = operation.parameters.stream() - .filter(p -> "path".equals(p.in)).toList(); - - for (OasParameter parameter : pathParams) { - String parameterValue; - if (context.getVariables().containsKey(parameter.getName())) { - parameterValue = "\\" + CitrusSettings.VARIABLE_PREFIX + parameter.getName() + CitrusSettings.VARIABLE_SUFFIX; - } else { - parameterValue = OpenApiTestDataGenerator.createRandomValueExpression((OasSchema) parameter.schema); - } - randomizedPath = Pattern.compile("\\{" + parameter.getName() + "}") - .matcher(randomizedPath) - .replaceAll(parameterValue); - } - } + private static Stream getRequiredOrPresentParametersIn(String header, OasItem item, TestContext context) { + return item.operation.getParametersIn(header).stream() + .filter(onlyRequiredOrPresentParameters(context)); + } - OasModelHelper.getRequestContentType(operation) - .ifPresent(contentType -> httpMessage.setHeader(HttpHeaders.CONTENT_TYPE, contentType)); + private static Predicate onlyRequiredOrPresentParameters(TestContext context) { + return param -> TRUE.equals(param.required) || context.getVariables().containsKey(param.getName()); + } - httpMessage.path(randomizedPath); - httpMessage.method(method); + private static String getSubstitutedPath(TestContext context, OasItem item) { + String randomizedPath = item.pathItem.getPath(); + List pathParams = item.operation.getParametersIn("path"); - return super.build(context, messageType); + for (OasParameter parameter : pathParams) { + String parameterValue; + if (context.getVariables().containsKey(parameter.getName())) { + parameterValue = "\\" + VARIABLE_PREFIX + parameter.getName() + VARIABLE_SUFFIX; + } else { + parameterValue = createRandomValueExpression((OasSchema) parameter.schema); + } + randomizedPath = Pattern.compile("\\{" + parameter.getName() + "}") + .matcher(randomizedPath) + .replaceAll(parameterValue); + } + return randomizedPath; } } } diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java index 1e235868af..f876bedf84 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientResponseActionBuilder.java @@ -44,6 +44,8 @@ */ public class OpenApiClientResponseActionBuilder extends HttpClientResponseActionBuilder { + protected final OpenApiClientResponseMessageBuilder messageBuilder; + /** * Default constructor initializes http response message builder. */ @@ -53,10 +55,22 @@ public OpenApiClientResponseActionBuilder(OpenApiSpecification openApiSpec, Stri public OpenApiClientResponseActionBuilder(HttpMessage httpMessage, OpenApiSpecification openApiSpec, String operationId, String statusCode) { - super(new OpenApiClientResponseMessageBuilder(httpMessage, openApiSpec, operationId, statusCode), httpMessage); + this(new OpenApiClientResponseMessageBuilder(httpMessage, openApiSpec, operationId, statusCode)); + } + + protected OpenApiClientResponseActionBuilder(OpenApiClientResponseMessageBuilder messageBuilder) { + super(new OpenApiClientResponseMessageBuilder( + messageBuilder.httpMessage, + messageBuilder.openApiSpec, + messageBuilder.operationId, + messageBuilder.statusCode + ), + messageBuilder.httpMessage + ); + this.messageBuilder = messageBuilder; } - private static class OpenApiClientResponseMessageBuilder extends HttpMessageBuilder { + protected static class OpenApiClientResponseMessageBuilder extends HttpMessageBuilder { private final OpenApiSpecification openApiSpec; private final String operationId; diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java index ffa4e9dcdf..d111475007 100644 --- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java +++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java @@ -27,10 +27,13 @@ import org.citrusframework.testng.spring.TestNGCitrusSpringSupport; import org.citrusframework.util.SocketUtils; import org.springframework.http.HttpStatus; +import org.testng.annotations.Ignore; import org.testng.annotations.Test; import static org.citrusframework.http.actions.HttpActionBuilder.http; +import static org.citrusframework.message.MessageType.XML; import static org.citrusframework.openapi.actions.OpenApiActionBuilder.openapi; +import static org.citrusframework.openapi.actions.OpenApiClientActionBuilder.OpenApiOperationBuilder.operation; /** * @author Christoph Deppisch @@ -59,16 +62,111 @@ public class OpenApiClientIT extends TestNGCitrusSpringSupport { @CitrusTest public void getPetById() { variable("petId", "1001"); + variable("verbose", "true"); + variable("correlationIds", "1234abcd"); when(openapi(petstoreSpec) .client(httpClient) .send("getPetById") + .fork(true) + .message() + ); + + then(http().server(httpServer) + .receive() + .get("/pet/1001") + .queryParam("verbose", "true") + .message() + .header("correlationIds", "1234abcd") + .accept("@contains('application/json')@") + ); + + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.json")) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK)); + } + + @CitrusTest + public void getPetById_requiredParamsShouldBeGeneratedIfNotProvided() { + + when(openapi(petstoreSpec) + .client(httpClient) + .send("getPetById") + .fork(true) + ); + + then(http().server(httpServer) + .receive() + .get("@matches('/pet/\\d+')@") + .message() + ); + + variable("petId", "1001"); + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.json")) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK)); + } + + @CitrusTest + public void getPetById_setParameterExplicitly() { + when(openapi(petstoreSpec) + .client(httpClient) + .send(operation("getPetById") + .withParameter("petId", "1001") + .withParameter("correlationIds", "5599") + .withParameter("verbose", "true")) + .fork(true)); + + then(http().server(httpServer) + .receive() + .get("/pet/1001") + .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") + .accept("@contains('application/json')@")); + + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.json")) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK)); + } + + @CitrusTest + public void getPetById_generated() { + when(openapi(petstoreSpec) + .client(httpClient) + .send(operation("getPetById") + .withParameter("petId", "1001") + .withParameter("correlationIds", "5599") + .withParameter("verbose", "true")) .fork(true)); then(http().server(httpServer) .receive() - .get("/pet/${petId}") + .get("/pet/1001") .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") .accept("@contains('application/json')@")); then(http().server(httpServer) @@ -84,7 +182,7 @@ public void getPetById() { } @CitrusTest - public void getAddPet() { + public void postAddPet() { variable("petId", "1001"); when(openapi(petstoreSpec) @@ -97,18 +195,18 @@ public void getAddPet() { .post("/pet") .message() .body(""" - { - "id": "@isNumber()@", - "name": "@notEmpty()@", - "category": { - "id": "@isNumber()@", - "name": "@notEmpty()@" - }, - "photoUrls": "@notEmpty()@", - "tags": "@ignore@", - "status": "@matches(sold|pending|available)@" - } - """) + { + "id": "@isNumber()@", + "name": "@notEmpty()@", + "category": { + "id": "@isNumber()@", + "name": "@notEmpty()@" + }, + "photoUrls": "@notEmpty()@", + "tags": "@ignore@", + "status": "@matches(sold|pending|available)@" + } + """) .contentType("application/json;charset=UTF-8")); then(http().server(httpServer) diff --git a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java index a6c383d93a..6e0150af94 100644 --- a/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java +++ b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/yaml/OpenApiClientTest.java @@ -16,11 +16,6 @@ package org.citrusframework.openapi.yaml; -import java.io.IOException; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ArrayBlockingQueue; - import org.citrusframework.TestActor; import org.citrusframework.TestCase; import org.citrusframework.TestCaseMetaInfo; @@ -29,7 +24,6 @@ import org.citrusframework.endpoint.EndpointAdapter; import org.citrusframework.endpoint.direct.DirectEndpointAdapter; import org.citrusframework.endpoint.direct.DirectSyncEndpointConfiguration; -import org.citrusframework.endpoint.resolver.EndpointUriResolver; import org.citrusframework.http.client.HttpClient; import org.citrusframework.http.message.HttpMessage; import org.citrusframework.http.message.HttpMessageBuilder; @@ -38,7 +32,6 @@ import org.citrusframework.message.DefaultMessage; import org.citrusframework.message.DefaultMessageQueue; import org.citrusframework.message.Message; -import org.citrusframework.message.MessageHeaders; import org.citrusframework.message.MessageQueue; import org.citrusframework.spi.BindToRegistry; import org.citrusframework.util.SocketUtils; @@ -53,12 +46,23 @@ import org.mockito.Mockito; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.io.IOException; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.citrusframework.endpoint.resolver.EndpointUriResolver.ENDPOINT_URI_HEADER_NAME; +import static org.citrusframework.endpoint.resolver.EndpointUriResolver.REQUEST_PATH_HEADER_NAME; import static org.citrusframework.http.endpoint.builder.HttpEndpoints.http; +import static org.citrusframework.http.message.HttpMessageHeaders.*; +import static org.citrusframework.message.MessageHeaders.ID; +import static org.citrusframework.message.MessageHeaders.TIMESTAMP; +import static org.springframework.http.HttpMethod.GET; /** * @author Christoph Deppisch @@ -148,103 +152,109 @@ public void shouldLoadOpenApiClientActions() throws IOException { testLoader.load(); TestCase result = testLoader.getTestCase(); - Assert.assertEquals(result.getName(), "OpenApiClientTest"); - Assert.assertEquals(result.getMetaInfo().getAuthor(), "Christoph"); - Assert.assertEquals(result.getMetaInfo().getStatus(), TestCaseMetaInfo.Status.FINAL); - Assert.assertEquals(result.getActionCount(), 4L); - Assert.assertEquals(result.getTestAction(0).getClass(), SendMessageAction.class); - Assert.assertEquals(result.getTestAction(0).getName(), "openapi:send-request"); + assertThat(result.getName()).isEqualTo("OpenApiClientTest"); + assertThat(result.getMetaInfo().getAuthor()).isEqualTo("Christoph"); + assertThat(result.getMetaInfo().getStatus()).isEqualTo(TestCaseMetaInfo.Status.FINAL); + assertThat(result.getActions()).hasSize(4); + + assertThat(result.getTestAction(0).getClass()).isEqualTo(SendMessageAction.class); + assertThat(result.getTestAction(0).getName()).isEqualTo("openapi:send-request"); - Assert.assertEquals(result.getTestAction(1).getClass(), ReceiveMessageAction.class); - Assert.assertEquals(result.getTestAction(1).getName(), "openapi:receive-response"); + assertThat(result.getTestAction(1).getClass()).isEqualTo(ReceiveMessageAction.class); + assertThat(result.getTestAction(1).getName()).isEqualTo("openapi:receive-response"); int actionIndex = 0; - SendMessageAction sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++); - Assert.assertFalse(sendMessageAction.isForkMode()); - Assert.assertTrue(sendMessageAction.getMessageBuilder() instanceof HttpMessageBuilder); - HttpMessageBuilder httpMessageBuilder = ((HttpMessageBuilder)sendMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - Assert.assertEquals(httpMessageBuilder.buildMessagePayload(context, sendMessageAction.getMessageType()), ""); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().size(), 5L); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REQUEST_METHOD), HttpMethod.GET.name()); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(EndpointUriResolver.REQUEST_PATH_HEADER_NAME), "/pet/${petId}"); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REQUEST_URI), "/pet/${petId}"); - Assert.assertNull(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_QUERY_PARAMS)); - Assert.assertNull(httpMessageBuilder.getMessage().getHeaders().get(EndpointUriResolver.ENDPOINT_URI_HEADER_NAME)); - Assert.assertEquals(sendMessageAction.getEndpointUri(), "httpClient"); + var sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++); + assertThat(sendMessageAction.isForkMode()).isFalse(); + assertThat(sendMessageAction.getEndpointUri()).isEqualTo("httpClient"); + + String messageType = sendMessageAction.getMessageType(); + assertThat(sendMessageAction.getMessageBuilder()) + .isNotNull() + .isInstanceOf(HttpMessageBuilder.class) + .extracting(HttpMessageBuilder.class::cast) + .satisfies(httpMessageBuilder -> { + assertThat(httpMessageBuilder.buildMessagePayload(context, messageType)).isEqualTo(""); + assertThat(httpMessageBuilder.getMessage().getHeaders()) + .hasSize(5) + .hasEntrySatisfying(ID, e -> assertThat(e).isNotNull()) + .hasEntrySatisfying(TIMESTAMP, e -> assertThat(e).isNotNull()) + .doesNotContainKey(HTTP_QUERY_PARAMS) + .doesNotContainKey(ENDPOINT_URI_HEADER_NAME) + .hasEntrySatisfying(HTTP_REQUEST_METHOD, e -> assertThat(e).isEqualTo(GET.name())) + .hasEntrySatisfying(REQUEST_PATH_HEADER_NAME, e -> assertThat(e).isEqualTo("/pet/${petId}")) + .hasEntrySatisfying(HTTP_REQUEST_URI, e -> assertThat(e).isEqualTo("/pet/${petId}")); + }); Message controlMessage = new DefaultMessage(""); Message request = inboundQueue.receive(); headerValidator.validateMessage(request, controlMessage, context, new HeaderValidationContext()); validator.validateMessage(request, controlMessage, context, new DefaultValidationContext()); - ReceiveMessageAction receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex++); - Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3); - Assert.assertEquals(receiveMessageAction.getReceiveTimeout(), 0L); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(2) instanceof HeaderValidationContext); - - httpMessageBuilder = ((HttpMessageBuilder)receiveMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - - Assert.assertEquals(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType()), - "{\"id\": \"@isNumber()@\",\"category\": {\"id\": \"@isNumber()@\",\"name\": \"@notEmpty()@\"},\"name\": \"@notEmpty()@\",\"photoUrls\": \"@ignore@\",\"tags\": \"@ignore@\",\"status\": \"@matches(available|pending|sold)@\"}"); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().size(), 5L); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_STATUS_CODE), 200); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REASON_PHRASE), "OK"); - Assert.assertEquals(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_CONTENT_TYPE), "application/json"); - Assert.assertNull(receiveMessageAction.getEndpoint()); - Assert.assertEquals(receiveMessageAction.getEndpointUri(), "httpClient"); - Assert.assertEquals(receiveMessageAction.getMessageProcessors().size(), 0); - Assert.assertEquals(receiveMessageAction.getControlMessageProcessors().size(), 0); + var receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex++); + assertThat(receiveMessageAction.getValidationContexts()).hasSize(3); + assertThat(receiveMessageAction.getReceiveTimeout()).isEqualTo(0L); + assertThat(receiveMessageAction.getValidationContexts().get(0)).isInstanceOf(XmlMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(1)).isInstanceOf(JsonMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(2)).isInstanceOf(HeaderValidationContext.class); + + var httpMessageBuilder = ((HttpMessageBuilder) receiveMessageAction.getMessageBuilder()); + assertThat(httpMessageBuilder).isNotNull(); + + assertThat(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType())).isEqualTo("{\"id\": \"@isNumber()@\",\"category\": {\"id\": \"@isNumber()@\",\"name\": \"@notEmpty()@\"},\"name\": \"@notEmpty()@\",\"photoUrls\": \"@ignore@\",\"tags\": \"@ignore@\",\"status\": \"@matches(available|pending|sold)@\"}"); + assertThat(httpMessageBuilder.getMessage().getHeaders()).hasSize(5); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(ID)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(TIMESTAMP)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_STATUS_CODE)).isEqualTo(200); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_REASON_PHRASE)).isEqualTo("OK"); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(HttpMessageHeaders.HTTP_CONTENT_TYPE)).isEqualTo("application/json"); + assertThat(receiveMessageAction.getEndpoint()).isNull(); + assertThat(receiveMessageAction.getEndpointUri()).isEqualTo("httpClient"); + assertThat(receiveMessageAction.getMessageProcessors()).isEmpty(); + assertThat(receiveMessageAction.getControlMessageProcessors()).isEmpty(); sendMessageAction = (SendMessageAction) result.getTestAction(actionIndex++); - Assert.assertFalse(sendMessageAction.isForkMode()); - httpMessageBuilder = ((HttpMessageBuilder)sendMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - Assert.assertTrue(httpMessageBuilder.buildMessagePayload(context, sendMessageAction.getMessageType()).toString().startsWith("{\"id\": ")); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); + assertThat(sendMessageAction.isForkMode()).isFalse(); + httpMessageBuilder = ((HttpMessageBuilder) sendMessageAction.getMessageBuilder()); + assertThat(httpMessageBuilder).isNotNull(); + assertThat(httpMessageBuilder.buildMessagePayload(context, messageType).toString().startsWith("{\"id\": ")).isTrue(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(ID)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(TIMESTAMP)).isNotNull(); Map requestHeaders = httpMessageBuilder.buildMessageHeaders(context); - Assert.assertEquals(requestHeaders.size(), 4L); - Assert.assertEquals(requestHeaders.get(HttpMessageHeaders.HTTP_REQUEST_METHOD), HttpMethod.POST.name()); - Assert.assertEquals(requestHeaders.get(EndpointUriResolver.REQUEST_PATH_HEADER_NAME), "/pet"); - Assert.assertEquals(requestHeaders.get(HttpMessageHeaders.HTTP_REQUEST_URI), "/pet"); - Assert.assertEquals(requestHeaders.get(HttpMessageHeaders.HTTP_CONTENT_TYPE), "application/json"); - Assert.assertNull(sendMessageAction.getEndpoint()); - Assert.assertEquals(sendMessageAction.getEndpointUri(), "httpClient"); + assertThat(requestHeaders).hasSize(4); + assertThat(requestHeaders.get(HTTP_REQUEST_METHOD)).isEqualTo(HttpMethod.POST.name()); + assertThat(requestHeaders.get(REQUEST_PATH_HEADER_NAME)).isEqualTo("/pet"); + assertThat(requestHeaders.get(HTTP_REQUEST_URI)).isEqualTo("/pet"); + assertThat(requestHeaders.get(HttpMessageHeaders.HTTP_CONTENT_TYPE)).isEqualTo("application/json"); + assertThat(sendMessageAction.getEndpoint()).isNull(); + assertThat(sendMessageAction.getEndpointUri()).isEqualTo("httpClient"); receiveMessageAction = (ReceiveMessageAction) result.getTestAction(actionIndex); - Assert.assertEquals(receiveMessageAction.getValidationContexts().size(), 3); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(0) instanceof XmlMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(1) instanceof JsonMessageValidationContext); - Assert.assertTrue(receiveMessageAction.getValidationContexts().get(2) instanceof HeaderValidationContext); - - httpMessageBuilder = ((HttpMessageBuilder)receiveMessageAction.getMessageBuilder()); - Assert.assertNotNull(httpMessageBuilder); - Assert.assertEquals(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType()), ""); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.ID)); - Assert.assertNotNull(httpMessageBuilder.getMessage().getHeaders().get(MessageHeaders.TIMESTAMP)); + assertThat(receiveMessageAction.getValidationContexts()).hasSize(3); + assertThat(receiveMessageAction.getValidationContexts().get(0)).isInstanceOf(XmlMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(1)).isInstanceOf(JsonMessageValidationContext.class); + assertThat(receiveMessageAction.getValidationContexts().get(2)).isInstanceOf(HeaderValidationContext.class); + + httpMessageBuilder = ((HttpMessageBuilder) receiveMessageAction.getMessageBuilder()); + assertThat(httpMessageBuilder).isNotNull(); + assertThat(httpMessageBuilder.buildMessagePayload(context, receiveMessageAction.getMessageType())).isEqualTo(""); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(ID)).isNotNull(); + assertThat(httpMessageBuilder.getMessage().getHeaders().get(TIMESTAMP)).isNotNull(); Map responseHeaders = httpMessageBuilder.buildMessageHeaders(context); - Assert.assertEquals(responseHeaders.size(), 2L); - Assert.assertEquals(responseHeaders.get(HttpMessageHeaders.HTTP_STATUS_CODE), 201); - Assert.assertEquals(responseHeaders.get(HttpMessageHeaders.HTTP_REASON_PHRASE), "CREATED"); - Assert.assertNull(receiveMessageAction.getEndpoint()); - Assert.assertEquals(receiveMessageAction.getEndpointUri(), "httpClient"); + assertThat(responseHeaders).hasSize(2); + assertThat(responseHeaders.get(HttpMessageHeaders.HTTP_STATUS_CODE)).isEqualTo(201); + assertThat(responseHeaders.get(HttpMessageHeaders.HTTP_REASON_PHRASE)).isEqualTo("CREATED"); + assertThat(receiveMessageAction.getEndpoint()).isNull(); + assertThat(receiveMessageAction.getEndpointUri()).isEqualTo("httpClient"); - Assert.assertEquals(receiveMessageAction.getVariableExtractors().size(), 0L); + assertThat(receiveMessageAction.getVariableExtractors()).isEmpty(); } @Test public void shouldLookupTestActionBuilder() { - Assert.assertTrue(YamlTestActionBuilder.lookup("openapi").isPresent()); - Assert.assertEquals(YamlTestActionBuilder.lookup("openapi").get().getClass(), OpenApi.class); + assertThat(YamlTestActionBuilder.lookup("openapi").isPresent()).isTrue(); + assertThat(YamlTestActionBuilder.lookup("openapi").get()).isOfAnyClassIn(OpenApi.class); } } diff --git a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.json b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.json index 0d4e504a8f..e5fa21a119 100644 --- a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.json +++ b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.json @@ -1,14 +1,14 @@ { - "id": ${petId}, + "id": "${petId}", "name": "citrus:randomEnumValue('hasso','cutie','fluffy')", "category": { - "id": ${petId}, + "id": "${petId}", "name": "citrus:randomEnumValue('dog', 'cat', 'fish')" }, "photoUrls": [ "http://localhost:8080/photos/${petId}" ], "tags": [ { - "id": ${petId}, + "id": "${petId}", "name": "generated" } ], diff --git a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.xml b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.xml new file mode 100644 index 0000000000..a040089da1 --- /dev/null +++ b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.xml @@ -0,0 +1,15 @@ + + + ${petId} + citrus:randomEnumValue('hasso','cutie','fluffy') + + ${petId} + citrus:randomEnumValue('dog', 'cat', 'fish') + + http://localhost:8080/photos/${petId} + + ${petId} + generated + + citrus:randomEnumValue('available', 'pending', 'sold') + diff --git a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json index 618854948f..a3b60caff8 100644 --- a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json +++ b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.json @@ -111,6 +111,15 @@ }, "in": "query", "required": false + }, + { + "name": "correlationIds", + "description": "Output details", + "schema": { + "type": "string" + }, + "in": "header", + "required": false } ], "responses": { diff --git a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml index bf3bde73d9..5d3e874393 100644 --- a/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml +++ b/connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/petstore-v3.yaml @@ -76,6 +76,13 @@ paths: type: boolean in: query required: false + - + name: correlationIds + description: ID to trace a request + schema: + type: string + in: header + required: false responses: '200': content: diff --git a/pom.xml b/pom.xml index cbf7c9a9be..c368aabc72 100644 --- a/pom.xml +++ b/pom.xml @@ -255,6 +255,7 @@ 2.0.11 1.1.10.5 2.2 + 3.2.5 6.1.8 3.3.1 4.0.11 @@ -273,8 +274,8 @@ 1.4.20 3.9.2 - false - false + ${skipTests} + ${skipTests} false diff --git a/test-api-generator/citrus-test-api-generator-core/pom.xml b/test-api-generator/citrus-test-api-generator-core/pom.xml index 5e1af11aa2..1e4e83b249 100644 --- a/test-api-generator/citrus-test-api-generator-core/pom.xml +++ b/test-api-generator/citrus-test-api-generator-core/pom.xml @@ -22,6 +22,7 @@ + org.citrusframework citrus-api @@ -44,12 +45,6 @@ ${project.version} - - org.assertj - assertj-core - ${assertj.version} - test - com.fasterxml.jackson.dataformat jackson-dataformat-yaml @@ -71,6 +66,12 @@ ${project.version} test + + org.springframework.boot + spring-boot-test + ${spring.boot.version} + test + org.citrusframework citrus-validation-json @@ -78,9 +79,20 @@ test - org.springframework.boot - spring-boot-test - ${spring.boot.version} + org.junit.jupiter + junit-jupiter + test + + + org.citrusframework + citrus-openapi + ${project.version} + test + + + org.assertj + assertj-core + ${assertj.version} test @@ -210,7 +222,7 @@ generate - ${project.basedir}/src/test/resources/apis/petstore.yaml + src/test/resources/apis/petstore.yaml org.citrusframework.openapi.generator.rest.petstore org.citrusframework.openapi.generator.rest.petstore.request @@ -227,7 +239,7 @@ generate - ${project.basedir}/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml + src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml SOAP org.citrusframework.openapi.generator.soap.bookservice @@ -248,7 +260,7 @@ generate - ${project.basedir}/src/test/resources/apis/multiparttest-rest-resource.yaml + src/test/resources/apis/multiparttest-rest-resource.yaml org.citrusframework.openapi.generator.rest.multiparttest org.citrusframework.openapi.generator.rest.multiparttest.request diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/JavaCitrusCodegen.java b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/JavaCitrusCodegen.java index c624c885f6..b99b9273cb 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/JavaCitrusCodegen.java +++ b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/JavaCitrusCodegen.java @@ -291,4 +291,5 @@ private void addDefaultSupportingFiles(final String citrusFolder, final String e supportingFiles.add(new SupportingFile("namespace_handler.mustache", extensionFolder, apiPrefix + "NamespaceHandler.java")); supportingFiles.add(new SupportingFile("api-model.mustache", resourceFolder, apiPrefix.toLowerCase() + "-api-model.csv")); } + } diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformer.java b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformer.java index 8243be5aa8..fef2086994 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformer.java +++ b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformer.java @@ -86,6 +86,9 @@ public String transformToOpenApi() throws WsdlToOpenApiTransformationException { /** * Performs the actual transformation from bindings into OpenApi operations. + * + * @param bindings + * @return */ private OpenAPI transformToOpenApi(Map bindings) { OpenAPI openAPI = new OpenAPI(); diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api.mustache index a023345fb7..7eb1ab4e35 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api.mustache @@ -2,258 +2,109 @@ package {{package}}; -import org.citrusframework.testapi.GeneratedApi; -import org.citrusframework.testapi.GeneratedApiRequest; -import jakarta.servlet.http.Cookie; -import org.apache.commons.lang3.StringUtils; -import org.citrusframework.context.TestContext; -import org.citrusframework.exceptions.CitrusRuntimeException; -import org.citrusframework.spi.Resources; -import org.citrusframework.http.actions.HttpActionBuilder; -import org.citrusframework.http.actions.HttpClientRequestActionBuilder; -import org.citrusframework.http.actions.HttpClientRequestActionBuilder.HttpMessageBuilderSupport; -import org.citrusframework.util.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.ClassPathResource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; +import org.citrusframework.endpoint.Endpoint; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.openapi.OpenApiSpecification; +import org.citrusframework.openapi.actions.OpenApiClientActionBuilder; +import org.citrusframework.openapi.actions.OpenApiClientActionBuilder.OpenApiOperationBuilder; +import org.citrusframework.openapi.actions.OpenApiClientRequestActionBuilder; +import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder; -import {{invokerPackage}}.citrus.{{prefix}}AbstractTestRequest; +import java.util.function.UnaryOperator; -import java.io.IOException; -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import static org.citrusframework.spi.Resources.create; -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} -public class {{classname}} implements GeneratedApi -{ +public class {{classname}} { + private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( + create("{{inputSpec}}") + ); - public static final {{classname}} INSTANCE = new {{classname}}(); - - public String getApiTitle() { - return "{{appName}}"; + public static {{classname}} openapiPetstore(HttpClient httpClient) { + return new {{classname}}(httpClient); } - public String getApiVersion() { - return "{{appVersion}}"; + private final HttpClient httpClient; + + private {{classname}}(HttpClient httpClient) { + this.httpClient = httpClient; } - public String getApiPrefix() { - return "{{prefix}}"; + {{#operations}} + {{#operation}} + public PetstoreAction<{{operationIdCamelCase}}Request> {{#lambda.camelcase}}{{operationIdCamelCase}}{{/lambda.camelcase}}() { + return petstoreAction(new {{operationIdCamelCase}}Request()); } + {{/operation}} + {{/operations}} - public Map getApiInfoExtensions() { - Map infoExtensionMap = new HashMap<>(); - {{#infoExtensions}} - {{#entrySet}} - infoExtensionMap.put("{{key}}", "{{value}}"); - {{/entrySet}} - {{/infoExtensions}} - return infoExtensionMap; + private PetstoreAction petstoreAction(B requestBuilder) { + return new PetstoreAction<>(httpClient, petstoreSpec, requestBuilder); } {{#operations}} {{#operation}} - /** {{operationId}} ({{httpMethod}} {{httpPathPrefix}}{{{path}}}) - {{summary}} - {{description}} - **/ - public static class {{operationIdCamelCase}}Request extends {{prefix}}AbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "{{httpPathPrefix}}{{{path}}}"; - private final Logger coverageLogger = LoggerFactory.getLogger({{operationIdCamelCase}}Request.class); - - {{#queryParams}} - private String {{paramName}}; - - {{/queryParams}} - {{#pathParams}} - private String {{paramName}}; - - {{/pathParams}} - {{#isMultipart}} - {{#formParams}} - private String {{paramName}}; - - {{/formParams}} - {{/isMultipart}} - {{#authMethods}}{{#isBasic}} - @Value("${" + "{{apiEndpoint}}.basic.username:#{null}}") - private String basicUsername; - @Value("${" + "{{apiEndpoint}}.basic.password:#{null}}") - private String basicPassword; - - {{/isBasic}} - {{/authMethods}} - - public {{operationIdCamelCase}}Request() { - // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml - setName("{{prefix}}".toLowerCase() + ":{{operationId}}RequestType"); - } - - public String getOperationName() { + /** + * {{operationId}} ({{httpMethod}} {{httpPathPrefix}}{{{path}}}){{#summary}} + * {{summary}}{{/summary}}{{#description}} + * {{description}}{{/description}} + **/ + public static class {{operationIdCamelCase}}Request extends OperationRequestBuilder { + @Override + public String getOperationId() { return "{{operationId}}"; } - public String getMethod() { - return "{{httpMethod}}"; + {{#pathParams}} + public {{operationIdCamelCase}}Request with{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}({{dataType}} {{paramName}}) { + openApiOperation.withParameter("{{paramName}}", {{paramName}}); + return this; } + {{/pathParams}} - public String getPath() { - return "{{path}}"; + {{#queryParams}} + public {{operationIdCamelCase}}Request with{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}({{dataType}} {{paramName}}) { + openApiOperation.withParameter("{{paramName}}", {{paramName}}); + return this; } + {{/queryParams}} - /** - * This method sends the HTTP-Request - */ - public void sendRequest(TestContext context) { - HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() - .{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}(replacePathParams(ENDPOINT)); - - HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); - messageBuilderSupport.accept(responseAcceptType); - - if (cookies != null) { - cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); - } - - if (headers != null) { - headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); - headers.forEach(messageBuilderSupport::header); - } - - String bodyLog = ""; - {{#isMultipart}} - MultiValueMap multiValues = new LinkedMultiValueMap<>(); - {{#formParams}} - {{#required}} - if(StringUtils.isBlank({{paramName}})) { - throw new CitrusRuntimeException(String.format("Required attribute '%s' is not specified", "{{paramName}}")); - } - {{/required}} - {{#isBinary}} - if (StringUtils.isNotBlank({{paramName}})) { - multiValues.add("{{paramName}}", new ClassPathResource({{paramName}})); - bodyLog += {{paramName}}.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; - } - {{/isBinary}} - {{^isBinary}} - if (StringUtils.isNotBlank({{paramName}})) { - // first try to load from resource - ClassPathResource resource = null; - try { - resource = new ClassPathResource({{paramName}}); - } - catch(Exception ignore) { - // Use plain text instead of resource - } - - if(resource != null && resource.exists()){ - multiValues.add("{{paramName}}", resource); - } else { - multiValues.add("{{paramName}}", {{paramName}}); - } - bodyLog += {{paramName}}.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; - } - {{/isBinary}} - {{/formParams}} - - bodyLog += "\";\"" + MediaType.MULTIPART_FORM_DATA_VALUE + "\""; - messageBuilderSupport.contentType(MediaType.MULTIPART_FORM_DATA_VALUE) - .body(multiValues); - - {{/isMultipart}} - {{^isMultipart}} - String payload = null; - String payloadType = null; - if (StringUtils.isNotBlank(this.bodyFile)) { - try { - payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); - } catch (IOException e) { - throw new CitrusRuntimeException("Failed to read payload resource", e); - } - payloadType = this.bodyContentType; - } else if (StringUtils.isNotBlank(this.bodyLiteral)) { - payload = this.bodyLiteral; - payloadType = this.bodyLiteralContentType; - } - String body = ""; - String bodyType = ""; - if(payload != null && payloadType != null) { - messageBuilderSupport.body(payload).contentType(payloadType); - body = context.replaceDynamicContentInString(payload); - bodyType = context.replaceDynamicContentInString(payloadType); - } - - bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; - {{/isMultipart}} - - Map queryParams = new HashMap<>(); - {{#allParams}}{{#isQueryParam}} - - if (StringUtils.isNotBlank(this.{{paramName}})) { - queryParams.put("{{baseName}}", context.replaceDynamicContentInString(this.{{paramName}})); - httpClientRequestActionBuilder.queryParam("{{baseName}}", this.{{paramName}}); - } - {{/isQueryParam}}{{/allParams}} - String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); - {{#authMethods}}{{#isBasic}} - - if(basicUsername != null && basicPassword != null){ - messageBuilderSupport.header("Authorization", "Basic " + Base64.getEncoder().encodeToString((context.replaceDynamicContentInString(basicUsername)+":"+context.replaceDynamicContentInString(basicPassword)).getBytes())); - } - {{/isBasic}}{{/authMethods}} - httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); - httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + {{#headerParams}} + public {{operationIdCamelCase}}Request with{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}({{dataType}} {{paramName}}) { + openApiOperation.withParameter("{{paramName}}", {{paramName}}); + return this; + } + {{/headerParams}} + } - httpClientRequestActionBuilder.build().execute(context); + {{/operation}} + {{/operations}} + public static abstract class OperationRequestBuilder { + protected final OpenApiOperationBuilder openApiOperation = OpenApiOperationBuilder.operation(getOperationId()); - coverageLogger.trace(coverageMarker, "{{operationId}};{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}};\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - {{#queryParams}} + public abstract String getOperationId(); - public void set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(String {{paramName}}) { - this.{{paramName}} = {{paramName}}; + public OpenApiOperationBuilder build() { + return openApiOperation; } - {{/queryParams}} - {{#pathParams}} + } - public void set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(String {{paramName}}) { - this.{{paramName}} = {{paramName}}; - } - {{/pathParams}} - {{#isMultipart}} - {{#formParams}} + public static class PetstoreAction extends OpenApiClientActionBuilder { + private final T operation; - public void set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(String {{paramName}}) { - this.{{paramName}} = {{paramName}}; + private PetstoreAction(Endpoint httpClient, OpenApiSpecification specification, T operation) { + super(httpClient, specification); + this.operation = operation; } - {{/formParams}} - {{/isMultipart}} - {{#authMethods}}{{#isBasic}} - public void setBasicUsername(String basicUsername) { - this.basicUsername = basicUsername; + public OpenApiClientRequestActionBuilder send(UnaryOperator builderProvider) { + var builder = builderProvider.apply(operation); + var send = send(builder.build()); + send.fork(true); + return send; } - public void setBasicPassword(String basicPassword) { - this.basicPassword = basicPassword; - } - {{/isBasic}}{{/authMethods}} - private String replacePathParams(String endpoint) { - {{#pathParams}}endpoint = endpoint.replace("{" + "{{baseName}}" + "}", {{paramName}});{{/pathParams}} - return endpoint; + public OpenApiClientResponseActionBuilder receive() { + return receive(operation.getOperationId(), "200"); } } - {{/operation}} - {{/operations}} } diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_soap.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_soap.mustache index ba2f8d48a6..6db53348df 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_soap.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_soap.mustache @@ -1,7 +1,28 @@ -{{>licenseInfo}} +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ package {{package}}; +import jakarta.annotation.Generated; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -24,7 +45,7 @@ import org.springframework.util.CollectionUtils; import {{invokerPackage}}.citrus.{{prefix}}AbstractTestRequest; -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") public class {{classname}} implements GeneratedApi { public static final {{classname}} INSTANCE = new {{classname}}(); diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_configuration.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_configuration.mustache index 7bf35af55f..a895518c45 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_configuration.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_configuration.mustache @@ -1,4 +1,24 @@ -{{>licenseInfo}} +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ package {{invokerPackage}}.spring; @@ -9,12 +29,13 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT import {{package}}.{{classname}}; {{/apis}} {{/apiInfo}} +import javax.annotation.processing.Generated; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; @Configuration -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") public class {{prefix}}BeanConfiguration { {{#apiInfo}} {{#apis}} diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_definition_parser.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_definition_parser.mustache index 01e502f284..3707465ce5 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_definition_parser.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_definition_parser.mustache @@ -1,26 +1,52 @@ -{{>licenseInfo}} +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ package {{invokerPackage}}.citrus; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang3.StringUtils; +import javax.annotation.processing.Generated; + +import org.citrusframework.exceptions.CitrusRuntimeException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.core.Conventions; import org.springframework.util.Assert; +import org.apache.commons.lang3.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") public class {{prefix}}BeanDefinitionParser implements BeanDefinitionParser { private static final String COOKIE = "cookie"; diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model.mustache new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model.mustache @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model_doc.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model_doc.mustache new file mode 100644 index 0000000000..f8737ed4d9 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model_doc.mustache @@ -0,0 +1 @@ +# not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/namespace_handler.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/namespace_handler.mustache index 8ca4a446da..5344d50a78 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/namespace_handler.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/namespace_handler.mustache @@ -1,4 +1,24 @@ -{{>licenseInfo}} +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ package {{invokerPackage}}.citrus.extension; @@ -9,9 +29,12 @@ import {{package}}.{{classname}}; {{/apiInfo}} import {{invokerPackage}}.citrus.{{prefix}}BeanDefinitionParser; +import javax.annotation.processing.Generated; + import org.springframework.beans.factory.xml.NamespaceHandlerSupport; -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") public class {{prefix}}NamespaceHandler extends NamespaceHandlerSupport { @Override diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base.mustache index be87ee214a..fbf2003a58 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base.mustache @@ -1,9 +1,31 @@ -{{>licenseInfo}} +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ package {{invokerPackage}}.citrus; + import static org.springframework.util.CollectionUtils.isEmpty; +import jakarta.annotation.Generated; import jakarta.annotation.Nullable; import java.util.List; import java.util.Map; @@ -17,10 +39,10 @@ import org.citrusframework.http.actions.HttpClientRequestActionBuilder; import org.citrusframework.http.actions.HttpClientResponseActionBuilder; import org.citrusframework.http.actions.HttpClientResponseActionBuilder.HttpMessageBuilderSupport; import org.citrusframework.http.client.HttpClient; -import org.citrusframework.spi.Resources; import org.citrusframework.message.Message; import org.citrusframework.testapi.ApiActionBuilderCustomizerService; import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.spi.Resources; import org.citrusframework.validation.DelegatingPayloadVariableExtractor; import org.citrusframework.validation.PathExpressionValidationContext; import org.citrusframework.validation.json.JsonMessageValidationContext; @@ -30,7 +52,7 @@ import org.slf4j.MarkerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") public abstract class {{prefix}}AbstractTestRequest extends AbstractTestAction { protected final Marker coverageMarker = MarkerFactory.getMarker("{{#lambda.uppercase}}{{prefix}}{{/lambda.uppercase}}-API-COVERAGE"); diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base_soap.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base_soap.mustache index 04f3c5568c..bd49969597 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base_soap.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base_soap.mustache @@ -1,7 +1,28 @@ -{{>licenseInfo}} +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ package {{invokerPackage}}.citrus; +import jakarta.annotation.Generated; import java.util.List; import java.util.ServiceLoader; import org.citrusframework.actions.AbstractTestAction; @@ -27,7 +48,7 @@ import org.springframework.util.CollectionUtils; import javax.sql.DataSource; import java.util.Map; -{{>additionalModelTypeAnnotations}}{{>generatedAnnotation}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{>xmlAnnotation}} +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") public abstract class {{prefix}}AbstractTestRequest extends AbstractTestAction { protected final Marker coverageMarker = MarkerFactory.getMarker("{{#lambda.uppercase}}{{prefix}}{{/lambda.uppercase}}-API-COVERAGE"); diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GeneratedApiIT.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GeneratedApiIT.java index 4ad6df823a..0541ca9185 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GeneratedApiIT.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GeneratedApiIT.java @@ -42,7 +42,7 @@ import org.citrusframework.messaging.Producer; import org.citrusframework.messaging.SelectiveConsumer; import org.citrusframework.openapi.generator.rest.multiparttest.request.MultiparttestControllerApi.PostFileRequest; -import org.citrusframework.openapi.generator.rest.petstore.request.PetApi.AddPetRequest; +//import org.citrusframework.openapi.generator.rest.petstore.request.PetApi.AddPetRequest; import org.citrusframework.openapi.generator.rest.petstore.request.PetApi.GetPetByIdRequest; import org.citrusframework.spi.Resources; import org.citrusframework.testapi.ApiActionBuilderCustomizerService; @@ -72,512 +72,505 @@ /** * This test tests the generated API */ -@Isolated -@DirtiesContext -@ExtendWith({CitrusSpringExtension.class}) -@SpringBootTest(classes = {CitrusSpringConfig.class, GeneratedApiIT.Config.class}) -@TestPropertySource( - properties = { - "applicationServiceClient.basic.username=Max Mustermann", - "applicationServiceClient.basic.password=Top secret" - } -) +//@Isolated +//@DirtiesContext +//@ExtendWith({CitrusSpringExtension.class}) +//@SpringBootTest(classes = {CitrusSpringConfig.class, GeneratedApiIT.Config.class}) +//@TestPropertySource( +// properties = {"applicationServiceClient.basic.username=Max Mustermann", +// "applicationServiceClient.basic.password=Top secret"} +//) class GeneratedApiIT { - @Autowired - private ApplicationContext applicationContext; - - @Autowired - private HttpClient httpClientMock; - - @Mock - private Producer producerMock; - - @Mock - private SelectiveConsumer consumerMock; - - private TestContext testContext; - - @BeforeEach - void beforeEach() { - testContext = applicationContext.getBean(TestContext.class); - } - - @Test - void testValidationFailure() { - mockProducerAndConsumer(createReceiveMessage("{\"some\": \"payload\"}")); - assertThatThrownBy( - () -> executeTest("getPetByIdRequestTest", testContext)).hasCauseExactlyInstanceOf( - ValidationException.class); - } - - @Nested - class WithValidationMatcher { - - @BeforeEach - void beforeEach() { - mockProducerAndConsumer(createReceiveMessage("")); - } - - @Test - void testSendWithBody() { - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - try { - assertThat(httpMessage.getPayload()) - .isEqualTo( - readToString(Resources.create( - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/addPetMessage.json"), - StandardCharsets.UTF_8) - ); - } catch (IOException e) { - throw new CitrusRuntimeException("Unable to parse file!", e); - } - return true; - }; - - sendAndValidateMessage("sendWithBodyTest", messageMatcher); - } - - @Test - void testSendWithBodyLiteralWithVariable() { - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - assertThat(((String) httpMessage.getPayload()).trim()).isEqualTo("{\"id\": 15}"); - return true; - }; - sendAndValidateMessage("sendWithBodyLiteralWithVariableTest", messageMatcher); - } - - @Test - void testXCitrusApiHeaders() { - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - assertThat(httpMessage.getHeader("x-citrus-api-name")).isEqualTo("petstore"); - assertThat(httpMessage.getHeader("x-citrus-app")).isEqualTo("PETS"); - assertThat(httpMessage.getHeader("x-citrus-api-version")).isEqualTo("1.0.0"); - return true; - }; - - sendAndValidateMessage("sendWithBodyLiteralTest", messageMatcher); - } - - @Test - void testSendWithExtraHeaders() { - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - assertThat(httpMessage.getHeader("h1")).isEqualTo("v1"); - assertThat(httpMessage.getHeader("h2")).isEqualTo("v2"); - return true; - }; - - sendAndValidateMessage("sendWithExtraHeaderTest", messageMatcher); - } - - @Test - void testSendWithBodyLiteral() { - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - assertThat(((String) httpMessage.getPayload()).trim()).isEqualTo("{\"id\": 13}"); - return true; - }; - - sendAndValidateMessage("sendWithBodyLiteralTest", messageMatcher); - } - - private void sendAndValidateMessage(String testName, - ArgumentMatcher messageMatcher) { - GeneratedApiIT.this.sendAndValidateMessage(testName, messageMatcher, - AddPetRequest.class); - } - - } - - @Nested - class WithMultipartMessage { - - @Test - void testSendMultipartFile() { - mockProducerAndConsumer(createReceiveMessage("")); - - ArgumentMatcher messageMatcher = message -> { - assertThat(message.getPayload()).isInstanceOf(MultiValueMap.class); - MultiValueMap multiValueMap = (MultiValueMap) message.getPayload(); - List multipartFile = multiValueMap.get("multipartFile"); - try { - assertThat(((Resource) multipartFile.get(0)).getURL().toString()) - .endsWith( - "test-classes/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"); - } catch (IOException e) { - throw new CitrusRuntimeException("Unable to parse file!", e); - } - - return true; - }; - - sendAndValidateMessage("postFileTest", messageMatcher, PostFileRequest.class); - } - - @Test - void testSendMultipartWithFileAttribute() { - Message payload = createReceiveMessage("{\"id\": 1}"); - mockProducerAndConsumer(payload); - - executeTest("multipartWithFileAttributesTest", testContext); - ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); - verify(producerMock).send(messageArgumentCaptor.capture(), eq(testContext)); - Object producedMessagePayload = messageArgumentCaptor.getValue().getPayload(); - assertThat(producedMessagePayload).isInstanceOf(MultiValueMap.class); - - Object templateValue = ((MultiValueMap) producedMessagePayload).get("template"); - assertThat(templateValue) - .asInstanceOf(InstanceOfAssertFactories.LIST) - .element(0) - .hasFieldOrPropertyWithValue("path", - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/MultipartTemplate.xml"); - - Object additionalDataValue = ((MultiValueMap) producedMessagePayload).get( - "additionalData"); - assertThat(additionalDataValue) - .asInstanceOf(InstanceOfAssertFactories.LIST) - .element(0) - .hasFieldOrPropertyWithValue("path", - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/AdditionalData.json"); - - Object schemaValue = ((MultiValueMap) producedMessagePayload).get("_schema"); - assertThat(schemaValue) - .asInstanceOf(InstanceOfAssertFactories.LIST) - .element(0) - .hasFieldOrPropertyWithValue("path", - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/Schema.json"); - } - - @Test - void testSendMultipartWithPlainText() { - mockProducerAndConsumer(createReceiveMessage("{\"id\": 1}")); - executeTest("multipartWithPlainTextTest", testContext); - ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); - verify(producerMock).send(messageArgumentCaptor.capture(), eq(testContext)); - String producedMessagePayload = normalizeWhitespace( - messageArgumentCaptor.getValue().getPayload().toString(), - true, - true - ); - - String expectedPayload = - "{template=[ ], additionalData=[ {\"data1\":\"value1\"} ], _schema=[ {\"schema\":\"mySchema\"} ]}"; - assertThat(producedMessagePayload).isEqualTo(expectedPayload); - } - - @Test - void testSendMultipartWithMultipleDatatypes() { - Message receiveMessage = createReceiveMessage("{\"id\": 1}"); - mockProducerAndConsumer(receiveMessage); - - executeTest("multipartWithMultipleDatatypesTest", testContext); - ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); - verify(producerMock).send(messageArgumentCaptor.capture(), eq(testContext)); - String producedMessagePayload = normalizeWhitespace( - messageArgumentCaptor.getValue().getPayload().toString(), - true, - true - ); - - String expectedPayload = "{stringData=[Test], booleanData=[true], integerData=[1]}"; - assertThat(producedMessagePayload).isEqualTo(expectedPayload); - } - } - - @Nested - class WithDefaultReceiveMessage { - - private Message defaultRecieveMessage; - - @BeforeEach - void beforeEach() throws IOException { - defaultRecieveMessage = createReceiveMessage( - readToString(Resources.create( - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), - StandardCharsets.UTF_8) - ); - mockProducerAndConsumer(defaultRecieveMessage); - } - - @Test - void testJsonPathExtraction() { - TestCase testCase = executeTest("jsonPathExtractionTest", testContext); - TestAction testAction = testCase.getActions().get(0); - assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); - - assertThat(testContext.getVariable("name")).isEqualTo("Snoopy"); - assertThat(testContext.getVariable("id")).isEqualTo("12"); - } - - @Test - void testCustomizer() { - TestCase testCase = executeTest("getPetByIdRequestTest", testContext); - - TestAction testAction = testCase.getActions().get(0); - assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); - - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - assertThat(httpMessage.getHeader("x-citrus-api-version")).isEqualTo( - "1.0.0"); - - return true; - }; - verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); - verify(consumerMock).receive(testContext, 5000L); - } - - @Test - void testBasicAuthorization() { - TestCase testCase = executeTest("getPetByIdRequestTest", testContext); - - TestAction testAction = testCase.getActions().get(0); - assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); - - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - assertThat(httpMessage.getHeader("Authorization")).isEqualTo( - "Basic YWRtaW46dG9wLXNlY3JldA=="); - return true; - }; - verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); - verify(consumerMock).receive(testContext, 5000L); - } - - @Test - void testRequestPath() { - TestCase testCase = executeTest("getPetByIdRequestTest", testContext); - TestAction testAction = testCase.getActions().get(0); - assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); - - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - assertThat(httpMessage.getHeader("citrus_request_path")).isEqualTo("/pet/1234"); - return true; - }; - verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); - verify(consumerMock).receive(testContext, 5000L); - } - - @Test - void testCookies() { - TestCase testCase = executeTest("getPetByIdRequestTest", testContext); - TestAction testAction = testCase.getActions().get(0); - assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); - - ArgumentMatcher messageMatcher = message -> { - HttpMessage httpMessage = (HttpMessage) message; - Cookie cookie1 = httpMessage.getCookies().get(0); - Cookie cookie2 = httpMessage.getCookies().get(1); - assertThat(cookie1.getName()).isEqualTo("c1"); - assertThat(cookie1.getValue()).isEqualTo("v1"); - assertThat(cookie2.getName()).isEqualTo("c2"); - assertThat(cookie2.getValue()).isEqualTo("v2"); - return true; - }; - verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); - verify(consumerMock).receive(testContext, 5000L); - } - - @Test - void testJsonPathValidation() { - TestCase testCase = executeTest("jsonPathValidationTest", testContext); - assertTestActionType(testCase, GetPetByIdRequest.class); - } - - @Test - void scriptValidationFailureTest() { - TestCase testCase = executeTest("scriptValidationTest", testContext); - assertTestActionType(testCase, GetPetByIdRequest.class); - } - - @Test - void jsonSchemaValidationFailureTest() { - assertThatThrownBy(() -> executeTest("jsonSchemaValidationFailureTest", testContext)) - .hasCauseExactlyInstanceOf(ValidationException.class); - - SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean( - "failingTestSchema"); - - // Assert that schema validation was called - verify(testSchema).getSchema(); - JsonSchema schema = testSchema.getSchema(); - verify(schema).validate(any()); - } - - @Test - void jsonDeactivatedSchemaValidationTest() { - SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean( - "testSchema"); - Mockito.clearInvocations(testSchema, testSchema.getSchema()); - - TestCase testCase = executeTest("jsonDeactivatedSchemaValidationTest", testContext); - - assertTestActionType(testCase, GetPetByIdRequest.class); - - // Assert that schema validation was called - Mockito.verifyNoInteractions(testSchema); - } - - @Test - void defaultOas3SchemaValidationTest() { - SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean("oas3"); - Mockito.clearInvocations(testSchema, testSchema.getSchema()); - - TestCase testCase = executeTest("defaultOas3SchemaValidationTest", testContext); - - assertTestActionType(testCase, GetPetByIdRequest.class); - - // Assert that schema validation was called - verify(testSchema).getSchema(); - JsonSchema schema = testSchema.getSchema(); - verify(schema).validate(any()); - } - - @Test - void jsonSchemaValidationTest() { - SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean( - "testSchema"); - Mockito.clearInvocations(testSchema, testSchema.getSchema()); - - TestCase testCase = executeTest("jsonSchemaValidationTest", testContext); - - assertTestActionType(testCase, GetPetByIdRequest.class); - - // Assert that schema validation was called - verify(testSchema).getSchema(); - JsonSchema schema = testSchema.getSchema(); - verify(schema).validate(any()); - } - - @Test - void testJsonPathValidationFailure() { - mockProducerAndConsumer(defaultRecieveMessage); - - assertThatThrownBy(() -> executeTest("jsonPathValidationFailureTest", testContext)) - .hasCauseExactlyInstanceOf(ValidationException.class); - } - - private static Stream testValidationFailures() { - return Stream.of( - Arguments.of("failOnStatusTest", - "Values not equal for header element 'citrus_http_status_code', expected '201' but was '200'"), - Arguments.of( - "failOnReasonPhraseTest", - "Values not equal for header element 'citrus_http_reason_phrase', expected 'Almost OK' but was 'OK'" - ), - Arguments.of( - "failOnVersionTest", - "Values not equal for header element 'citrus_http_version', expected 'HTTP/1.0' but was 'HTTP/1.1'" - ) - ); - } - - @ParameterizedTest - @MethodSource - void testValidationFailures(String testName, String expectedErrorMessage) { - assertThatThrownBy(() -> executeTest(testName, testContext)) - .hasCauseExactlyInstanceOf(ValidationException.class) - .message() - .startsWith(expectedErrorMessage); - } - } + // TODO TAT-1291 migrate tests + // @Autowired +// private ApplicationContext applicationContext; +// +// @Autowired +// private HttpClient httpClientMock; +// +// @Mock +// private Producer producerMock; +// +// @Mock +// private SelectiveConsumer consumerMock; +// +// private TestContext testContext; +// +// @BeforeEach +// void beforeEach() { +// testContext = applicationContext.getBean(TestContext.class); +// } +// // @Test -// void testCoverageLogger() throws IOException { -// List logMessages = new ArrayList<>(); -// Logger logger = LoggerFactory.getLogger(GetPetByIdRequest.class); -// org.qos.logback.classic.Logger l = (org.qos.logback.classic.Logger) logger; -// l.setLevel(Level.TRACE); -// l.addAppender( -// new AppenderBase<>() { -// @Override -// protected void append(ILoggingEvent eventObject) {} -// -// @Override -// public synchronized void doAppend(ILoggingEvent eventObject) { -// logMessages.add(eventObject.getMessage()); -// super.doAppend(eventObject); +// void testValidationFailure() { +// mockProducerAndConsumer(createReceiveMessage("{\"some\": \"payload\"}")); +// assertThatThrownBy( +// () -> executeTest("getPetByIdRequestTest", testContext)).hasCauseExactlyInstanceOf( +// ValidationException.class); +// } +// +// @Nested +// class WithValidationMatcher { +// +// @BeforeEach +// void beforeEach() { +// mockProducerAndConsumer(createReceiveMessage("")); +// } +// +// @Test +// void testSendWithBody() { +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// try { +// assertThat(httpMessage.getPayload()) +// .isEqualTo( +// readToString(Resources.create( +// "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/addPetMessage.json"), +// StandardCharsets.UTF_8) +// ); +// } catch (IOException e) { +// throw new CitrusRuntimeException("Unable to parse file!", e); // } -// } -// ); +// return true; +// }; +// +// // sendAndValidateMessage("sendWithBodyTest", messageMatcher, AddPetRequest.class); +// } // +// @Test +// void testSendWithBodyLiteralWithVariable() { +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(((String) httpMessage.getPayload()).trim()).isEqualTo("{\"id\": 15}"); +// return true; +// }; +// // sendAndValidateMessage("sendWithBodyLiteralWithVariableTest", messageMatcher, AddPetRequest.class); +// } // +// @Test +// void testXCitrusApiHeaders() { +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(httpMessage.getHeader("x-citrus-api-name")).isEqualTo("petstore"); +// assertThat(httpMessage.getHeader("x-citrus-app")).isEqualTo("PETS"); +// assertThat(httpMessage.getHeader("x-citrus-api-version")).isEqualTo("1.0.0"); +// return true; +// }; // -// mockProducer(httpClient); +// // sendAndValidateMessage("sendWithBodyLiteralTest", messageMatcher, AddPetRequest.class); +// } // -// Message receiveMessage = createReceiveMessage( -// FileUtils.readToString(Resources.create("org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), StandardCharsets.UTF_8) -// ); +// @Test +// void testSendWithExtraHeaders() { +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(httpMessage.getHeader("h1")).isEqualTo("v1"); +// assertThat(httpMessage.getHeader("h2")).isEqualTo("v2"); +// return true; +// }; // -// mockConsumer(httpClient, testContext, receiveMessage); +// // sendAndValidateMessage("sendWithExtraHeaderTest", messageMatcher, AddPetRequest.class); +// } // -// executeTest("getPetByIdRequestTest", testContext); +// @Test +// void testSendWithBodyLiteral() { +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(((String) httpMessage.getPayload()).trim()).isEqualTo("{\"id\": 13}"); +// return true; +// }; // -// assertThat(logMessages.get(0)).isEqualTo("getPetById;GET;\"{}\";\"\";\"\""); +// // sendAndValidateMessage("sendWithBodyLiteralTest", messageMatcher, AddPetRequest.class); +// } // } - - /** - * Test the send message using the given matcher - */ - private void sendAndValidateMessage(String testName, ArgumentMatcher messageMatcher, - Class apiClass) { - - TestCase testCase = executeTest(testName, testContext); - assertTestActionType(testCase, apiClass); - - verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); - } - - /** - * Assert that an action of type 'apiClass' is contained in the list of test actions - */ - private void assertTestActionType(TestCase testCase, Class apiClass) { - TestAction testAction = testCase - .getActions() - .stream() - .filter(action -> apiClass.isAssignableFrom(action.getClass())) - .findAny() - .orElse(null); - assertThat(testAction).isNotNull(); - } - - private void mockProducerAndConsumer(Message receiveMessage) { - when(httpClientMock.createProducer()).thenReturn(producerMock); - when(httpClientMock.createConsumer()).thenReturn(consumerMock); - when(consumerMock.receive(testContext, 5000L)).thenReturn(receiveMessage); - } - - private TestCase executeTest(String testName, TestContext testContext) { - assertThat(CitrusInstanceManager.get()).isPresent(); - - Citrus citrus = CitrusInstanceManager.get().get(); - TestLoader loader = new SpringXmlTestLoader().citrusContext(citrus.getCitrusContext()) - .citrus(citrus) - .context(testContext); - loader.setTestName(testName); - loader.setPackageName("org.citrusframework.openapi.generator.GeneratedApiTest"); - loader.load(); - return loader.getTestCase(); - } - - private Message createReceiveMessage(String payload) { - Message receiveMessage = new DefaultMessage(); - receiveMessage.setPayload(payload); - receiveMessage.getHeaders().put("citrus_http_reason_phrase", "OK"); - receiveMessage.getHeaders().put("citrus_http_version", "HTTP/1.1"); - receiveMessage.getHeaders().put("citrus_http_status_code", 200); - return receiveMessage; - } - +// +// @Nested +// class WithMultipartMessage { +// +// @Test +// void testSendMultipartFile() { +// mockProducerAndConsumer(createReceiveMessage("")); +// +// ArgumentMatcher messageMatcher = message -> { +// assertThat(message.getPayload()).isInstanceOf(MultiValueMap.class); +// MultiValueMap multiValueMap = (MultiValueMap) message.getPayload(); +// List multipartFile = multiValueMap.get("multipartFile"); +// try { +// assertThat(((Resource) multipartFile.get(0)).getURL().toString()) +// .endsWith( +// "test-classes/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"); +// } catch (IOException e) { +// throw new CitrusRuntimeException("Unable to parse file!", e); +// } +// +// return true; +// }; +// +// sendAndValidateMessage("postFileTest", messageMatcher, PostFileRequest.class); +// } +// +// @Test +// void testSendMultipartWithFileAttribute() { +// Message payload = createReceiveMessage("{\"id\": 1}"); +// mockProducerAndConsumer(payload); +// +// executeTest("multipartWithFileAttributesTest", testContext); +// ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); +// verify(producerMock).send(messageArgumentCaptor.capture(), eq(testContext)); +// Object producedMessagePayload = messageArgumentCaptor.getValue().getPayload(); +// assertThat(producedMessagePayload).isInstanceOf(MultiValueMap.class); +// +// Object templateValue = ((MultiValueMap) producedMessagePayload).get("template"); +// assertThat(templateValue) +// .asInstanceOf(InstanceOfAssertFactories.LIST) +// .element(0) +// .hasFieldOrPropertyWithValue("path", +// "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/MultipartTemplate.xml"); +// +// Object additionalDataValue = ((MultiValueMap) producedMessagePayload).get( +// "additionalData"); +// assertThat(additionalDataValue) +// .asInstanceOf(InstanceOfAssertFactories.LIST) +// .element(0) +// .hasFieldOrPropertyWithValue("path", +// "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/AdditionalData.json"); +// +// Object schemaValue = ((MultiValueMap) producedMessagePayload).get("_schema"); +// assertThat(schemaValue) +// .asInstanceOf(InstanceOfAssertFactories.LIST) +// .element(0) +// .hasFieldOrPropertyWithValue("path", +// "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/Schema.json"); +// } +// +// @Test +// void testSendMultipartWithPlainText() { +// mockProducerAndConsumer(createReceiveMessage("{\"id\": 1}")); +// executeTest("multipartWithPlainTextTest", testContext); +// ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); +// verify(producerMock).send(messageArgumentCaptor.capture(), eq(testContext)); +// String producedMessagePayload = normalizeWhitespace( +// messageArgumentCaptor.getValue().getPayload().toString(), +// true, +// true +// ); +// +// String expectedPayload = +// "{template=[ ], additionalData=[ {\"data1\":\"value1\"} ], _schema=[ {\"schema\":\"mySchema\"} ]}"; +// assertThat(producedMessagePayload).isEqualTo(expectedPayload); +// } +// +// @Test +// void testSendMultipartWithMultipleDatatypes() { +// Message receiveMessage = createReceiveMessage("{\"id\": 1}"); +// mockProducerAndConsumer(receiveMessage); +// +// executeTest("multipartWithMultipleDatatypesTest", testContext); +// ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); +// verify(producerMock).send(messageArgumentCaptor.capture(), eq(testContext)); +// String producedMessagePayload = normalizeWhitespace( +// messageArgumentCaptor.getValue().getPayload().toString(), +// true, +// true +// ); +// +// String expectedPayload = "{stringData=[Test], booleanData=[true], integerData=[1]}"; +// assertThat(producedMessagePayload).isEqualTo(expectedPayload); +// } +// } +// +// @Nested +// class WithDefaultReceiveMessage { +// +// private Message defaultRecieveMessage; +// +// @BeforeEach +// void beforeEach() throws IOException { +// defaultRecieveMessage = createReceiveMessage( +// readToString(Resources.create( +// "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), +// StandardCharsets.UTF_8) +// ); +// mockProducerAndConsumer(defaultRecieveMessage); +// } +// +// @Test +// void testJsonPathExtraction() { +// TestCase testCase = executeTest("jsonPathExtractionTest", testContext); +// TestAction testAction = testCase.getActions().get(0); +// assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); +// +// assertThat(testContext.getVariable("name")).isEqualTo("Snoopy"); +// assertThat(testContext.getVariable("id")).isEqualTo("12"); +// } +// +// @Test +// void testCustomizer() { +// TestCase testCase = executeTest("getPetByIdRequestTest", testContext); +// +// TestAction testAction = testCase.getActions().get(0); +// assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); +// +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(httpMessage.getHeader("x-citrus-api-version")).isEqualTo( +// "1.0.0"); +// +// return true; +// }; +// verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); +// verify(consumerMock).receive(testContext, 5000L); +// } +// +// @Test +// void testBasicAuthorization() { +// TestCase testCase = executeTest("getPetByIdRequestTest", testContext); +// +// TestAction testAction = testCase.getActions().get(0); +// assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); +// +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(httpMessage.getHeader("Authorization")).isEqualTo( +// "Basic YWRtaW46dG9wLXNlY3JldA=="); +// return true; +// }; +// verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); +// verify(consumerMock).receive(testContext, 5000L); +// } +// +// @Test +// void testRequestPath() { +// TestCase testCase = executeTest("getPetByIdRequestTest", testContext); +// TestAction testAction = testCase.getActions().get(0); +// assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); +// +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(httpMessage.getHeader("citrus_request_path")).isEqualTo("/pet/1234"); +// return true; +// }; +// verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); +// verify(consumerMock).receive(testContext, 5000L); +// } +// +// @Test +// void testCookies() { +// TestCase testCase = executeTest("getPetByIdRequestTest", testContext); +// TestAction testAction = testCase.getActions().get(0); +// assertThat(testAction).isInstanceOf(GetPetByIdRequest.class); +// +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// Cookie cookie1 = httpMessage.getCookies().get(0); +// Cookie cookie2 = httpMessage.getCookies().get(1); +// assertThat(cookie1.getName()).isEqualTo("c1"); +// assertThat(cookie1.getValue()).isEqualTo("v1"); +// assertThat(cookie2.getName()).isEqualTo("c2"); +// assertThat(cookie2.getValue()).isEqualTo("v2"); +// return true; +// }; +// verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); +// verify(consumerMock).receive(testContext, 5000L); +// } +// +// @Test +// void testJsonPathValidation() { +// TestCase testCase = executeTest("jsonPathValidationTest", testContext); +// assertTestActionType(testCase, GetPetByIdRequest.class); +// } +// +// @Test +// void scriptValidationFailureTest() { +// TestCase testCase = executeTest("scriptValidationTest", testContext); +// assertTestActionType(testCase, GetPetByIdRequest.class); +// } +// +// @Test +// void jsonSchemaValidationFailureTest() { +// assertThatThrownBy(() -> executeTest("jsonSchemaValidationFailureTest", testContext)) +// .hasCauseExactlyInstanceOf(ValidationException.class); +// +// SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean( +// "failingTestSchema"); +// +// // Assert that schema validation was called +// verify(testSchema).getSchema(); +// JsonSchema schema = testSchema.getSchema(); +// verify(schema).validate(any()); +// } +// +// @Test +// void jsonDeactivatedSchemaValidationTest() { +// SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean( +// "testSchema"); +// Mockito.clearInvocations(testSchema, testSchema.getSchema()); +// +// TestCase testCase = executeTest("jsonDeactivatedSchemaValidationTest", testContext); +// +// assertTestActionType(testCase, GetPetByIdRequest.class); +// +// // Assert that schema validation was called +// Mockito.verifyNoInteractions(testSchema); +// } +// +// @Test +// void defaultOas3SchemaValidationTest() { +// SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean("oas3"); +// Mockito.clearInvocations(testSchema, testSchema.getSchema()); +// +// TestCase testCase = executeTest("defaultOas3SchemaValidationTest", testContext); +// +// assertTestActionType(testCase, GetPetByIdRequest.class); +// +// // Assert that schema validation was called +// verify(testSchema).getSchema(); +// JsonSchema schema = testSchema.getSchema(); +// verify(schema).validate(any()); +// } +// +// @Test +// void jsonSchemaValidationTest() { +// SimpleJsonSchema testSchema = (SimpleJsonSchema) applicationContext.getBean( +// "testSchema"); +// Mockito.clearInvocations(testSchema, testSchema.getSchema()); +// +// TestCase testCase = executeTest("jsonSchemaValidationTest", testContext); +// +// assertTestActionType(testCase, GetPetByIdRequest.class); +// +// // Assert that schema validation was called +// verify(testSchema).getSchema(); +// JsonSchema schema = testSchema.getSchema(); +// verify(schema).validate(any()); +// } +// +// @Test +// void testJsonPathValidationFailure() { +// mockProducerAndConsumer(defaultRecieveMessage); +// +// assertThatThrownBy(() -> executeTest("jsonPathValidationFailureTest", testContext)) +// .hasCauseExactlyInstanceOf(ValidationException.class); +// } +// +// private static Stream testValidationFailures() { +// return Stream.of( +// Arguments.of("failOnStatusTest", +// "Values not equal for header element 'citrus_http_status_code', expected '201' but was '200'"), +// Arguments.of( +// "failOnReasonPhraseTest", +// "Values not equal for header element 'citrus_http_reason_phrase', expected 'Almost OK' but was 'OK'" +// ), +// Arguments.of( +// "failOnVersionTest", +// "Values not equal for header element 'citrus_http_version', expected 'HTTP/1.0' but was 'HTTP/1.1'" +// ) +// ); +// } +// +// @ParameterizedTest +// @MethodSource +// void testValidationFailures(String testName, String expectedErrorMessage) { +// assertThatThrownBy(() -> executeTest(testName, testContext)) +// .hasCauseExactlyInstanceOf(ValidationException.class) +// .message() +// .startsWith(expectedErrorMessage); +// } +// } +// +//// @Test +//// void testCoverageLogger() throws IOException { +//// List logMessages = new ArrayList<>(); +//// Logger logger = LoggerFactory.getLogger(GetPetByIdRequest.class); +//// org.qos.logback.classic.Logger l = (org.qos.logback.classic.Logger) logger; +//// l.setLevel(Level.TRACE); +//// l.addAppender( +//// new AppenderBase<>() { +//// @Override +//// protected void append(ILoggingEvent eventObject) {} +//// +//// @Override +//// public synchronized void doAppend(ILoggingEvent eventObject) { +//// logMessages.add(eventObject.getMessage()); +//// super.doAppend(eventObject); +//// } +//// } +//// ); +//// +//// +//// +//// mockProducer(httpClient); +//// +//// Message receiveMessage = createReceiveMessage( +//// FileUtils.readToString(Resources.create("org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), StandardCharsets.UTF_8) +//// ); +//// +//// mockConsumer(httpClient, testContext, receiveMessage); +//// +//// executeTest("getPetByIdRequestTest", testContext); +//// +//// assertThat(logMessages.get(0)).isEqualTo("getPetById;GET;\"{}\";\"\";\"\""); +//// } +// +// /** +// * Test the send message using the given matcher +// */ +// private void sendAndValidateMessage(String testName, ArgumentMatcher messageMatcher, +// Class apiClass) { +// +// TestCase testCase = executeTest(testName, testContext); +// assertTestActionType(testCase, apiClass); +// +// verify(producerMock).send(ArgumentMatchers.argThat(messageMatcher), eq(testContext)); +// } +// +// /** +// * Assert that an action of type 'apiClass' is contained in the list of test actions +// */ +// private static void assertTestActionType(TestCase testCase, Class apiClass) { +// TestAction testAction = testCase +// .getActions() +// .stream() +// .filter(action -> apiClass.isAssignableFrom(action.getClass())) +// .findAny() +// .orElse(null); +// assertThat(testAction).isNotNull(); +// } +// +// private void mockProducerAndConsumer(Message receiveMessage) { +// when(httpClientMock.createProducer()).thenReturn(producerMock); +// when(httpClientMock.createConsumer()).thenReturn(consumerMock); +// when(consumerMock.receive(testContext, 5000L)).thenReturn(receiveMessage); +// } +// +// private static TestCase executeTest(String testName, TestContext testContext) { +// assertThat(CitrusInstanceManager.get()).isPresent(); +// +// Citrus citrus = CitrusInstanceManager.get().get(); +// TestLoader loader = new SpringXmlTestLoader().citrusContext(citrus.getCitrusContext()) +// .citrus(citrus) +// .context(testContext); +// loader.setTestName(testName); +// loader.setPackageName("org.citrusframework.openapi.generator.GeneratedApiTest"); +// loader.load(); +// return loader.getTestCase(); +// } +// +// private Message createReceiveMessage(String payload) { +// Message receiveMessage = new DefaultMessage(); +// receiveMessage.setPayload(payload); +// receiveMessage.getHeaders().put("citrus_http_reason_phrase", "OK"); +// receiveMessage.getHeaders().put("citrus_http_version", "HTTP/1.1"); +// receiveMessage.getHeaders().put("citrus_http_status_code", 200); +// return receiveMessage; +// } +// public static class Config { @Bean(name = {"applicationServiceClient", "multipartTestEndpoint", - "soapSampleStoreEndpoint", "petStoreEndpoint"}) + "soapSampleStoreEndpoint", "petStoreEndpoint"}) public HttpClient applicationServiceClient() { HttpClient clientMock = mock(); EndpointConfiguration endpointConfigurationMock = mock(); @@ -591,9 +584,9 @@ public ApiActionBuilderCustomizerService customizer() { return new ApiActionBuilderCustomizerService() { @Override public > T build( - GeneratedApi generatedApi, TestAction action, TestContext context, T builder) { + GeneratedApi generatedApi, TestAction action, TestContext context, T builder) { builder.getMessageBuilderSupport() - .header("x-citrus-api-version", generatedApi.getApiVersion()); + .header("x-citrus-api-version", generatedApi.getApiVersion()); return builder; } }; @@ -620,7 +613,7 @@ public SimpleJsonSchema failingTestSchema() { Set nokReport = new HashSet<>(); nokReport.add(new ValidationMessage.Builder().customMessage( - "This is a simulated validation error message").build()); + "This is a simulated validation error message").build()); when(schemaMock.validate(any())).thenReturn(nokReport); return jsonSchemaMock; } diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdIT.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdIT.java index 72b9369ca3..fc37512b03 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdIT.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdIT.java @@ -1,254 +1,154 @@ package org.citrusframework.openapi.generator; -import static org.assertj.core.api.Assertions.assertThat; -import static org.citrusframework.container.Assert.Builder.assertException; -import static org.citrusframework.util.FileUtils.readToString; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Map; import org.citrusframework.TestCaseRunner; import org.citrusframework.annotations.CitrusResource; import org.citrusframework.annotations.CitrusTest; import org.citrusframework.config.CitrusSpringConfig; import org.citrusframework.context.TestContext; -import org.citrusframework.endpoint.EndpointConfiguration; import org.citrusframework.http.client.HttpClient; -import org.citrusframework.http.client.HttpEndpointConfiguration; -import org.citrusframework.junit.jupiter.spring.CitrusSpringExtension; -import org.citrusframework.message.DefaultMessage; +import org.citrusframework.http.client.HttpClientBuilder; +import org.citrusframework.http.server.HttpServer; +import org.citrusframework.http.server.HttpServerBuilder; +import org.citrusframework.junit.jupiter.spring.CitrusSpringSupport; import org.citrusframework.message.Message; -import org.citrusframework.messaging.Producer; -import org.citrusframework.messaging.SelectiveConsumer; -import org.citrusframework.openapi.generator.GetPetByIdIT.Config; -import org.citrusframework.openapi.generator.rest.petstore.request.PetApi.GetPetByIdRequest; import org.citrusframework.openapi.generator.rest.petstore.spring.PetStoreBeanConfiguration; -import org.citrusframework.spi.Resources; -import org.junit.jupiter.api.BeforeEach; +import org.citrusframework.util.SocketUtils; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpStatus; +import org.springframework.test.context.ContextConfiguration; -@ExtendWith(CitrusSpringExtension.class) -@SpringBootTest(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, Config.class}) -class GetPetByIdIT { +import java.io.File; - @Autowired - private GetPetByIdRequest getPetByIdRequest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.citrusframework.http.actions.HttpActionBuilder.http; +import static org.citrusframework.message.MessageType.JSON; +import static org.citrusframework.openapi.generator.rest.petstore.request.PetApi.openapiPetstore; +import static org.citrusframework.validation.PathExpressionValidationContext.Builder.pathExpression; +import static org.springframework.http.HttpStatus.NO_CONTENT; +import static org.springframework.http.HttpStatus.OK; - @Autowired - @Qualifier("petStoreEndpoint") - private HttpClient httpClient; - private String defaultResponse; +@CitrusSpringSupport +@ContextConfiguration(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, GetPetByIdIT.Config.class}) +class GetPetByIdIT { - @BeforeEach - public void beforeTest() throws IOException { - defaultResponse = readToString(Resources.create( - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), - StandardCharsets.UTF_8) ; + @Autowired + private HttpClient httpClient; - mockProducer(); - mockConsumer(); - } + @Autowired + private HttpServer httpServer; - /** - * TODO #1161 - Improve with builder pattern - */ @Test @CitrusTest void testByJsonPath(@CitrusResource TestCaseRunner runner) { - // Given - getPetByIdRequest.setPetId("1234"); - - // Then - getPetByIdRequest.setResponseStatus(HttpStatus.OK.value()); - getPetByIdRequest.setResponseReasonPhrase(HttpStatus.OK.getReasonPhrase()); - - // Assert body by json path - getPetByIdRequest.setResponseValue(Map.of("$.name", "Snoopy")); - - // When - runner.$(getPetByIdRequest); - } - - /** - * TODO #1161 - Improve with builder pattern - */ - @Test - @CitrusTest - void testValidationFailureByJsonPath(@CitrusResource TestCaseRunner runner) { - - // Given - getPetByIdRequest.setPetId("1234"); - - // Then - getPetByIdRequest.setResponseStatus(HttpStatus.OK.value()); - getPetByIdRequest.setResponseReasonPhrase(HttpStatus.OK.getReasonPhrase()); - - // Assert body by json path - getPetByIdRequest.setResponseValue(Map.of("$.name", "Garfield")); - - // When - runner.$(assertException() - .exception(org.citrusframework.exceptions.CitrusRuntimeException.class) - .message("Values not equal for element '$.name', expected 'Garfield' but was 'Snoopy'") - .when( - getPetByIdRequest - ) + runner.$( + openapiPetstore(httpClient) + .getPetById() + .send(request -> request + .withPetId(2002L) + .withCorrelationIds("5599") + .withVerbose(true) + ) ); - // When - - } - - /** - * TODO #1161 - Improve with builder pattern - */ - @Test - @CitrusTest - void testByResource(@CitrusResource TestCaseRunner runner) { - - // Given - getPetByIdRequest.setPetId("1234"); - - // Then - getPetByIdRequest.setResponseStatus(HttpStatus.OK.value()); - getPetByIdRequest.setResponseReasonPhrase(HttpStatus.OK.getReasonPhrase()); - // Assert body by resource - getPetByIdRequest.setResource( - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"); - // When - runner.$(getPetByIdRequest); - } - - /** - * TODO #1161 - Improve with builder pattern - */ - @Test - @CitrusTest - void testValidationFailureByResource(@CitrusResource TestCaseRunner runner) { - - // Given - getPetByIdRequest.setPetId("1234"); - - // Then - getPetByIdRequest.setResponseStatus(HttpStatus.OK.value()); - getPetByIdRequest.setResponseReasonPhrase(HttpStatus.OK.getReasonPhrase()); - // Assert body by resource - getPetByIdRequest.setResource( - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage2.json"); - - // When - runner.$(assertException() - .exception(org.citrusframework.exceptions.CitrusRuntimeException.class) - .message("Values not equal for entry: '$['name']', expected 'Garfield' but was 'Snoopy'") - .when( - getPetByIdRequest - ) + respondPet(runner); + + runner.$( + openapiPetstore(httpClient) + .getPetById() + .receive() + .message() + .validate( + pathExpression() + .jsonPath("$.name", "Snoopy") + .jsonPath("$.id", 2002) + ) ); } - /** - * TODO #1161 - Improve with builder pattern - */ @Test @CitrusTest - void validateByVariable(@CitrusResource TestContext testContext, - @CitrusResource TestCaseRunner runner) { - - // Given - getPetByIdRequest.setPetId("1234"); - - // Then - getPetByIdRequest.setResponseStatus(HttpStatus.OK.value()); - getPetByIdRequest.setResponseReasonPhrase(HttpStatus.OK.getReasonPhrase()); - - // Assert load data into variables - getPetByIdRequest.setResponseVariable(Map.of("$", "RESPONSE", "$.id", "ID")); - - // When - runner.$(getPetByIdRequest); - - // Then - assertThat(testContext) - .satisfies( - c -> assertThat(c.getVariable("RESPONSE")) - .isNotNull(), - c -> assertThat(c.getVariable("ID")) - .isNotNull() - .isEqualTo("12") - ); - } - - /** - * TODO #1161 - Improve with builder pattern - */ - @Test - @CitrusTest - void validateReceivedResponse(@CitrusResource TestContext testContext) { - - // Given - getPetByIdRequest.setPetId("1234"); - - // When - getPetByIdRequest.sendRequest(testContext); - - // Then - Message receiveResponse = getPetByIdRequest.receiveResponse(testContext); - assertThat(receiveResponse) - .isNotNull() - .extracting(Message::getPayload) - .asString() - .isEqualToIgnoringWhitespace(defaultResponse); - assertThat(receiveResponse.getHeaders()) - .containsEntry("citrus_http_status_code", 200) - .containsEntry("citrus_http_reason_phrase", "OK"); - } - - private void mockProducer() { - Producer producerMock = mock(); - when(httpClient.createProducer()).thenReturn(producerMock); - } - - private void mockConsumer() { - Message receiveMessage = createReceiveMessage(); + void testJsonFileBody(@CitrusResource TestCaseRunner runner) { + + runner.$( + openapiPetstore(httpClient) + .getPetById() + .send(request -> request + .withPetId(2002L) + .withCorrelationIds("5599") + .withVerbose(true) + ) + ); - SelectiveConsumer consumer = mock(SelectiveConsumer.class); - when(httpClient.createConsumer()).thenReturn(consumer); - when(consumer.receive(any(), eq(5000L))).thenReturn(receiveMessage); + respondPet(runner); + + var expectedResponse = new File("src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"); + runner.$( + openapiPetstore(httpClient) + .getPetById() + .receive() + .message() + .validate((Message message, TestContext context) -> { + assertThat(expectedResponse).exists().content().satisfies(expectedContent -> { + assertThat(message.getPayload(String.class)).isEqualToIgnoringWhitespace(expectedContent); + }); + }) + ); } - private Message createReceiveMessage() { - Message receiveMessage = new DefaultMessage(); - receiveMessage.setPayload(defaultResponse); - receiveMessage.getHeaders().put("citrus_http_reason_phrase", "OK"); - receiveMessage.getHeaders().put("citrus_http_version", "HTTP/1.1"); - receiveMessage.getHeaders().put("Content-Type", 200); - receiveMessage.getHeaders().put("citrus_http_status_code", 200); - return receiveMessage; + private void respondPet(TestCaseRunner runner) { + runner.$(http().server(httpServer) + .receive() + .get("/pet/2002") + .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") + .accept("@contains('application/json')@")); + + runner.$(http().server(httpServer) + .send() + .response(OK) + .message() + .body(""" + { + "id": ${petId}, + "name": "Snoopy", + "tags": [], + "photoUrls": [], + "category": { + "name": "a name", + "id": 112233 + }, + "status": "available" + } + """) + .contentType("application/json").type(JSON)); } @TestConfiguration public static class Config { - @Bean(name = {"applicationServiceClient", "petStoreEndpoint"}) - public HttpClient applicationServiceClient() { - HttpClient client = mock(HttpClient.class); - EndpointConfiguration endpointConfiguration = mock(EndpointConfiguration.class); - when(client.getEndpointConfiguration()).thenReturn(new HttpEndpointConfiguration()); - when(endpointConfiguration.getTimeout()).thenReturn(5000L); - return client; + private final int port = SocketUtils.findAvailableTcpPort(8080); + + @Bean + public HttpClient httpClient() { + return new HttpClientBuilder() + .requestUrl("http://localhost:%d".formatted(port)) + .build(); + } + + @Bean + public HttpServer httpServer() { + return new HttpServerBuilder() + .port(port) + // .endpointAdapter(endpointAdapter) + .timeout(5000L) + .autoStart(true) + .defaultStatus(NO_CONTENT) + .build(); } } } diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenIT.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenIT.java index 1d205f9400..6909086960 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenIT.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenIT.java @@ -1,16 +1,10 @@ package org.citrusframework.openapi.generator; -import static java.nio.file.Files.readString; -import static java.nio.file.Files.walk; import static org.assertj.core.api.Assertions.assertThat; -import static org.citrusframework.openapi.generator.JavaCitrusCodegenTest.getAbsoluteTargetDirectoryPath; -import static org.citrusframework.openapi.generator.JavaCitrusCodegenTest.getAbsoluteTestResourcePath; -import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.List; import java.util.stream.Stream; import org.apache.commons.lang3.stream.Streams; import org.citrusframework.exceptions.CitrusRuntimeException; @@ -25,108 +19,75 @@ /** * This test case is designed to validate the consistency of the code generation process and detect * any discrepancies between the generated API files and the reference files stored in - * '/JavaCitrusCodegenIT/expectedgen/'. It compares the results of API generation against the - * reference files, and a failure indicates potential changes in mustache templates or code - * generation logic. + * '/JavaCitrusCodegenIntegrationTest/expectedgen/'. It compares the results of API generation + * against the reference files, and a failure indicates potential changes in mustache templates or + * code generation logic. *

* If this test fails, it is essential to review the code generation process and underlying * templates carefully. If the changes are intentional and verified, update the reference files by - * copying the generated API sources to the '/JavaCitrusCodegenIT/expectedgen/' directory. To ensure - * accurate copying, without unwanted code formatting, use a simple File Explorer instead of relying - * on IDE-based operations. + * copying the generated API sources to the '/JavaCitrusCodegenIntegrationTest/expectedgen/' + * directory. To ensure accurate copying, without unwanted code formatting, use a simple File + * Explorer instead of relying on IDE-based operations. */ class JavaCitrusCodegenIT { - public static final String BASE_PACKAGE = "org/citrusframework/openapi/generator"; - - private static long countFilesRecursively(Path dir) throws IOException { - try (Stream walk = walk(dir)) { - return walk.filter(Files::isRegularFile).count(); - } - } - - @Test - void noAdditionalFiles() throws IOException { - long expectedFileCount = countFilesRecursively( - Path.of(getAbsoluteTestResourcePath( - BASE_PACKAGE + "/JavaCitrusCodegenIT/expectedgen/rest"))); - long actualFileCount = countFilesRecursively( - Path.of(getAbsoluteTargetDirectoryPath( - "generated-test-sources/" + BASE_PACKAGE + "/rest"))); - - assertEquals(expectedFileCount, actualFileCount, - "Directories do not have the same number of files."); - } - - static Stream getResourcesForRest() throws IOException { - return geClassResourcesIgnoringInnerClasses(BASE_PACKAGE + "/rest"); - } - - @ParameterizedTest - @MethodSource("getResourcesForRest") - void testGeneratedFiles(Resource resource) throws IOException { - File classFile = resource.getFile(); - String absolutePath = classFile.getAbsolutePath(); - String javaFilePath = absolutePath - .replace("test-classes", "generated-test-sources") - .replace(".class", ".java"); - - assertFileContent(new File(javaFilePath), "rest"); - } - - static Stream getResourcesForSoap() throws IOException { - return geClassResourcesIgnoringInnerClasses( - BASE_PACKAGE + "/soap/bookservice"); - } - - @ParameterizedTest - @MethodSource("getResourcesForSoap") - void testGeneratedSoapFiles(Resource resource) throws IOException { - File classFile = resource.getFile(); - String absolutePath = classFile.getAbsolutePath(); - - String javaFilePath = absolutePath - .replace("test-classes", "generated-test-sources") - .replace(".class", ".java"); - - assertFileContent(new File(javaFilePath), "soap"); - } - - private static Stream geClassResourcesIgnoringInnerClasses(String path) - throws IOException { - return Streams.of( - new PathMatchingResourcePatternResolver().getResources(path + "/**/*.class")) - .filter(resource -> { - try { - return !resource.getURI().toString().contains("$"); - } catch (Exception e) { - throw new CitrusRuntimeException("Unable to retrieve URL from resource!"); - } - } - ).map(Arguments::arguments); - } - - /* - * NOTE: when changes have been performed to mustache templates, the expected files need to be updated. - * Be aware that file content may change according to IDE formatting rules if the files are copied via IDE. - * Files should therefore be copied using a file explorer which ensures that content of files does not change. - */ - private void assertFileContent(File file, String apiDir) throws IOException { - assertThat(file).exists(); - - String expectedFilePath = BASE_PACKAGE + "/JavaCitrusCodegenIT/expectedgen/" + file.getAbsolutePath().substring(file.getAbsolutePath().indexOf(apiDir)); - ClassPathResource classPathResource = new ClassPathResource(expectedFilePath); - - String actualContent = readString(file.toPath()); - String expectedContent = readString(classPathResource.getFile().toPath()); - - // Replace "Generated" with a placeholder - String generatedAnnotationPattern = "@jakarta\\.annotation\\.Generated\\(.*?\\)"; - String placeholder = "@jakarta.annotation.Generated(value = \"org.citrusframework.openapi.generator.JavaCitrusCodegen\", date = \"TIMESTAMP\", comments = \"Generator version: VERSION\")"; - - actualContent = actualContent.replaceAll(generatedAnnotationPattern, placeholder); - expectedContent = expectedContent.replaceAll(generatedAnnotationPattern, placeholder); - - assertThat(actualContent).isEqualTo(expectedContent); - } +// static Stream getResourcesForRest() throws IOException { +// return geClassResourcesIgnoringInnerClasses("org/citrusframework/openapi/generator/rest"); +// } +// +// @ParameterizedTest +// @MethodSource("getResourcesForRest") +// void testGeneratedFiles(Resource resource) throws IOException { +// File classFile = resource.getFile(); +// String absolutePath = classFile.getAbsolutePath(); +// String javaFilePath = absolutePath.replace("test-classes", "generated-test-sources") +// .replace(".class", ".java"); +// +// assertFileContent(new File(javaFilePath), "rest"); +// } +// +// static Stream getResourcesForSoap() throws IOException { +// return geClassResourcesIgnoringInnerClasses( +// "org/citrusframework/openapi/generator/soap/bookservice"); +// } +// +// @ParameterizedTest +// @MethodSource("getResourcesForSoap") +// void testGeneratedSoapFiles(Resource resource) throws IOException { +// File classFile = resource.getFile(); +// String absolutePath = classFile.getAbsolutePath(); +// +// String javaFilePath = absolutePath.replace("test-classes", "generated-test-sources") +// .replace(".class", ".java"); +// +// assertFileContent(new File(javaFilePath), "soap"); +// } +// +// private static Stream geClassResourcesIgnoringInnerClasses(String path) +// throws IOException { +// return Streams.of(new PathMatchingResourcePatternResolver().getResources( +// path + "/**/*.class")).filter(resource -> { +// try { +// return !resource.getURI().toString().contains("$"); +// } catch (Exception e) { +// throw new CitrusRuntimeException("Unable to retrieve URL from resource!"); +// } +// }).map(Arguments::arguments); +// } +// +// private void assertFileContent(File file, String apiDir) throws IOException { +// assertThat(file).exists(); +// String expectedFilePath = +// "org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/" +// + file.getAbsolutePath().substring(file.getAbsolutePath().indexOf(apiDir)); +// +// ClassPathResource classPathResource = new ClassPathResource(expectedFilePath); +// +// /* +// * NOTE: when changes have been performed to mustache templates, the expected files need to be updated. +// * Be aware that file content may change according to IDE formatting rules if the files are copied via IDE. +// * Files should therefore be copied using a file explorer which ensures that content of files does not change. +// */ +// assertThat(file).hasSameTextualContentAs(classPathResource.getFile()); +// } } diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenTest.java index f3860de3b4..394839c407 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenTest.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenTest.java @@ -5,7 +5,6 @@ import java.io.File; import java.io.IOException; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; @@ -33,31 +32,6 @@ class JavaCitrusCodegenTest { - /** - * Get the absolute path to the test resources directory. - * - * @param pathToFileInTestResources The file within {@code src/test/resources} to look for - * @return the absolute path to the file - */ - static String getAbsoluteTestResourcePath(String pathToFileInTestResources) { - URL resourceUrl = JavaCitrusCodegenTest.class.getClassLoader().getResource(pathToFileInTestResources); - assert resourceUrl != null; - File inputSpecFile = new File(resourceUrl.getFile()); - return inputSpecFile.getAbsolutePath(); - } - - /** - * Get the absolute path to the project's target directory. - * - * @param pathToFileInTargetDirectory The file within {@code target} to look for - * @return the absolute path to the file - */ - static String getAbsoluteTargetDirectoryPath(String pathToFileInTargetDirectory) { - String projectBaseDir = System.getProperty("user.dir"); // Base directory of the project - File outputDirFile = new File(projectBaseDir, "target/" + pathToFileInTargetDirectory); - return outputDirFile.getAbsolutePath(); - } - @Test void retrieveGeneratorBsSpi() { JavaCitrusCodegen codegen = (JavaCitrusCodegen) CodegenConfigLoader.forName("java-citrus"); @@ -107,113 +81,72 @@ void areAdditionalPropertiesProcessedTest() { assertThat(codegen.getTargetXmlnsNamespace()).isEqualTo(targetXmlnsNamespace); } - @Test - void areReservedWordsEscapedTest() throws IOException { - String absoluteInputSpecPath = getAbsoluteTestResourcePath("apis/petstore_reservedWords.yaml"); - String absoluteOutputDirPath = getAbsoluteTargetDirectoryPath("JavaCitrusCodegenTest/petstore_escapedWords"); - - final CodegenConfigurator configurator = new CodegenConfigurator() - .setGeneratorName(CODEGEN_NAME) - .setInputSpec(absoluteInputSpecPath) - .setOutputDir(absoluteOutputDirPath); - - final ClientOptInput clientOptInput = configurator.toClientOptInput(); - DefaultGenerator generator = new DefaultGenerator(); - List outputFiles = generator.opts(clientOptInput).generate(); - - Optional file = outputFiles.stream() - .filter(x -> "PetApi.java".equals(x.getName())) - .findFirst(); - - assertThat(file).isPresent(); - - List lines = Files.readAllLines(file.get().toPath(), StandardCharsets.UTF_8); - - // "name" is a reserved word, so it should be escaped with an underline for the second parameter - assertThat( - lines.stream() - .filter(x -> x.contains("\"name\", this._name")) - .count()) - .isEqualTo(1L); - } - - @Test - void arePathParamsFieldsPresent() throws IOException { - String absoluteInputSpecPath = getAbsoluteTestResourcePath("apis/petstore.yaml"); - String absoluteOutputDirPath = getAbsoluteTargetDirectoryPath("JavaCitrusCodegenTest/petstore"); - - final CodegenConfigurator configurator = new CodegenConfigurator() - .setGeneratorName(CODEGEN_NAME) - .setInputSpec(absoluteInputSpecPath) - .setOutputDir(absoluteOutputDirPath); - - final ClientOptInput clientOptInput = configurator.toClientOptInput(); - DefaultGenerator generator = new DefaultGenerator(); - List outputFiles = generator.opts(clientOptInput).generate(); - - Optional file = outputFiles.stream() - .filter(x -> "PetApi.java".equals(x.getName())) - .findFirst(); - - assertThat(file).isPresent(); - - List lines = Files.readAllLines(file.get().toPath(), StandardCharsets.UTF_8); - - // "name" is a reserved word, so it should be escaped with an underline for the second parameter - assertThat( - lines.stream() - .filter(x -> x.contains("private String petId;")) - .count()) - .isEqualTo(4L); - assertThat( - lines.stream() - .filter(x -> x.contains( - "endpoint = endpoint.replace(\"{\" + \"petId\" + \"}\", petId);")) - .count()) - .isEqualTo(4L); - } - - @Test - void areBasicAuthFieldsPresent() throws IOException { - String absoluteInputSpecPath = getAbsoluteTestResourcePath("apis/petstore.yaml"); - String absoluteOutputDirPath = getAbsoluteTargetDirectoryPath("JavaCitrusCodegenTest/petstore"); - - final CodegenConfigurator configurator = new CodegenConfigurator() - .setGeneratorName(CODEGEN_NAME) - .setInputSpec(absoluteInputSpecPath) - .setOutputDir(absoluteOutputDirPath); - - final ClientOptInput clientOptInput = configurator.toClientOptInput(); - DefaultGenerator generator = new DefaultGenerator(); - List outputFiles = generator.opts(clientOptInput).generate(); - - Optional file = outputFiles.stream() - .filter(x -> "PetApi.java".equals(x.getName())) - .findFirst(); - - assertThat(file).isPresent(); - - List lines = Files.readAllLines(file.get().toPath(), StandardCharsets.UTF_8); - - // "name" is a reserved word, so it should be escaped with an underline for the second parameter - assertThat( - lines.stream() - .filter(x -> x.contains("@Value(\"${\" + \"apiEndpoint.basic.username:#{null}}\")")) - .count()) - .isEqualTo(1L); - assertThat( - lines.stream() - .filter(x -> x.contains("private String basicUsername;")) - .count()) - .isEqualTo(1L); - assertThat( - lines.stream() - .filter(x -> - x.contains( - "messageBuilderSupport.header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString((context.replaceDynamicContentInString(basicUsername)+\":\"+context.replaceDynamicContentInString(basicPassword)).getBytes()));" - ) - ) - .count()) - .isEqualTo(1L); - } +// @Test +// void areReservedWordsEscapedTest() throws IOException { +// final CodegenConfigurator configurator = new CodegenConfigurator() +// .setGeneratorName(CODEGEN_NAME) +// .setInputSpec("src/test/resources/apis/petstore_reservedWords.yaml") +// .setOutputDir("target/JavaCitrusCodegenTest/petstore_escapedWords"); +// +// final ClientOptInput clientOptInput = configurator.toClientOptInput(); +// DefaultGenerator generator = new DefaultGenerator(); +// List outputFiles = generator.opts(clientOptInput).generate(); +// +// Optional file = outputFiles.stream().filter(x -> "PetApi.java".equals(x.getName())) +// .findFirst(); +// +// assertThat(file).isPresent(); +// +// List lines = Files.readAllLines(file.get().toPath(), StandardCharsets.UTF_8); +// +// // "name" is a reserved word, so it should be escaped with an underline for the second parameter +// assertThat(lines.stream().filter(x -> x.contains("\"name\", this._name")).count()).isEqualTo(1L); +// } +// +// @Test +// void arePathParamsFieldsPresent() { +// var fixture = new JavaCitrusCodegen(); +// String inputSpec = "src\\test\\resources\\apis\\petstore.yaml"; +// fixture.setInputSpec(inputSpec); +// +// fixture.processOpts(); +// +// assertThat(fixture.additionalProperties()).containsEntry("inputSpecRelative", "src/test/resources/apis/petstore.yaml"); +// } +// +// @Test +// void areBasicAuthFieldsPresent() throws IOException { +// final CodegenConfigurator configurator = new CodegenConfigurator() +// .setGeneratorName(CODEGEN_NAME) +// .setInputSpec("src/test/resources/apis/petstore.yaml") +// .setOutputDir("target/JavaCitrusCodegenTest/petstore"); +// +// final ClientOptInput clientOptInput = configurator.toClientOptInput(); +// DefaultGenerator generator = new DefaultGenerator(); +// List outputFiles = generator.opts(clientOptInput).generate(); +// +// Optional file = outputFiles.stream().filter(x -> "PetApi.java".equals(x.getName())) +// .findFirst(); +// +// assertThat(file).isPresent(); +// +// List lines = Files.readAllLines(file.get().toPath(), StandardCharsets.UTF_8); +// +// // "name" is a reserved word, so it should be escaped with an underline for the second parameter +// assertThat(lines.stream() +// .filter(x -> x.contains("@Value(\"${\" + \"apiEndpoint.basic.username:#{null}}\")")) +// .count()).isEqualTo(1L); +// assertThat( +// lines.stream().filter(x -> x.contains("private String basicUsername;")).count()).isEqualTo(1L); +// assertThat( +// lines +// .stream() +// .filter(x -> +// x.contains( +// "messageBuilderSupport.header(\"Authorization\", \"Basic \" + Base64.getEncoder().encodeToString((context.replaceDynamicContentInString(basicUsername)+\":\"+context.replaceDynamicContentInString(basicPassword)).getBytes()));" +// ) +// ) +// .count() +// ).isEqualTo(1L); +// } } diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/ServiceLoaderTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/ServiceLoaderTest.java index 419cea9ade..6b17f01f13 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/ServiceLoaderTest.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/ServiceLoaderTest.java @@ -5,8 +5,8 @@ import java.util.List; import java.util.ServiceLoader; import java.util.ServiceLoader.Provider; -import org.citrusframework.openapi.generator.util.TestApiActionBuilderCustomizer; import org.citrusframework.testapi.ApiActionBuilderCustomizerService; +import org.citrusframework.openapi.generator.util.TestApiActionBuilderCustomizer; import org.junit.jupiter.api.Test; class ServiceLoaderTest { diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/util/TestApiActionBuilderCustomizer.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/util/TestApiActionBuilderCustomizer.java index 1b0b8824a7..0aaa9761ab 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/util/TestApiActionBuilderCustomizer.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/util/TestApiActionBuilderCustomizer.java @@ -1,6 +1,7 @@ package org.citrusframework.openapi.generator.util; import org.citrusframework.TestAction; +import org.citrusframework.TestActionBuilder; import org.citrusframework.actions.SendMessageAction.SendMessageActionBuilder; import org.citrusframework.context.TestContext; import org.citrusframework.testapi.ApiActionBuilderCustomizerService; diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml index 79249f26ed..5524ac3c41 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml @@ -164,6 +164,16 @@ paths: required: true type: integer format: int64 + - name: verbose + description: Output details + in: query + required: false + type: boolean + - name: correlationIds + description: ID to trace a request + in: header + required: false + type: string responses: '200': description: successful operation diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json index b68ed32a5e..27d38acb91 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json @@ -1,6 +1,11 @@ { - "id": 12, + "id": 2002, "name": "Snoopy", - "tags": ["comic dog"], - "photoUrls": ["url1", "url2"] -} + "tags": [], + "photoUrls": [], + "category": { + "name": "a name", + "id": 112233 + }, + "status": "available" +} \ No newline at end of file diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestAbstractTestRequest.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestAbstractTestRequest.java new file mode 100644 index 0000000000..478a8e8742 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestAbstractTestRequest.java @@ -0,0 +1,261 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.multiparttest.citrus; + + +import static org.springframework.util.CollectionUtils.isEmpty; + +import jakarta.annotation.Generated; +import jakarta.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import javax.sql.DataSource; +import org.citrusframework.actions.AbstractTestAction; +import org.citrusframework.actions.ReceiveMessageAction; +import org.citrusframework.context.TestContext; +import org.citrusframework.http.actions.HttpActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder; +import org.citrusframework.http.actions.HttpClientResponseActionBuilder; +import org.citrusframework.http.actions.HttpClientResponseActionBuilder.HttpMessageBuilderSupport; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.message.Message; +import org.citrusframework.testapi.ApiActionBuilderCustomizerService; +import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.spi.Resources; +import org.citrusframework.validation.DelegatingPayloadVariableExtractor; +import org.citrusframework.validation.PathExpressionValidationContext; +import org.citrusframework.validation.json.JsonMessageValidationContext; +import org.citrusframework.validation.script.ScriptValidationContext; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public abstract class MultipartTestAbstractTestRequest extends AbstractTestAction { + + protected final Marker coverageMarker = MarkerFactory.getMarker("MULTIPARTTEST-API-COVERAGE"); + + @Autowired + @Qualifier("multipartTestEndpoint") + protected HttpClient httpClient; + + @Autowired(required = false) + protected DataSource dataSource; + + @Autowired(required = false) + private List actionBuilderCustomizerServices; + + // attributes of differentNodes + protected boolean schemaValidation; + protected String schema; + protected String bodyContentType; + protected String bodyLiteralContentType; + protected String bodyFile; + protected String bodyLiteral; + protected String responseAcceptType = "*/*"; + protected String responseType = "json"; + protected int responseStatus = 200; + protected String responseReasonPhrase = "OK"; + protected String responseVersion = "HTTP/1.1"; + + // children of response element + protected String resource; + protected Map responseVariable; // Contains the 'JSON-PATH' as key and the 'VARIABLE NAME' as value + protected Map responseValue; // Contains the 'JSON-PATH' as key and the 'VALUE TO BE VALIDATED' as value + protected Map cookies; + protected Map headers; + protected String script; + protected String type; // default script type is groovy - supported types see com.consol.citrus.script.ScriptTypes + + @Override + public void doExecute(TestContext context) { + sendRequest(context); + recieveResponse(context); + } + + /** + * This method receives the HTTP-Response. + * + * @deprecated use {@link MultipartTestAbstractTestRequest#receiveResponse(TestContext)} instead. + */ + public ReceiveMessageAction recieveResponse(TestContext context) { + + HttpClientResponseActionBuilder httpClientResponseActionBuilder = new HttpActionBuilder().client(httpClient).receive().response(); + HttpMessageBuilderSupport messageBuilderSupport = httpClientResponseActionBuilder.getMessageBuilderSupport(); + + messageBuilderSupport + .statusCode(responseStatus) + .reasonPhrase(responseReasonPhrase) + .version(responseVersion) + .validate(new JsonMessageValidationContext.Builder().schemaValidation(schemaValidation).schema(schema)); + + if (resource != null) { + messageBuilderSupport.body(Resources.create(resource)); + } + + if (!isEmpty(responseVariable)) { + DelegatingPayloadVariableExtractor.Builder extractorBuilder = new DelegatingPayloadVariableExtractor.Builder(); + responseVariable.forEach(extractorBuilder::expression); + messageBuilderSupport.extract(extractorBuilder); + } + + if (!isEmpty(responseValue)) { + PathExpressionValidationContext.Builder validationContextBuilder = new PathExpressionValidationContext.Builder(); + responseValue.forEach(validationContextBuilder::expression); + messageBuilderSupport.validate(validationContextBuilder); + } + + if (script != null) { + ScriptValidationContext.Builder scriptValidationContextBuilder = new ScriptValidationContext.Builder(); + if (type != null) { + scriptValidationContextBuilder.scriptType(type); + } + scriptValidationContextBuilder.script(script); + messageBuilderSupport.validate(scriptValidationContextBuilder); + } + + messageBuilderSupport.type(responseType); + httpClientResponseActionBuilder.withReferenceResolver(context.getReferenceResolver()); + var responseAction = httpClientResponseActionBuilder.build(); + + responseAction.execute(context); + + return responseAction; + } + + public @Nullable Message receiveResponse(TestContext context) { + var responseAction = recieveResponse(context); + + var messageStore = context.getMessageStore(); + return messageStore.getMessage(messageStore.constructMessageName(responseAction, httpClient)); + } + + public abstract void sendRequest(TestContext context); + + public void setSchemaValidation(boolean schemaValidation) { + this.schemaValidation = schemaValidation; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public void setBodyLiteral(String bodyLiteral) { + this.bodyLiteral = bodyLiteral; + } + + public void setBodyContentType(String bodyContentType) { + this.bodyContentType = bodyContentType; + } + + public void setBodyLiteralContentType(String bodyLiteralContentType) { + this.bodyLiteralContentType = bodyLiteralContentType; + } + + public void setResponseAcceptType(String responseAcceptType) { + this.responseAcceptType = responseAcceptType; + } + + public void setCookie(Map cookies) { + this.cookies = cookies; + } + + public void setHeader(Map headers) { + this.headers = headers; + } + + public void setBodyFile(String bodyFile) { + this.bodyFile = bodyFile; + } + + public void setResponseType(String responseType) { + this.responseType = responseType; + } + + public void setResponseStatus(int responseStatus) { + this.responseStatus = responseStatus; + } + + public void setResponseReasonPhrase(String responseReasonPhrase) { + this.responseReasonPhrase = responseReasonPhrase; + } + + public void setResponseVersion(String responseVersion) { + this.responseVersion = responseVersion; + } + + public void setResource(String resource) { + this.resource = resource; + } + + public void setResponseVariable(Map responseVariable) { + this.responseVariable = responseVariable; + } + + public void setResponseValue(Map responseValue) { + this.responseValue = responseValue; + } + + public void setScript(String script) { + this.script = script; + } + + public void setType(String type) { + this.type = type; + } + + protected HttpClientRequestActionBuilder customizeBuilder(GeneratedApi generatedApi, + TestContext context, HttpClientRequestActionBuilder httpClientRequestActionBuilder) { + + httpClientRequestActionBuilder = customizeByBeans(generatedApi, context, + httpClientRequestActionBuilder); + + httpClientRequestActionBuilder = customizeBySpi(generatedApi, context, httpClientRequestActionBuilder); + + return httpClientRequestActionBuilder; + } + + private HttpClientRequestActionBuilder customizeBySpi(GeneratedApi generatedApi, TestContext context, + HttpClientRequestActionBuilder httpClientRequestActionBuilder) { + ServiceLoader serviceLoader = ServiceLoader.load( + ApiActionBuilderCustomizerService.class, ApiActionBuilderCustomizerService.class.getClassLoader()); + for (ApiActionBuilderCustomizerService service :serviceLoader) { + httpClientRequestActionBuilder = service.build(generatedApi, this, context, httpClientRequestActionBuilder); + } + return httpClientRequestActionBuilder; + } + + private HttpClientRequestActionBuilder customizeByBeans( + GeneratedApi generatedApi, TestContext context, + HttpClientRequestActionBuilder httpClientRequestActionBuilder) { + if (actionBuilderCustomizerServices != null) { + for (ApiActionBuilderCustomizerService apiActionBuilderCustomizer : actionBuilderCustomizerServices) { + httpClientRequestActionBuilder = apiActionBuilderCustomizer.build(generatedApi, this, + context, httpClientRequestActionBuilder); + } + } + return httpClientRequestActionBuilder; + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestBeanDefinitionParser.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestBeanDefinitionParser.java new file mode 100644 index 0000000000..1ca5480c9e --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestBeanDefinitionParser.java @@ -0,0 +1,231 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.multiparttest.citrus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.processing.Generated; + +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class MultipartTestBeanDefinitionParser implements BeanDefinitionParser { + + private static final String COOKIE = "cookie"; + private static final String HEADER = "header"; + private static final String SOAP_HEADER = "soapHeader"; + private static final String MIME_HEADER = "mimeHeader"; + private static final String NAME = "name"; + private static final String REQUEST_BODY = "body"; + private static final String REQUEST_BODY_LITERAL = "bodyLiteral"; + private static final String MULTIPART_BODY = "multipartBody"; + private static final String RESPONSE = "response"; + private static final String RESPONSE_JSONPATH = "json-path"; + private static final String RESPONSE_XPATH = "xpath"; + private static final String EXPRESSION = "expression"; + private static final String VALUE = "value"; + private static final String RESPONSE_RESOURCE = "resource"; + private static final String FILE = "file"; + private static final String RESPONSE_VARIABLE = "responseVariable"; + private static final String RESPONSE_VALUE = "responseValue"; + private static final String SCRIPT = "script"; + private static final String TYPE = "type"; + private static final String SQL = "sql"; + private static final String COLUMN = "column"; + private static final String VARIABLE = "variable"; + // new + private static final String SCHEMA = "schema"; + // new + private static final String SCHEMA_VALIDATION = "schemaValidation"; + + private final Class beanClass; + + public MultipartTestBeanDefinitionParser(Class beanClass) { + this.beanClass = beanClass; + } + + public BeanDefinition parse(Element element) { + return parse(element, null); + } + + /** + * Note: The {@link MultipartTestBeanDefinitionParser#parse(Element element)} allows access direct + * access without the {@link org.springframework.beans.factory.xml.ParserContext} for convenience. + */ + @Override + public BeanDefinition parse(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass); + retrieveRootNodeAttributes(element, builder); + retrieveOptionalNodeAttributes(element, REQUEST_BODY, builder); + retrieveTextContentAndNodeAttributes(element, REQUEST_BODY_LITERAL, builder); + retrieveOptionalNodeAttributes(element, RESPONSE, builder); + retrieveParamNodeData(element, builder, COOKIE); + retrieveParamNodeData(element, builder, HEADER); + retrieveParamNodeData(element, builder, SOAP_HEADER); + retrieveParamNodeData(element, builder, MIME_HEADER); + retrieveOptionalNodeAttributes(element, SCHEMA, builder); + retrieveOptionalNodeAttributes(element, SCHEMA_VALIDATION, builder); + retrieveOptionalMultipartElements(element, builder); + retrieveResponseNodeData(element, builder); + builder.addPropertyValue("name", element.getTagName()); + return builder.getBeanDefinition(); + } + + private void retrieveOptionalMultipartElements(Element element, BeanDefinitionBuilder builder) { + var multipartBodyElement = DomUtils.getChildElementByTagName(element, MULTIPART_BODY); + if (multipartBodyElement != null) { + var multipartBodyChildElements = DomUtils.getChildElements(multipartBodyElement); + for(int i = 0; i < multipartBodyChildElements.size(); i++){ + var multipartBodyChildElement = multipartBodyChildElements.get(i); + String propertyName = Conventions.attributeNameToPropertyName(multipartBodyChildElement.getLocalName()); + builder.addPropertyValue(propertyName, multipartBodyChildElement.getTextContent()); + } + } + } + + private void retrieveRootNodeAttributes(Element element, BeanDefinitionBuilder builder) { + NamedNodeMap attributes = element.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName), "Illegal property name returned, it must not be null or empty."); + builder.addPropertyValue(propertyName, attribute.getValue()); + } + } + + private void retrieveOptionalNodeAttributes(Element element, String elementName, BeanDefinitionBuilder builder) { + if (!DomUtils.getChildElementsByTagName(element, elementName).isEmpty()) { + Element el = DomUtils.getChildElementsByTagName(element, elementName).get(0); + NamedNodeMap attributes = el.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName), "Illegal property name returned, it must not be null or empty."); + String variableName = el.getLocalName() + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + builder.addPropertyValue(variableName, attribute.getValue()); + } + } + } + + private void retrieveTextContentAndNodeAttributes(Element element, String elementName, BeanDefinitionBuilder builder) { + if (!DomUtils.getChildElementsByTagName(element, elementName).isEmpty()) { + Element el1 = DomUtils.getChildElementsByTagName(element, elementName).get(0); + NamedNodeMap attributes = el1.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName1 = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName1), "Illegal property name returned, it must not be null or empty."); + String variableName = el1.getLocalName() + propertyName1.substring(0, 1).toUpperCase() + propertyName1.substring(1); + builder.addPropertyValue(variableName, attribute.getValue()); + } + Element el = DomUtils.getChildElementsByTagName(element, elementName).get(0); + builder.addPropertyValue(elementName, el.getTextContent()); + } + } + + private void retrieveParamNodeData(Element element, BeanDefinitionBuilder builder, String paramType) { + if (!DomUtils.getChildElementsByTagName(element, paramType).isEmpty()) { + Map params = new HashMap<>(); + List elements = DomUtils.getChildElementsByTagName(element, paramType); + elements.forEach(e -> { + String name = e.getAttribute(NAME); + String value = e.getAttribute(VALUE); + + Assert.state(StringUtils.isNotBlank(name), "Illegal attribute value returned. The 'name' attribute must not be null or empty."); + Assert.state(StringUtils.isNotBlank(value), "Illegal attribute value returned. The 'value' attribute must not be null or empty."); + + params.put(name, value); + }); + builder.addPropertyValue(paramType, params); + } + } + + private void retrieveResponseNodeData(Element element, BeanDefinitionBuilder builder) { + + if (!DomUtils.getChildElementsByTagName(element, RESPONSE).isEmpty()) { + Element response = DomUtils.getChildElementsByTagName(element, RESPONSE).get(0); + List elements = DomUtils.getChildElements(response); + + Map responseVariable = new HashMap<>(); + Map responseValue = new HashMap<>(); + + for (int i = 0; i < elements.size(); i++) { + Element e = elements.get(i); + + if (e.getTagName().contains(RESPONSE_JSONPATH) || e.getTagName().contains(RESPONSE_XPATH)) { + String expression = e.getAttribute(EXPRESSION); + String value = e.getAttribute(VALUE); + + Assert.state(StringUtils.isNotBlank(expression), "Illegal attribute value returned. The 'expression' attribute must not be null or empty."); + Assert.state(StringUtils.isNotBlank(value), "Illegal attribute value returned. The 'value' attribute must not be null or empty."); + + // variable to save @variable('ebid')@ else value to validate + if (value.matches("\\@variable\\('.*'\\)\\@")) { + Matcher match = Pattern.compile("\\'(.*?)\\'").matcher(value); + if (match.find()) { + responseVariable.put(expression, value.substring(match.start() + 1, match.end() - 1)); + } + } else { + responseValue.put(expression, value); + } + } else if (e.getTagName().contains(SCRIPT)) { + String script = e.getTextContent(); + Assert.state(StringUtils.isNotBlank(script), "Illegal attribute value returned. The 'script' attribute must not be null or empty."); + builder.addPropertyValue(SCRIPT, script); + + if (!e.getAttribute(TYPE).isEmpty()) { + String type = e.getAttribute(TYPE); + Assert.state(StringUtils.isNotBlank(type), "Illegal attribute value returned. The 'type' attribute must not be null or empty."); + builder.addPropertyValue(TYPE, type); + } + } else if (e.getTagName().contains(RESPONSE_RESOURCE)) { + String filePath = e.getAttribute(FILE); + Assert.state(StringUtils.isNotBlank(filePath), "Illegal attribute value returned. The 'file' attribute must not be null or empty."); + builder.addPropertyValue(RESPONSE_RESOURCE, filePath); + } + + } + + builder.addPropertyValue(RESPONSE_VARIABLE, responseVariable); + builder.addPropertyValue(RESPONSE_VALUE, responseValue); + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/extension/MultipartTestNamespaceHandler.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/extension/MultipartTestNamespaceHandler.java new file mode 100644 index 0000000000..9f36ba2472 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/extension/MultipartTestNamespaceHandler.java @@ -0,0 +1,45 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.multiparttest.citrus.extension; + +import org.citrusframework.openapi.generator.rest.multiparttest.request.MultiparttestControllerApi; +import org.citrusframework.openapi.generator.rest.multiparttest.citrus.MultipartTestBeanDefinitionParser; + +import javax.annotation.processing.Generated; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class MultipartTestNamespaceHandler extends NamespaceHandlerSupport { + + @Override + public void init() { + registerBeanDefinitionParser("deleteObjectRequest", new MultipartTestBeanDefinitionParser(MultiparttestControllerApi.DeleteObjectRequest.class)); + registerBeanDefinitionParser("fileExistsRequest", new MultipartTestBeanDefinitionParser(MultiparttestControllerApi.FileExistsRequest.class)); + registerBeanDefinitionParser("generateReportRequest", new MultipartTestBeanDefinitionParser(MultiparttestControllerApi.GenerateReportRequest.class)); + registerBeanDefinitionParser("multipleDatatypesRequest", new MultipartTestBeanDefinitionParser(MultiparttestControllerApi.MultipleDatatypesRequest.class)); + registerBeanDefinitionParser("postFileRequest", new MultipartTestBeanDefinitionParser(MultiparttestControllerApi.PostFileRequest.class)); + registerBeanDefinitionParser("postRandomRequest", new MultipartTestBeanDefinitionParser(MultiparttestControllerApi.PostRandomRequest.class)); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/Metadata.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/Metadata.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/Metadata.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/PutObjectResult.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/PutObjectResult.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/PutObjectResult.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/request/MultiparttestControllerApi.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/request/MultiparttestControllerApi.java new file mode 100644 index 0000000000..bde720eb16 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/request/MultiparttestControllerApi.java @@ -0,0 +1,766 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.multiparttest.request; + +import jakarta.annotation.Generated; +import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.testapi.GeneratedApiRequest; +import jakarta.servlet.http.Cookie; +import org.apache.commons.lang3.StringUtils; +import org.citrusframework.context.TestContext; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.spi.Resources; +import org.citrusframework.http.actions.HttpActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder.HttpMessageBuilderSupport; +import org.citrusframework.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import org.citrusframework.openapi.generator.rest.multiparttest.citrus.MultipartTestAbstractTestRequest; + +import java.io.IOException; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class MultiparttestControllerApi implements GeneratedApi +{ + + public static final MultiparttestControllerApi INSTANCE = new MultiparttestControllerApi(); + + public String getApiTitle() { + return "multiparttest API"; + } + + public String getApiVersion() { + return "2.0.0"; + } + + public String getApiPrefix() { + return "MultipartTest"; + } + + public Map getApiInfoExtensions() { + Map infoExtensionMap = new HashMap<>(); + infoExtensionMap.put("x-citrus-api-name", "multiparttest-rest-resource"); + infoExtensionMap.put("x-citrus-app", "MPT"); + return infoExtensionMap; + } + + /** deleteObject (DELETE /api/v2/multitest-file/{bucket}/{filename}) + Delete file. + + **/ + public static class DeleteObjectRequest extends MultipartTestAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/api/v2/multitest-file/{bucket}/{filename}"; + private final Logger coverageLogger = LoggerFactory.getLogger(DeleteObjectRequest.class); + + private String bucket; + + private String filename; + + + public DeleteObjectRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("MultipartTest".toLowerCase() + ":deleteObjectRequestType"); + } + + public String getOperationName() { + return "deleteObject"; + } + + public String getMethod() { + return "DELETE"; + } + + public String getPath() { + return "/api/v2/multitest-file/{bucket}/{filename}"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .delete(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "deleteObject;DELETE;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "bucket" + "}", bucket);endpoint = endpoint.replace("{" + "filename" + "}", filename); + return endpoint; + } + } + /** fileExists (GET /api/v2/multitest-file/{bucket}/{filename}/exists) + Checks if file exist. + + **/ + public static class FileExistsRequest extends MultipartTestAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/api/v2/multitest-file/{bucket}/{filename}/exists"; + private final Logger coverageLogger = LoggerFactory.getLogger(FileExistsRequest.class); + + private String bucket; + + private String filename; + + + public FileExistsRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("MultipartTest".toLowerCase() + ":fileExistsRequestType"); + } + + public String getOperationName() { + return "fileExists"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/api/v2/multitest-file/{bucket}/{filename}/exists"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .get(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "fileExists;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "bucket" + "}", bucket);endpoint = endpoint.replace("{" + "filename" + "}", filename); + return endpoint; + } + } + /** generateReport (POST /api/v2/multitest-reportgeneration) + summary + + **/ + public static class GenerateReportRequest extends MultipartTestAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/api/v2/multitest-reportgeneration"; + private final Logger coverageLogger = LoggerFactory.getLogger(GenerateReportRequest.class); + + private String template; + + private String additionalData; + + private String _schema; + + + public GenerateReportRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("MultipartTest".toLowerCase() + ":generateReportRequestType"); + } + + public String getOperationName() { + return "generateReport"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/api/v2/multitest-reportgeneration"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + MultiValueMap multiValues = new LinkedMultiValueMap<>(); + if(StringUtils.isBlank(template)) { + throw new CitrusRuntimeException(String.format("Required attribute '%s' is not specified", "template")); + } + if (StringUtils.isNotBlank(template)) { + // first try to load from resource + ClassPathResource resource = null; + try { + resource = new ClassPathResource(template); + } + catch(Exception ignore) { + // Use plain text instead of resource + } + + if(resource != null && resource.exists()){ + multiValues.add("template", resource); + } else { + multiValues.add("template", template); + } + bodyLog += template.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + if (StringUtils.isNotBlank(additionalData)) { + // first try to load from resource + ClassPathResource resource = null; + try { + resource = new ClassPathResource(additionalData); + } + catch(Exception ignore) { + // Use plain text instead of resource + } + + if(resource != null && resource.exists()){ + multiValues.add("additionalData", resource); + } else { + multiValues.add("additionalData", additionalData); + } + bodyLog += additionalData.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + if (StringUtils.isNotBlank(_schema)) { + // first try to load from resource + ClassPathResource resource = null; + try { + resource = new ClassPathResource(_schema); + } + catch(Exception ignore) { + // Use plain text instead of resource + } + + if(resource != null && resource.exists()){ + multiValues.add("_schema", resource); + } else { + multiValues.add("_schema", _schema); + } + bodyLog += _schema.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + + bodyLog += "\";\"" + MediaType.MULTIPART_FORM_DATA_VALUE + "\""; + messageBuilderSupport.contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .body(multiValues); + + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "generateReport;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setTemplate(String template) { + this.template = template; + } + + public void setAdditionalData(String additionalData) { + this.additionalData = additionalData; + } + + public void set_schema(String _schema) { + this._schema = _schema; + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** multipleDatatypes (POST /api/v2/multitest-multipledatatypes) + summary + + **/ + public static class MultipleDatatypesRequest extends MultipartTestAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/api/v2/multitest-multipledatatypes"; + private final Logger coverageLogger = LoggerFactory.getLogger(MultipleDatatypesRequest.class); + + private String stringData; + + private String booleanData; + + private String integerData; + + + public MultipleDatatypesRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("MultipartTest".toLowerCase() + ":multipleDatatypesRequestType"); + } + + public String getOperationName() { + return "multipleDatatypes"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/api/v2/multitest-multipledatatypes"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + MultiValueMap multiValues = new LinkedMultiValueMap<>(); + if (StringUtils.isNotBlank(stringData)) { + // first try to load from resource + ClassPathResource resource = null; + try { + resource = new ClassPathResource(stringData); + } + catch(Exception ignore) { + // Use plain text instead of resource + } + + if(resource != null && resource.exists()){ + multiValues.add("stringData", resource); + } else { + multiValues.add("stringData", stringData); + } + bodyLog += stringData.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + if (StringUtils.isNotBlank(booleanData)) { + // first try to load from resource + ClassPathResource resource = null; + try { + resource = new ClassPathResource(booleanData); + } + catch(Exception ignore) { + // Use plain text instead of resource + } + + if(resource != null && resource.exists()){ + multiValues.add("booleanData", resource); + } else { + multiValues.add("booleanData", booleanData); + } + bodyLog += booleanData.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + if (StringUtils.isNotBlank(integerData)) { + // first try to load from resource + ClassPathResource resource = null; + try { + resource = new ClassPathResource(integerData); + } + catch(Exception ignore) { + // Use plain text instead of resource + } + + if(resource != null && resource.exists()){ + multiValues.add("integerData", resource); + } else { + multiValues.add("integerData", integerData); + } + bodyLog += integerData.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + + bodyLog += "\";\"" + MediaType.MULTIPART_FORM_DATA_VALUE + "\""; + messageBuilderSupport.contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .body(multiValues); + + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "multipleDatatypes;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setStringData(String stringData) { + this.stringData = stringData; + } + + public void setBooleanData(String booleanData) { + this.booleanData = booleanData; + } + + public void setIntegerData(String integerData) { + this.integerData = integerData; + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** postFile (POST /api/v2/multitest-file/{bucket}/{filename}) + Uploads file. + + **/ + public static class PostFileRequest extends MultipartTestAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/api/v2/multitest-file/{bucket}/{filename}"; + private final Logger coverageLogger = LoggerFactory.getLogger(PostFileRequest.class); + + private String bucket; + + private String filename; + + private String multipartFile; + + + public PostFileRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("MultipartTest".toLowerCase() + ":postFileRequestType"); + } + + public String getOperationName() { + return "postFile"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/api/v2/multitest-file/{bucket}/{filename}"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + MultiValueMap multiValues = new LinkedMultiValueMap<>(); + if (StringUtils.isNotBlank(multipartFile)) { + multiValues.add("multipartFile", new ClassPathResource(multipartFile)); + bodyLog += multipartFile.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + + bodyLog += "\";\"" + MediaType.MULTIPART_FORM_DATA_VALUE + "\""; + messageBuilderSupport.contentType(MediaType.MULTIPART_FORM_DATA_VALUE) + .body(multiValues); + + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "postFile;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public void setMultipartFile(String multipartFile) { + this.multipartFile = multipartFile; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "bucket" + "}", bucket);endpoint = endpoint.replace("{" + "filename" + "}", filename); + return endpoint; + } + } + /** postRandom (POST /api/v2/multitest-file/{bucket}/{filename}/random) + Uploads random file. + + **/ + public static class PostRandomRequest extends MultipartTestAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/api/v2/multitest-file/{bucket}/{filename}/random"; + private final Logger coverageLogger = LoggerFactory.getLogger(PostRandomRequest.class); + + private String bucket; + + private String filename; + + + public PostRandomRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("MultipartTest".toLowerCase() + ":postRandomRequestType"); + } + + public String getOperationName() { + return "postRandom"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/api/v2/multitest-file/{bucket}/{filename}/random"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "postRandom;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setBucket(String bucket) { + this.bucket = bucket; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "bucket" + "}", bucket);endpoint = endpoint.replace("{" + "filename" + "}", filename); + return endpoint; + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/spring/MultipartTestBeanConfiguration.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/spring/MultipartTestBeanConfiguration.java new file mode 100644 index 0000000000..d93b11f104 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/spring/MultipartTestBeanConfiguration.java @@ -0,0 +1,72 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.multiparttest.spring; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +import org.citrusframework.openapi.generator.rest.multiparttest.request.MultiparttestControllerApi; +import javax.annotation.processing.Generated; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +@Configuration +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class MultipartTestBeanConfiguration { + + @Bean + @Scope(SCOPE_PROTOTYPE) + public MultiparttestControllerApi.DeleteObjectRequest deleteObjectRequest() { + return new MultiparttestControllerApi.DeleteObjectRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public MultiparttestControllerApi.FileExistsRequest fileExistsRequest() { + return new MultiparttestControllerApi.FileExistsRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public MultiparttestControllerApi.GenerateReportRequest generateReportRequest() { + return new MultiparttestControllerApi.GenerateReportRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public MultiparttestControllerApi.MultipleDatatypesRequest multipleDatatypesRequest() { + return new MultiparttestControllerApi.MultipleDatatypesRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public MultiparttestControllerApi.PostFileRequest postFileRequest() { + return new MultiparttestControllerApi.PostFileRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public MultiparttestControllerApi.PostRandomRequest postRandomRequest() { + return new MultiparttestControllerApi.PostRandomRequest(); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreAbstractTestRequest.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreAbstractTestRequest.java new file mode 100644 index 0000000000..1e9282b44b --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreAbstractTestRequest.java @@ -0,0 +1,261 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.petstore.citrus; + + +import static org.springframework.util.CollectionUtils.isEmpty; + +import jakarta.annotation.Generated; +import jakarta.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import javax.sql.DataSource; +import org.citrusframework.actions.AbstractTestAction; +import org.citrusframework.actions.ReceiveMessageAction; +import org.citrusframework.context.TestContext; +import org.citrusframework.http.actions.HttpActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder; +import org.citrusframework.http.actions.HttpClientResponseActionBuilder; +import org.citrusframework.http.actions.HttpClientResponseActionBuilder.HttpMessageBuilderSupport; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.message.Message; +import org.citrusframework.testapi.ApiActionBuilderCustomizerService; +import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.spi.Resources; +import org.citrusframework.validation.DelegatingPayloadVariableExtractor; +import org.citrusframework.validation.PathExpressionValidationContext; +import org.citrusframework.validation.json.JsonMessageValidationContext; +import org.citrusframework.validation.script.ScriptValidationContext; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public abstract class PetStoreAbstractTestRequest extends AbstractTestAction { + + protected final Marker coverageMarker = MarkerFactory.getMarker("PETSTORE-API-COVERAGE"); + + @Autowired + @Qualifier("petStoreEndpoint") + protected HttpClient httpClient; + + @Autowired(required = false) + protected DataSource dataSource; + + @Autowired(required = false) + private List actionBuilderCustomizerServices; + + // attributes of differentNodes + protected boolean schemaValidation; + protected String schema; + protected String bodyContentType; + protected String bodyLiteralContentType; + protected String bodyFile; + protected String bodyLiteral; + protected String responseAcceptType = "*/*"; + protected String responseType = "json"; + protected int responseStatus = 200; + protected String responseReasonPhrase = "OK"; + protected String responseVersion = "HTTP/1.1"; + + // children of response element + protected String resource; + protected Map responseVariable; // Contains the 'JSON-PATH' as key and the 'VARIABLE NAME' as value + protected Map responseValue; // Contains the 'JSON-PATH' as key and the 'VALUE TO BE VALIDATED' as value + protected Map cookies; + protected Map headers; + protected String script; + protected String type; // default script type is groovy - supported types see com.consol.citrus.script.ScriptTypes + + @Override + public void doExecute(TestContext context) { + sendRequest(context); + recieveResponse(context); + } + + /** + * This method receives the HTTP-Response. + * + * @deprecated use {@link PetStoreAbstractTestRequest#receiveResponse(TestContext)} instead. + */ + public ReceiveMessageAction recieveResponse(TestContext context) { + + HttpClientResponseActionBuilder httpClientResponseActionBuilder = new HttpActionBuilder().client(httpClient).receive().response(); + HttpMessageBuilderSupport messageBuilderSupport = httpClientResponseActionBuilder.getMessageBuilderSupport(); + + messageBuilderSupport + .statusCode(responseStatus) + .reasonPhrase(responseReasonPhrase) + .version(responseVersion) + .validate(new JsonMessageValidationContext.Builder().schemaValidation(schemaValidation).schema(schema)); + + if (resource != null) { + messageBuilderSupport.body(Resources.create(resource)); + } + + if (!isEmpty(responseVariable)) { + DelegatingPayloadVariableExtractor.Builder extractorBuilder = new DelegatingPayloadVariableExtractor.Builder(); + responseVariable.forEach(extractorBuilder::expression); + messageBuilderSupport.extract(extractorBuilder); + } + + if (!isEmpty(responseValue)) { + PathExpressionValidationContext.Builder validationContextBuilder = new PathExpressionValidationContext.Builder(); + responseValue.forEach(validationContextBuilder::expression); + messageBuilderSupport.validate(validationContextBuilder); + } + + if (script != null) { + ScriptValidationContext.Builder scriptValidationContextBuilder = new ScriptValidationContext.Builder(); + if (type != null) { + scriptValidationContextBuilder.scriptType(type); + } + scriptValidationContextBuilder.script(script); + messageBuilderSupport.validate(scriptValidationContextBuilder); + } + + messageBuilderSupport.type(responseType); + httpClientResponseActionBuilder.withReferenceResolver(context.getReferenceResolver()); + var responseAction = httpClientResponseActionBuilder.build(); + + responseAction.execute(context); + + return responseAction; + } + + public @Nullable Message receiveResponse(TestContext context) { + var responseAction = recieveResponse(context); + + var messageStore = context.getMessageStore(); + return messageStore.getMessage(messageStore.constructMessageName(responseAction, httpClient)); + } + + public abstract void sendRequest(TestContext context); + + public void setSchemaValidation(boolean schemaValidation) { + this.schemaValidation = schemaValidation; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public void setBodyLiteral(String bodyLiteral) { + this.bodyLiteral = bodyLiteral; + } + + public void setBodyContentType(String bodyContentType) { + this.bodyContentType = bodyContentType; + } + + public void setBodyLiteralContentType(String bodyLiteralContentType) { + this.bodyLiteralContentType = bodyLiteralContentType; + } + + public void setResponseAcceptType(String responseAcceptType) { + this.responseAcceptType = responseAcceptType; + } + + public void setCookie(Map cookies) { + this.cookies = cookies; + } + + public void setHeader(Map headers) { + this.headers = headers; + } + + public void setBodyFile(String bodyFile) { + this.bodyFile = bodyFile; + } + + public void setResponseType(String responseType) { + this.responseType = responseType; + } + + public void setResponseStatus(int responseStatus) { + this.responseStatus = responseStatus; + } + + public void setResponseReasonPhrase(String responseReasonPhrase) { + this.responseReasonPhrase = responseReasonPhrase; + } + + public void setResponseVersion(String responseVersion) { + this.responseVersion = responseVersion; + } + + public void setResource(String resource) { + this.resource = resource; + } + + public void setResponseVariable(Map responseVariable) { + this.responseVariable = responseVariable; + } + + public void setResponseValue(Map responseValue) { + this.responseValue = responseValue; + } + + public void setScript(String script) { + this.script = script; + } + + public void setType(String type) { + this.type = type; + } + + protected HttpClientRequestActionBuilder customizeBuilder(GeneratedApi generatedApi, + TestContext context, HttpClientRequestActionBuilder httpClientRequestActionBuilder) { + + httpClientRequestActionBuilder = customizeByBeans(generatedApi, context, + httpClientRequestActionBuilder); + + httpClientRequestActionBuilder = customizeBySpi(generatedApi, context, httpClientRequestActionBuilder); + + return httpClientRequestActionBuilder; + } + + private HttpClientRequestActionBuilder customizeBySpi(GeneratedApi generatedApi, TestContext context, + HttpClientRequestActionBuilder httpClientRequestActionBuilder) { + ServiceLoader serviceLoader = ServiceLoader.load( + ApiActionBuilderCustomizerService.class, ApiActionBuilderCustomizerService.class.getClassLoader()); + for (ApiActionBuilderCustomizerService service :serviceLoader) { + httpClientRequestActionBuilder = service.build(generatedApi, this, context, httpClientRequestActionBuilder); + } + return httpClientRequestActionBuilder; + } + + private HttpClientRequestActionBuilder customizeByBeans( + GeneratedApi generatedApi, TestContext context, + HttpClientRequestActionBuilder httpClientRequestActionBuilder) { + if (actionBuilderCustomizerServices != null) { + for (ApiActionBuilderCustomizerService apiActionBuilderCustomizer : actionBuilderCustomizerServices) { + httpClientRequestActionBuilder = apiActionBuilderCustomizer.build(generatedApi, this, + context, httpClientRequestActionBuilder); + } + } + return httpClientRequestActionBuilder; + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreBeanDefinitionParser.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreBeanDefinitionParser.java new file mode 100644 index 0000000000..b52f5b5f42 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreBeanDefinitionParser.java @@ -0,0 +1,231 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.petstore.citrus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.processing.Generated; + +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class PetStoreBeanDefinitionParser implements BeanDefinitionParser { + + private static final String COOKIE = "cookie"; + private static final String HEADER = "header"; + private static final String SOAP_HEADER = "soapHeader"; + private static final String MIME_HEADER = "mimeHeader"; + private static final String NAME = "name"; + private static final String REQUEST_BODY = "body"; + private static final String REQUEST_BODY_LITERAL = "bodyLiteral"; + private static final String MULTIPART_BODY = "multipartBody"; + private static final String RESPONSE = "response"; + private static final String RESPONSE_JSONPATH = "json-path"; + private static final String RESPONSE_XPATH = "xpath"; + private static final String EXPRESSION = "expression"; + private static final String VALUE = "value"; + private static final String RESPONSE_RESOURCE = "resource"; + private static final String FILE = "file"; + private static final String RESPONSE_VARIABLE = "responseVariable"; + private static final String RESPONSE_VALUE = "responseValue"; + private static final String SCRIPT = "script"; + private static final String TYPE = "type"; + private static final String SQL = "sql"; + private static final String COLUMN = "column"; + private static final String VARIABLE = "variable"; + // new + private static final String SCHEMA = "schema"; + // new + private static final String SCHEMA_VALIDATION = "schemaValidation"; + + private final Class beanClass; + + public PetStoreBeanDefinitionParser(Class beanClass) { + this.beanClass = beanClass; + } + + public BeanDefinition parse(Element element) { + return parse(element, null); + } + + /** + * Note: The {@link PetStoreBeanDefinitionParser#parse(Element element)} allows access direct + * access without the {@link org.springframework.beans.factory.xml.ParserContext} for convenience. + */ + @Override + public BeanDefinition parse(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass); + retrieveRootNodeAttributes(element, builder); + retrieveOptionalNodeAttributes(element, REQUEST_BODY, builder); + retrieveTextContentAndNodeAttributes(element, REQUEST_BODY_LITERAL, builder); + retrieveOptionalNodeAttributes(element, RESPONSE, builder); + retrieveParamNodeData(element, builder, COOKIE); + retrieveParamNodeData(element, builder, HEADER); + retrieveParamNodeData(element, builder, SOAP_HEADER); + retrieveParamNodeData(element, builder, MIME_HEADER); + retrieveOptionalNodeAttributes(element, SCHEMA, builder); + retrieveOptionalNodeAttributes(element, SCHEMA_VALIDATION, builder); + retrieveOptionalMultipartElements(element, builder); + retrieveResponseNodeData(element, builder); + builder.addPropertyValue("name", element.getTagName()); + return builder.getBeanDefinition(); + } + + private void retrieveOptionalMultipartElements(Element element, BeanDefinitionBuilder builder) { + var multipartBodyElement = DomUtils.getChildElementByTagName(element, MULTIPART_BODY); + if (multipartBodyElement != null) { + var multipartBodyChildElements = DomUtils.getChildElements(multipartBodyElement); + for(int i = 0; i < multipartBodyChildElements.size(); i++){ + var multipartBodyChildElement = multipartBodyChildElements.get(i); + String propertyName = Conventions.attributeNameToPropertyName(multipartBodyChildElement.getLocalName()); + builder.addPropertyValue(propertyName, multipartBodyChildElement.getTextContent()); + } + } + } + + private void retrieveRootNodeAttributes(Element element, BeanDefinitionBuilder builder) { + NamedNodeMap attributes = element.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName), "Illegal property name returned, it must not be null or empty."); + builder.addPropertyValue(propertyName, attribute.getValue()); + } + } + + private void retrieveOptionalNodeAttributes(Element element, String elementName, BeanDefinitionBuilder builder) { + if (!DomUtils.getChildElementsByTagName(element, elementName).isEmpty()) { + Element el = DomUtils.getChildElementsByTagName(element, elementName).get(0); + NamedNodeMap attributes = el.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName), "Illegal property name returned, it must not be null or empty."); + String variableName = el.getLocalName() + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + builder.addPropertyValue(variableName, attribute.getValue()); + } + } + } + + private void retrieveTextContentAndNodeAttributes(Element element, String elementName, BeanDefinitionBuilder builder) { + if (!DomUtils.getChildElementsByTagName(element, elementName).isEmpty()) { + Element el1 = DomUtils.getChildElementsByTagName(element, elementName).get(0); + NamedNodeMap attributes = el1.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName1 = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName1), "Illegal property name returned, it must not be null or empty."); + String variableName = el1.getLocalName() + propertyName1.substring(0, 1).toUpperCase() + propertyName1.substring(1); + builder.addPropertyValue(variableName, attribute.getValue()); + } + Element el = DomUtils.getChildElementsByTagName(element, elementName).get(0); + builder.addPropertyValue(elementName, el.getTextContent()); + } + } + + private void retrieveParamNodeData(Element element, BeanDefinitionBuilder builder, String paramType) { + if (!DomUtils.getChildElementsByTagName(element, paramType).isEmpty()) { + Map params = new HashMap<>(); + List elements = DomUtils.getChildElementsByTagName(element, paramType); + elements.forEach(e -> { + String name = e.getAttribute(NAME); + String value = e.getAttribute(VALUE); + + Assert.state(StringUtils.isNotBlank(name), "Illegal attribute value returned. The 'name' attribute must not be null or empty."); + Assert.state(StringUtils.isNotBlank(value), "Illegal attribute value returned. The 'value' attribute must not be null or empty."); + + params.put(name, value); + }); + builder.addPropertyValue(paramType, params); + } + } + + private void retrieveResponseNodeData(Element element, BeanDefinitionBuilder builder) { + + if (!DomUtils.getChildElementsByTagName(element, RESPONSE).isEmpty()) { + Element response = DomUtils.getChildElementsByTagName(element, RESPONSE).get(0); + List elements = DomUtils.getChildElements(response); + + Map responseVariable = new HashMap<>(); + Map responseValue = new HashMap<>(); + + for (int i = 0; i < elements.size(); i++) { + Element e = elements.get(i); + + if (e.getTagName().contains(RESPONSE_JSONPATH) || e.getTagName().contains(RESPONSE_XPATH)) { + String expression = e.getAttribute(EXPRESSION); + String value = e.getAttribute(VALUE); + + Assert.state(StringUtils.isNotBlank(expression), "Illegal attribute value returned. The 'expression' attribute must not be null or empty."); + Assert.state(StringUtils.isNotBlank(value), "Illegal attribute value returned. The 'value' attribute must not be null or empty."); + + // variable to save @variable('ebid')@ else value to validate + if (value.matches("\\@variable\\('.*'\\)\\@")) { + Matcher match = Pattern.compile("\\'(.*?)\\'").matcher(value); + if (match.find()) { + responseVariable.put(expression, value.substring(match.start() + 1, match.end() - 1)); + } + } else { + responseValue.put(expression, value); + } + } else if (e.getTagName().contains(SCRIPT)) { + String script = e.getTextContent(); + Assert.state(StringUtils.isNotBlank(script), "Illegal attribute value returned. The 'script' attribute must not be null or empty."); + builder.addPropertyValue(SCRIPT, script); + + if (!e.getAttribute(TYPE).isEmpty()) { + String type = e.getAttribute(TYPE); + Assert.state(StringUtils.isNotBlank(type), "Illegal attribute value returned. The 'type' attribute must not be null or empty."); + builder.addPropertyValue(TYPE, type); + } + } else if (e.getTagName().contains(RESPONSE_RESOURCE)) { + String filePath = e.getAttribute(FILE); + Assert.state(StringUtils.isNotBlank(filePath), "Illegal attribute value returned. The 'file' attribute must not be null or empty."); + builder.addPropertyValue(RESPONSE_RESOURCE, filePath); + } + + } + + builder.addPropertyValue(RESPONSE_VARIABLE, responseVariable); + builder.addPropertyValue(RESPONSE_VALUE, responseValue); + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/extension/PetStoreNamespaceHandler.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/extension/PetStoreNamespaceHandler.java new file mode 100644 index 0000000000..1382acba99 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/extension/PetStoreNamespaceHandler.java @@ -0,0 +1,60 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.petstore.citrus.extension; + +import org.citrusframework.openapi.generator.rest.petstore.request.StoreApi; +import org.citrusframework.openapi.generator.rest.petstore.request.UserApi; +import org.citrusframework.openapi.generator.rest.petstore.citrus.PetStoreBeanDefinitionParser; + +import javax.annotation.processing.Generated; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class PetStoreNamespaceHandler extends NamespaceHandlerSupport { + + @Override + public void init() { + registerBeanDefinitionParser("addPetRequest", new PetStoreBeanDefinitionParser(PetApi.AddPetRequest.class)); + registerBeanDefinitionParser("deletePetRequest", new PetStoreBeanDefinitionParser(PetApi.DeletePetRequest.class)); + registerBeanDefinitionParser("findPetsByStatusRequest", new PetStoreBeanDefinitionParser(PetApi.FindPetsByStatusRequest.class)); + registerBeanDefinitionParser("findPetsByTagsRequest", new PetStoreBeanDefinitionParser(PetApi.FindPetsByTagsRequest.class)); + registerBeanDefinitionParser("getPetByIdRequest", new PetStoreBeanDefinitionParser(PetApi.GetPetByIdRequest.class)); + registerBeanDefinitionParser("updatePetRequest", new PetStoreBeanDefinitionParser(PetApi.UpdatePetRequest.class)); + registerBeanDefinitionParser("updatePetWithFormRequest", new PetStoreBeanDefinitionParser(PetApi.UpdatePetWithFormRequest.class)); + registerBeanDefinitionParser("uploadFileRequest", new PetStoreBeanDefinitionParser(PetApi.UploadFileRequest.class)); + registerBeanDefinitionParser("deleteOrderRequest", new PetStoreBeanDefinitionParser(StoreApi.DeleteOrderRequest.class)); + registerBeanDefinitionParser("getInventoryRequest", new PetStoreBeanDefinitionParser(StoreApi.GetInventoryRequest.class)); + registerBeanDefinitionParser("getOrderByIdRequest", new PetStoreBeanDefinitionParser(StoreApi.GetOrderByIdRequest.class)); + registerBeanDefinitionParser("placeOrderRequest", new PetStoreBeanDefinitionParser(StoreApi.PlaceOrderRequest.class)); + registerBeanDefinitionParser("createUserRequest", new PetStoreBeanDefinitionParser(UserApi.CreateUserRequest.class)); + registerBeanDefinitionParser("createUsersWithArrayInputRequest", new PetStoreBeanDefinitionParser(UserApi.CreateUsersWithArrayInputRequest.class)); + registerBeanDefinitionParser("createUsersWithListInputRequest", new PetStoreBeanDefinitionParser(UserApi.CreateUsersWithListInputRequest.class)); + registerBeanDefinitionParser("deleteUserRequest", new PetStoreBeanDefinitionParser(UserApi.DeleteUserRequest.class)); + registerBeanDefinitionParser("getUserByNameRequest", new PetStoreBeanDefinitionParser(UserApi.GetUserByNameRequest.class)); + registerBeanDefinitionParser("loginUserRequest", new PetStoreBeanDefinitionParser(UserApi.LoginUserRequest.class)); + registerBeanDefinitionParser("logoutUserRequest", new PetStoreBeanDefinitionParser(UserApi.LogoutUserRequest.class)); + registerBeanDefinitionParser("updateUserRequest", new PetStoreBeanDefinitionParser(UserApi.UpdateUserRequest.class)); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Category.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Category.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Category.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/ModelApiResponse.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/ModelApiResponse.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/ModelApiResponse.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Order.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Order.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Order.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Pet.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Pet.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Pet.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Tag.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Tag.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Tag.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/User.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/User.java new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/User.java @@ -0,0 +1 @@ +// not in use diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/PetApi.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/PetApi.java new file mode 100644 index 0000000000..92a8b3fe96 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/PetApi.java @@ -0,0 +1,93 @@ +package org.citrusframework.openapi.generator.rest.petstore.request; + +import org.citrusframework.endpoint.Endpoint; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.openapi.OpenApiSpecification; +import org.citrusframework.openapi.actions.OpenApiClientActionBuilder; +import org.citrusframework.openapi.actions.OpenApiClientActionBuilder.OpenApiOperationBuilder; +import org.citrusframework.openapi.actions.OpenApiClientRequestActionBuilder; +import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder; + +import java.util.function.UnaryOperator; + +import static org.citrusframework.spi.Resources.create; + +public class PetApi { + private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( + create("src/test/resources/apis/petstore.yaml") + ); + + public static PetApi openapiPetstore(HttpClient httpClient) { + return new PetApi(httpClient); + } + + private final HttpClient httpClient; + + private PetApi(HttpClient httpClient) { + this.httpClient = httpClient; + } + + public PetstoreAction getPetById() { + return petstoreAction(new GetPetByIdRequest()); + } + + private PetstoreAction petstoreAction(B requestBuilder) { + return new PetstoreAction<>(httpClient, petstoreSpec, requestBuilder); + } + + /** + * getPetById (GET /pet/{petId}) + * Find pet by ID + **/ + public static class GetPetByIdRequest extends OperationRequestBuilder { + @Override + public String getOperationId() { + return "getPetById"; + } + + public GetPetByIdRequest withPetId(String petId) { + openApiOperation.withParameter("petId", petId); + return this; + } + + public GetPetByIdRequest withCorrelationIds(String correlationIds) { + openApiOperation.withParameter("correlationIds", correlationIds); + return this; + } + + public GetPetByIdRequest withVerbose(boolean verbose) { + openApiOperation.withParameter("verbose", verbose); + return this; + } + } + + public static abstract class OperationRequestBuilder { + protected final OpenApiOperationBuilder openApiOperation = OpenApiOperationBuilder.operation(getOperationId()); + + public abstract String getOperationId(); + + public OpenApiOperationBuilder build() { + return openApiOperation; + } + } + + public static class PetstoreAction extends OpenApiClientActionBuilder { + private final T operation; + + private PetstoreAction(Endpoint httpClient, OpenApiSpecification specification, T operation) { + super(httpClient, specification); + this.operation = operation; + } + + public OpenApiClientRequestActionBuilder send(UnaryOperator builderProvider) { + var builder = builderProvider.apply(operation); + var send = send(builder.build()); + send.fork(true); + return send; + } + + public OpenApiClientResponseActionBuilder receive() { + return receive(operation.getOperationId(), "200"); + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/StoreApi.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/StoreApi.java new file mode 100644 index 0000000000..e8062b4fb0 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/StoreApi.java @@ -0,0 +1,449 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.petstore.request; + +import jakarta.annotation.Generated; +import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.testapi.GeneratedApiRequest; +import jakarta.servlet.http.Cookie; +import org.apache.commons.lang3.StringUtils; +import org.citrusframework.context.TestContext; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.spi.Resources; +import org.citrusframework.http.actions.HttpActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder.HttpMessageBuilderSupport; +import org.citrusframework.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import org.citrusframework.openapi.generator.rest.petstore.citrus.PetStoreAbstractTestRequest; + +import java.io.IOException; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class StoreApi implements GeneratedApi +{ + + public static final StoreApi INSTANCE = new StoreApi(); + + public String getApiTitle() { + return "OpenAPI Petstore"; + } + + public String getApiVersion() { + return "1.0.0"; + } + + public String getApiPrefix() { + return "PetStore"; + } + + public Map getApiInfoExtensions() { + Map infoExtensionMap = new HashMap<>(); + infoExtensionMap.put("x-citrus-api-name", "petstore"); + infoExtensionMap.put("x-citrus-app", "PETS"); + return infoExtensionMap; + } + + /** deleteOrder (DELETE /store/order/{order_id}) + Delete purchase order by ID + + **/ + public static class DeleteOrderRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/store/order/{order_id}"; + private final Logger coverageLogger = LoggerFactory.getLogger(DeleteOrderRequest.class); + + private String orderId; + + + public DeleteOrderRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":deleteOrderRequestType"); + } + + public String getOperationName() { + return "deleteOrder"; + } + + public String getMethod() { + return "DELETE"; + } + + public String getPath() { + return "/store/order/{order_id}"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .delete(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "deleteOrder;DELETE;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "order_id" + "}", orderId); + return endpoint; + } + } + /** getInventory (GET /store/inventory) + Returns pet inventories by status + + **/ + public static class GetInventoryRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/store/inventory"; + private final Logger coverageLogger = LoggerFactory.getLogger(GetInventoryRequest.class); + + + public GetInventoryRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":getInventoryRequestType"); + } + + public String getOperationName() { + return "getInventory"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/store/inventory"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .get(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "getInventory;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** getOrderById (GET /store/order/{order_id}) + Find purchase order by ID + + **/ + public static class GetOrderByIdRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/store/order/{order_id}"; + private final Logger coverageLogger = LoggerFactory.getLogger(GetOrderByIdRequest.class); + + private String orderId; + + + public GetOrderByIdRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":getOrderByIdRequestType"); + } + + public String getOperationName() { + return "getOrderById"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/store/order/{order_id}"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .get(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "getOrderById;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "order_id" + "}", orderId); + return endpoint; + } + } + /** placeOrder (POST /store/order) + Place an order for a pet + + **/ + public static class PlaceOrderRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/store/order"; + private final Logger coverageLogger = LoggerFactory.getLogger(PlaceOrderRequest.class); + + + public PlaceOrderRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":placeOrderRequestType"); + } + + public String getOperationName() { + return "placeOrder"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/store/order"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "placeOrder;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/UserApi.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/UserApi.java new file mode 100644 index 0000000000..d52e4351f1 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/UserApi.java @@ -0,0 +1,835 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.petstore.request; + +import jakarta.annotation.Generated; +import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.testapi.GeneratedApiRequest; +import jakarta.servlet.http.Cookie; +import org.apache.commons.lang3.StringUtils; +import org.citrusframework.context.TestContext; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.spi.Resources; +import org.citrusframework.http.actions.HttpActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder.HttpMessageBuilderSupport; +import org.citrusframework.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import org.citrusframework.openapi.generator.rest.petstore.citrus.PetStoreAbstractTestRequest; + +import java.io.IOException; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class UserApi implements GeneratedApi +{ + + public static final UserApi INSTANCE = new UserApi(); + + public String getApiTitle() { + return "OpenAPI Petstore"; + } + + public String getApiVersion() { + return "1.0.0"; + } + + public String getApiPrefix() { + return "PetStore"; + } + + public Map getApiInfoExtensions() { + Map infoExtensionMap = new HashMap<>(); + infoExtensionMap.put("x-citrus-api-name", "petstore"); + infoExtensionMap.put("x-citrus-app", "PETS"); + return infoExtensionMap; + } + + /** createUser (POST /user) + Create user + + **/ + public static class CreateUserRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user"; + private final Logger coverageLogger = LoggerFactory.getLogger(CreateUserRequest.class); + + + public CreateUserRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":createUserRequestType"); + } + + public String getOperationName() { + return "createUser"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/user"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "createUser;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** createUsersWithArrayInput (POST /user/createWithArray) + Creates list of users with given input array + + **/ + public static class CreateUsersWithArrayInputRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user/createWithArray"; + private final Logger coverageLogger = LoggerFactory.getLogger(CreateUsersWithArrayInputRequest.class); + + + public CreateUsersWithArrayInputRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":createUsersWithArrayInputRequestType"); + } + + public String getOperationName() { + return "createUsersWithArrayInput"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/user/createWithArray"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "createUsersWithArrayInput;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** createUsersWithListInput (POST /user/createWithList) + Creates list of users with given input array + + **/ + public static class CreateUsersWithListInputRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user/createWithList"; + private final Logger coverageLogger = LoggerFactory.getLogger(CreateUsersWithListInputRequest.class); + + + public CreateUsersWithListInputRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":createUsersWithListInputRequestType"); + } + + public String getOperationName() { + return "createUsersWithListInput"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/user/createWithList"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .post(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "createUsersWithListInput;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** deleteUser (DELETE /user/{username}) + Delete user + + **/ + public static class DeleteUserRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user/{username}"; + private final Logger coverageLogger = LoggerFactory.getLogger(DeleteUserRequest.class); + + private String username; + + + public DeleteUserRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":deleteUserRequestType"); + } + + public String getOperationName() { + return "deleteUser"; + } + + public String getMethod() { + return "DELETE"; + } + + public String getPath() { + return "/user/{username}"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .delete(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "deleteUser;DELETE;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setUsername(String username) { + this.username = username; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "username" + "}", username); + return endpoint; + } + } + /** getUserByName (GET /user/{username}) + Get user by user name + + **/ + public static class GetUserByNameRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user/{username}"; + private final Logger coverageLogger = LoggerFactory.getLogger(GetUserByNameRequest.class); + + private String username; + + + public GetUserByNameRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":getUserByNameRequestType"); + } + + public String getOperationName() { + return "getUserByName"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/user/{username}"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .get(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "getUserByName;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setUsername(String username) { + this.username = username; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "username" + "}", username); + return endpoint; + } + } + /** loginUser (GET /user/login) + Logs user into the system + + **/ + public static class LoginUserRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user/login"; + private final Logger coverageLogger = LoggerFactory.getLogger(LoginUserRequest.class); + + private String username; + + private String password; + + + public LoginUserRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":loginUserRequestType"); + } + + public String getOperationName() { + return "loginUser"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/user/login"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .get(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + + if (StringUtils.isNotBlank(this.username)) { + queryParams.put("username", context.replaceDynamicContentInString(this.username)); + httpClientRequestActionBuilder.queryParam("username", this.username); + } + + + if (StringUtils.isNotBlank(this.password)) { + queryParams.put("password", context.replaceDynamicContentInString(this.password)); + httpClientRequestActionBuilder.queryParam("password", this.password); + } + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "loginUser;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** logoutUser (GET /user/logout) + Logs out current logged in user session + + **/ + public static class LogoutUserRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user/logout"; + private final Logger coverageLogger = LoggerFactory.getLogger(LogoutUserRequest.class); + + + public LogoutUserRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":logoutUserRequestType"); + } + + public String getOperationName() { + return "logoutUser"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/user/logout"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .get(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "logoutUser;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** updateUser (PUT /user/{username}) + Updated user + + **/ + public static class UpdateUserRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/user/{username}"; + private final Logger coverageLogger = LoggerFactory.getLogger(UpdateUserRequest.class); + + private String username; + + + public UpdateUserRequest() { + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("PetStore".toLowerCase() + ":updateUserRequestType"); + } + + public String getOperationName() { + return "updateUser"; + } + + public String getMethod() { + return "PUT"; + } + + public String getPath() { + return "/user/{username}"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + HttpClientRequestActionBuilder httpClientRequestActionBuilder = new HttpActionBuilder().client(httpClient).send() + .put(replacePathParams(ENDPOINT)); + + HttpMessageBuilderSupport messageBuilderSupport = httpClientRequestActionBuilder.getMessageBuilderSupport(); + messageBuilderSupport.accept(responseAcceptType); + + if (cookies != null) { + cookies.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + } + + if (headers != null) { + headers.forEach((k, v) -> messageBuilderSupport.cookie(new Cookie(k, v))); + headers.forEach(messageBuilderSupport::header); + } + + String bodyLog = ""; + String payload = null; + String payloadType = null; + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + bodyLog = body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""; + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "updateUser;PUT;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setUsername(String username) { + this.username = username; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "username" + "}", username); + return endpoint; + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/spring/PetStoreBeanConfiguration.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/spring/PetStoreBeanConfiguration.java new file mode 100644 index 0000000000..66dce7320c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/spring/PetStoreBeanConfiguration.java @@ -0,0 +1,157 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.petstore.spring; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +import org.citrusframework.openapi.generator.rest.petstore.request.StoreApi; +import org.citrusframework.openapi.generator.rest.petstore.request.UserApi; +import javax.annotation.processing.Generated; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +@Configuration +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class PetStoreBeanConfiguration { + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.AddPetRequest addPetRequest() { + return new PetApi.AddPetRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.DeletePetRequest deletePetRequest() { + return new PetApi.DeletePetRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.FindPetsByStatusRequest findPetsByStatusRequest() { + return new PetApi.FindPetsByStatusRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.FindPetsByTagsRequest findPetsByTagsRequest() { + return new PetApi.FindPetsByTagsRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.GetPetByIdRequest getPetByIdRequest() { + return new PetApi.GetPetByIdRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.UpdatePetRequest updatePetRequest() { + return new PetApi.UpdatePetRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.UpdatePetWithFormRequest updatePetWithFormRequest() { + return new PetApi.UpdatePetWithFormRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public PetApi.UploadFileRequest uploadFileRequest() { + return new PetApi.UploadFileRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public StoreApi.DeleteOrderRequest deleteOrderRequest() { + return new StoreApi.DeleteOrderRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public StoreApi.GetInventoryRequest getInventoryRequest() { + return new StoreApi.GetInventoryRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public StoreApi.GetOrderByIdRequest getOrderByIdRequest() { + return new StoreApi.GetOrderByIdRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public StoreApi.PlaceOrderRequest placeOrderRequest() { + return new StoreApi.PlaceOrderRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.CreateUserRequest createUserRequest() { + return new UserApi.CreateUserRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.CreateUsersWithArrayInputRequest createUsersWithArrayInputRequest() { + return new UserApi.CreateUsersWithArrayInputRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.CreateUsersWithListInputRequest createUsersWithListInputRequest() { + return new UserApi.CreateUsersWithListInputRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.DeleteUserRequest deleteUserRequest() { + return new UserApi.DeleteUserRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.GetUserByNameRequest getUserByNameRequest() { + return new UserApi.GetUserByNameRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.LoginUserRequest loginUserRequest() { + return new UserApi.LoginUserRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.LogoutUserRequest logoutUserRequest() { + return new UserApi.LogoutUserRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public UserApi.UpdateUserRequest updateUserRequest() { + return new UserApi.UpdateUserRequest(); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlAbstractTestRequest.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlAbstractTestRequest.java new file mode 100644 index 0000000000..4e493250fc --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlAbstractTestRequest.java @@ -0,0 +1,203 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.soap.bookservice.citrus; + +import jakarta.annotation.Generated; +import java.util.List; +import java.util.ServiceLoader; +import org.citrusframework.actions.AbstractTestAction; +import org.citrusframework.context.TestContext; +import org.citrusframework.http.actions.HttpClientRequestActionBuilder; +import org.citrusframework.testapi.ApiActionBuilderCustomizerService; +import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.spi.Resources; +import org.citrusframework.validation.DelegatingPayloadVariableExtractor; +import org.citrusframework.validation.PathExpressionValidationContext; +import org.citrusframework.validation.script.ScriptValidationContext; +import org.citrusframework.ws.actions.ReceiveSoapMessageAction; +import org.citrusframework.ws.actions.ReceiveSoapMessageAction.SoapMessageBuilderSupport; +import org.citrusframework.ws.actions.SendSoapMessageAction; +import org.citrusframework.ws.actions.SoapActionBuilder; +import org.citrusframework.ws.client.WebServiceClient; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.util.CollectionUtils; + +import javax.sql.DataSource; +import java.util.Map; + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public abstract class OpenApiFromWsdlAbstractTestRequest extends AbstractTestAction { + + protected final Marker coverageMarker = MarkerFactory.getMarker("OPENAPIFROMWSDL-API-COVERAGE"); + + @Autowired + @Qualifier("soapSampleEndpoint") + protected WebServiceClient wsClient; + + @Autowired(required = false) + protected DataSource dataSource; + + @Autowired(required = false) + private List actionBuilderCustomizerServices; + + // attributes of differentNodes + protected String bodyContentType; + protected String bodyLiteralContentType; + protected String bodyFile; + protected String bodyLiteral; + + // children of response element + protected String resource; + protected Map responseVariable; // Contains the 'XPATH' as key and the 'VARIABLE NAME' as value + protected Map responseValue; // Contains the 'XPATH' as key and the 'VALUE TO BE VALIDATED' as value + protected String script; + protected String type; // default script type is groovy - supported types see com.consol.citrus.script.ScriptTypes + protected Map soapHeaders; + protected Map mimeHeaders; + + @Override + public void doExecute(TestContext context) { + sendRequest(context); + receiveResponse(context); + } + + /** + * This method receives the HTTP-Response + */ + public void receiveResponse(TestContext context) { + + ReceiveSoapMessageAction.Builder soapReceiveMessageActionBuilder = new SoapActionBuilder().client(wsClient).receive(); + SoapMessageBuilderSupport messageBuilderSupport = soapReceiveMessageActionBuilder.getMessageBuilderSupport(); + + if (resource != null) { + messageBuilderSupport.body(Resources.create(resource)); + } + + if (!CollectionUtils.isEmpty(responseVariable)) { + DelegatingPayloadVariableExtractor.Builder extractorBuilder = new DelegatingPayloadVariableExtractor.Builder(); + responseVariable.forEach(extractorBuilder::expression); + messageBuilderSupport.extract(extractorBuilder); + } + + if (!CollectionUtils.isEmpty(responseValue)) { + PathExpressionValidationContext.Builder validationContextBuilder = new PathExpressionValidationContext.Builder(); + responseValue.forEach(validationContextBuilder::expression); + messageBuilderSupport.validate(validationContextBuilder); + } + + if (script != null) { + ScriptValidationContext.Builder scriptValidationContextBuilder = new ScriptValidationContext.Builder(); + if (type != null) { + scriptValidationContextBuilder.scriptType(type); + } + scriptValidationContextBuilder.script(script); + messageBuilderSupport.validate(scriptValidationContextBuilder); + } + + soapReceiveMessageActionBuilder.withReferenceResolver(context.getReferenceResolver()); + soapReceiveMessageActionBuilder.build().execute(context); + } + + public abstract void sendRequest(TestContext context); + + public void setBodyLiteral(String bodyLiteral) { + this.bodyLiteral = bodyLiteral; + } + public void setBodyContentType(String bodyContentType) { + this.bodyContentType = bodyContentType; + } + + public void setBodyLiteralContentType(String bodyLiteralContentType) { + this.bodyLiteralContentType = bodyLiteralContentType; + } + + public void setBodyFile(String bodyFile) { + this.bodyFile = bodyFile; + } + + public void setResource(String resource) { + this.resource = resource; + } + + public void setResponseVariable(Map responseVariable) { + this.responseVariable = responseVariable; + } + + public void setResponseValue(Map responseValue) { + this.responseValue = responseValue; + } + + public void setScript(String script) { + this.script = script; + } + + public void setType(String type) { + this.type = type; + } + + public void setSoapHeader(Map soapHeaders) { + this.soapHeaders = soapHeaders; + } + + public void setMimeHeader(Map mimeHeaders) { + this.mimeHeaders = mimeHeaders; + } + + protected SendSoapMessageAction.Builder customizeBuilder(GeneratedApi generatedApi, + TestContext context, SendSoapMessageAction.Builder sendSoapMessageActionBuilder) { + + sendSoapMessageActionBuilder = customizeByBeans(generatedApi, context, sendSoapMessageActionBuilder); + + sendSoapMessageActionBuilder = customizeBySpi(generatedApi, context, sendSoapMessageActionBuilder); + + return sendSoapMessageActionBuilder; + } + + private SendSoapMessageAction.Builder customizeBySpi(GeneratedApi generatedApi, TestContext context, + SendSoapMessageAction.Builder sendSoapMessageActionBuilder) { + + ServiceLoader serviceLoader = ServiceLoader.load( + ApiActionBuilderCustomizerService.class, ApiActionBuilderCustomizerService.class.getClassLoader()); + for (ApiActionBuilderCustomizerService service :serviceLoader) { + sendSoapMessageActionBuilder = service.build(generatedApi, this, context, sendSoapMessageActionBuilder); + } + + return sendSoapMessageActionBuilder; + } + + private SendSoapMessageAction.Builder customizeByBeans( + GeneratedApi generatedApi, TestContext context, SendSoapMessageAction.Builder sendSoapMessageActionBuilder) { + + if (actionBuilderCustomizerServices != null) { + for (ApiActionBuilderCustomizerService apiActionBuilderCustomizer : actionBuilderCustomizerServices) { + sendSoapMessageActionBuilder = apiActionBuilderCustomizer.build(generatedApi, this, + context, sendSoapMessageActionBuilder); + } + } + + return sendSoapMessageActionBuilder; + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlBeanDefinitionParser.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlBeanDefinitionParser.java new file mode 100644 index 0000000000..e59e1bc033 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlBeanDefinitionParser.java @@ -0,0 +1,231 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.soap.bookservice.citrus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.processing.Generated; + +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class OpenApiFromWsdlBeanDefinitionParser implements BeanDefinitionParser { + + private static final String COOKIE = "cookie"; + private static final String HEADER = "header"; + private static final String SOAP_HEADER = "soapHeader"; + private static final String MIME_HEADER = "mimeHeader"; + private static final String NAME = "name"; + private static final String REQUEST_BODY = "body"; + private static final String REQUEST_BODY_LITERAL = "bodyLiteral"; + private static final String MULTIPART_BODY = "multipartBody"; + private static final String RESPONSE = "response"; + private static final String RESPONSE_JSONPATH = "json-path"; + private static final String RESPONSE_XPATH = "xpath"; + private static final String EXPRESSION = "expression"; + private static final String VALUE = "value"; + private static final String RESPONSE_RESOURCE = "resource"; + private static final String FILE = "file"; + private static final String RESPONSE_VARIABLE = "responseVariable"; + private static final String RESPONSE_VALUE = "responseValue"; + private static final String SCRIPT = "script"; + private static final String TYPE = "type"; + private static final String SQL = "sql"; + private static final String COLUMN = "column"; + private static final String VARIABLE = "variable"; + // new + private static final String SCHEMA = "schema"; + // new + private static final String SCHEMA_VALIDATION = "schemaValidation"; + + private final Class beanClass; + + public OpenApiFromWsdlBeanDefinitionParser(Class beanClass) { + this.beanClass = beanClass; + } + + public BeanDefinition parse(Element element) { + return parse(element, null); + } + + /** + * Note: The {@link OpenApiFromWsdlBeanDefinitionParser#parse(Element element)} allows access direct + * access without the {@link org.springframework.beans.factory.xml.ParserContext} for convenience. + */ + @Override + public BeanDefinition parse(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass); + retrieveRootNodeAttributes(element, builder); + retrieveOptionalNodeAttributes(element, REQUEST_BODY, builder); + retrieveTextContentAndNodeAttributes(element, REQUEST_BODY_LITERAL, builder); + retrieveOptionalNodeAttributes(element, RESPONSE, builder); + retrieveParamNodeData(element, builder, COOKIE); + retrieveParamNodeData(element, builder, HEADER); + retrieveParamNodeData(element, builder, SOAP_HEADER); + retrieveParamNodeData(element, builder, MIME_HEADER); + retrieveOptionalNodeAttributes(element, SCHEMA, builder); + retrieveOptionalNodeAttributes(element, SCHEMA_VALIDATION, builder); + retrieveOptionalMultipartElements(element, builder); + retrieveResponseNodeData(element, builder); + builder.addPropertyValue("name", element.getTagName()); + return builder.getBeanDefinition(); + } + + private void retrieveOptionalMultipartElements(Element element, BeanDefinitionBuilder builder) { + var multipartBodyElement = DomUtils.getChildElementByTagName(element, MULTIPART_BODY); + if (multipartBodyElement != null) { + var multipartBodyChildElements = DomUtils.getChildElements(multipartBodyElement); + for(int i = 0; i < multipartBodyChildElements.size(); i++){ + var multipartBodyChildElement = multipartBodyChildElements.get(i); + String propertyName = Conventions.attributeNameToPropertyName(multipartBodyChildElement.getLocalName()); + builder.addPropertyValue(propertyName, multipartBodyChildElement.getTextContent()); + } + } + } + + private void retrieveRootNodeAttributes(Element element, BeanDefinitionBuilder builder) { + NamedNodeMap attributes = element.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName), "Illegal property name returned, it must not be null or empty."); + builder.addPropertyValue(propertyName, attribute.getValue()); + } + } + + private void retrieveOptionalNodeAttributes(Element element, String elementName, BeanDefinitionBuilder builder) { + if (!DomUtils.getChildElementsByTagName(element, elementName).isEmpty()) { + Element el = DomUtils.getChildElementsByTagName(element, elementName).get(0); + NamedNodeMap attributes = el.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName), "Illegal property name returned, it must not be null or empty."); + String variableName = el.getLocalName() + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + builder.addPropertyValue(variableName, attribute.getValue()); + } + } + } + + private void retrieveTextContentAndNodeAttributes(Element element, String elementName, BeanDefinitionBuilder builder) { + if (!DomUtils.getChildElementsByTagName(element, elementName).isEmpty()) { + Element el1 = DomUtils.getChildElementsByTagName(element, elementName).get(0); + NamedNodeMap attributes = el1.getAttributes(); + for (int x = 0; x < attributes.getLength(); x++) { + Attr attribute = (Attr) attributes.item(x); + String propertyName1 = Conventions.attributeNameToPropertyName(attribute.getLocalName()); + Assert.state(StringUtils.isNotBlank(propertyName1), "Illegal property name returned, it must not be null or empty."); + String variableName = el1.getLocalName() + propertyName1.substring(0, 1).toUpperCase() + propertyName1.substring(1); + builder.addPropertyValue(variableName, attribute.getValue()); + } + Element el = DomUtils.getChildElementsByTagName(element, elementName).get(0); + builder.addPropertyValue(elementName, el.getTextContent()); + } + } + + private void retrieveParamNodeData(Element element, BeanDefinitionBuilder builder, String paramType) { + if (!DomUtils.getChildElementsByTagName(element, paramType).isEmpty()) { + Map params = new HashMap<>(); + List elements = DomUtils.getChildElementsByTagName(element, paramType); + elements.forEach(e -> { + String name = e.getAttribute(NAME); + String value = e.getAttribute(VALUE); + + Assert.state(StringUtils.isNotBlank(name), "Illegal attribute value returned. The 'name' attribute must not be null or empty."); + Assert.state(StringUtils.isNotBlank(value), "Illegal attribute value returned. The 'value' attribute must not be null or empty."); + + params.put(name, value); + }); + builder.addPropertyValue(paramType, params); + } + } + + private void retrieveResponseNodeData(Element element, BeanDefinitionBuilder builder) { + + if (!DomUtils.getChildElementsByTagName(element, RESPONSE).isEmpty()) { + Element response = DomUtils.getChildElementsByTagName(element, RESPONSE).get(0); + List elements = DomUtils.getChildElements(response); + + Map responseVariable = new HashMap<>(); + Map responseValue = new HashMap<>(); + + for (int i = 0; i < elements.size(); i++) { + Element e = elements.get(i); + + if (e.getTagName().contains(RESPONSE_JSONPATH) || e.getTagName().contains(RESPONSE_XPATH)) { + String expression = e.getAttribute(EXPRESSION); + String value = e.getAttribute(VALUE); + + Assert.state(StringUtils.isNotBlank(expression), "Illegal attribute value returned. The 'expression' attribute must not be null or empty."); + Assert.state(StringUtils.isNotBlank(value), "Illegal attribute value returned. The 'value' attribute must not be null or empty."); + + // variable to save @variable('ebid')@ else value to validate + if (value.matches("\\@variable\\('.*'\\)\\@")) { + Matcher match = Pattern.compile("\\'(.*?)\\'").matcher(value); + if (match.find()) { + responseVariable.put(expression, value.substring(match.start() + 1, match.end() - 1)); + } + } else { + responseValue.put(expression, value); + } + } else if (e.getTagName().contains(SCRIPT)) { + String script = e.getTextContent(); + Assert.state(StringUtils.isNotBlank(script), "Illegal attribute value returned. The 'script' attribute must not be null or empty."); + builder.addPropertyValue(SCRIPT, script); + + if (!e.getAttribute(TYPE).isEmpty()) { + String type = e.getAttribute(TYPE); + Assert.state(StringUtils.isNotBlank(type), "Illegal attribute value returned. The 'type' attribute must not be null or empty."); + builder.addPropertyValue(TYPE, type); + } + } else if (e.getTagName().contains(RESPONSE_RESOURCE)) { + String filePath = e.getAttribute(FILE); + Assert.state(StringUtils.isNotBlank(filePath), "Illegal attribute value returned. The 'file' attribute must not be null or empty."); + builder.addPropertyValue(RESPONSE_RESOURCE, filePath); + } + + } + + builder.addPropertyValue(RESPONSE_VARIABLE, responseVariable); + builder.addPropertyValue(RESPONSE_VALUE, responseValue); + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/extension/OpenApiFromWsdlNamespaceHandler.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/extension/OpenApiFromWsdlNamespaceHandler.java new file mode 100644 index 0000000000..b844e884ca --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/extension/OpenApiFromWsdlNamespaceHandler.java @@ -0,0 +1,42 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.soap.bookservice.citrus.extension; + +import org.citrusframework.openapi.generator.soap.bookservice.request.BookServiceSoapApi; +import org.citrusframework.openapi.generator.soap.bookservice.citrus.OpenApiFromWsdlBeanDefinitionParser; + +import javax.annotation.processing.Generated; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class OpenApiFromWsdlNamespaceHandler extends NamespaceHandlerSupport { + + @Override + public void init() { + registerBeanDefinitionParser("addBookRequest", new OpenApiFromWsdlBeanDefinitionParser(BookServiceSoapApi.AddBookRequest.class)); + registerBeanDefinitionParser("getAllBooksRequest", new OpenApiFromWsdlBeanDefinitionParser(BookServiceSoapApi.GetAllBooksRequest.class)); + registerBeanDefinitionParser("getBookRequest", new OpenApiFromWsdlBeanDefinitionParser(BookServiceSoapApi.GetBookRequest.class)); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/request/BookServiceSoapApi.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/request/BookServiceSoapApi.java new file mode 100644 index 0000000000..f253418eec --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/request/BookServiceSoapApi.java @@ -0,0 +1,346 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.soap.bookservice.request; + +import jakarta.annotation.Generated; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.citrusframework.context.TestContext; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.testapi.GeneratedApi; +import org.citrusframework.testapi.GeneratedApiRequest; +import org.citrusframework.openapi.generator.soap.bookservice.citrus.OpenApiFromWsdlAbstractTestRequest; +import org.citrusframework.spi.Resources; +import org.citrusframework.util.FileUtils; +import org.citrusframework.ws.actions.SendSoapMessageAction; +import org.citrusframework.ws.actions.SendSoapMessageAction.Builder.SendSoapMessageBuilderSupport; +import org.citrusframework.ws.actions.SoapActionBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; + +import org.citrusframework.openapi.generator.soap.bookservice.citrus.OpenApiFromWsdlAbstractTestRequest; + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class BookServiceSoapApi implements GeneratedApi +{ + public static final BookServiceSoapApi INSTANCE = new BookServiceSoapApi(); + + public String getApiTitle() { + return "Generated api from wsdl"; + } + + public String getApiVersion() { + return "1.0.0"; + } + + public String getApiPrefix() { + return "OpenApiFromWsdl"; + } + + public Map getApiInfoExtensions() { + Map infoExtensionMap = new HashMap<>(); + return infoExtensionMap; + } + + /** + addBook (POST /AddBook) + + + **/ + public static class AddBookRequest extends OpenApiFromWsdlAbstractTestRequest implements GeneratedApiRequest { + + private final Logger coverageLogger = LoggerFactory.getLogger(AddBookRequest.class); + + // Query params + + + public AddBookRequest(){ + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("OpenApiFromWsdl".toLowerCase() + ":addBookRequestType"); + } + + public String getOperationName() { + return "addBook"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/AddBook"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + + SendSoapMessageAction.Builder soapSendMessageActionBuilder = new SoapActionBuilder().client(wsClient).send(); + SendSoapMessageBuilderSupport messageBuilderSupport = soapSendMessageActionBuilder.getMessageBuilderSupport(); + + messageBuilderSupport.soapAction("addBook"); + + String payload = null; + String payloadType = null; + + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + if (!CollectionUtils.isEmpty(soapHeaders)) { + for (Entry entry : soapHeaders.entrySet()) { + messageBuilderSupport = messageBuilderSupport.header(entry.getKey(), + entry.getValue()); + } + } + + if (!CollectionUtils.isEmpty(mimeHeaders)) { + for (Entry entry : mimeHeaders.entrySet()) { + messageBuilderSupport = messageBuilderSupport.header("citrus_http_" + entry.getKey(), + entry.getValue()); + } + } + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + soapSendMessageActionBuilder.withReferenceResolver(context.getReferenceResolver()); + soapSendMessageActionBuilder = customizeBuilder(INSTANCE, context, soapSendMessageActionBuilder); + + soapSendMessageActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "addBook;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""); + } + + + } + /** + getAllBooks (POST /GetAllBooks) + + + **/ + public static class GetAllBooksRequest extends OpenApiFromWsdlAbstractTestRequest implements GeneratedApiRequest { + + private final Logger coverageLogger = LoggerFactory.getLogger(GetAllBooksRequest.class); + + // Query params + + + public GetAllBooksRequest(){ + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("OpenApiFromWsdl".toLowerCase() + ":getAllBooksRequestType"); + } + + public String getOperationName() { + return "getAllBooks"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/GetAllBooks"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + + SendSoapMessageAction.Builder soapSendMessageActionBuilder = new SoapActionBuilder().client(wsClient).send(); + SendSoapMessageBuilderSupport messageBuilderSupport = soapSendMessageActionBuilder.getMessageBuilderSupport(); + + messageBuilderSupport.soapAction("getAllBooks"); + + String payload = null; + String payloadType = null; + + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + if (!CollectionUtils.isEmpty(soapHeaders)) { + for (Entry entry : soapHeaders.entrySet()) { + messageBuilderSupport = messageBuilderSupport.header(entry.getKey(), + entry.getValue()); + } + } + + if (!CollectionUtils.isEmpty(mimeHeaders)) { + for (Entry entry : mimeHeaders.entrySet()) { + messageBuilderSupport = messageBuilderSupport.header("citrus_http_" + entry.getKey(), + entry.getValue()); + } + } + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + soapSendMessageActionBuilder.withReferenceResolver(context.getReferenceResolver()); + soapSendMessageActionBuilder = customizeBuilder(INSTANCE, context, soapSendMessageActionBuilder); + + soapSendMessageActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "getAllBooks;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""); + } + + + } + /** + getBook (POST /GetBook) + + + **/ + public static class GetBookRequest extends OpenApiFromWsdlAbstractTestRequest implements GeneratedApiRequest { + + private final Logger coverageLogger = LoggerFactory.getLogger(GetBookRequest.class); + + // Query params + + + public GetBookRequest(){ + // The name will be overwritten with the tag name using the actual namespace as prefix, when the class is loaded from xml + setName("OpenApiFromWsdl".toLowerCase() + ":getBookRequestType"); + } + + public String getOperationName() { + return "getBook"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/GetBook"; + } + + /** + * This method sends the HTTP-Request + */ + public void sendRequest(TestContext context) { + + SendSoapMessageAction.Builder soapSendMessageActionBuilder = new SoapActionBuilder().client(wsClient).send(); + SendSoapMessageBuilderSupport messageBuilderSupport = soapSendMessageActionBuilder.getMessageBuilderSupport(); + + messageBuilderSupport.soapAction("getBook"); + + String payload = null; + String payloadType = null; + + if (StringUtils.isNotBlank(this.bodyFile)) { + try { + payload = FileUtils.readToString(Resources.create(this.bodyFile), FileUtils.getDefaultCharset()); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read payload resource", e); + } + payloadType = this.bodyContentType; + } else if (StringUtils.isNotBlank(this.bodyLiteral)) { + payload = this.bodyLiteral; + payloadType = this.bodyLiteralContentType; + } + + String body = ""; + String bodyType = ""; + if(payload != null && payloadType != null) { + messageBuilderSupport.body(payload).contentType(payloadType); + body = context.replaceDynamicContentInString(payload); + bodyType = context.replaceDynamicContentInString(payloadType); + } + + if (!CollectionUtils.isEmpty(soapHeaders)) { + for (Entry entry : soapHeaders.entrySet()) { + messageBuilderSupport = messageBuilderSupport.header(entry.getKey(), + entry.getValue()); + } + } + + if (!CollectionUtils.isEmpty(mimeHeaders)) { + for (Entry entry : mimeHeaders.entrySet()) { + messageBuilderSupport = messageBuilderSupport.header("citrus_http_" + entry.getKey(), + entry.getValue()); + } + } + + Map queryParams = new HashMap<>(); + + String query = queryParams.entrySet().stream().map(e -> "\"" + e.getKey() + "\":\"" + e.getValue() + "\"").collect(Collectors.joining(",", "{", "}")); + + soapSendMessageActionBuilder.withReferenceResolver(context.getReferenceResolver()); + soapSendMessageActionBuilder = customizeBuilder(INSTANCE, context, soapSendMessageActionBuilder); + + soapSendMessageActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "getBook;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""); + } + + + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/spring/OpenApiFromWsdlBeanConfiguration.java b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/spring/OpenApiFromWsdlBeanConfiguration.java new file mode 100644 index 0000000000..919d03669b --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/spring/OpenApiFromWsdlBeanConfiguration.java @@ -0,0 +1,54 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.soap.bookservice.spring; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +import org.citrusframework.openapi.generator.soap.bookservice.request.BookServiceSoapApi; +import javax.annotation.processing.Generated; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +@Configuration +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class OpenApiFromWsdlBeanConfiguration { + + @Bean + @Scope(SCOPE_PROTOTYPE) + public BookServiceSoapApi.AddBookRequest addBookRequest() { + return new BookServiceSoapApi.AddBookRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public BookServiceSoapApi.GetAllBooksRequest getAllBooksRequest() { + return new BookServiceSoapApi.GetAllBooksRequest(); + } + + @Bean + @Scope(SCOPE_PROTOTYPE) + public BookServiceSoapApi.GetBookRequest getBookRequest() { + return new BookServiceSoapApi.GetBookRequest(); + } +} diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/pom.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/pom.xml index 134d44aee9..defd001b40 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/pom.xml +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/pom.xml @@ -36,12 +36,17 @@ + org.citrusframework citrus-test-api-generator-core ${project.version} + + org.openapitools + openapi-generator + org.openapitools openapi-generator-maven-plugin diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/CodeGenMojoWrapper.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/CodeGenMojoWrapper.java index 5b1d088727..9413a61a17 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/CodeGenMojoWrapper.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/CodeGenMojoWrapper.java @@ -18,7 +18,9 @@ import static java.lang.String.format; import static org.citrusframework.openapi.generator.JavaCitrusCodegen.CODEGEN_NAME; +import static java.lang.String.format; +import org.citrusframework.openapi.generator.JavaCitrusCodegen; import java.io.File; import java.util.HashMap; import java.util.Map; @@ -96,4 +98,5 @@ private void setPrivateField(String fieldName, Object fieldValue) throws MojoExe format("Could not reflectively set field value '%s' for field '%s'", fieldValue, fieldName)); } } + } diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoIntegrationTest.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoIntegrationTest.java index 4cccffe386..8026cbc03a 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoIntegrationTest.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoIntegrationTest.java @@ -12,6 +12,7 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.springframework.test.util.ReflectionTestUtils.getField; +import jakarta.annotation.Nonnull; import jakarta.validation.constraints.NotNull; import java.io.File; import java.io.FileWriter; @@ -36,9 +37,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; -class TestApiGeneratorMojoIntegrationTest extends AbstractMojoTestCase { +public class TestApiGeneratorMojoIntegrationTest extends AbstractMojoTestCase { public static final String OTHER_META_FILE_CONTENT = "somenamespace=somevalue"; @@ -48,17 +50,17 @@ class TestApiGeneratorMojoIntegrationTest extends AbstractMojoTestCase { * Array containing path templates for each generated file, specified with tokens. Tokens can be replaced with values of the respective * testing scenario. */ - private static final String[] STANDARD_FILE_PATH_TEMPLATES = new String[]{ - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/citrus/extension/%CAMEL_PREFIX%NamespaceHandler.java", - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/citrus/%CAMEL_PREFIX%AbstractTestRequest.java", - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/citrus/%CAMEL_PREFIX%BeanDefinitionParser.java", - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/spring/%CAMEL_PREFIX%BeanConfiguration.java", - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%MODEL_FOLDER%/PingReqType.java", - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%MODEL_FOLDER%/PingRespType.java", - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%REQUEST_FOLDER%/PingApi.java", - "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%REQUEST_FOLDER%/PungApi.java", - "%TARGET_FOLDER%/%GENERATED_RESOURCES_FOLDER%/%SCHEMA_FOLDER%/%LOWER_PREFIX%-api.xsd", - "%TARGET_FOLDER%/%GENERATED_RESOURCES_FOLDER%/%LOWER_PREFIX%-api-model.csv" + private static final String[] STANDARD_FILE_PATH_TEMPLATES = new String[]{ + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/citrus/extension/%CAMEL_PREFIX%NamespaceHandler.java", + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/citrus/%CAMEL_PREFIX%AbstractTestRequest.java", + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/citrus/%CAMEL_PREFIX%BeanDefinitionParser.java", + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%INVOKER_FOLDER%/spring/%CAMEL_PREFIX%BeanConfiguration.java", + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%MODEL_FOLDER%/PingReqType.java", + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%MODEL_FOLDER%/PingRespType.java", + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%REQUEST_FOLDER%/PingApi.java", + "%TARGET_FOLDER%/%GENERATED_SOURCES_FOLDER%/%REQUEST_FOLDER%/PungApi.java", + "%TARGET_FOLDER%/%GENERATED_RESOURCES_FOLDER%/%SCHEMA_FOLDER%/%LOWER_PREFIX%-api.xsd", + "%TARGET_FOLDER%/%GENERATED_RESOURCES_FOLDER%/%LOWER_PREFIX%-api-model.csv" }; /** @@ -66,8 +68,8 @@ class TestApiGeneratorMojoIntegrationTest extends AbstractMojoTestCase { * testing scenario. */ private static final String[] SPRING_META_FILE_TEMPLATES = new String[]{ - "%BASE_FOLDER%/%META_INF_FOLDER%/spring.handlers", - "%BASE_FOLDER%/%META_INF_FOLDER%/spring.schemas" + "%BASE_FOLDER%/%META_INF_FOLDER%/spring.handlers", + "%BASE_FOLDER%/%META_INF_FOLDER%/spring.schemas" }; private TestApiGeneratorMojo fixture; @@ -78,31 +80,27 @@ void beforeEachSetup() throws Exception { setUp(); } - static Stream executeMojoWithConfigurations() { + public static Stream executeMojoWithConfigurations() { return Stream.of( - arguments("pom-missing-prefix", - new MojoExecutionException("Required parameter 'prefix' not set for api at index '0'!")), - arguments("pom-missing-source", - new MojoExecutionException("Required parameter 'source' not set for api at index '0'!")), - arguments("pom-minimal-config", null), - arguments("pom-minimal-with-version-config", null), - arguments("pom-multi-config", null), - arguments("pom-full-config", null), - arguments("pom-full-with-version-config", null), - arguments("pom-soap-config", null) + arguments("pom-missing-prefix", + new MojoExecutionException("Required parameter 'prefix' not set for api at index '0'!")), + arguments("pom-missing-source", + new MojoExecutionException("Required parameter 'source' not set for api at index '0'!")), + arguments("pom-minimal-config", null), + arguments("pom-minimal-with-version-config", null), + arguments("pom-multi-config", null), + arguments("pom-full-config", null), + arguments("pom-full-with-version-config", null), + arguments("pom-soap-config", null) ); } @ParameterizedTest @MethodSource - void executeMojoWithConfigurations(String configName, Exception expectedException) - throws Exception { + public void executeMojoWithConfigurations(String configName, Exception expectedException) + throws Exception { - try { - fixture = fixtureFromPom(configName); - } catch (MojoExecutionException | MojoFailureException e) { - Assertions.fail("Test setup failed!", e); - } + fixture = fixtureFromPom(configName); @SuppressWarnings("unchecked") List apiConfigs = (List) getField(fixture, "apis"); @@ -124,10 +122,33 @@ void executeMojoWithConfigurations(String configName, Exception expectedExceptio } else { // When/Then assertThatThrownBy(() -> fixture.execute()).isInstanceOf(expectedException.getClass()) - .hasMessage(expectedException.getMessage()); + .hasMessage(expectedException.getMessage()); } } + @ParameterizedTest + @CsvSource({ + "api/PetApi.java" + }) + public void testGenerateAndCompare(String fileName) throws Exception { + fixture = fixtureFromPom("pom-minimal-petstore"); + + fixture.execute(); + + assertThat(getGeneratedFile(fileName)) + .isFile() + .exists() + .hasSameTextualContentAs(getExpectedFile(fileName)); + } + + private static File getGeneratedFile(String file) { + return new File("target/pom-minimal-pet/target/generated-test-sources/org/citrusframework/automation/minimal/" + file); + } + + private static File getExpectedFile(String file) { + return new File("src/test/resources/expected/" + file); + } + /** * Writes values to spring meta files, to make sure existing non generated and existing generated values are treated properly. */ @@ -197,7 +218,7 @@ private void assertSchemasInSpringSchemas(ApiConfig apiConfig) throws IOExceptio String targetNamespace = replaceDynamicVarsToLowerCase(apiConfig.getTargetXmlnsNamespace(), apiConfig.getPrefix(), apiConfig.getVersion()); targetNamespace = targetNamespace.replace(":", "\\:"); - String schemaPath = replaceDynamicVarsToLowerCase((String)getField(fixture, "schemaFolder"), apiConfig.getPrefix(), apiConfig.getVersion()); + String schemaPath = replaceDynamicVarsToLowerCase((String) getField(fixture, "schemaFolder"), apiConfig.getPrefix(), apiConfig.getVersion()); String text = String.format("%s.xsd=%s/%s-api.xsd", targetNamespace, schemaPath, apiConfig.getPrefix().toLowerCase()); @@ -221,13 +242,13 @@ private void assertApiType(ApiConfig apiConfig) throws IOException { private void assertTargetNamespace(ApiConfig apiConfig) throws IOException { assertThat(getContentOfFile(apiConfig, "-api.xsd")).contains( - String.format("targetNamespace=\"%s\"", - replaceDynamicVarsToLowerCase(apiConfig.getTargetXmlnsNamespace(), apiConfig.getPrefix(), apiConfig.getVersion()))); + String.format("targetNamespace=\"%s\"", + replaceDynamicVarsToLowerCase(apiConfig.getTargetXmlnsNamespace(), apiConfig.getPrefix(), apiConfig.getVersion()))); } private void assertEndpointName(ApiConfig apiConfig) throws IOException { assertThat(getContentOfFile(apiConfig, "AbstractTestRequest")).contains( - String.format("@Qualifier(\"%s\")", apiConfig.qualifiedEndpoint())); + String.format("@Qualifier(\"%s\")", apiConfig.qualifiedEndpoint())); } private String getContentOfFile(ApiConfig apiConfig, String fileIdentifier) throws IOException { @@ -244,8 +265,8 @@ private String getContentOfFile(ApiConfig apiConfig, String fileIdentifier) thro private String getTemplateContaining(String text) { return concat(stream(STANDARD_FILE_PATH_TEMPLATES), stream(SPRING_META_FILE_TEMPLATES)) - .filter(path -> path.contains(text)).findFirst() - .orElseThrow(() -> new AssertionError(String.format("Can't find file template with content: '%s'", text))); + .filter(path -> path.contains(text)).findFirst() + .orElseThrow(() -> new AssertionError(String.format("Can't find file template with content: '%s'", text))); } @NotNull @@ -257,31 +278,31 @@ private String resolveFilePath(ApiConfig apiConfig, String filePathTemplate) { String camelCasePrefix = new String(prefixCharArray); String invokerFolder = toFolder( - replaceDynamicVarsToLowerCase(apiConfig.getInvokerPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); + replaceDynamicVarsToLowerCase(apiConfig.getInvokerPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); String modelFolder = toFolder( - replaceDynamicVarsToLowerCase(apiConfig.getModelPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); + replaceDynamicVarsToLowerCase(apiConfig.getModelPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); String requestFolder = toFolder( - replaceDynamicVarsToLowerCase(apiConfig.getApiPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); + replaceDynamicVarsToLowerCase(apiConfig.getApiPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); String schemaFolder = toFolder( - replaceDynamicVars((String)getField(fixture, "schemaFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); + replaceDynamicVars((String) getField(fixture, "schemaFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); String generatedSourcesFolder = toFolder( - replaceDynamicVars((String)getField(fixture, "sourceFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); + replaceDynamicVars((String) getField(fixture, "sourceFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); String generatedResourcesFolder = toFolder( - replaceDynamicVars((String)getField(fixture, "resourceFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); + replaceDynamicVars((String) getField(fixture, "resourceFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); return filePathTemplate - .replace("%BASE_FOLDER%", fixture.getMavenProject().getBasedir().getPath()) - .replace("%TARGET_FOLDER%", fixture.getMavenProject().getBuild().getDirectory()) - .replace("%SOURCE_FOLDER%", fixture.getMavenProject().getBuild().getSourceDirectory()) - .replace("%GENERATED_SOURCES_FOLDER%", generatedSourcesFolder) - .replace("%GENERATED_RESOURCES_FOLDER%", generatedResourcesFolder) - .replace("%INVOKER_FOLDER%", invokerFolder) - .replace("%MODEL_FOLDER%", modelFolder) - .replace("%REQUEST_FOLDER%", requestFolder) - .replace("%SCHEMA_FOLDER%", schemaFolder) - .replace("%LOWER_PREFIX%", lowerCasePrefix) - .replace("%CAMEL_PREFIX%", camelCasePrefix) - .replace("%META_INF_FOLDER%", toFolder((String) getField(fixture, "metaInfFolder"))); + .replace("%BASE_FOLDER%", fixture.getMavenProject().getBasedir().getPath()) + .replace("%TARGET_FOLDER%", fixture.getMavenProject().getBuild().getDirectory()) + .replace("%SOURCE_FOLDER%", fixture.getMavenProject().getBuild().getSourceDirectory()) + .replace("%GENERATED_SOURCES_FOLDER%", generatedSourcesFolder) + .replace("%GENERATED_RESOURCES_FOLDER%", generatedResourcesFolder) + .replace("%INVOKER_FOLDER%", invokerFolder) + .replace("%MODEL_FOLDER%", modelFolder) + .replace("%REQUEST_FOLDER%", requestFolder) + .replace("%SCHEMA_FOLDER%", schemaFolder) + .replace("%LOWER_PREFIX%", lowerCasePrefix) + .replace("%CAMEL_PREFIX%", camelCasePrefix) + .replace("%META_INF_FOLDER%", toFolder((String) getField(fixture, "metaInfFolder"))); } private String toFolder(String text) { @@ -293,19 +314,25 @@ private String toFolder(String text) { return text.replace(".", "/"); } + @Nonnull private TestApiGeneratorMojo fixtureFromPom(String configName) throws Exception { - String goal = "create-test-api"; + try { + String goal = "create-test-api"; - File pomFile = new File(getBasedir(), String.format("src/test/resources/%s/%s", getClass().getSimpleName(), configName + ".xml")); - assertThat(pomFile).exists(); + File pomFile = new File(getBasedir(), String.format("src/test/resources/%s/%s", getClass().getSimpleName(), configName + ".xml")); + assertThat(pomFile).exists(); - MavenProject mavenProject = new CitrusOpenApiGeneratorMavenProjectStub(configName); + MavenProject mavenProject = new CitrusOpenApiGeneratorMavenProjectStub(configName); - TestApiGeneratorMojo testApiGeneratorMojo = (TestApiGeneratorMojo) lookupMojo(goal, pomFile); - testApiGeneratorMojo.setMavenProject(mavenProject); - testApiGeneratorMojo.setMojoExecution(newMojoExecution(goal)); + TestApiGeneratorMojo testApiGeneratorMojo = (TestApiGeneratorMojo) lookupMojo(goal, pomFile); + testApiGeneratorMojo.setMavenProject(mavenProject); + testApiGeneratorMojo.setMojoExecution(newMojoExecution(goal)); - return testApiGeneratorMojo; + return testApiGeneratorMojo; + } catch (MojoExecutionException | MojoFailureException e) { + Assertions.fail("Test setup failed!", e); + return new TestApiGeneratorMojo(); + } } } diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoUnitTest.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoUnitTest.java index 8309b94dcb..1bb8781631 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoUnitTest.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoUnitTest.java @@ -1,7 +1,5 @@ package org.citrusframework.maven.plugin; -import static java.lang.Boolean.TRUE; -import static org.assertj.core.api.Assertions.assertThat; import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_API_PACKAGE; import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_API_TYPE; import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_INVOKER_PACKAGE; @@ -13,11 +11,16 @@ import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_TARGET_NAMESPACE_TEMPLATE; import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.replaceDynamicVars; import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.replaceDynamicVarsToLowerCase; +import static java.lang.Boolean.TRUE; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.Mockito.doReturn; import static org.springframework.test.util.ReflectionTestUtils.getField; import jakarta.validation.constraints.NotNull; +import org.citrusframework.maven.plugin.TestApiGeneratorMojo; +import org.citrusframework.maven.plugin.TestApiGeneratorMojo.ApiConfig; +import org.citrusframework.maven.plugin.TestApiGeneratorMojo.ApiType; import java.io.File; import java.util.HashMap; import java.util.Map; @@ -27,8 +30,6 @@ import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.testing.AbstractMojoTestCase; import org.apache.maven.project.MavenProject; -import org.citrusframework.maven.plugin.TestApiGeneratorMojo.ApiConfig; -import org.citrusframework.maven.plugin.TestApiGeneratorMojo.ApiType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-petstore.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-petstore.xml new file mode 100644 index 0000000000..96215c81eb --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-petstore.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + minimal-config-petstore + + + + + citrus-test-api-generator-maven-plugin + + + + Minimal + api/petstore.yaml + + + + + + + create-test-api + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/petstore.yaml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/petstore.yaml new file mode 100644 index 0000000000..0ff3414db5 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/petstore.yaml @@ -0,0 +1,206 @@ +swagger: '2.0' +info: + description: 'This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.' + version: 1.0.0 + x-citrus-app: PETS + x-citrus-api-name: petstore + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +host: petstore.swagger.io +basePath: /v2 +tags: + - name: pet + description: Everything about your Pets + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user +schemes: + - http +paths: + '/pet/{petId}': + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + produces: +# - application/xml + - application/json + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + type: integer + format: int64 + - name: verbose + description: Output details + in: query + required: false + type: boolean + - name: correlationIds + description: ID to trace a request + in: header + required: false + type: string + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + - basicAuth: [] +securityDefinitions: + petstore_auth: + type: oauth2 + authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' + flow: implicit + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + api_key: + type: apiKey + name: api_key + in: header + basicAuth: + type: basic +definitions: + Order: + title: Pet Order + description: An order for a pets from the pet store + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + xml: + name: Order + Category: + title: Pet category + description: A category for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + User: + title: a User + description: A User who is purchasing from the pet store + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + format: int32 + description: User Status + xml: + name: User + Tag: + title: Pet Tag + description: A tag for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + title: a Pet + description: A pet for sale in the pet store + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/definitions/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + $ref: '#/definitions/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + title: An uploaded response + description: Describes the result of uploading an image resource + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/expected/api/PetApi.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/expected/api/PetApi.java new file mode 100644 index 0000000000..3ae217d495 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/expected/api/PetApi.java @@ -0,0 +1,93 @@ +package org.citrusframework.automation.minimal.api; + +import org.citrusframework.endpoint.Endpoint; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.openapi.OpenApiSpecification; +import org.citrusframework.openapi.actions.OpenApiClientActionBuilder; +import org.citrusframework.openapi.actions.OpenApiClientActionBuilder.OpenApiOperationBuilder; +import org.citrusframework.openapi.actions.OpenApiClientRequestActionBuilder; +import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder; + +import java.util.function.UnaryOperator; + +import static org.citrusframework.spi.Resources.create; + +public class PetApi { + private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( + create("api/petstore.yaml") + ); + + public static PetApi openapiPetstore(HttpClient httpClient) { + return new PetApi(httpClient); + } + + private final HttpClient httpClient; + + private PetApi(HttpClient httpClient) { + this.httpClient = httpClient; + } + + public PetstoreAction getPetById() { + return petstoreAction(new GetPetByIdRequest()); + } + + private PetstoreAction petstoreAction(B requestBuilder) { + return new PetstoreAction<>(httpClient, petstoreSpec, requestBuilder); + } + + /** + * getPetById (GET /pet/{petId}) + * Find pet by ID + **/ + public static class GetPetByIdRequest extends OperationRequestBuilder { + @Override + public String getOperationId() { + return "getPetById"; + } + + public GetPetByIdRequest withPetId(Long petId) { + openApiOperation.withParameter("petId", petId); + return this; + } + + public GetPetByIdRequest withVerbose(Boolean verbose) { + openApiOperation.withParameter("verbose", verbose); + return this; + } + + public GetPetByIdRequest withCorrelationIds(String correlationIds) { + openApiOperation.withParameter("correlationIds", correlationIds); + return this; + } + } + + public static abstract class OperationRequestBuilder { + protected final OpenApiOperationBuilder openApiOperation = OpenApiOperationBuilder.operation(getOperationId()); + + public abstract String getOperationId(); + + public OpenApiOperationBuilder build() { + return openApiOperation; + } + } + + public static class PetstoreAction extends OpenApiClientActionBuilder { + private final T operation; + + private PetstoreAction(Endpoint httpClient, OpenApiSpecification specification, T operation) { + super(httpClient, specification); + this.operation = operation; + } + + public OpenApiClientRequestActionBuilder send(UnaryOperator builderProvider) { + var builder = builderProvider.apply(operation); + var send = send(builder.build()); + send.fork(true); + return send; + } + + public OpenApiClientResponseActionBuilder receive() { + return receive(operation.getOperationId(), "200"); + } + } +} diff --git a/test-api-generator/readme.md b/test-api-generator/readme.md new file mode 100644 index 0000000000..f4552f36b6 --- /dev/null +++ b/test-api-generator/readme.md @@ -0,0 +1,81 @@ +# TAT-1291 + +## Possible solution + +```java + +@CitrusTest +public void getPetById() { +// variable("petId", "1001"); + // native + when(openapi(petstoreSpec) + .client(httpClient) + .withParameter("petId", "1001") // TODO: to be implemented + .send("getPetById") + .fork(true)); + // generated - TODO: to be implemented + when(openapiPetstore() + .client(httpClient) + .getPetById() + .withPetId("1001") + .send() // maybe obsolete? + .fork(true)); +} +``` + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +```