From 42f6e6fe336403de3deb11831ffb2ae67c5197dc Mon Sep 17 00:00:00 2001 From: Thorsten Schlathoelter Date: Mon, 13 May 2024 11:45:01 +0200 Subject: [PATCH 01/16] feat(#1156): provide test api generator --- .../message/MessagePayloadUtils.java | 34 + .../citrusframework/testapi/GeneratedApi.java | 45 + .../testapi/GeneratedApiRequest.java | 29 + .../message/MessagePayloadUtilsTest.java | 67 +- .../ApiActionBuilderCustomizerService.java | 13 + pom.xml | 15 + src/manual/connector-openapi.adoc | 398 ++++++++ src/manual/index.adoc | 2 + .../citrus-test-api-generator-core/pom.xml | 202 ++++ .../openapi/generator/JavaCitrusCodegen.java | 345 +++++++ .../SimpleWsdlToOpenApiTransformer.java | 187 ++++ .../WsdlToOpenApiTransformationException.java | 8 + .../org.openapitools.codegen.CodegenConfig | 1 + .../resources/java-citrus/api-model.mustache | 2 + .../main/resources/java-citrus/api.mustache | 265 ++++++ .../resources/java-citrus/api_doc.mustache | 1 + .../resources/java-citrus/api_soap.mustache | 165 ++++ .../resources/java-citrus/api_test.mustache | 1 + .../java-citrus/bean_configuration.mustache | 38 + .../bean_definition_parser.mustache | 215 +++++ .../main/resources/java-citrus/model.mustache | 1 + .../resources/java-citrus/model_doc.mustache | 1 + .../java-citrus/namespace_handler.mustache | 36 + .../resources/java-citrus/schema.mustache | 217 +++++ .../java-citrus/schema_soap.mustache | 107 +++ .../resources/java-citrus/test_base.mustache | 245 +++++ .../java-citrus/test_base_soap.mustache | 187 ++++ .../openapi/generator/GeneratedApiIT.java | 626 +++++++++++++ .../openapi/generator/GetPetByIdTest.java | 254 ++++++ .../generator/JavaCitrusCodegenIT.java | 93 ++ .../generator/JavaCitrusCodegenTest.java | 166 ++++ .../openapi/generator/ServiceLoaderTest.java | 24 + .../SimpleWsdlToOpenApiTransformerTest.java | 29 + .../generator/SpringBeanConfigurationIT.java | 55 ++ .../util/TestApiActionBuilderCustomizer.java | 22 + ....testapi.ApiActionBuilderCustomizerService | 1 + .../test/resources/META-INF/spring.handlers | 3 + .../test/resources/META-INF/spring.schemas | 3 + .../apis/multiparttest-rest-resource.yaml | 249 +++++ .../src/test/resources/apis/petstore.yaml | 700 ++++++++++++++ .../apis/petstore_reservedWords.yaml | 120 +++ .../src/test/resources/citrus-context.xml | 7 + .../defaultOas3SchemaValidationTest.xml | 25 + .../failOnReasonPhraseTest.xml | 25 + .../GeneratedApiTest/failOnStatusTest.xml | 25 + .../GeneratedApiTest/failOnVersionTest.xml | 25 + .../getPetByIdRequestTest.xml | 24 + .../jsonDeactivatedSchemaValidationTest.xml | 26 + .../GeneratedApiTest/jsonPathExtractTest.xml | 25 + .../jsonPathExtractionTest.xml | 25 + .../jsonPathValidationFailureTest.xml | 25 + .../jsonPathValidationTest.xml | 25 + .../jsonSchemaValidationFailureTest.xml | 25 + .../jsonSchemaValidationTest.xml | 25 + .../multipartWithFileAttributesTest.xml | 22 + .../multipartWithMultipleDatatypesTest.xml | 24 + .../multipartWithPlainTextTest.xml | 32 + .../payloads/AdditionalData.json | 6 + .../payloads/MultipartTemplate.xml | 4 + .../GeneratedApiTest/payloads/Schema.json | 1 + .../payloads/addPetMessage.json | 6 + .../payloads/getPetByIdControlMessage1.json | 6 + .../payloads/getPetByIdControlMessage2.json | 6 + .../GeneratedApiTest/postFileTest.xml | 22 + .../GeneratedApiTest/scriptValidationTest.xml | 25 + .../sendWithBodyLiteralTest.xml | 20 + .../sendWithBodyLiteralWithVariableTest.xml | 23 + .../GeneratedApiTest/sendWithBodyTest.xml | 20 + .../sendWithExtraHeaderTest.xml | 22 + .../MultipartTestAbstractTestRequest.java | 245 +++++ .../MultipartTestBeanDefinitionParser.java | 215 +++++ .../MultipartTestNamespaceHandler.java | 29 + .../rest/multiparttest/model/Metadata.java | 1 + .../multiparttest/model/PutObjectResult.java | 1 + .../request/MultiparttestControllerApi.java | 750 +++++++++++++++ .../MultipartTestBeanConfiguration.java | 56 ++ .../citrus/PetStoreAbstractTestRequest.java | 245 +++++ .../citrus/PetStoreBeanDefinitionParser.java | 215 +++++ .../extension/PetStoreNamespaceHandler.java | 45 + .../rest/petstore/model/Category.java | 1 + .../rest/petstore/model/ModelApiResponse.java | 1 + .../rest/petstore/model/Order.java | 1 + .../expectedgen/rest/petstore/model/Pet.java | 1 + .../expectedgen/rest/petstore/model/Tag.java | 1 + .../expectedgen/rest/petstore/model/User.java | 1 + .../rest/petstore/request/PetApi.java | 862 ++++++++++++++++++ .../rest/petstore/request/StoreApi.java | 433 +++++++++ .../rest/petstore/request/UserApi.java | 819 +++++++++++++++++ .../spring/PetStoreBeanConfiguration.java | 142 +++ .../OpenApiFromWsdlAbstractTestRequest.java | 187 ++++ .../OpenApiFromWsdlBeanDefinitionParser.java | 215 +++++ .../OpenApiFromWsdlNamespaceHandler.java | 26 + .../request/BookServiceSoapApi.java | 330 +++++++ .../OpenApiFromWsdlBeanConfiguration.java | 38 + .../BookDatatypes.xsd | 67 ++ .../BookService-generated.yaml | 33 + .../BookService.wsdl | 110 +++ .../pom.xml | 111 +++ .../maven/plugin/CodeGenMojoWrapper.java | 86 ++ .../maven/plugin/SpringMetaFileGenerator.java | 156 ++++ .../maven/plugin/TestApiGeneratorMojo.java | 412 +++++++++ .../TestApiGeneratorMojoIntegrationTest.java | 314 +++++++ .../plugin/TestApiGeneratorMojoUnitTest.java | 286 ++++++ ...itrusOpenApiGeneratorMavenProjectStub.java | 42 + .../pom-full-config.xml | 42 + .../pom-full-with-version-config.xml | 42 + .../pom-minimal-config.xml | 32 + .../pom-minimal-with-version-config.xml | 33 + .../pom-missing-prefix.xml | 32 + .../pom-missing-source.xml | 32 + .../pom-multi-config.xml | 40 + .../pom-soap-config.xml | 33 + .../src/test/resources/api/test-api.yml | 125 +++ test-api-generator/pom.xml | 37 + .../text/PlainTextMessageValidator.java | 44 +- 115 files changed, 12627 insertions(+), 60 deletions(-) create mode 100644 core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java create mode 100644 core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java create mode 100644 core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java create mode 100644 test-api-generator/citrus-test-api-generator-core/pom.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/JavaCitrusCodegen.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformer.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api-model.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_doc.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_soap.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_test.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_configuration.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_definition_parser.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/model_doc.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/namespace_handler.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base_soap.mustache create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GeneratedApiIT.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdTest.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenIT.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenTest.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/ServiceLoaderTest.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/util/TestApiActionBuilderCustomizer.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/services/org.citrusframework.testapi.ApiActionBuilderCustomizerService create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.handlers create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.schemas create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/multiparttest-rest-resource.yaml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore_reservedWords.yaml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/citrus-context.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/defaultOas3SchemaValidationTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnReasonPhraseTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnStatusTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnVersionTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/getPetByIdRequestTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonDeactivatedSchemaValidationTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractionTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationFailureTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationFailureTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithFileAttributesTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithMultipleDatatypesTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithPlainTextTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/AdditionalData.json create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/MultipartTemplate.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/Schema.json create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/addPetMessage.json create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage2.json create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/postFileTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/scriptValidationTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralWithVariableTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithExtraHeaderTest.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestAbstractTestRequest.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/MultipartTestBeanDefinitionParser.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/citrus/extension/MultipartTestNamespaceHandler.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/Metadata.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/model/PutObjectResult.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/request/MultiparttestControllerApi.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/multiparttest/spring/MultipartTestBeanConfiguration.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreAbstractTestRequest.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/PetStoreBeanDefinitionParser.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/citrus/extension/PetStoreNamespaceHandler.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Category.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/ModelApiResponse.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Order.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Pet.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/Tag.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/model/User.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/PetApi.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/StoreApi.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/request/UserApi.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/rest/petstore/spring/PetStoreBeanConfiguration.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlAbstractTestRequest.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/OpenApiFromWsdlBeanDefinitionParser.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/citrus/extension/OpenApiFromWsdlNamespaceHandler.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/request/BookServiceSoapApi.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/JavaCitrusCodegenIntegrationTest/expectedgen/soap/bookservice/spring/OpenApiFromWsdlBeanConfiguration.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookDatatypes.xsd create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService.wsdl create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/pom.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/CodeGenMojoWrapper.java create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoIntegrationTest.java create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoUnitTest.java create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/stubs/CitrusOpenApiGeneratorMavenProjectStub.java create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-config.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-with-version-config.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-config.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-with-version-config.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-prefix.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-source.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-multi-config.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-soap-config.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/test-api.yml create mode 100644 test-api-generator/pom.xml diff --git a/core/citrus-api/src/main/java/org/citrusframework/message/MessagePayloadUtils.java b/core/citrus-api/src/main/java/org/citrusframework/message/MessagePayloadUtils.java index 09145650a7..4c2a8824c8 100644 --- a/core/citrus-api/src/main/java/org/citrusframework/message/MessagePayloadUtils.java +++ b/core/citrus-api/src/main/java/org/citrusframework/message/MessagePayloadUtils.java @@ -212,4 +212,38 @@ public static String prettyPrintJson(String payload) { } return sb.toString(); } + + /** + * Normalizes the given text by replacing all whitespace characters (identified by {@link Character#isWhitespace) by a single space + * and replacing windows style line endings with unix style line endings. + */ + public static String normalizeWhitespace(String text, boolean normalizeWhitespace, boolean normalizeLineEndingsToUnix) { + if (text == null || text.isEmpty()) { + return text; + } + + if (normalizeWhitespace) { + StringBuilder result = new StringBuilder(); + boolean lastWasSpace = true; + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (Character.isWhitespace(c)) { + if (!lastWasSpace) { + result.append(' '); + } + lastWasSpace = true; + } else { + result.append(c); + lastWasSpace = false; + } + } + return result.toString().trim(); + } + + if (normalizeLineEndingsToUnix) { + return text.replaceAll("\\r(\\n)?", "\n"); + } + + return text; + } } diff --git a/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java new file mode 100644 index 0000000000..a3d6bfdad2 --- /dev/null +++ b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java @@ -0,0 +1,45 @@ +package org.citrusframework.testapi; + +import java.util.Map; + +/** + * Interface representing a generated API from an OpenAPI specification. + * Provides methods to retrieve metadata about the API such as title, version, + * prefix, and information extensions. + */ +public interface GeneratedApi { + + /** + * Retrieves the title of the OpenAPI specification, as specified in the info section of the API. + * + * @return the title of the OpenAPI specification + */ + String getApiTitle(); + + /** + * Retrieves the version of the OpenAPI specification, as specified in the info section of the API. + * + * @return the version of the OpenAPI specification + */ + String getApiVersion(); + + /** + * Retrieves the prefix used for the API, as specified in the API generation configuration. + * + * @return the prefix used for the API + */ + String getApiPrefix(); + + /** + * Retrieves the specification extensions of the OpenAPI defined in the "info" section. + *

+ * Specification extensions, also known as vendor extensions, are custom key-value pairs used to describe extra + * functionality not covered by the standard OpenAPI Specification. These properties start with "x-". + * This method collects only the extensions defined in the "info" section of the API. + *

+ * + * @return a map containing the specification extensions defined in the "info" section of the API, + * where keys are extension names and values are extension values + */ + Map getApiInfoExtensions(); +} \ No newline at end of file diff --git a/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java new file mode 100644 index 0000000000..5b519ac118 --- /dev/null +++ b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java @@ -0,0 +1,29 @@ +package org.citrusframework.testapi; + +/** + * Interface representing a generated API request corresponding to an operation in an OpenAPI specification. + * Provides methods to retrieve metadata about the request such as operation name, HTTP method, and path. + */ +public interface GeneratedApiRequest { + + /** + * Retrieves the name of the OpenAPI operation associated with the request. + * + * @return the name of the OpenAPI operation + */ + String getOperationName(); + + /** + * Retrieves the HTTP method used for the request. + * + * @return the HTTP method used for the request (e.g., GET, POST) + */ + String getMethod(); + + /** + * Retrieves the path used for the request. + * + * @return the path used for the request + */ + String getPath(); +} diff --git a/core/citrus-api/src/test/java/org/citrusframework/message/MessagePayloadUtilsTest.java b/core/citrus-api/src/test/java/org/citrusframework/message/MessagePayloadUtilsTest.java index 369385adc4..d89f582038 100644 --- a/core/citrus-api/src/test/java/org/citrusframework/message/MessagePayloadUtilsTest.java +++ b/core/citrus-api/src/test/java/org/citrusframework/message/MessagePayloadUtilsTest.java @@ -16,56 +16,79 @@ package org.citrusframework.message; +import static org.testng.Assert.assertEquals; + import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class MessagePayloadUtilsTest { @Test public void shouldPrettyPrintJson() { - Assert.assertEquals(MessagePayloadUtils.prettyPrint(""), ""); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{}"), "{}"); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("[]"), "[]"); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\"}"), + assertEquals(MessagePayloadUtils.prettyPrint(""), ""); + assertEquals(MessagePayloadUtils.prettyPrint("{}"), "{}"); + assertEquals(MessagePayloadUtils.prettyPrint("[]"), "[]"); + assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\"}"), String.format("{%n \"user\": \"citrus\"%n}")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"text\":\"\"}"), + assertEquals(MessagePayloadUtils.prettyPrint("{\"text\":\"\"}"), String.format("{%n \"text\": \"\"%n}")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n { \"user\":%n%n \"citrus\" }")), + assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n { \"user\":%n%n \"citrus\" }")), String.format("{%n \"user\": \"citrus\"%n}")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32}"), + assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32}"), String.format("{%n \"user\": \"citrus\",%n \"age\": 32%n}")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("[22, 32]"), + assertEquals(MessagePayloadUtils.prettyPrint("[22, 32]"), String.format("[%n22,%n32%n]")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}]"), + assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}]"), String.format("[%n {%n \"user\": \"citrus\",%n \"age\": 32%n }%n]")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}, {\"user\":\"foo\",\"age\": 99}]"), + assertEquals(MessagePayloadUtils.prettyPrint("[{\"user\":\"citrus\",\"age\": 32}, {\"user\":\"foo\",\"age\": 99}]"), String.format("[%n {%n \"user\": \"citrus\",%n \"age\": 32%n },%n {%n \"user\": \"foo\",%n \"age\": 99%n }%n]")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pet\":{\"name\": \"fluffy\", \"age\": 4}}"), + assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pet\":{\"name\": \"fluffy\", \"age\": 4}}"), String.format("{%n \"user\": \"citrus\",%n \"age\": 32,%n \"pet\": {%n \"name\": \"fluffy\",%n \"age\": 4%n }%n}")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pets\":[\"fluffy\",\"hasso\"]}"), + assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"age\": 32,\"pets\":[\"fluffy\",\"hasso\"]}"), String.format("{%n \"user\": \"citrus\",%n \"age\": 32,%n \"pets\": [%n \"fluffy\",%n \"hasso\"%n ]%n}")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[\"fluffy\",\"hasso\"],\"age\": 32}"), + assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[\"fluffy\",\"hasso\"],\"age\": 32}"), String.format("{%n \"user\": \"citrus\",%n \"pets\": [%n \"fluffy\",%n \"hasso\"%n ],%n \"age\": 32%n}")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[{\"name\": \"fluffy\", \"age\": 4},{\"name\": \"hasso\", \"age\": 2}],\"age\": 32}"), + assertEquals(MessagePayloadUtils.prettyPrint("{\"user\":\"citrus\",\"pets\":[{\"name\": \"fluffy\", \"age\": 4},{\"name\": \"hasso\", \"age\": 2}],\"age\": 32}"), String.format("{%n \"user\": \"citrus\",%n \"pets\": [%n {%n \"name\": \"fluffy\",%n \"age\": 4%n },%n {%n \"name\": \"hasso\",%n \"age\": 2%n }%n ],%n \"age\": 32%n}")); } @Test public void shouldPrettyPrintXml() { - Assert.assertEquals(MessagePayloadUtils.prettyPrint(""), ""); - Assert.assertEquals(MessagePayloadUtils.prettyPrint(""), + assertEquals(MessagePayloadUtils.prettyPrint(""), ""); + assertEquals(MessagePayloadUtils.prettyPrint(""), String.format("%n%n")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("Citrus rocks!"), + assertEquals(MessagePayloadUtils.prettyPrint("Citrus rocks!"), String.format("%n Citrus rocks!%n%n")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint("Citrus rocks!"), + assertEquals(MessagePayloadUtils.prettyPrint("Citrus rocks!"), String.format("%n%n Citrus rocks!%n%n")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%nCitrus rocks!%n%n")), + assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%nCitrus rocks!%n%n")), String.format("%n Citrus rocks!%n%n")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n %nCitrus rocks!%n %n")), + assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n %nCitrus rocks!%n %n")), String.format("%n Citrus rocks!%n%n")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n ")), + assertEquals(MessagePayloadUtils.prettyPrint(String.format("%n%n ")), String.format("%n %n %n %n%n")); - Assert.assertEquals(MessagePayloadUtils.prettyPrint(String.format("")), + assertEquals(MessagePayloadUtils.prettyPrint(String.format("")), String.format("%n %n %n %n%n")); } + + @DataProvider + public Object[][] normalizeWhitespaceProvider() { + return new Object[][] { + // Test data: payload, ignoreWhitespace, ignoreNewLineType, expected result + {"Hello \t\r\nWorld\r\n", true, true, "Hello World"}, + {"Hello \t\r\nWorld\r\n", true, false, "Hello World"}, + {"Hello \t\r\nWorld\r\n", false, true, "Hello \t\nWorld\n"}, + {"Hello \t\r\nWorld\r\n", false, false, "Hello \t\r\nWorld\r\n"}, + {"", true, true, ""}, + {"", false, false, ""}, + {null, true, true, null}, + {null, false, false, null} + }; + } + + @Test(dataProvider = "normalizeWhitespaceProvider") + public void normalizeWhitespace(String text, boolean normalizeWhitespace, boolean normalizeLineEndingsToUnix, String expected) { + assertEquals(MessagePayloadUtils.normalizeWhitespace(text, normalizeWhitespace, normalizeLineEndingsToUnix), expected); + } } diff --git a/core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java b/core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java new file mode 100644 index 0000000000..2b77d97f7b --- /dev/null +++ b/core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java @@ -0,0 +1,13 @@ +package org.citrusframework.testapi; + +import org.citrusframework.TestAction; +import org.citrusframework.actions.SendMessageAction.SendMessageActionBuilder; +import org.citrusframework.context.TestContext; + +/** + * Implementors of this interface are used to customize the SendMessageActionBuilder with application specific information. E.g. cookies + * or transactionIds. + */ +public interface ApiActionBuilderCustomizerService { + > T build(GeneratedApi generatedApi, TestAction action, TestContext context, T builder); +} diff --git a/pom.xml b/pom.xml index 8ffc43ad17..981380096e 100644 --- a/pom.xml +++ b/pom.xml @@ -175,6 +175,7 @@ 1.6.13 3.9.0 3.12.0 + 3.3.0 3.0.1 3.3.1 1.11.2 @@ -247,12 +248,14 @@ 3.1.0 4.1.105.Final 4.12.0 + 7.5.0 4.7.5 3.0.3 4.19.1 2.0.11 1.1.10.5 2.2 + 3.2.5 6.1.6 4.0.10 6.2.4 @@ -512,6 +515,13 @@ ${wsdl4j.version} + + org.openapitools + openapi-generator + ${openapi-generator-maven-plugin} + provided + + org.springframework spring-test @@ -860,6 +870,11 @@ jackson-databind ${jackson.version} + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson.version} + com.fasterxml.jackson.module jackson-module-jaxb-annotations diff --git a/src/manual/connector-openapi.adoc b/src/manual/connector-openapi.adoc index 53333ff1f0..b89f3889c2 100644 --- a/src/manual/connector-openapi.adoc +++ b/src/manual/connector-openapi.adoc @@ -309,3 +309,401 @@ Also, the server will verify the HTTP request method, the Content-Type header as The given HTTP status code defines the response that should be sent by the server. The server will generate a proper response according to the OpenAPI specification. This also includes a potential response message body (e.g. pet object). + +[[openapi-server]] +=== OpenAPI Test API Generator + +For an even deeper integration with a given OpenAPI, Citrus offers the possibility to generate a dedicated Test API which provides test actions tailored to the specific operations of the OpenAPI under evaluation. +These actions can be used in XML or Java DSL. +This functionality is provided by the `Citrus OpenAPI Test API Generator` which leverages the link:https://github.com/swagger-api/swagger-codegen/tree/master[OpenAPI Code Generator] to generate code, but provides custom templates tailored for seamless integration within the Citrus framework. + +The generator provides the following features: + +* generation of a Test API +** from OpenAPI Specification +** [TODO #1163] from WSDL via an intermediate step that generates a "light" OpenApi specification from a WSDL +* integration into Citrus XML test cases +** integration into XML editors via generated XSD +*** schema validation +*** auto completion +* integration into Citrus Java test cases via Java DSL [TODO #1161] + +The following directory structure/table specifies the files, which are generated by the generator. +Note that the `Prefix` is a configuration parameter which should uniquely identify a generated API. +It is specified in the build configuration for the Test API. +``` +target/ +├───generated-test-resources/ +│ ├───META-INF/ +│ │ ├───spring.handlers +│ │ └───spring.schemas +│ └───schema/ +│ └───xsd/ +│ └───prefix-api.xsd +└───generated-test-sources/ + └───org/ + └───citrusframework/ + └───automation/ + └───prefix/ + ├───api/ + │ └───MyApi.java + ├───citrus/ + │ ├───extension/ + │ │ └───PrefixNamespaceHandler.java + │ ├───PrefixAbstractTestRequest.java + │ └───PrefixBeanDefinitionParser.java + ├───model/ + │ ├───MyReqTypeA.java + │ └───MyReqTypeB.java + └───spring/ + └───PrefixBeanConfiguration.java +``` + +|=== +| File | Content + +| `spring.handlers` | Spring namespace handler configuration, that contains all NamespaceHandlers for all generated APIs. +| `spring.schemas` | Spring schema definitions, with mappings of namespaces to schemas for all generated APIs. +| `prefix-api.xsd` | XSD schema for the integration of the Test API into XML. +| `PrefixNamespaceHandler.java` | A Spring class, that registers bean definition parsers for Test API XML elements. +| `PrefixAbstractTestRequest.java` | Abstract superclass of all Test API actions. +| `PrefixBeanDefinitionParser.java` | Spring bean definition parser, responsible for parsing Test API XML elements into test actions. +| `MyReqTypeA.java, MyReqTypeB.java` | Model files generated with respect to the schema definition of the OpenAPI. +| `PrefixBeanConfiguration.java` | A Spring @Configuration class, that registers all Test API actions as Spring beans. +|=== + +==== Configuration of Test API generation + +Code generation is typically performed during the build process. +For the Citrus Test API Generator, it is carried out by a Maven plugin. +While the standard generator plugin, `org.openapitools:openapi-generator-maven-plugin`, can be employed for this purpose, configuring it can be cumbersome, especially when dealing with multiple APIs. +To address this challenge, Citrus offers its adaptation of this standard generator Maven plugin. +This `Citrus OpenAPI Generator Plugin` simplifies the configuration of test API generation by providing predefined defaults and supporting the generation of multiple APIs. +Additionally, it enhances support for generating Spring integration files (`spring.handlers` and `spring.schemas`), thereby facilitating the integration of generated APIs into Spring-based applications. +Consequently, utilizing the Citrus Generator Plugin is recommended in most scenarios. + +The following shows the configuration of test api generation for different scenarios: + +.Citrus OpenAPI Generator Plugin - multiple APIs, minimal configuration +[source,xml,indent=0,role="primary"] +---- + + citrus-test-api-generator-maven-plugin + + + + + Multi1 + api/test-api.yml + + + Multi2 + api/test-api.yml + + + Multi3 + api/test-api.yml + + + + + + + create-test-api + + + + + +---- + +.Citrus OpenAPI Generator Plugin - single API full configuration +[source,xml,indent=0,role="secondary"] +---- + + citrus-test-api-generator-maven-plugin + + + + my-generated-sources + my-generated-resources + myschema/xsd + src/main/resources/META-INF + + Full + api/test-api.yml + org.mypackage.%PREFIX%.api + myEndpoint + org.mypackage.%PREFIX%.invoker + org.mypackage.%PREFIX%.model + "http://company/citrus-test-api/myNamespace" + + + + + + + + create-test-api + + + + +---- + +.Standard OpenAPI Generator Plugin +[source,xml,indent=0,role="secondary"] +---- + + + org.openapitools + openapi-generator-maven-plugin + + + + org.citrusframework + citrus-test-api-generator-core + ${project.version} + + + + + REST + generated-test-resources + generated-test-sources + true + + true + + java-citrus + ${project.build.directory} + + + + generate-openapi-petstore-files + compile + + generate + + + ${project.basedir}/src/test/resources/apis/petstore.yaml + + org.citrusframework.openapi.generator.rest.petstore + org.citrusframework.openapi.generator.rest.petstore.request + org.citrusframework.openapi.generator.rest.petstore.model + PetStore + petStoreEndpoint + + + + + generate-openapi-files-for-soap + compile + + generate + + + ${project.basedir}/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml + + SOAP + org.citrusframework.openapi.generator.soap.bookservice + org.citrusframework.openapi.generator.soap.bookservice.request + org.citrusframework.openapi.generator.soap.bookservice.model + SoapSample + OpenApiFromWsdl + soapSampleEndpoint + + + + + +---- + +These are the primary elements you can configure in the `` section: + +|=== +| Configuration element | Maven Property | Description | Default Value + +| `schemaFolder` | `citrus.test.api.generator.schema.folder` | Location for the generated XSD schemas | `schema/xsd/%VERSION%` +| `resourceFolder` | `citrus.test.api.generator.resource.folder` | Location to which resources are generated | `generated-resources` +| `sourceFolder` | `citrus.test.api.generator.source.folder` | Location to which sources are generated | `generated-sources` +| `metaInfFolder` | `citrus.test.api.generator.meta.inf.folder` | Location to which spring meta files are generated/updated | `target/generated-test-resources/META-INF` +| `generateSpringIntegrationFiles` | `citrus.test.api.generator.generate.spring.integration.files` | Specifies whether spring integration files should be generated | `true` +| Nested api element | | | +| `prefix` | `citrus.test.api.generator.prefix` | Specifies the prefix used for the test API, typically an acronym | (no default, required) +| `source` | `citrus.test.api.generator.source` | Specifies the source of the test API | (no default, required) +| `version` | `citrus.test.api.generator.version` | Specifies the version of the API, may be null | (none) +| `endpoint` | `citrus.test.api.generator.endpoint` | Specifies the endpoint of the test API | `applicationServiceClient` +| `type` | `citrus.test.api.generator.type` | Specifies the type of the test API | `REST`, other option is `SOAP` +| `useTags` | `citrus.test.api.generator.use.tags` | Specifies whether tags should be used by the generator | `true` +| `invokerPackage` | `citrus.test.api.generator.invoker.package` | Package for the test API classes | `org.citrusframework.automation.%PREFIX%.%VERSION%` +| `apiPackage` | `citrus.test.api.generator.api.package` | Package for the test API interface classes | `org.citrusframework.automation.%PREFIX%.%VERSION%.api` +| `modelPackage` | `citrus.test.api.generator.model.package` | Package for the test API model classes | `org.citrusframework.automation.%PREFIX%.%VERSION%.model` +| `targetXmlnsNamespace` | `citrus.test.api.generator.namespace` | XML namespace used by the API | `http://www.citrusframework.org/schema/%VERSION%/%PREFIX%-api` +|=== + + +Note: `%PREFIX%` and `%VERSION%` are placeholders that will be replaced by their specific values as configured. +The plugin performs a conversion to lowercase for `PREFIX` used in package names and in `targetXmlnsNamespace`. + +==== Running the generator + +To run the generator, execute the following command in your project directory: + +[source,bash] +---- +mvn citrus-test-api-generator-maven-plugin:create-test-api +---- + + +This command will generate the classes and XSD files as configured for your APIs in the specified locations. + +==== Spring meta file generation + +The `citrus-test-api-generator-maven-plugin` supports the generation of Spring integration files, specifically `spring.handlers` and `spring.schemas`. +These files are essential for Spring applications utilizing XML configuration, as they provide mapping information for custom XML namespaces. + +===== Purpose + +The generated Spring integration files serve the purpose of mapping custom XML namespaces to their corresponding namespace handler and schema locations. +This mapping allows Spring to properly parse and validate XML configuration files containing custom elements and attributes. + +===== Configuration + +The maven plugin generates these Spring integration files based on the provided configuration in the `citrus-test-api-generator-maven-plugin` section of the pom.xml file. +For each API specified, the plugin writes entries into the `spring.handlers` and `spring.schemas` files according to the configured XML namespaces and their corresponding handlers and schemas. + +===== Important Consideration + +When there are other non-generated Spring schemas or handlers present in the `META-INF` folder, it's crucial to ensure that the `metaInfFolder` configuration points to the existing `META-INF` directory in the main resources, which is usually `src/main/resources/META-INF`. +This ensures that the plugin correctly updates the existing files without overwriting them. + +To identify generated schemas, their namespace should include the following segment `citrus-test-schema`. +During updates of the meta files, the generator filters out lines containing this segment from existing files and then re-adds them, preserving any non-generated content. + +==== Usage + +Once generated, the `spring.handlers` and `spring.schemas` files, along with any existing non-generated content, should be included in the resources of your Spring application. +During runtime, Spring will use these files to resolve custom XML namespaces and handle elements accordingly. +This automatically happens if one of the following folders is chosen: + +- `target/generated-test-resources/META-INF` (default) +- `target/generated-resources/META-INF` for pure testing projects that provide their code on main rather than test +- `src/main/resources/META-INF` - for mixing existing meta files with generated + +==== Configuration of the Test Classpath + +In case you choose to generate the API into `generated-test` folders, the maven build requires further configuration to add the `generated-test` folders to the classpath. +The link:https://www.mojohaus.org/build-helper-maven-plugin/usage.html[build-helper-maven-plugin] is used to accomplish this configuration step. + +[source,xml] +---- + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-sources + generate-test-sources + + add-test-source + + + + ${project.build.directory}/generated-test-sources + + + + + add-test-resource + generate-test-resources + + add-test-resource + + + + + ${project.build.directory}/generated-test-resources + + + + + + + + +---- + +==== Sample usage + +To utilize the test API in XML, it's necessary to import the respective namespace. Once imported, requests can be directly employed as actions, as illustrated in the sample below. +Further examples can be found here `org.citrusframework.openapi.generator.GeneratedApiIT`. + +.XML DSL +[source,xml,indent=0,role="secondary"] +---- + + + + + + + + + + + + +---- + +To utilize the test API in Java, it's necessary to import the API configuration, that provides the respective request actions. +The request to test can then be autowired, configured and autowired, as illustrated in the sample below. +Further examples can be found here `org.citrusframework.openapi.generator.GetPetByIdTest`. + +.Java DSL +[source,java,indent=0,role="secondary"] +---- +@ExtendWith(CitrusSpringExtension.class) +@SpringBootTest(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class}) +class GetPetByIdTest { + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private GetPetByIdRequest getPetByIdRequest; + + @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); + } +} + +---- diff --git a/src/manual/index.adoc b/src/manual/index.adoc index 9ae7fccb18..9d4c2ec4a0 100644 --- a/src/manual/index.adoc +++ b/src/manual/index.adoc @@ -62,6 +62,8 @@ include::endpoint-restdocs.adoc[] include::endpoint-component.adoc[] include::endpoint-adapter.adoc[] +include::testapi.adoc[] + include::connectors.adoc[] include::functions.adoc[] diff --git a/test-api-generator/citrus-test-api-generator-core/pom.xml b/test-api-generator/citrus-test-api-generator-core/pom.xml new file mode 100644 index 0000000000..c74f91249a --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/pom.xml @@ -0,0 +1,202 @@ + + 4.0.0 + + citrus-test-api-generator + org.citrusframework + 4.3.0-SNAPSHOT + ../pom.xml + + + citrus-test-api-generator-core + jar + + Citrus :: Test API Generator :: Core + Generates a Citrus Test-API for OpenAPI and WSDL specifications. + + + + + org.citrusframework + citrus-api + ${project.version} + + + org.citrusframework + citrus-http + ${project.version} + + + org.citrusframework + citrus-spring + ${project.version} + test + + + org.citrusframework + citrus-ws + ${project.version} + + + + org.assertj + assertj-core + ${assertj.version} + test + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + org.openapitools + openapi-generator + + + wsdl4j + wsdl4j + + + + + org.citrusframework + citrus-junit5 + ${project.version} + test + + + org.springframework.boot + spring-boot-test + ${spring.boot.test.version} + test + + + org.citrusframework + citrus-validation-json + ${project.version} + test + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${maven.helper.plugin.version} + + + add-generated-specs + generate-test-resources + + add-test-resource + + + + + ${project.build.directory}/generated-test-resources + ${project.build.outputDirectory} + + + + + + add-generated-classes + generate-test-sources + + add-test-source + + + + ${project.build.directory}/generated-test-sources + + + + + + + + org.openapitools + openapi-generator-maven-plugin + + + org.citrusframework + citrus-test-api-generator-core + ${project.version} + + + + + REST + generated-test-resources + generated-test-sources + true + + true + java-citrus + ${project.build.directory} + + + + generate-openapi-petstore-files + compile + + generate + + + ${project.basedir}/src/test/resources/apis/petstore.yaml + + org.citrusframework.openapi.generator.rest.petstore + org.citrusframework.openapi.generator.rest.petstore.request + org.citrusframework.openapi.generator.rest.petstore.model + PetStore + petStoreEndpoint + + + + + generate-openapi-files-for-soap + compile + + generate + + + ${project.basedir}/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml + + SOAP + org.citrusframework.openapi.generator.soap.bookservice + org.citrusframework.openapi.generator.soap.bookservice.request + org.citrusframework.openapi.generator.soap.bookservice.model + SoapSample + OpenApiFromWsdl + soapSampleEndpoint + + + + + + + generate-openapi-multiparttest-files + compile + + generate + + + ${project.basedir}/src/test/resources/apis/multiparttest-rest-resource.yaml + + org.citrusframework.openapi.generator.rest.multiparttest + org.citrusframework.openapi.generator.rest.multiparttest.request + org.citrusframework.openapi.generator.rest.multiparttest.model + MultipartTest + multipartTestEndpoint + + + + + + + + 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 new file mode 100644 index 0000000000..7aa18331b5 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/JavaCitrusCodegen.java @@ -0,0 +1,345 @@ +package org.citrusframework.openapi.generator; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toMap; +import static org.openapitools.codegen.CliOption.newString; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import java.io.File; +import java.util.ArrayList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.openapitools.codegen.CodegenType; +import org.openapitools.codegen.SupportingFile; +import org.openapitools.codegen.languages.AbstractJavaCodegen; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Thorsten Schlathoelter + */ +public class JavaCitrusCodegen extends AbstractJavaCodegen { + + private static final Logger logger = LoggerFactory.getLogger(JavaCitrusCodegen.class); + + private static final String ABSTRACT_TEST_REQUEST_JAVA = "AbstractTestRequest.java"; + private static final String API_TYPE_REST = "REST"; + private static final String API_TYPE_SOAP = "SOAP"; + public static final String CODEGEN_NAME = "java-citrus"; + + // possible optional parameters + public static final String API_ENDPOINT = "apiEndpoint"; + public static final String API_TYPE = "apiType"; + public static final String GENERATED_SCHEMA_FOLDER = "generatedSchemaFolder"; + public static final String HTTP_PATH_PREFIX = "httpPathPrefix"; + public static final String OPENAPI_SCHEMA = "openapiSchema"; + public static final String PREFIX = "prefix"; + public static final String RESOURCE_FOLDER = "resourceFolder"; + public static final String SOURCE_FOLDER = "sourceFolder"; + public static final String TARGET_XMLNS_NAMESPACE = "targetXmlnsNamespace"; + + // default values for optional parameters + protected String apiPrefix = "Api"; + + protected String httpClient = API_ENDPOINT; + protected String httpPathPrefix = "api"; + protected String openapiSchema = "oas3"; + protected String resourceFolder = + "src" + File.separator + "main" + File.separator + "resources"; + protected String generatedSchemaFolder = "schema" + File.separator + "xsd"; + protected String targetXmlnsNamespace; + + protected String apiVersion = "1.0.0"; + + public JavaCitrusCodegen() { + super(); + // the root folder where all files are emitted + outputFolder = "generated-code" + File.separator + "java"; + + // this is the location which templates will be read from in the - resources - directory + templateDir = CODEGEN_NAME; + + // register additional properties which will be available in the templates + additionalProperties.put("apiVersion", apiVersion); + + // set default + additionalProperties.put(API_TYPE, API_TYPE_REST); + + // add additional reserved words used in CitrusAbstractTestRequest and its base class to prevent name collisions + Set reservedWordsTemp = reservedWords(); + reservedWordsTemp.addAll( + asList( + "name", + "description", + "actor", + "httpClient", + "dataSource", + "schemaValidation", + "schema", + "headerContentType", + "headerAccept", + "bodyFile", + "responseType", + "responseStatus", + "responseReasonPhrase", + "responseVersion", + "resource", + "responseVariable", + "responseValue", + "cookies", + "script", + "type" + ) + ); + setReservedWordsLowerCase(new ArrayList<>(reservedWordsTemp)); + + // add posibility to set a new value for the properties + cliOptions.add(newString(API_ENDPOINT, + "Which http client should be used (default " + httpClient + ").")); + cliOptions.add( + newString( + API_TYPE, + "Specifies the type of API to be generated specify SOAP to generate a SOAP API. By default a REST API will be generated" + ) + ); + cliOptions.add( + newString(GENERATED_SCHEMA_FOLDER, + "The schema output directory (default " + generatedSchemaFolder + ").") + ); + cliOptions.add(newString(HTTP_PATH_PREFIX, + "Add a prefix to http path for all APIs (default " + httpPathPrefix + ").")); + cliOptions.add(newString(OPENAPI_SCHEMA, + "Which OpenAPI schema should be used (default " + openapiSchema + ").")); + cliOptions.add( + newString( + PREFIX, + "Add a prefix before the name of the files. First character should be upper case (default " + + apiPrefix + ")." + ) + ); + cliOptions.add(newString(PREFIX, "The api prefix (default " + apiPrefix + ").")); + cliOptions.add(newString(RESOURCE_FOLDER, + "Where the resource files are emitted (default " + resourceFolder + ").")); + cliOptions.add( + newString(TARGET_XMLNS_NAMESPACE, + "Xmlns namespace of the schema (default " + targetXmlnsNamespace + ").") + ); + } + + /** + * Returns human-friendly help for the generator. Provide the consumer with help tips, + * parameters here + * + * @return A string value for the help message + */ + @Override + public String getHelp() { + return "Generates citrus api requests."; + } + + /** + * Configures a friendly name for the generator. This will be used by the generator to select + * the library with the -g flag. + * + * @return the friendly name for the generator + */ + @Override + public String getName() { + return CODEGEN_NAME; + } + + /** + * Configures the type of generator. + * + * @return the CodegenType for this generator + * @see org.openapitools.codegen.CodegenType + */ + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(API_ENDPOINT)) { + this.setHttpClient(additionalProperties.get(API_ENDPOINT).toString()); + } + additionalProperties.put(API_ENDPOINT, httpClient); + + if (additionalProperties.containsKey(GENERATED_SCHEMA_FOLDER)) { + this.setGeneratedSchemaFolder( + additionalProperties.get(GENERATED_SCHEMA_FOLDER).toString()); + } + additionalProperties.put(GENERATED_SCHEMA_FOLDER, generatedSchemaFolder); + + if (additionalProperties.containsKey(HTTP_PATH_PREFIX)) { + this.setHttpPathPrefix(additionalProperties.get(HTTP_PATH_PREFIX).toString()); + additionalProperties.put(HTTP_PATH_PREFIX, httpPathPrefix); + } else { + logger.warn( + "Using empty http-path-prefix for code generation. A http-path-prefix can be configured using \"{}\" property.", + HTTP_PATH_PREFIX + ); + httpPathPrefix = ""; + } + + if (additionalProperties.containsKey(OPENAPI_SCHEMA)) { + this.setOpenapiSchema(additionalProperties.get(OPENAPI_SCHEMA).toString()); + } + additionalProperties.put(OPENAPI_SCHEMA, openapiSchema); + + if (additionalProperties.containsKey(PREFIX)) { + this.setApiPrefix(additionalProperties.get(PREFIX).toString()); + additionalProperties.put(PREFIX, apiPrefix); + additionalProperties.put(PREFIX + "LowerCase", apiPrefix.toLowerCase()); + } else { + logger.warn( + "Using empty prefix for code generation. A prefix can be configured using \"{}\" property.", + PREFIX); + apiPrefix = ""; + } + + if (additionalProperties.containsKey(RESOURCE_FOLDER)) { + this.setResourceFolder(additionalProperties.get(RESOURCE_FOLDER).toString()); + } + additionalProperties.put(RESOURCE_FOLDER, resourceFolder); + + if (additionalProperties.containsKey(TARGET_XMLNS_NAMESPACE)) { + this.setTargetXmlnsNamespace( + additionalProperties.get(TARGET_XMLNS_NAMESPACE).toString()); + } else { + this.targetXmlnsNamespace = String.format( + "http://www.citrusframework.org/citrus-test-schema/%s-api", apiPrefix.toLowerCase()); + } + additionalProperties.put(TARGET_XMLNS_NAMESPACE, targetXmlnsNamespace); + + // define different folders where the files will be emitted + final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", + File.separator); + final String citrusFolder = invokerFolder + File.separator + "citrus"; + final String extensionFolder = citrusFolder + File.separator + "extension"; + final String springFolder = invokerFolder + File.separator + "spring"; + final String schemaFolder = resourceFolder + File.separator + generatedSchemaFolder; + + Object apiType = additionalProperties.get(API_TYPE); + if (API_TYPE_REST.equals(apiType)) { + addRestSupportingFiles(citrusFolder, schemaFolder); + } else if (API_TYPE_SOAP.equals(apiType)) { + addSoapSupportingFiles(citrusFolder, schemaFolder); + } else { + throw new IllegalArgumentException(String.format("Unknown API_TYPE: '%s'", apiType)); + } + + addDefaultSupportingFiles(citrusFolder, extensionFolder, springFolder); + } + + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + super.preprocessOpenAPI(openAPI); + + Info info = openAPI.getInfo(); + Map extensions = info.getExtensions(); + if (extensions != null) { + additionalProperties.putAll(extensions); + + Map infoExtensions = extensions.entrySet().stream() + .filter(entry -> entry.getKey().toUpperCase( + ).startsWith("X-")) + .collect(toMap(Entry::getKey, Entry::getValue)); + additionalProperties.put("infoExtensions", infoExtensions); + } + } + + public void setApiPrefix(String apiPrefix) { + this.apiPrefix = apiPrefix; + } + + public String getHttpClient() { + return httpClient; + } + + public void setHttpClient(String httpClient) { + this.httpClient = httpClient; + } + + public String getHttpPathPrefix() { + return httpPathPrefix; + } + + public void setHttpPathPrefix(String httpPathPrefix) { + this.httpPathPrefix = httpPathPrefix; + } + + public String getOpenapiSchema() { + return openapiSchema; + } + + public void setOpenapiSchema(String openapiSchema) { + this.openapiSchema = openapiSchema; + } + + public String getResourceFolder() { + return resourceFolder; + } + + public void setResourceFolder(String resourceFolder) { + this.resourceFolder = resourceFolder; + } + + public String getGeneratedSchemaFolder() { + return generatedSchemaFolder; + } + + public void setGeneratedSchemaFolder(String generatedSchemaFolder) { + this.generatedSchemaFolder = generatedSchemaFolder; + } + + public String getTargetXmlnsNamespace() { + return targetXmlnsNamespace; + } + + public void setTargetXmlnsNamespace(String targetXmlnsNamespace) { + this.targetXmlnsNamespace = targetXmlnsNamespace; + } + + public String getApiPrefix() { + return apiPrefix; + } + + private void addRestSupportingFiles(final String citrusFolder, String schemaFolder) { + supportingFiles.add(new SupportingFile("schema.mustache", schemaFolder, + apiPrefix.toLowerCase() + "-api.xsd")); + supportingFiles.add(new SupportingFile("test_base.mustache", citrusFolder, + apiPrefix + ABSTRACT_TEST_REQUEST_JAVA)); + } + + private void addSoapSupportingFiles(final String citrusFolder, String schemaFolder) { + // Remove the default api template file + apiTemplateFiles().remove("api.mustache"); + apiTemplateFiles().put("api_soap.mustache", ".java"); + + supportingFiles.add(new SupportingFile("schema_soap.mustache", schemaFolder, + apiPrefix.toLowerCase() + "-api.xsd")); + supportingFiles.add(new SupportingFile("api_soap.mustache", citrusFolder, + apiPrefix + ABSTRACT_TEST_REQUEST_JAVA)); + supportingFiles.add(new SupportingFile("test_base_soap.mustache", citrusFolder, + apiPrefix + ABSTRACT_TEST_REQUEST_JAVA)); + } + + private void addDefaultSupportingFiles(final String citrusFolder, final String extensionFolder, + final String springFolder) { + supportingFiles.add(new SupportingFile("bean_configuration.mustache", springFolder, + apiPrefix + "BeanConfiguration.java")); + supportingFiles.add(new SupportingFile("bean_definition_parser.mustache", citrusFolder, + apiPrefix + "BeanDefinitionParser.java")); + 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 new file mode 100644 index 0000000000..68d7bf3579 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformer.java @@ -0,0 +1,187 @@ +package org.citrusframework.openapi.generator; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +import static com.fasterxml.jackson.databind.MapperFeature.SORT_PROPERTIES_ALPHABETICALLY; +import static java.lang.String.format; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.responses.ApiResponses; +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import javax.wsdl.Binding; +import javax.wsdl.BindingOperation; +import javax.wsdl.Definition; +import javax.wsdl.WSDLException; +import javax.wsdl.factory.WSDLFactory; +import javax.wsdl.xml.WSDLReader; +import javax.xml.namespace.QName; +import org.citrusframework.openapi.generator.exception.WsdlToOpenApiTransformationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +/** + * Transforms a wsdl specification into a simple OpenApi specification for usage with the OpenApiGenerator. + *

+ * + * Note that this transformer only transforms bindings from the wsdl into operations in the OpenApi. + */ +public class SimpleWsdlToOpenApiTransformer { + + private static final Logger logger = LoggerFactory.getLogger(SimpleWsdlToOpenApiTransformer.class); + + private static final YAMLMapper yamlMapper = (YAMLMapper) YAMLMapper.builder() + .enable(SORT_PROPERTIES_ALPHABETICALLY) + .build() + .setSerializationInclusion(NON_NULL); + + private final URI wsdlUri; + + public SimpleWsdlToOpenApiTransformer(URI wsdlUri) { + this.wsdlUri = wsdlUri; + } + + /** + * Transforms the wsdl of this transfromer into a OpenApi yaml representation. + * + * @return the OpenApi yaml + * @throws WsdlToOpenApiTransformationException if the parsing fails + */ + public String transformToOpenApi() throws WsdlToOpenApiTransformationException { + try { + Definition wsdlDefinition = readWSDL(); + Map bindings = wsdlDefinition.getBindings(); + OpenAPI openAPI = transformToOpenApi(bindings); + return convertToYaml(openAPI); + } catch (Exception e) { + throw new WsdlToOpenApiTransformationException("Unable to parse wsdl", e); + } + } + + /** + * Performs the actual transformation from bindings into OpenApi operations. + * + * @param bindings + * @return + */ + private OpenAPI transformToOpenApi(Map bindings) { + OpenAPI openAPI = new OpenAPI(); + openAPI.setInfo(createInfo()); + + Paths paths = new Paths(); + openAPI.setPaths(paths); + for (Entry entry : bindings.entrySet()) { + Object key = entry.getKey(); + Object value = entry.getValue(); + + if (key instanceof QName && value instanceof Binding) { + addOperations(openAPI, (QName) key, (Binding) value); + } + } + return openAPI; + } + + private Definition readWSDL() throws WSDLException { + logger.debug("Reading wsdl file from path: {}", wsdlUri); + + WSDLReader reader = WSDLFactory.newInstance().newWSDLReader(); + + // switch off the verbose mode + reader.setFeature("javax.wsdl.verbose", false); + reader.setFeature("javax.wsdl.importDocuments", true); + + if (logger.isDebugEnabled()) { + logger.debug("Reading the WSDL. Base uri is {}", wsdlUri); + } + + return reader.readWSDL(wsdlUri.toString()); + } + + private Info createInfo() { + Info info = new Info(); + info.setTitle("Generated api from wsdl"); + + info.setDescription( + format( + "This api has been generated from the following wsdl '%s'. It's purpose is solely to serve as input for SOAP API generation. Note that only operations are extracted from the WSDL. No schema information whatsoever is generated!", + java.nio.file.Paths.get(wsdlUri).getFileName() + ) + ); + info.setVersion("1.0.0"); + + Contact contact = new Contact(); + contact.setName("org.citrusframework.openapi.generator.SimpleWsdlToOpenApiTransformer"); + info.setContact(contact); + return info; + } + + private void addOperations(OpenAPI openApi, QName qName, Binding binding) { + String localPart = qName.getLocalPart(); + + String bindingApiName; + if (localPart.endsWith("SoapBinding")) { + bindingApiName = localPart.substring(0, localPart.length() - "SoapBinding".length()); + } else { + bindingApiName = localPart; + } + + List bindingOperations = binding.getBindingOperations(); + for (Object operation : bindingOperations) { + if (operation instanceof BindingOperation bindingOperation) { + addOperation( + openApi.getPaths(), + bindingOperation.getName(), + retrieveOperationDescription(bindingOperation), + bindingApiName + ); + } + } + } + + private void addOperation(Paths paths, String name, String description, String tag) { + Operation postOperation = new Operation(); + + logger.debug("Adding operation to spec: {}", name); + + postOperation.setOperationId(name); + postOperation.setDescription(description); + postOperation.tags(Collections.singletonList(tag)); + ApiResponses responses = new ApiResponses(); + postOperation.responses(responses); + + PathItem pi = new PathItem(); + pi.setPost(postOperation); + + paths.addPathItem("/" + name, pi); + } + + /** + * Retrieve the description of the bindingOperation via the documentation of the associated operation. + */ + private String retrieveOperationDescription(BindingOperation bindingOperation) { + String description = ""; + javax.wsdl.Operation soapOperation = bindingOperation.getOperation(); + if (soapOperation != null) { + Element documentationElement = soapOperation.getDocumentationElement(); + if (documentationElement != null) { + description = documentationElement.getTextContent(); + } + } + + return description; + } + + private String convertToYaml(OpenAPI openAPI) throws JsonProcessingException { + return yamlMapper.writeValueAsString(openAPI); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java new file mode 100644 index 0000000000..fd845194bf --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java @@ -0,0 +1,8 @@ +package org.citrusframework.openapi.generator.exception; + +public class WsdlToOpenApiTransformationException extends Exception { + + public WsdlToOpenApiTransformationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/test-api-generator/citrus-test-api-generator-core/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig new file mode 100644 index 0000000000..33a85c5059 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -0,0 +1 @@ +org.citrusframework.openapi.generator.JavaCitrusCodegen \ No newline at end of file diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api-model.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api-model.mustache new file mode 100644 index 0000000000..ae513df561 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api-model.mustache @@ -0,0 +1,2 @@ +OperationId;Path;Method;Parameters{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} +{{operationId}};{{path}};{{httpMethod}};{{#allParams}}{{paramName}}:{{{dataType}}}{{^-last}},{{/-last}}{{/allParams}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} 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 new file mode 100644 index 0000000000..5d90d5f80c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api.mustache @@ -0,0 +1,265 @@ +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package {{package}}; + +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 {{invokerPackage}}.citrus.{{prefix}}AbstractTestRequest; + +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 {{classname}} implements GeneratedApi +{ + + public static final {{classname}} INSTANCE = new {{classname}}(); + + public String getApiTitle() { + return "{{appName}}"; + } + + public String getApiVersion() { + return "{{appVersion}}"; + } + + public String getApiPrefix() { + return "{{prefix}}"; + } + + public Map getApiInfoExtensions() { + Map infoExtensionMap = new HashMap<>(); + {{#infoExtensions}} + {{#entrySet}} + infoExtensionMap.put("{{key}}", "{{value}}"); + {{/entrySet}} + {{/infoExtensions}} + return infoExtensionMap; + } + + {{#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() { + return "{{operationId}}"; + } + + public String getMethod() { + return "{{httpMethod}}"; + } + + public String getPath() { + return "{{path}}"; + } + + /** + * 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); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "{{operationId}};{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}};\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + {{#queryParams}} + + public void set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(String {{paramName}}) { + this.{{paramName}} = {{paramName}}; + } + {{/queryParams}} + {{#pathParams}} + + public void set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(String {{paramName}}) { + this.{{paramName}} = {{paramName}}; + } + {{/pathParams}} + {{#isMultipart}} + {{#formParams}} + + public void set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(String {{paramName}}) { + this.{{paramName}} = {{paramName}}; + } + {{/formParams}} + {{/isMultipart}} + {{#authMethods}}{{#isBasic}} + + public void setBasicUsername(String basicUsername) { + this.basicUsername = basicUsername; + } + + public void setBasicPassword(String basicPassword) { + this.basicPassword = basicPassword; + } + {{/isBasic}}{{/authMethods}} + private String replacePathParams(String endpoint) { + {{#pathParams}}endpoint = endpoint.replace("{" + "{{baseName}}" + "}", {{paramName}});{{/pathParams}} + return endpoint; + } + } + {{/operation}} + {{/operations}} +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_doc.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_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/api_doc.mustache @@ -0,0 +1 @@ +# not in use 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 new file mode 100644 index 0000000000..d39d1aff12 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_soap.mustache @@ -0,0 +1,165 @@ +/** + * ================================================== + * 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; +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 {{invokerPackage}}.citrus.{{prefix}}AbstractTestRequest; + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class {{classname}} implements GeneratedApi +{ + public static final {{classname}} INSTANCE = new {{classname}}(); + + public String getApiTitle() { + return "{{appName}}"; + } + + public String getApiVersion() { + return "{{appVersion}}"; + } + + public String getApiPrefix() { + return "{{prefix}}"; + } + + public Map getApiInfoExtensions() { + Map infoExtensionMap = new HashMap<>(); + {{#infoExtensions}} + {{#entrySet}} + infoExtensionMap.put("{{key}}", "{{value}}"); + {{/entrySet}} + {{/infoExtensions}} + return infoExtensionMap; + } + + {{#operations}} + {{#operation}} + /** + {{operationId}} ({{httpMethod}} {{httpPathPrefix}}{{{path}}}) + {{summary}} + {{description}} + **/ + public static class {{operationIdCamelCase}}Request extends {{prefix}}AbstractTestRequest implements GeneratedApiRequest { + + private final Logger coverageLogger = LoggerFactory.getLogger({{operationIdCamelCase}}Request.class); + + // Query params + {{#allParams}}{{#isQueryParam}}private String {{paramName}}; + {{/isQueryParam}}{{/allParams}} + + 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() { + return "{{operationId}}"; + } + + public String getMethod() { + return "{{httpMethod}}"; + } + + public String getPath() { + return "{{path}}"; + } + + /** + * 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("{{operationId}}"); + + 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<>(); + {{#allParams}}{{#isQueryParam}} + if (StringUtils.isNotBlank(this.{{paramName}})) { + queryParams.put("{{baseName}}", context.replaceDynamicContentInString(this.{{paramName}})); + sendSoapMessageActionBuilder.queryParam("{{baseName}}", this.{{paramName}}); + } + {{/isQueryParam}}{{/allParams}} + 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, "{{operationId}};{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}};\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + body.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + bodyType + "\""); + } + + {{#allParams}}{{#isQueryParam}} + public void set{{#lambda.titlecase}}{{paramName}}{{/lambda.titlecase}}(String {{paramName}}) { + this.{{paramName}} = {{paramName}}; + } + {{/isQueryParam}}{{/allParams}} + } + {{/operation}} + {{/operations}} +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_test.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_test.mustache new file mode 100644 index 0000000000..d5341fea2c --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api_test.mustache @@ -0,0 +1 @@ +// not in use 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 new file mode 100644 index 0000000000..d4b8dd2e01 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_configuration.mustache @@ -0,0 +1,38 @@ +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package {{invokerPackage}}.spring; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +{{#apiInfo}} +{{#apis}} +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 +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class {{prefix}}BeanConfiguration { +{{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + + @Bean + @Scope(SCOPE_PROTOTYPE) + public {{classname}}.{{operationIdCamelCase}}Request {{operationId}}Request() { + return new {{classname}}.{{operationIdCamelCase}}Request(); + } + {{/operation}} + {{/operations}} + {{/apis}} +{{/apiInfo}} +} 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 new file mode 100644 index 0000000000..4e0957f1af --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/bean_definition_parser.mustache @@ -0,0 +1,215 @@ +/** + * ================================================== + * 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 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 {{prefix}}BeanDefinitionParser 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 {{prefix}}BeanDefinitionParser(Class beanClass) { + this.beanClass = beanClass; + } + + public BeanDefinition parse(Element element) { + return parse(element, null); + } + + /** + * Note: The {@link {{prefix}}BeanDefinitionParser#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/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 new file mode 100644 index 0000000000..ad260e35ba --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/namespace_handler.mustache @@ -0,0 +1,36 @@ +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package {{invokerPackage}}.citrus.extension; + +{{#apiInfo}} +{{#apis}} +import {{package}}.{{classname}}; +{{/apis}} +{{/apiInfo}} +import {{invokerPackage}}.citrus.{{prefix}}BeanDefinitionParser; + +import javax.annotation.processing.Generated; + +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + + +@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") +public class {{prefix}}NamespaceHandler extends NamespaceHandlerSupport { + + @Override + public void init() { + {{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + registerBeanDefinitionParser("{{operationId}}Request", new {{prefix}}BeanDefinitionParser({{classname}}.{{operationIdCamelCase}}Request.class)); + {{/operation}} + {{/operations}} + {{/apis}} + {{/apiInfo}} + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache new file mode 100644 index 0000000000..faf2807dd1 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + + {{#isMultipart}} + + + {{#formParams}} + + + + {{^required}}Optional {{/required}}{{#required}}Required{{/required}} - must either be set as attribute or element: {{#description}} +

{{description}}

{{/description}} + + + + {{/formParams}} + + + {{/isMultipart}} + + + + + {{operationId}} +

{{httpMethod}} {{httpPathPrefix}}{{{path}}}

+
    + {{#queryParams}} +
  • {{paramName}} {{description}}
  • + {{/queryParams}} + {{#pathParams}} +
  • {{baseName}} {{description}}
  • + {{/pathParams}} + {{#bodyParams}} +
  • Body: {{description}}
  • + {{/bodyParams}} + {{#authMethods}}{{#isBasic}} +
  • basicUsername http basic authentication username
  • +
  • basicPassword http basic authentication password
  • + {{/isBasic}}{{/authMethods}} + {{#isMultipart}} + {{#formParams}} +
  • {{paramName}} {{description}}
  • + {{/formParams}} + {{/isMultipart}} +
+
+
+ + + + {{#isMultipart}} + + {{/isMultipart}} + {{^isMultipart}} + {{#bodyParams}} + + + + {{^required}}Optional {{/required}}Body - {{summary}}{{#description}} +

{{description}}

{{/description}} +
+
+
+ {{/bodyParams}} + {{/isMultipart}} + +
+ {{#queryParams}} + + + {{description}} + + + {{/queryParams}} + {{#pathParams}} + + + {{description}} + + + {{/pathParams}} + {{#isMultipart}} + {{#formParams}} + + + + The filename of the {{paramName}} to upload + + + {{/formParams}} + {{/isMultipart}} + {{#authMethods}}{{#isBasic}} + + + http basic authentication username + + + + + http basic authentication password + + + {{/isBasic}}{{/authMethods}} +
+
+
+ {{/operation}} + {{/operations}} + {{/apis}} + {{#apis}} + {{#operations}} + {{#operation}} + + + {{/operation}} + {{/operations}} + {{/apis}} + {{/apiInfo}} + diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache new file mode 100644 index 0000000000..877bdc1884 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + + + + {{operationId}} +

{{{path}}}

+
  • + Body: {{description}}
  • +
+
+
+ + + + + + + {{^required}}SOAP {{/required}}Body{{summary}}{{#description}} +

{{description}}

{{/description}} +
+
+
+ +
+
+
+
+ + {{/operation}} + {{/operations}} + {{/apis}} + {{#apis}} + {{#operations}} + {{#operation}} + + + {{/operation}} + {{/operations}} + {{/apis}} + {{/apiInfo}} +
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 new file mode 100644 index 0000000000..f3566c6716 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base.mustache @@ -0,0 +1,245 @@ +/** + * ================================================== + * 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; +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 {{prefix}}AbstractTestRequest extends AbstractTestAction { + + protected final Marker coverageMarker = MarkerFactory.getMarker("{{#lambda.uppercase}}{{prefix}}{{/lambda.uppercase}}-API-COVERAGE"); + + @Autowired + @Qualifier("{{apiEndpoint}}") + 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 {{prefix}}AbstractTestRequest#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/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 new file mode 100644 index 0000000000..051485041e --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/test_base_soap.mustache @@ -0,0 +1,187 @@ +/** + * ================================================== + * 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; +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 {{prefix}}AbstractTestRequest extends AbstractTestAction { + + protected final Marker coverageMarker = MarkerFactory.getMarker("{{#lambda.uppercase}}{{prefix}}{{/lambda.uppercase}}-API-COVERAGE"); + + @Autowired + @Qualifier("{{apiEndpoint}}") + 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/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 new file mode 100644 index 0000000000..0240db1100 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GeneratedApiIT.java @@ -0,0 +1,626 @@ +package org.citrusframework.openapi.generator; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.citrusframework.message.MessagePayloadUtils.normalizeWhitespace; +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.verify; +import static org.mockito.Mockito.when; + +import com.networknt.schema.JsonSchema; +import com.networknt.schema.ValidationMessage; +import jakarta.servlet.http.Cookie; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.citrusframework.Citrus; +import org.citrusframework.CitrusInstanceManager; +import org.citrusframework.TestAction; +import org.citrusframework.TestCase; +import org.citrusframework.actions.SendMessageAction.SendMessageActionBuilder; +import org.citrusframework.common.SpringXmlTestLoader; +import org.citrusframework.common.TestLoader; +import org.citrusframework.config.CitrusSpringConfig; +import org.citrusframework.context.TestContext; +import org.citrusframework.endpoint.EndpointConfiguration; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.exceptions.ValidationException; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.http.client.HttpEndpointConfiguration; +import org.citrusframework.http.message.HttpMessage; +import org.citrusframework.json.schema.SimpleJsonSchema; +import org.citrusframework.junit.jupiter.spring.CitrusSpringExtension; +import org.citrusframework.message.DefaultMessage; +import org.citrusframework.message.Message; +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.GetPetByIdRequest; +import org.citrusframework.spi.Resources; +import org.citrusframework.testapi.ApiActionBuilderCustomizerService; +import org.citrusframework.testapi.GeneratedApi; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.Isolated; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.Resource; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; +import org.springframework.util.MultiValueMap; + +/** + * 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"} +) +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); + } + } + +// @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 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; + } + + public static class Config { + + @Bean(name = {"applicationServiceClient", "multipartTestEndpoint", + "soapSampleStoreEndpoint", "petStoreEndpoint"}) + public HttpClient applicationServiceClient() { + HttpClient clientMock = mock(); + EndpointConfiguration endpointConfigurationMock = mock(); + when(clientMock.getEndpointConfiguration()).thenReturn(new HttpEndpointConfiguration()); + when(endpointConfigurationMock.getTimeout()).thenReturn(5000L); + return clientMock; + } + + @Bean + public ApiActionBuilderCustomizerService customizer() { + return new ApiActionBuilderCustomizerService() { + @Override + public > T build( + GeneratedApi generatedApi, TestAction action, TestContext context, T builder) { + builder.getMessageBuilderSupport() + .header("x-citrus-api-version", generatedApi.getApiVersion()); + return builder; + } + }; + } + + @Bean({"oas3", "testSchema"}) + public SimpleJsonSchema testSchema() { + JsonSchema schemaMock = mock(); + SimpleJsonSchema jsonSchemaMock = mock(); + + when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); + + Set okReport = new HashSet<>(); + when(schemaMock.validate(any())).thenReturn(okReport); + return jsonSchemaMock; + } + + @Bean + public SimpleJsonSchema failingTestSchema() { + JsonSchema schemaMock = mock(); + SimpleJsonSchema jsonSchemaMock = mock(); + + when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); + + Set nokReport = new HashSet<>(); + nokReport.add(new ValidationMessage.Builder().customMessage( + "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/GetPetByIdTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdTest.java new file mode 100644 index 0000000000..6421cee946 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdTest.java @@ -0,0 +1,254 @@ +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.message.Message; +import org.citrusframework.messaging.Producer; +import org.citrusframework.messaging.SelectiveConsumer; +import org.citrusframework.openapi.generator.GetPetByIdTest.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.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; + +@ExtendWith(CitrusSpringExtension.class) +@SpringBootTest(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, Config.class}) +class GetPetByIdTest { + + @Autowired + private GetPetByIdRequest getPetByIdRequest; + + @Autowired + @Qualifier("petStoreEndpoint") + private HttpClient httpClient; + + private String defaultResponse; + + @BeforeEach + public void beforeTest() throws IOException { + defaultResponse = readToString(Resources.create( + "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), + StandardCharsets.UTF_8) ; + + mockProducer(); + mockConsumer(); + } + + /** + * 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 + ) + ); + // 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 + ) + ); + } + + /** + * 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(); + + SelectiveConsumer consumer = mock(SelectiveConsumer.class); + when(httpClient.createConsumer()).thenReturn(consumer); + when(consumer.receive(any(), eq(5000L))).thenReturn(receiveMessage); + } + + 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; + } + + @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; + } + } +} 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 new file mode 100644 index 0000000000..dd4f52ece7 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenIT.java @@ -0,0 +1,93 @@ +package org.citrusframework.openapi.generator; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.stream.Stream; +import org.apache.commons.lang3.stream.Streams; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +/** + * 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 + * '/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 '/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 { + + 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 new file mode 100644 index 0000000000..57f49cd861 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/JavaCitrusCodegenTest.java @@ -0,0 +1,166 @@ +package org.citrusframework.openapi.generator; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.citrusframework.openapi.generator.JavaCitrusCodegen.CODEGEN_NAME; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.openapitools.codegen.ClientOptInput; +import org.openapitools.codegen.CodegenConfigLoader; +import org.openapitools.codegen.DefaultGenerator; +import org.openapitools.codegen.config.CodegenConfigurator; + +/** + * This test validates the code generation process. + *

+ * It may also serve as an entry point for debugging the code generation process. When executed in debug mode, it allows you to + * step through the generation process and inspect the resulting output in the specified output directory. + *

+ * To debug the code generator: + *

    + *
  1. Set a breakpoint in the {@code postProcessOperationsWithModels()} method of {@code JavaCitrusCodegen.java}.
  2. + *
  3. In your IDE, launch this test by right-clicking and selecting Debug As > JUnit Test.
  4. + *
+ */ + +class JavaCitrusCodegenTest { + + @Test + void retrieveGeneratorBsSpi() { + JavaCitrusCodegen codegen = (JavaCitrusCodegen) CodegenConfigLoader.forName("java-citrus"); + assertThat(codegen).isNotNull(); + } + + @Test + void arePredefinedValuesNotEmptyTest() { + JavaCitrusCodegen codegen = new JavaCitrusCodegen(); + + assertThat(codegen.getName()).isEqualTo(CODEGEN_NAME); + assertThat(codegen.getHelp()).isNotEmpty(); + assertThat(codegen.getHttpClient()).isNotEmpty(); + assertThat(codegen.getOpenapiSchema()).isNotEmpty(); + assertThat(codegen.getApiPrefix()).isNotEmpty(); + assertThat(codegen.getHttpPathPrefix()).isNotEmpty(); + assertThat(codegen.getTargetXmlnsNamespace()).isNull(); + assertThat(codegen.getGeneratedSchemaFolder()).isNotEmpty(); + } + + @Test + void areAdditionalPropertiesProcessedTest() { + final String httpClient = "myTestEndpoint"; + final String openapiSchema = "testSchema"; + final String prefix = "testPrefix"; + final String httpPathPrefix = "test/path"; + final String targetXmlnsNamespace = "http://www.citrusframework.org/schema/test/extension"; + final String generatedSchemaFolder = "generatedResourceFolder"; + + Map properties = new HashMap<>(); + properties.put(JavaCitrusCodegen.API_ENDPOINT, httpClient); + properties.put(JavaCitrusCodegen.GENERATED_SCHEMA_FOLDER, generatedSchemaFolder); + properties.put(JavaCitrusCodegen.HTTP_PATH_PREFIX, httpPathPrefix); + properties.put(JavaCitrusCodegen.OPENAPI_SCHEMA, openapiSchema); + properties.put(JavaCitrusCodegen.PREFIX, prefix); + properties.put(JavaCitrusCodegen.TARGET_XMLNS_NAMESPACE, targetXmlnsNamespace); + + JavaCitrusCodegen codegen = new JavaCitrusCodegen(); + codegen.additionalProperties().putAll(properties); + codegen.processOpts(); + + assertThat(codegen.getApiPrefix()).isEqualTo(prefix); + assertThat(codegen.getGeneratedSchemaFolder()).isEqualTo(generatedSchemaFolder); + assertThat(codegen.getHttpClient()).isEqualTo(httpClient); + assertThat(codegen.getHttpPathPrefix()).isEqualTo(httpPathPrefix); + assertThat(codegen.getOpenapiSchema()).isEqualTo(openapiSchema); + assertThat(codegen.getTargetXmlnsNamespace()).isEqualTo(targetXmlnsNamespace); + } + + @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() 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("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 { + 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 new file mode 100644 index 0000000000..6b17f01f13 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/ServiceLoaderTest.java @@ -0,0 +1,24 @@ +package org.citrusframework.openapi.generator; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.ServiceLoader; +import java.util.ServiceLoader.Provider; +import org.citrusframework.testapi.ApiActionBuilderCustomizerService; +import org.citrusframework.openapi.generator.util.TestApiActionBuilderCustomizer; +import org.junit.jupiter.api.Test; + +class ServiceLoaderTest { + + @Test + void test() { + ServiceLoader serviceLoader = ServiceLoader.load( + ApiActionBuilderCustomizerService.class, ApiActionBuilderCustomizerService.class.getClassLoader()); + List> list = serviceLoader.stream().toList(); + assertThat(list).hasSize(1); + ApiActionBuilderCustomizerService apiActionBuilderCustomizerService = list.iterator().next() + .get(); + assertThat(apiActionBuilderCustomizerService).isInstanceOf(TestApiActionBuilderCustomizer.class); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest.java new file mode 100644 index 0000000000..1ae66986fd --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest.java @@ -0,0 +1,29 @@ +package org.citrusframework.openapi.generator; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.citrusframework.util.FileUtils.readToString; + +import java.io.IOException; +import org.citrusframework.openapi.generator.exception.WsdlToOpenApiTransformationException; +import org.citrusframework.spi.Resource; +import org.citrusframework.spi.Resources.ClasspathResource; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; + +class SimpleWsdlToOpenApiTransformerTest { + + @Test + void testTransform() throws WsdlToOpenApiTransformationException, IOException { + ClassPathResource wsdlResource = new ClassPathResource( + "/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService.wsdl"); + + SimpleWsdlToOpenApiTransformer simpleWsdlToOpenApiTransformer = new SimpleWsdlToOpenApiTransformer(wsdlResource.getURI()); + String generatedYaml = simpleWsdlToOpenApiTransformer.transformToOpenApi(); + + Resource expectedYamlResource = new ClasspathResource( + "/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml"); + + String expectedYaml = readToString(expectedYamlResource); + assertThat(generatedYaml).isEqualToIgnoringWhitespace(expectedYaml); + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java new file mode 100644 index 0000000000..2f5dbf7179 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java @@ -0,0 +1,55 @@ +package org.citrusframework.openapi.generator; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.citrusframework.annotations.CitrusResource; +import org.citrusframework.annotations.CitrusTest; +import org.citrusframework.config.CitrusSpringConfig; +import org.citrusframework.context.TestContext; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.http.client.HttpEndpointConfiguration; +import org.citrusframework.junit.jupiter.spring.CitrusSpringSupport; +import org.citrusframework.openapi.generator.rest.petstore.request.PetApi.AddPetRequest; +import org.citrusframework.openapi.generator.rest.petstore.spring.PetStoreBeanConfiguration; +import org.junit.jupiter.api.Test; +import org.citrusframework.openapi.generator.SpringBeanConfigurationIT.ClientConfiguration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; + +@CitrusSpringSupport +@ContextConfiguration(classes = {CitrusSpringConfig.class, ClientConfiguration.class, PetStoreBeanConfiguration.class}) +class SpringBeanConfigurationIT { + + @Autowired + private ApplicationContext applicationContext; + + @Test + @CitrusTest + void fromReferenceResolverIsPrototypeScoped(@CitrusResource TestContext testContext) { + var addPetRequest = testContext.getReferenceResolver().resolve(AddPetRequest.class); + assertThat(addPetRequest) + .isNotNull() + .isNotEqualTo(testContext.getReferenceResolver().resolve(AddPetRequest.class)); + } + + @Test + void fromSpringApplicationContextIsPrototypeScoped() { + assertThat(applicationContext.getBean(AddPetRequest.class)) + .isNotNull() + .isNotEqualTo(applicationContext.getBean(AddPetRequest.class)); + } + + @TestConfiguration + public static class ClientConfiguration { + + @Bean(name= {"applicationServiceClient", "petStoreEndpoint"}) + public HttpClient applicationServiceClient() { + var config = new HttpEndpointConfiguration(); + config.setRequestUrl("http://localhost:9000"); + return new HttpClient(config); + } + } +} 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 new file mode 100644 index 0000000000..0aaa9761ab --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/util/TestApiActionBuilderCustomizer.java @@ -0,0 +1,22 @@ +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; +import org.citrusframework.testapi.GeneratedApi; + +public class TestApiActionBuilderCustomizer implements ApiActionBuilderCustomizerService { + + @Override + public > T build(GeneratedApi generatedApi, TestAction action, + TestContext context, T builder) { + + generatedApi.getApiInfoExtensions().forEach((key, value) -> { + builder.getMessageBuilderSupport().header(key, value); + }); + + return builder; + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/services/org.citrusframework.testapi.ApiActionBuilderCustomizerService b/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/services/org.citrusframework.testapi.ApiActionBuilderCustomizerService new file mode 100644 index 0000000000..ba96f521f6 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/services/org.citrusframework.testapi.ApiActionBuilderCustomizerService @@ -0,0 +1 @@ +org.citrusframework.openapi.generator.util.TestApiActionBuilderCustomizer \ No newline at end of file diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.handlers b/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.handlers new file mode 100644 index 0000000000..1f0c4bdb95 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.handlers @@ -0,0 +1,3 @@ +http\://www.citrusframework.org/citrus-test-schema/multiparttest-api=org.citrusframework.openapi.generator.rest.multiparttest.citrus.extension.MultipartTestNamespaceHandler +http\://www.citrusframework.org/citrus-test-schema/openapifromwsdl-api=org.citrusframework.openapi.generator.soap.bookservice.citrus.extension.OpenApiFromWsdlNamespaceHandler +http\://www.citrusframework.org/citrus-test-schema/petstore-api=org.citrusframework.openapi.generator.rest.petstore.citrus.extension.PetStoreNamespaceHandler diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.schemas b/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.schemas new file mode 100644 index 0000000000..0050010472 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/META-INF/spring.schemas @@ -0,0 +1,3 @@ +http\://www.citrusframework.org/citrus-test-schema/multiparttest-api/multiparttest-api.xsd=schema/xsd/multiparttest-api.xsd +http\://www.citrusframework.org/citrus-test-schema/openapifromwsdl-api/openapifromwsdl-api.xsd=schema/xsd/openapifromwsdl-api.xsd +http\://www.citrusframework.org/citrus-test-schema/petstore-api/petstore-api.xsd=schema/xsd/petstore-api.xsd diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/multiparttest-rest-resource.yaml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/multiparttest-rest-resource.yaml new file mode 100644 index 0000000000..b77b55c4d2 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/multiparttest-rest-resource.yaml @@ -0,0 +1,249 @@ +openapi: 3.0.3 +info: + title: multiparttest API + version: 2.0.0 + description: | + The service to test mutlipart + x-citrus-app: MPT + x-citrus-api-name: multiparttest-rest-resource + contact: + name: IT-Services-CI TAuBE + email: IT-Serv-CI-ETAdl@post.ch + url: https://confluence.pnet.ch/pages/viewpage.action?pageId=314828825 +tags: + - name: multiparttest-controller +paths: + /api/v2/multitest-file/{bucket}/{filename}/random: + post: + tags: + - multiparttest-controller + operationId: postRandom + summary: Uploads random file. + parameters: + - name: bucket + description: The name of an existing s3 bucket. + in: path + required: true + schema: + type: string + - name: filename + description: The name under which to store the random file. + in: path + required: true + schema: + type: string + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PutObjectResult' + 500: + description: Internal Server Error + /api/v2/multitest-file/{bucket}/{filename}: + post: + tags: + - multiparttest-controller + operationId: postFile + summary: Uploads file. + parameters: + - name: bucket + description: The name of an existing s3 bucket. + in: path + required: true + schema: + type: string + - name: filename + description: The name of the file which should be uploaded. It may override any existing file with the same name. + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + multipartFile: + type: string + format: binary + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PutObjectResult' + 500: + description: Internal Server Error + delete: + tags: + - multiparttest-controller + operationId: deleteObject + summary: Delete file. + parameters: + - name: bucket + in: path + required: true + schema: + type: string + description: The name of an existing s3 bucket. + - name: filename + in: path + required: true + schema: + type: string + description: The name of the file which should be deleted. + responses: + 200: + description: OK + content: + application/json: + schema: + type: boolean + 500: + description: Internal Server Error + /api/v2/multitest-reportgeneration: + post: + tags: + - multiparttest-controller + operationId: generateReport + summary: summary + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: ['template'] + properties: + template: + description: | + Content of the template. + type: string + additionalData: + $ref: '#/components/schemas/AdditionalData' + schema: + description: | + An optional JSON schema to validate the created report against. + type: string + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PutObjectResult' + 500: + description: Internal Server Error + /api/v2/multitest-multipledatatypes: + post: + tags: + - multiparttest-controller + operationId: multipleDatatypes + summary: summary + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + stringData: + type: string + booleanData: + type: boolean + integerData: + type: integer + + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PutObjectResult' + 500: + description: Internal Server Error + /api/v2/multitest-file/{bucket}/{filename}/exists: + get: + tags: + - multiparttest-controller + operationId: fileExists + summary: Checks if file exist. + parameters: + - name: bucket + description: The name of an existing s3 bucket. + in: path + required: true + schema: + type: string + - name: filename + description: The name of the file on which the status should be checked. + in: path + required: true + schema: + type: string + responses: + 200: + description: OK + content: + application/json: + schema: + type: boolean + 500: + description: Internal Server Error +components: + schemas: + Metadata: + type: object + properties: + userMetadata: + type: object + additionalProperties: + type: string + rawMetadata: + type: object + additionalProperties: + type: string + httpExpiresDate: + type: string + format: date-time + expirationTime: + type: string + format: date-time + expirationTimeRuleId: + type: string + ongoingRestore: + type: boolean + restoreExpirationTime: + type: string + format: date-time + bucketKeyEnabled: + type: boolean + PutObjectResult: + type: object + properties: + versionId: + type: string + eTag: + type: string + expirationTime: + type: string + format: date-time + expirationTimeRuleId: + type: string + contentMd5: + type: string + metadata: + $ref: '#/components/schemas/Metadata' + isRequesterCharged: + type: boolean + AdditionalData: + description: | + Additional data provided to the report. For each dataset requested, provide a json + object with the name of the dataset. + type: string 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 new file mode 100644 index 0000000000..79249f26ed --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml @@ -0,0 +1,700 @@ +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: + post: + tags: + - pet + summary: Add a new pet to the store + description: '' + operationId: addPet + consumes: + - application/json + - application/xml + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + $ref: '#/definitions/Pet' + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + put: + tags: + - pet + summary: Update an existing pet + description: '' + operationId: updatePet + consumes: + - application/json + - application/xml + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + $ref: '#/definitions/Pet' + responses: + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + produces: + - application/xml + - application/json + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + collectionFormat: csv + responses: + '200': + description: successful operation + schema: + type: array + items: + $ref: '#/definitions/Pet' + '400': + description: Invalid status value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: 'Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.' + operationId: findPetsByTags + produces: + - application/xml + - application/json + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + type: array + items: + type: string + collectionFormat: csv + responses: + '200': + description: successful operation + schema: + type: array + items: + $ref: '#/definitions/Pet' + '400': + description: Invalid tag value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + deprecated: true + '/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 + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + - basicAuth: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + consumes: + - application/x-www-form-urlencoded + produces: + - application/xml + - application/json + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + type: integer + format: int64 + - name: name + in: formData + description: Updated name of the pet + required: false + type: string + - name: status + in: formData + description: Updated status of the pet + required: false + type: string + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + delete: + tags: + - pet + summary: Deletes a pet + description: '' + operationId: deletePet + produces: + - application/xml + - application/json + parameters: + - name: api_key + in: header + required: false + type: string + - name: petId + in: path + description: Pet id to delete + required: true + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + '/pet/{petId}/uploadImage': + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + consumes: + - multipart/form-data + produces: + - application/json + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + type: integer + format: int64 + - name: additionalMetadata + in: formData + description: Additional data to pass to server + required: false + type: string + - name: file + in: formData + description: file to upload + required: false + type: file + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/ApiResponse' + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + produces: + - application/json + parameters: [] + responses: + '200': + description: successful operation + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: '' + operationId: placeOrder + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: order placed for purchasing the pet + required: true + schema: + $ref: '#/definitions/Order' + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Order' + '400': + description: Invalid Order + '/store/order/{order_id}': + get: + tags: + - store + summary: Find purchase order by ID + description: 'For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions' + operationId: getOrderById + produces: + - application/xml + - application/json + parameters: + - name: order_id + in: path + description: ID of pet that needs to be fetched + required: true + type: integer + maximum: 5 + minimum: 1 + format: int64 + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Order' + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + operationId: deleteOrder + produces: + - application/xml + - application/json + parameters: + - name: order_id + in: path + description: ID of the order that needs to be deleted + required: true + type: string + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: Created user object + required: true + schema: + $ref: '#/definitions/User' + responses: + default: + description: successful operation + /user/createWithArray: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithArrayInput + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: List of user object + required: true + schema: + type: array + items: + $ref: '#/definitions/User' + responses: + default: + description: successful operation + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithListInput + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: List of user object + required: true + schema: + type: array + items: + $ref: '#/definitions/User' + responses: + default: + description: successful operation + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + produces: + - application/xml + - application/json + parameters: + - name: username + in: query + description: The user name for login + required: true + type: string + - name: password + in: query + description: The password for login in clear text + required: true + type: string + responses: + '200': + description: successful operation + schema: + type: string + headers: + X-Rate-Limit: + type: integer + format: int32 + description: calls per hour allowed by the user + X-Expires-After: + type: string + format: date-time + description: date in UTC when toekn expires + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + produces: + - application/xml + - application/json + parameters: [] + responses: + default: + description: successful operation + '/user/{username}': + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + produces: + - application/xml + - application/json + parameters: + - name: username + in: path + description: 'The name that needs to be fetched. Use user1 for testing.' + required: true + type: string + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + produces: + - application/xml + - application/json + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + type: string + - in: body + name: body + description: Updated user object + required: true + schema: + $ref: '#/definitions/User' + responses: + '400': + description: Invalid user supplied + '404': + description: User not found + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + produces: + - application/xml + - application/json + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found +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-core/src/test/resources/apis/petstore_reservedWords.yaml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore_reservedWords.yaml new file mode 100644 index 0000000000..7175b75f0e --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore_reservedWords.yaml @@ -0,0 +1,120 @@ +swagger: '2.0' +info: + description: 'This is a modified Petstore server, that uses the reserved word "name" as parameter name. This should be renamed to "_name" in the generated code.' + version: 1.0.0 + 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 +schemes: + - http +paths: + /pet/findByName: + get: + tags: + - pet + summary: Finds Pet by name + description: Name can be any text + operationId: findPetByName + produces: + - application/xml + - application/json + parameters: + # name is a reserved word and should be masked with an '_' in the generated api + - name: name + in: query + description: Name of the pet + required: true + type: string + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Pet' + '400': + description: Invalid name value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' +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 +definitions: + Category: + title: Pet category + description: A category for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + 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 diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/citrus-context.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/citrus-context.xml new file mode 100644 index 0000000000..3f2a783fca --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/citrus-context.xml @@ -0,0 +1,7 @@ + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/defaultOas3SchemaValidationTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/defaultOas3SchemaValidationTest.xml new file mode 100644 index 0000000000..1784d89782 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/defaultOas3SchemaValidationTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnReasonPhraseTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnReasonPhraseTest.xml new file mode 100644 index 0000000000..60f8dc25d0 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnReasonPhraseTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnStatusTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnStatusTest.xml new file mode 100644 index 0000000000..ca47b78103 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnStatusTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnVersionTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnVersionTest.xml new file mode 100644 index 0000000000..5047fc38a0 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/failOnVersionTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/getPetByIdRequestTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/getPetByIdRequestTest.xml new file mode 100644 index 0000000000..c6f1afc8b5 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/getPetByIdRequestTest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonDeactivatedSchemaValidationTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonDeactivatedSchemaValidationTest.xml new file mode 100644 index 0000000000..7f4b46ed43 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonDeactivatedSchemaValidationTest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractTest.xml new file mode 100644 index 0000000000..efb6b3e5a8 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractionTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractionTest.xml new file mode 100644 index 0000000000..2ead5c459b --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathExtractionTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationFailureTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationFailureTest.xml new file mode 100644 index 0000000000..4d4bff102f --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationFailureTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationTest.xml new file mode 100644 index 0000000000..320e32fcd9 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonPathValidationTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationFailureTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationFailureTest.xml new file mode 100644 index 0000000000..3aeb456f25 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationFailureTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationTest.xml new file mode 100644 index 0000000000..50f146bc18 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/jsonSchemaValidationTest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithFileAttributesTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithFileAttributesTest.xml new file mode 100644 index 0000000000..4e24ad3e06 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithFileAttributesTest.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithMultipleDatatypesTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithMultipleDatatypesTest.xml new file mode 100644 index 0000000000..a727b3bd06 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithMultipleDatatypesTest.xml @@ -0,0 +1,24 @@ + + + + + + Test + true + 1 + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithPlainTextTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithPlainTextTest.xml new file mode 100644 index 0000000000..a3082ee857 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/multipartWithPlainTextTest.xml @@ -0,0 +1,32 @@ + + + + + + + + ]]> + + + {"data1":"value1"} + + + {"schema":"mySchema"} + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/AdditionalData.json b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/AdditionalData.json new file mode 100644 index 0000000000..a921a4b0c2 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/AdditionalData.json @@ -0,0 +1,6 @@ +{ + "Konto": { + "iban": "DE43100500000920018963", + "amount": 1234 + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/MultipartTemplate.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/MultipartTemplate.xml new file mode 100644 index 0000000000..a3dd52a043 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/MultipartTemplate.xml @@ -0,0 +1,4 @@ + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/Schema.json b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/Schema.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/Schema.json @@ -0,0 +1 @@ +{} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/addPetMessage.json b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/addPetMessage.json new file mode 100644 index 0000000000..b68ed32a5e --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/addPetMessage.json @@ -0,0 +1,6 @@ +{ + "id": 12, + "name": "Snoopy", + "tags": ["comic dog"], + "photoUrls": ["url1", "url2"] +} 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 new file mode 100644 index 0000000000..b68ed32a5e --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json @@ -0,0 +1,6 @@ +{ + "id": 12, + "name": "Snoopy", + "tags": ["comic dog"], + "photoUrls": ["url1", "url2"] +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage2.json b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage2.json new file mode 100644 index 0000000000..267e7887a0 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage2.json @@ -0,0 +1,6 @@ +{ + "id": 12, + "name": "Garfield", + "tags": ["comic cat"], + "photoUrls": ["url1", "url2"] +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/postFileTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/postFileTest.xml new file mode 100644 index 0000000000..a04b808966 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/postFileTest.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/scriptValidationTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/scriptValidationTest.xml new file mode 100644 index 0000000000..39efdef351 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/scriptValidationTest.xml @@ -0,0 +1,25 @@ + + + + + + + assert json.id == 12 + assert json.name == 'Snoopy' + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralTest.xml new file mode 100644 index 0000000000..3daad673c3 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralTest.xml @@ -0,0 +1,20 @@ + + + + + {"id": 13} + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralWithVariableTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralWithVariableTest.xml new file mode 100644 index 0000000000..11aacdf5a0 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyLiteralWithVariableTest.xml @@ -0,0 +1,23 @@ + + + + + + + + {"id": ${id}} + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyTest.xml new file mode 100644 index 0000000000..8cb689bf3f --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithBodyTest.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithExtraHeaderTest.xml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithExtraHeaderTest.xml new file mode 100644 index 0000000000..b2b002aa24 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/sendWithExtraHeaderTest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + 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..d35d8934bf --- /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,245 @@ +/** + * ================================================== + * 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..d9730d0d42 --- /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,215 @@ +/** + * ================================================== + * 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..0d81b2d521 --- /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,29 @@ +/** + * ================================================== + * 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..36d2bca5f4 --- /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,750 @@ +/** + * ================================================== + * 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..090a9bfb7e --- /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,56 @@ +/** + * ================================================== + * 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..9ff2150d21 --- /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,245 @@ +/** + * ================================================== + * 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..32920fb8ef --- /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,215 @@ +/** + * ================================================== + * 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..af5b731084 --- /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,45 @@ +/** + * ================================================== + * GENERATED CLASS, ALL CHANGES WILL BE LOST + * ================================================== + */ + +package org.citrusframework.openapi.generator.rest.petstore.citrus.extension; + +import org.citrusframework.openapi.generator.rest.petstore.request.PetApi; +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..a014b1c53f --- /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,862 @@ +/** + * ================================================== + * 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 PetApi implements GeneratedApi +{ + + public static final PetApi INSTANCE = new PetApi(); + + 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; + } + + /** addPet (POST /pet) + Add a new pet to the store + + **/ + public static class AddPetRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet"; + private final Logger coverageLogger = LoggerFactory.getLogger(AddPetRequest.class); + + + public AddPetRequest() { + // 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() + ":addPetRequestType"); + } + + public String getOperationName() { + return "addPet"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/pet"; + } + + /** + * 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, "addPet;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** deletePet (DELETE /pet/{petId}) + Deletes a pet + + **/ + public static class DeletePetRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet/{petId}"; + private final Logger coverageLogger = LoggerFactory.getLogger(DeletePetRequest.class); + + private String petId; + + + public DeletePetRequest() { + // 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() + ":deletePetRequestType"); + } + + public String getOperationName() { + return "deletePet"; + } + + public String getMethod() { + return "DELETE"; + } + + public String getPath() { + return "/pet/{petId}"; + } + + /** + * 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, "deletePet;DELETE;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setPetId(String petId) { + this.petId = petId; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "petId" + "}", petId); + return endpoint; + } + } + /** findPetsByStatus (GET /pet/findByStatus) + Finds Pets by status + + **/ + public static class FindPetsByStatusRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet/findByStatus"; + private final Logger coverageLogger = LoggerFactory.getLogger(FindPetsByStatusRequest.class); + + private String status; + + + public FindPetsByStatusRequest() { + // 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() + ":findPetsByStatusRequestType"); + } + + public String getOperationName() { + return "findPetsByStatus"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/pet/findByStatus"; + } + + /** + * 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.status)) { + queryParams.put("status", context.replaceDynamicContentInString(this.status)); + httpClientRequestActionBuilder.queryParam("status", this.status); + } + + 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, "findPetsByStatus;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setStatus(String status) { + this.status = status; + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** findPetsByTags (GET /pet/findByTags) + Finds Pets by tags + + **/ + public static class FindPetsByTagsRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet/findByTags"; + private final Logger coverageLogger = LoggerFactory.getLogger(FindPetsByTagsRequest.class); + + private String tags; + + + public FindPetsByTagsRequest() { + // 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() + ":findPetsByTagsRequestType"); + } + + public String getOperationName() { + return "findPetsByTags"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/pet/findByTags"; + } + + /** + * 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.tags)) { + queryParams.put("tags", context.replaceDynamicContentInString(this.tags)); + httpClientRequestActionBuilder.queryParam("tags", this.tags); + } + + 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, "findPetsByTags;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setTags(String tags) { + this.tags = tags; + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** getPetById (GET /pet/{petId}) + Find pet by ID + + **/ + public static class GetPetByIdRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet/{petId}"; + private final Logger coverageLogger = LoggerFactory.getLogger(GetPetByIdRequest.class); + + private String petId; + + + @Value("${" + "petStoreEndpoint.basic.username:#{null}}") + private String basicUsername; + @Value("${" + "petStoreEndpoint.basic.password:#{null}}") + private String basicPassword; + + + public GetPetByIdRequest() { + // 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() + ":getPetByIdRequestType"); + } + + public String getOperationName() { + return "getPetById"; + } + + public String getMethod() { + return "GET"; + } + + public String getPath() { + return "/pet/{petId}"; + } + + /** + * 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(",", "{", "}")); + + + if(basicUsername != null && basicPassword != null){ + messageBuilderSupport.header("Authorization", "Basic " + Base64.getEncoder().encodeToString((context.replaceDynamicContentInString(basicUsername)+":"+context.replaceDynamicContentInString(basicPassword)).getBytes())); + } + + httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); + httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); + + httpClientRequestActionBuilder.build().execute(context); + + coverageLogger.trace(coverageMarker, "getPetById;GET;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setPetId(String petId) { + this.petId = petId; + } + + + public void setBasicUsername(String basicUsername) { + this.basicUsername = basicUsername; + } + + public void setBasicPassword(String basicPassword) { + this.basicPassword = basicPassword; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "petId" + "}", petId); + return endpoint; + } + } + /** updatePet (PUT /pet) + Update an existing pet + + **/ + public static class UpdatePetRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet"; + private final Logger coverageLogger = LoggerFactory.getLogger(UpdatePetRequest.class); + + + public UpdatePetRequest() { + // 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() + ":updatePetRequestType"); + } + + public String getOperationName() { + return "updatePet"; + } + + public String getMethod() { + return "PUT"; + } + + public String getPath() { + return "/pet"; + } + + /** + * 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, "updatePet;PUT;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + private String replacePathParams(String endpoint) { + + return endpoint; + } + } + /** updatePetWithForm (POST /pet/{petId}) + Updates a pet in the store with form data + + **/ + public static class UpdatePetWithFormRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet/{petId}"; + private final Logger coverageLogger = LoggerFactory.getLogger(UpdatePetWithFormRequest.class); + + private String petId; + + + public UpdatePetWithFormRequest() { + // 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() + ":updatePetWithFormRequestType"); + } + + public String getOperationName() { + return "updatePetWithForm"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/pet/{petId}"; + } + + /** + * 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, "updatePetWithForm;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setPetId(String petId) { + this.petId = petId; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "petId" + "}", petId); + return endpoint; + } + } + /** uploadFile (POST /pet/{petId}/uploadImage) + uploads an image + + **/ + public static class UploadFileRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { + + private static final String ENDPOINT = "/pet/{petId}/uploadImage"; + private final Logger coverageLogger = LoggerFactory.getLogger(UploadFileRequest.class); + + private String petId; + + private String additionalMetadata; + + private String _file; + + + public UploadFileRequest() { + // 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() + ":uploadFileRequestType"); + } + + public String getOperationName() { + return "uploadFile"; + } + + public String getMethod() { + return "POST"; + } + + public String getPath() { + return "/pet/{petId}/uploadImage"; + } + + /** + * 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(additionalMetadata)) { + // first try to load from resource + ClassPathResource resource = null; + try { + resource = new ClassPathResource(additionalMetadata); + } + catch(Exception ignore) { + // Use plain text instead of resource + } + + if(resource != null && resource.exists()){ + multiValues.add("additionalMetadata", resource); + } else { + multiValues.add("additionalMetadata", additionalMetadata); + } + bodyLog += additionalMetadata.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; + } + if (StringUtils.isNotBlank(_file)) { + multiValues.add("_file", new ClassPathResource(_file)); + bodyLog += _file.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, "uploadFile;POST;\"" + + query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + + bodyLog); + } + + public void setPetId(String petId) { + this.petId = petId; + } + + public void setAdditionalMetadata(String additionalMetadata) { + this.additionalMetadata = additionalMetadata; + } + + public void set_file(String _file) { + this._file = _file; + } + + private String replacePathParams(String endpoint) { + endpoint = endpoint.replace("{" + "petId" + "}", petId); + 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/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..406f97f2f9 --- /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,433 @@ +/** + * ================================================== + * 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..01f4558954 --- /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,819 @@ +/** + * ================================================== + * 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..cb89458c0b --- /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,142 @@ +/** + * ================================================== + * 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.PetApi; +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..8acda64ca3 --- /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,187 @@ +/** + * ================================================== + * 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..1c72d58016 --- /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,215 @@ +/** + * ================================================== + * 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..04773ddb99 --- /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,26 @@ +/** + * ================================================== + * 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..dd84878034 --- /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,330 @@ +/** + * ================================================== + * 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..e462cae9f8 --- /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,38 @@ +/** + * ================================================== + * 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-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookDatatypes.xsd b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookDatatypes.xsd new file mode 100644 index 0000000000..c093acef11 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookDatatypes.xsd @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml new file mode 100644 index 0000000000..480e462dfe --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService-generated.yaml @@ -0,0 +1,33 @@ +--- +info: + contact: + name: "org.citrusframework.openapi.generator.SimpleWsdlToOpenApiTransformer" + description: "This api has been generated from the following wsdl 'BookService.wsdl'.\ + \ It's purpose is solely to serve as input for SOAP API generation. Note that\ + \ only operations are extracted from the WSDL. No schema information whatsoever\ + \ is generated!" + title: "Generated api from wsdl" + version: "1.0.0" +openapi: "3.0.1" +paths: + /GetBook: + post: + description: "" + operationId: "GetBook" + responses: {} + tags: + - "BookServiceSOAP" + /AddBook: + post: + description: "" + operationId: "AddBook" + responses: {} + tags: + - "BookServiceSOAP" + /GetAllBooks: + post: + description: "" + operationId: "GetAllBooks" + responses: {} + tags: + - "BookServiceSOAP" \ No newline at end of file diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService.wsdl b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService.wsdl new file mode 100644 index 0000000000..5243c102d5 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/resources/org/citrusframework/openapi/generator/SimpleWsdlToOpenApiTransformerTest/BookService.wsdl @@ -0,0 +1,110 @@ + + + Definition for a web service called BookService, + which can be used to add or retrieve books from a collection. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 new file mode 100644 index 0000000000..e92fd84d94 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/pom.xml @@ -0,0 +1,111 @@ + + 4.0.0 + + + citrus-test-api-generator + org.citrusframework + 4.3.0-SNAPSHOT + ../pom.xml + + + citrus-test-api-generator-maven-plugin + maven-plugin + + Citrus :: Test API Generator :: Maven Plugin + Maven Plugin for generation of Citrus Test API + + + 2.2.21 + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${maven.plugin.plugin.version} + + true + + + + + + + + + org.citrusframework + citrus-test-api-generator-core + ${project.version} + + + + commons-io + commons-io + ${commons.io.version} + + + io.swagger.core.v3 + swagger-core + ${swagger.version} + + + io.swagger.core.v3 + swagger-models-jakarta + ${swagger.version} + + + org.apache.maven + maven-artifact + ${maven.version} + provided + + + org.apache.maven + maven-core + ${maven.version} + provided + + + org.apache.maven + maven-plugin-api + ${maven.version} + provided + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven.plugin.annotations.version} + provided + + + + + org.apache.maven + maven-compat + ${maven.version} + test + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + ${maven.plugin.testing.harness.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.junit.jupiter + junit-jupiter-params + test + + + 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 new file mode 100644 index 0000000000..ea92db1efb --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/CodeGenMojoWrapper.java @@ -0,0 +1,86 @@ +package org.citrusframework.maven.plugin; + +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; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.project.MavenProject; +import org.openapitools.codegen.plugin.CodeGenMojo; + +/** + * Wrapper class that uses reflection to expose several properties of the {@link CodeGenMojo} for explicit assignment. + * + * @author Thorsten Schlathoelter + */ +public class CodeGenMojoWrapper extends CodeGenMojo { + + private final Map configOptionsProperties = new HashMap<>(); + + public CodeGenMojoWrapper() throws MojoExecutionException { + setFixedConfigOptions(); + setPrivateField("configOptions", configOptionsProperties); + } + + private void setFixedConfigOptions() throws MojoExecutionException { + setPrivateField("generateSupportingFiles", true); + setPrivateField( "generatorName", CODEGEN_NAME); + } + + public CodeGenMojoWrapper project(MavenProject mavenProject) throws MojoExecutionException { + setPrivateField("project", mavenProject); + return this; + } + + public CodeGenMojoWrapper output(File output) throws MojoExecutionException { + setPrivateField("output", output); + return this; + } + + public CodeGenMojoWrapper inputSpec(String inputSpec) throws MojoExecutionException { + setPrivateField("inputSpec", inputSpec); + return this; + } + + public CodeGenMojoWrapper mojoExecution(MojoExecution mojoExecution) throws MojoExecutionException { + setPrivateField("mojo", mojoExecution); + return this; + } + + public CodeGenMojoWrapper configOptions(Map configOptionsProperties) { + this.configOptionsProperties.putAll(configOptionsProperties); + return this; + } + + public CodeGenMojoWrapper schemaFolder(String schemaFolder) { + configOptionsProperties.put(JavaCitrusCodegen.GENERATED_SCHEMA_FOLDER, schemaFolder); + return this; + } + + public CodeGenMojoWrapper resourceFolder(String resourceFolder) { + configOptionsProperties.put(JavaCitrusCodegen.RESOURCE_FOLDER, resourceFolder); + return this; + } + + public CodeGenMojoWrapper sourceFolder(String sourceFolder) { + configOptionsProperties.put(JavaCitrusCodegen.SOURCE_FOLDER, sourceFolder); + return this; + } + + @SuppressWarnings("java:S3011") // Accessibility bypass + private void setPrivateField(String fieldName, Object fieldValue) throws MojoExecutionException { + try { + var field = CodeGenMojo.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(this, fieldValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new MojoExecutionException( + 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/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java new file mode 100644 index 0000000000..23ae7b161f --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java @@ -0,0 +1,156 @@ +package org.citrusframework.maven.plugin; + +import static java.lang.String.format; +import static java.util.Collections.emptyList; + +import org.citrusframework.maven.plugin.TestApiGeneratorMojo.ApiConfig; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import org.apache.maven.plugin.MojoExecutionException; +import org.citrusframework.exceptions.CitrusRuntimeException; + +/** + * Utility class responsible for generating the Spring meta files 'spring.handlers' and 'spring.schemas', used + * in Spring integration testing. These meta files define mappings between XML namespace URIs and corresponding + * handler classes. The class provides methods to generate these meta files based on the configuration provided. + *

+ * The generated meta files can be created either in the generated folder or in the main resources folder. See + * {@link TestApiGeneratorMojo#RESOURCE_FOLDER_PROPERTY} for details. The implemented algorithm carefully updates these + * files and tries to keep non generated information unchanged. Therefore, a special segment in the namespace uri is used, namely + * {@link TestApiGeneratorMojo#CITRUS_TEST_SCHEMA}. + *

+ * + * @author Thorsten Schlathoelter + * + */ +public class SpringMetaFileGenerator { + + private final TestApiGeneratorMojo testApiGeneratorMojo; + + public SpringMetaFileGenerator(TestApiGeneratorMojo testApiGeneratorMojo) { + this.testApiGeneratorMojo = testApiGeneratorMojo; + } + + public void generateSpringIntegrationMetaFiles() throws MojoExecutionException { + + String springMetafileDirectory = format("%s/%s", testApiGeneratorMojo.getMavenProject().getBasedir(), + testApiGeneratorMojo.metaInfFolder()); + File metaFolder = new File(springMetafileDirectory); + if (!metaFolder.exists() && !metaFolder.mkdirs()) { + throw new CitrusRuntimeException( + format("Unable to create spring meta file directory: '%s'", springMetafileDirectory)); + } + + try { + writeSpringSchemaMetaFile(metaFolder); + writeSpringHandlerMetaFile(metaFolder); + } catch (MetaFileWriteException e) { + throw new MojoExecutionException(e); + } + } + + private void writeSpringSchemaMetaFile(File springMetafileDirectory) throws MojoExecutionException { + + String filename = "spring.schemas"; + writeSpringMetaFile(springMetafileDirectory, filename, (fileWriter, apiConfig) -> { + String targetXmlnsNamespace = TestApiGeneratorMojo.replaceDynamicVarsToLowerCase(apiConfig.getTargetXmlnsNamespace(), apiConfig.getPrefix(), + apiConfig.getVersion()); + String schemaFolderPath = TestApiGeneratorMojo.replaceDynamicVars(testApiGeneratorMojo.schemaFolder(apiConfig), apiConfig.getPrefix(), + apiConfig.getVersion()); + String schemaPath = String.format("%s/%s-api.xsd", schemaFolderPath, apiConfig.getPrefix().toLowerCase()); + appendLine(fileWriter, format("%s.xsd=%s%n", targetXmlnsNamespace.replace("http://", "http\\://"), schemaPath), filename); + }); + } + + private void writeSpringHandlerMetaFile(File springMetafileDirectory) throws MojoExecutionException { + String filename = "spring.handlers"; + writeSpringMetaFile(springMetafileDirectory, filename, (fileWriter, apiConfig) -> { + String targetXmlnsNamespace = TestApiGeneratorMojo.replaceDynamicVarsToLowerCase(apiConfig.getTargetXmlnsNamespace(), apiConfig.getPrefix(), + apiConfig.getVersion()); + String invokerPackage = TestApiGeneratorMojo.replaceDynamicVarsToLowerCase(apiConfig.getInvokerPackage(), apiConfig.getPrefix(), apiConfig.getVersion()); + String namespaceHandlerClass = invokerPackage + ".citrus.extension." + apiConfig.getPrefix() + "NamespaceHandler"; + appendLine(fileWriter, format("%s=%s%n", targetXmlnsNamespace.replace("http://", "http\\://"), namespaceHandlerClass), + filename); + }); + } + + private void writeSpringMetaFile(File springMetafileDirectory, String filename, BiConsumer contentFormatter) + throws MojoExecutionException { + + File handlerFile = new File(format("%s/%s", springMetafileDirectory.getPath(), filename)); + List filteredLines = readAndFilterLines(handlerFile); + + try (FileWriter fileWriter = new FileWriter(handlerFile)) { + + for (String line : filteredLines) { + fileWriter.write(format("%s%n", line)); + } + + for (ApiConfig apiConfig : testApiGeneratorMojo.getApiConfigs()) { + contentFormatter.accept(fileWriter, apiConfig); + } + + } catch (IOException e) { + throw new MojoExecutionException("Unable to write spring meta file!", e); + } + } + + /** + * Reads the lines from the specified file and filters out lines indicating a generated test API, + * while maintaining all non-generated test API lines. This method is used to process files + * containing both generated and non-generated test APIs, allowing seamless integration and + * modification of both types of APIs in the same source files. + * + *

+ * Generated test API lines are identified by the presence of the {@code CITRUS_TEST_SCHEMA} + * string and excluded from the output of this method, while all other lines are preserved. + * This enables the algorithm to operate on files that are not purely generated, for example, + * when mixing generated with non-generated APIs in 'src/main/META-INF'. + *

+ * + * @param file the file to read and filter + * @return a list of filtered lines, excluding lines indicating a generated test API + * @throws CitrusRuntimeException if an error occurs while reading the file + */ + private static List readAndFilterLines(File file) { + + if (!file.exists()) { + return emptyList(); + } + + List filteredLines = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.contains(TestApiGeneratorMojo.CITRUS_TEST_SCHEMA)) { + filteredLines.add(line); + } + } + } catch (IOException e) { + throw new CitrusRuntimeException(format("Unable to read file file: '%s'", file.getPath()), e); + } + + return filteredLines; + } + + private void appendLine(FileWriter fileWriter, String format, String filename) { + try { + fileWriter.append(format); + } catch (IOException e) { + throw new MetaFileWriteException(format("Unable to write spring meta file '%s'!", filename), e); + } + } + + private static final class MetaFileWriteException extends RuntimeException { + + public MetaFileWriteException(String message, Throwable cause) { + super(message, cause); + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java new file mode 100644 index 0000000000..d5b2b1cd53 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java @@ -0,0 +1,412 @@ +package org.citrusframework.maven.plugin; + +import static org.citrusframework.openapi.generator.JavaCitrusCodegen.API_ENDPOINT; +import static org.citrusframework.openapi.generator.JavaCitrusCodegen.API_TYPE; +import static org.citrusframework.openapi.generator.JavaCitrusCodegen.PREFIX; +import static org.citrusframework.openapi.generator.JavaCitrusCodegen.TARGET_XMLNS_NAMESPACE; +import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.isBlank; + +import com.google.common.annotations.VisibleForTesting; +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.openapitools.codegen.plugin.CodeGenMojo; +import org.sonatype.plexus.build.incremental.BuildContext; +import org.sonatype.plexus.build.incremental.DefaultBuildContext; + +/** + * The Citrus OpenAPI Generator Maven Plugin is designed to facilitate the integration of multiple OpenAPI specifications + * into the Citrus testing framework by automatically generating necessary test classes and XSDs. This plugin wraps the + * {@code CodeGenMojo} and extends its functionality to support multiple API configurations. + *

+ * Features: + * - Multiple API Configurations: Easily configure multiple OpenAPI specifications to generate test APIs with specific prefixes. + * - Citrus Integration: Generates classes and XSDs tailored for use within the Citrus framework, streamlining the process + * of creating robust integration tests. + *

+ * + * @author Thorsten Schlathoelter + * + */ +@Mojo( + name = "create-test-api", + defaultPhase = LifecyclePhase.GENERATE_TEST_SOURCES, + requiresDependencyCollection = ResolutionScope.TEST, + requiresDependencyResolution = ResolutionScope.TEST, + threadSafe = true +) +public class TestApiGeneratorMojo extends AbstractMojo { + + public static final String DEFAULT_SOURCE_FOLDER = "generated-test-sources"; + public static final String DEFAULT_RESOURCE_FOLDER = "generated-test-resources"; + public static final String DEFAULT_BASE_PACKAGE = "org.citrusframework.automation.%PREFIX%.%VERSION%"; + public static final String DEFAULT_INVOKER_PACKAGE = DEFAULT_BASE_PACKAGE; + public static final String DEFAULT_API_PACKAGE = DEFAULT_BASE_PACKAGE+".api"; + public static final String DEFAULT_MODEL_PACKAGE = DEFAULT_BASE_PACKAGE+".model"; + public static final String DEFAULT_SCHEMA_FOLDER_TEMPLATE = "schema/xsd/%VERSION%"; + public static final ApiType DEFAULT_API_TYPE = ApiType.REST; + + /** + * Marker fragment in the schema name of a generated schema. Used to distinguish generated from non generated values, when manipulating + * spring meta-data files. + */ + public static final String CITRUS_TEST_SCHEMA = "citrus-test-schema"; + + /** + * Specifies the default target namespace template. When changing the default value, it's important to maintain the 'citrus-test-schema' + * part, as this name serves to differentiate between generated and non-generated schemas. This differentiation aids in the creation of + * supporting Spring files such as 'spring.handlers' and 'spring.schemas'. + */ + public static final String DEFAULT_TARGET_NAMESPACE_TEMPLATE = + "http://www.citrusframework.org/" + CITRUS_TEST_SCHEMA + "/%VERSION%/%PREFIX%-api"; + + /** + * The default META-INF folder. Note that it points into the main resources, not generated resources, to allow for non generated + * schemas/handlers. See also {@link TestApiGeneratorMojo}#DEFAULT_TARGET_NAMESPACE_TEMPLATE. + */ + public static final String DEFAULT_META_INF_FOLDER = "target/generated-test-resources/META-INF"; + + @Component + private final BuildContext buildContext = new DefaultBuildContext(); + + @Parameter(defaultValue = "${project}", readonly = true) + private MavenProject mavenProject; + + @Parameter(defaultValue = "${mojoExecution}", readonly = true) + private MojoExecution mojoExecution; + + /** + * sourceFolder: specifies the location to which the sources are generated. Defaults to 'generated-test-sources'. + */ + public static final String SOURCE_FOLDER_PROPERTY = "citrus.test.api.generator.source.folder"; + @Parameter(property = SOURCE_FOLDER_PROPERTY, defaultValue = DEFAULT_SOURCE_FOLDER) + @SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) // Maven injected + private String sourceFolder = DEFAULT_SOURCE_FOLDER; + + /** + * resourceFolder: specifies the location to which the resources are generated. Defaults to 'generated-test-resources'. + */ + public static final String RESOURCE_FOLDER_PROPERTY = "citrus.test.api.generator.resource.folder"; + @Parameter(property = RESOURCE_FOLDER_PROPERTY, defaultValue = DEFAULT_RESOURCE_FOLDER) + @SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) // Maven injected + private String resourceFolder = DEFAULT_RESOURCE_FOLDER; + + /** + * schemaFolder: specifies the location for the generated xsd schemas. Defaults to 'schema/xsd/%VERSION%' + */ + public static final String API_SCHEMA_FOLDER = "citrus.test.api.generator.schema.folder"; + @Parameter(property = API_SCHEMA_FOLDER, defaultValue = DEFAULT_SCHEMA_FOLDER_TEMPLATE) + @SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) // Maven injected + private String schemaFolder = DEFAULT_SCHEMA_FOLDER_TEMPLATE; + + /** + * metaInfFolder: specifies the location to which the resources are generated. Defaults to 'generated-resources'. + */ + public static final String META_INF_FOLDER = "citrus.test.api.generator.meta.inf.folder"; + @Parameter(property = META_INF_FOLDER, defaultValue = DEFAULT_META_INF_FOLDER) + @SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) // Maven injected + private String metaInfFolder = DEFAULT_META_INF_FOLDER; + + /** + * resourceFolder: specifies the location to which the resources are generated. Defaults to 'generated-resources'. + */ + public static final String GENERATE_SPRING_INTEGRATION_FILES = "citrus.test.api.generator.generate.spring.integration.files"; + @Parameter(property = GENERATE_SPRING_INTEGRATION_FILES, defaultValue = "true") + @SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) // Maven injected + private boolean generateSpringIntegrationFiles = true; + + @Parameter + private List apis; + + protected MavenProject getMavenProject() { + return mavenProject; + } + + protected void setMavenProject(MavenProject mavenProject) { + this.mavenProject = mavenProject; + } + + public List getApiConfigs() { + return apis; + } + + public String metaInfFolder() { + return metaInfFolder; + } + + @VisibleForTesting + void setMojoExecution(MojoExecution mojoExecution) { + this.mojoExecution = mojoExecution; + } + + /** + * Returns the fully qualified schema folder + */ + public String schemaFolder(ApiConfig apiConfig) { + return replaceDynamicVars(schemaFolder, apiConfig.getPrefix(), apiConfig.getVersion()); + } + + @Override + public void execute() throws MojoExecutionException { + + for (int index = 0; index < apis.size(); index++) { + ApiConfig apiConfig = apis.get(index); + validateApiConfig(index, apiConfig); + CodeGenMojo codeGenMojo = configureCodeGenMojo(apiConfig); + codeGenMojo.execute(); + } + + if (generateSpringIntegrationFiles) { + new SpringMetaFileGenerator(this).generateSpringIntegrationMetaFiles(); + } + } + + CodeGenMojo configureCodeGenMojo(ApiConfig apiConfig) throws MojoExecutionException { + CodeGenMojo codeGenMojo = new CodeGenMojoWrapper() + .resourceFolder(resourceFolder) + .sourceFolder(sourceFolder) + .schemaFolder(schemaFolder(apiConfig)) + .output(new File(mavenProject.getBuild().getDirectory())) + .mojoExecution(mojoExecution) + .project(mavenProject) + .inputSpec(apiConfig.getSource()) + .configOptions(apiConfig.toConfigOptionsProperties()); + + codeGenMojo.setPluginContext(getPluginContext()); + codeGenMojo.setBuildContext(buildContext); + return codeGenMojo; + } + + private void validateApiConfig(int apiIndex, ApiConfig apiConfig) throws MojoExecutionException { + requireNonBlankParameter("prefix", apiIndex, apiConfig.getPrefix()); + requireNonBlankParameter("source", apiIndex, apiConfig.getSource()); + } + + private void requireNonBlankParameter(String name, int index, String parameterValue) throws MojoExecutionException { + if (isBlank(parameterValue)) { + throw new MojoExecutionException(format("Required parameter '%s' not set for api at index '%d'!", name, index)); + } + } + + /** + * Replace the placeholders '%PREFIX%' and '%VERSION%' in the given text. + */ + static String replaceDynamicVars(String text, String prefix, String version) { + + if (text == null) { + return null; + } + + return text.replace("%PREFIX%", prefix) + .replace(".%VERSION%", version != null ? "." + version : "") + .replace("/%VERSION%", version != null ? "/" + version : "") + .replace("-%VERSION%", version != null ? "-" + version : "") + .replace("%VERSION%", version != null ? version : ""); + } + + /** + * Replace the placeholders '%PREFIX%' and '%VERSION%' in the given text, performing a toLowerCase on the prefix. + */ + static String replaceDynamicVarsToLowerCase(String text, String prefix, String version) { + return replaceDynamicVars(text, prefix.toLowerCase(), version); + } + + public enum ApiType { + REST, SOAP + } + + /** + * Note that the default values are not properly set by maven processor. Therefore, the default values have been assigned additionally + * on field level. + */ + public static class ApiConfig { + + public static final String DEFAULT_ENDPOINT = "PREFIX_ENDPOINT"; + + /** + * prefix: specifies the prefixed used for the test api. Typically, an acronym for the application which is being tested. + */ + public static final String API_PREFIX_PROPERTY = "citrus.test.api.generator.prefix"; + @Parameter(required = true, property = API_PREFIX_PROPERTY) + private String prefix; + + /** + * source: specifies the source of the test api. + */ + public static final String API_SOURCE_PROPERTY = "citrus.test.api.generator.source"; + @Parameter(required = true, property = API_SOURCE_PROPERTY) + private String source; + + /** + * version: specifies the version of the api. May be null. + */ + public static final String API_VERSION_PROPERTY = "citrus.test.api.generator.version"; + @Parameter(property = API_VERSION_PROPERTY) + private String version; + + /** + * endpoint: specifies the endpoint of the test api. Defaults to 'prefixEndpoint'. + */ + public static final String API_ENDPOINT_PROPERTY = "citrus.test.api.generator.endpoint"; + @Parameter(property = API_ENDPOINT_PROPERTY, defaultValue = DEFAULT_ENDPOINT) + private String endpoint = DEFAULT_ENDPOINT; + + /** + * type: specifies the type of the test api. Defaults to 'REST' + */ + public static final String API_TYPE_PROPERTY = "citrus.test.api.generator.type"; + @Parameter(property = API_TYPE_PROPERTY, defaultValue = "REST") + private ApiType type = DEFAULT_API_TYPE; + + /** + * useTags: specifies whether tags should be used by the generator. Defaults to 'true'. If useTags is set to true, the generator + * will organize the generated code based on the tags defined in your API specification. + */ + public static final String API_USE_TAGS_PROPERTY = "citrus.test.api.generator.use.tags"; + @Parameter(property = API_USE_TAGS_PROPERTY, defaultValue = "true") + private boolean useTags = true; + + /** + * invokerPackage: specifies the package for the test api classes. Defaults to + * 'org.citrusframework.automation.%PREFIX%.%VERSION%'. + */ + public static final String API_INVOKER_PACKAGE_PROPERTY = "citrus.test.api.generator.invoker.package"; + @Parameter(property = API_INVOKER_PACKAGE_PROPERTY, defaultValue = DEFAULT_INVOKER_PACKAGE) + private String invokerPackage = DEFAULT_INVOKER_PACKAGE; + + /** + * apiPackage: specifies the package for the test api classes. Defaults to + * 'org.citrusframework.automation.%PREFIX%.%VERSION%.api'. + */ + public static final String API_API_PACKAGE_PROPERTY = "citrus.test.api.generator.api.package"; + @Parameter(property = API_API_PACKAGE_PROPERTY, defaultValue = DEFAULT_API_PACKAGE) + private String apiPackage = DEFAULT_API_PACKAGE; + + /** + * modelPackage: specifies the package for the test api classes. Defaults to + * 'org.citrusframework.automation.%PREFIX%.%VERSION%.model'. + */ + public static final String API_MODEL_PACKAGE_PROPERTY = "citrus.test.api.generator.model.package"; + @Parameter(property = API_MODEL_PACKAGE_PROPERTY, defaultValue = DEFAULT_MODEL_PACKAGE) + private String modelPackage = DEFAULT_MODEL_PACKAGE; + + /** + * targetXmlNamespace: specifies the xml namespace to be used by the api. Defaults to + * 'http://www.citrusframework.org/schema/%VERSION%/%PREFIX%-api' + */ + @SuppressWarnings("JavadocLinkAsPlainText") + public static final String API_NAMESPACE_PROPERTY = "citrus.test.api.generator.namespace"; + @Parameter(property = API_NAMESPACE_PROPERTY, defaultValue = DEFAULT_TARGET_NAMESPACE_TEMPLATE) + private String targetXmlnsNamespace = DEFAULT_TARGET_NAMESPACE_TEMPLATE; + + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String qualifiedEndpoint() { + return DEFAULT_ENDPOINT.equals(endpoint) ? getPrefix().toLowerCase() + "Endpoint" : endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public ApiType getType() { + return type; + } + + public void setType(ApiType type) { + this.type = type; + } + + public void setUseTags(boolean useTags) { + this.useTags = useTags; + } + + public String getInvokerPackage() { + return invokerPackage; + } + + public void setInvokerPackage(String invokerPackage) { + this.invokerPackage = invokerPackage; + } + + public String getApiPackage() { + return apiPackage; + } + + public void setApiPackage(String apiPackage) { + this.apiPackage = apiPackage; + } + + public String getModelPackage() { + return modelPackage; + } + + public void setModelPackage(String modelPackage) { + this.modelPackage = modelPackage; + } + + public String getTargetXmlnsNamespace() { + return targetXmlnsNamespace; + } + + public void setTargetXmlnsNamespace(String targetXmlnsNamespace) { + this.targetXmlnsNamespace = targetXmlnsNamespace; + } + + Map toConfigOptionsProperties() { + + Map configOptionsProperties = new HashMap<>(); + configOptionsProperties.put(PREFIX, prefix); + configOptionsProperties.put(API_ENDPOINT, qualifiedEndpoint()); + configOptionsProperties.put(API_TYPE, type.toString()); + configOptionsProperties.put(TARGET_XMLNS_NAMESPACE, + replaceDynamicVarsToLowerCase(targetXmlnsNamespace, prefix, version)); + configOptionsProperties.put("invokerPackage", + replaceDynamicVarsToLowerCase(invokerPackage, prefix, version)); + configOptionsProperties.put("apiPackage", + replaceDynamicVarsToLowerCase(apiPackage, prefix, version)); + configOptionsProperties.put("modelPackage", + replaceDynamicVarsToLowerCase(modelPackage, prefix, version)); + configOptionsProperties.put("useTags", useTags); + + return configOptionsProperties; + } + + } + + +} 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 new file mode 100644 index 0000000000..d4bcc740b9 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoIntegrationTest.java @@ -0,0 +1,314 @@ +package org.citrusframework.maven.plugin; + +import static com.google.common.collect.Streams.concat; +import static java.lang.Boolean.TRUE; +import static java.util.Arrays.stream; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.CITRUS_TEST_SCHEMA; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.replaceDynamicVars; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.replaceDynamicVarsToLowerCase; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.springframework.test.util.ReflectionTestUtils.getField; + +import jakarta.validation.constraints.NotNull; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.testing.AbstractMojoTestCase; +import org.apache.maven.project.MavenProject; +import org.assertj.core.api.Assertions; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.exceptions.TestCaseFailedException; +import org.citrusframework.maven.plugin.TestApiGeneratorMojo.ApiConfig; +import org.citrusframework.maven.plugin.TestApiGeneratorMojo.ApiType; +import org.citrusframework.maven.plugin.stubs.CitrusOpenApiGeneratorMavenProjectStub; +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.MethodSource; + +/** + * @author Thorsten Schlathoelter + */ +class TestApiGeneratorMojoIntegrationTest extends AbstractMojoTestCase { + + public static final String OTHER_META_FILE_CONTENT = "somenamespace=somevalue"; + + public static final String OTHER_CITRUS_META_FILE_CONTENT = String.format("somenamespace/%s/aa=somevalue", CITRUS_TEST_SCHEMA); + + /** + * 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" + }; + + /** + * Array containing path templates for each generated spring meta file, specified with tokens. Tokens can be replaced with values of the respective + * 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" + }; + + private TestApiGeneratorMojo fixture; + + @BeforeEach + @SuppressWarnings("JUnitMixedFramework") + void beforeEachSetup() throws Exception { + setUp(); + } + + 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) + ); + } + + @ParameterizedTest + @MethodSource + void executeMojoWithConfigurations(String configName, Exception expectedException) + throws Exception { + + try { + fixture = fixtureFromPom(configName); + } catch (MojoExecutionException | MojoFailureException e) { + Assertions.fail("Test setup failed!", e); + } + + @SuppressWarnings("unchecked") + List apiConfigs = (List) getField(fixture, "apis"); + + assertThat(apiConfigs).isNotNull(); + + if (expectedException == null) { + // Given + writeSomeValuesToSpringMetaFiles(apiConfigs); + + // When + assertThatCode(() -> fixture.execute()).doesNotThrowAnyException(); + + // Then + for (ApiConfig apiConfig : apiConfigs) { + assertFilesGenerated(apiConfig); + assertSpecificFileContent(apiConfig); + } + } else { + // When/Then + assertThatThrownBy(() -> fixture.execute()).isInstanceOf(expectedException.getClass()) + .hasMessage(expectedException.getMessage()); + } + } + + /** + * Writes values to spring meta files, to make sure existing non generated and existing generated values are treated properly. + */ + private void writeSomeValuesToSpringMetaFiles(List apiConfigs) { + for (ApiConfig apiConfig : apiConfigs) { + for (String filePathTemplate : SPRING_META_FILE_TEMPLATES) { + + String filePath = resolveFilePath(apiConfig, filePathTemplate); + File file = new File(filePath); + if (!file.getParentFile().exists() && !new File(filePath).getParentFile().mkdirs()) { + Assertions.fail("Unable to prepare test data."); + } + + try (FileWriter fileWriter = new FileWriter(filePath)) { + fileWriter.append(String.format("%s%n", OTHER_META_FILE_CONTENT)); + fileWriter.append(String.format("%s%n", OTHER_CITRUS_META_FILE_CONTENT)); + } catch (IOException e) { + throw new CitrusRuntimeException("Unable to write spring meta files", e); + } + } + } + } + + private void assertFilesGenerated(ApiConfig apiConfig) { + + for (String filePathTemplate : STANDARD_FILE_PATH_TEMPLATES) { + String filePath = resolveFilePath(apiConfig, filePathTemplate); + assertThat(new File(filePath)).isFile().exists(); + } + + if (TRUE.equals(getField(fixture, "generateSpringIntegrationFiles"))) { + for (String filePathTemplate : SPRING_META_FILE_TEMPLATES) { + String filePath = resolveFilePath(apiConfig, filePathTemplate); + assertThat(new File(filePath)).isFile().exists(); + } + } + } + + private void assertSpecificFileContent(ApiConfig apiConfig) { + try { + assertEndpointName(apiConfig); + assertTargetNamespace(apiConfig); + assertApiType(apiConfig); + assertSchemasInSpringSchemas(apiConfig); + assertHandlersInSpringHandlers(apiConfig); + } catch (IOException e) { + throw new TestCaseFailedException(e); + } + } + + private void assertHandlersInSpringHandlers(ApiConfig apiConfig) throws IOException { + String targetNamespace = replaceDynamicVarsToLowerCase(apiConfig.getTargetXmlnsNamespace(), apiConfig.getPrefix(), apiConfig.getVersion()); + targetNamespace = targetNamespace.replace(":", "\\:"); + String invokerPackage = replaceDynamicVarsToLowerCase(apiConfig.getInvokerPackage(), apiConfig.getPrefix(), apiConfig.getVersion()); + + String text = String.format("%s=%s.citrus.extension.%sNamespaceHandler", targetNamespace, invokerPackage, apiConfig.getPrefix()); + + assertThat(getContentOfFile(apiConfig, "spring.handlers")).contains(text); + + // Other specific meta info should be retained + assertThat(getContentOfFile(apiConfig, "spring.handlers")).contains(OTHER_META_FILE_CONTENT); + // Other citrus generated meta info should be deleted + assertThat(getContentOfFile(apiConfig, "spring.handlers")).doesNotContain(OTHER_CITRUS_META_FILE_CONTENT); + } + + private void assertSchemasInSpringSchemas(ApiConfig apiConfig) throws IOException { + + String targetNamespace = replaceDynamicVarsToLowerCase(apiConfig.getTargetXmlnsNamespace(), apiConfig.getPrefix(), apiConfig.getVersion()); + targetNamespace = targetNamespace.replace(":", "\\:"); + 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()); + + // Other specific meta info should be retained assertThat(getContentOfFile(apiConfig, "spring.schemas")).contains(OTHER_META_FILE_CONTENT); + assertThat(getContentOfFile(apiConfig, "spring.schemas")).contains(String.format("%s", text)); + // Other citrus generated meta info should be deleted + assertThat(getContentOfFile(apiConfig, "spring.schemas")).doesNotContain(OTHER_CITRUS_META_FILE_CONTENT); + } + + private void assertApiType(ApiConfig apiConfig) throws IOException { + String text; + switch (apiConfig.getType()) { + case REST -> text = "HttpClient httpClient"; + case SOAP -> text = "WebServiceClient wsClient"; + default -> throw new IllegalArgumentException(String.format("No apiTye set in ApiConfig. Expected one of %s", + stream(ApiType.values()).map(ApiType::toString).collect( + Collectors.joining()))); + } + assertThat(getContentOfFile(apiConfig, "AbstractTestRequest.java")).contains(text); + } + + private void assertTargetNamespace(ApiConfig apiConfig) throws IOException { + assertThat(getContentOfFile(apiConfig, "-api.xsd")).contains( + 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())); + } + + private String getContentOfFile(ApiConfig apiConfig, String fileIdentifier) throws IOException { + String filePathTemplate = getTemplateContaining(fileIdentifier); + String filePath = resolveFilePath(apiConfig, filePathTemplate); + + File file = new File(filePath); + + assertThat(file).exists(); + Path path = Paths.get(filePath); + byte[] bytes = Files.readAllBytes(path); + return new String(bytes, StandardCharsets.UTF_8); + } + + 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))); + } + + @NotNull + private String resolveFilePath(ApiConfig apiConfig, String filePathTemplate) { + + String lowerCasePrefix = apiConfig.getPrefix().toLowerCase(); + char[] prefixCharArray = apiConfig.getPrefix().toCharArray(); + prefixCharArray[0] = Character.toUpperCase(prefixCharArray[0]); + String camelCasePrefix = new String(prefixCharArray); + + String invokerFolder = toFolder( + replaceDynamicVarsToLowerCase(apiConfig.getInvokerPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); + String modelFolder = toFolder( + replaceDynamicVarsToLowerCase(apiConfig.getModelPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); + String requestFolder = toFolder( + replaceDynamicVarsToLowerCase(apiConfig.getApiPackage(), apiConfig.getPrefix(), apiConfig.getVersion())); + String schemaFolder = toFolder( + replaceDynamicVars((String)getField(fixture, "schemaFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); + String generatedSourcesFolder = toFolder( + replaceDynamicVars((String)getField(fixture, "sourceFolder"), apiConfig.getPrefix(), apiConfig.getVersion())); + String generatedResourcesFolder = toFolder( + 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"))); + } + + private String toFolder(String text) { + + if (text == null) { + return ""; + } + + return text.replace(".", "/"); + } + + private TestApiGeneratorMojo fixtureFromPom(String configName) throws Exception { + String goal = "create-test-api"; + + File pomFile = new File(getBasedir(), String.format("src/test/resources/%s/%s", getClass().getSimpleName(), configName + ".xml")); + assertThat(pomFile).exists(); + + MavenProject mavenProject = new CitrusOpenApiGeneratorMavenProjectStub(configName); + + TestApiGeneratorMojo testApiGeneratorMojo = (TestApiGeneratorMojo) lookupMojo(goal, pomFile); + testApiGeneratorMojo.setMavenProject(mavenProject); + testApiGeneratorMojo.setMojoExecution(newMojoExecution(goal)); + + return 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 new file mode 100644 index 0000000000..c63753525f --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/TestApiGeneratorMojoUnitTest.java @@ -0,0 +1,286 @@ +package org.citrusframework.maven.plugin; + +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; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_META_INF_FOLDER; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_MODEL_PACKAGE; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_RESOURCE_FOLDER; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_SCHEMA_FOLDER_TEMPLATE; +import static org.citrusframework.maven.plugin.TestApiGeneratorMojo.DEFAULT_SOURCE_FOLDER; +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; +import java.util.stream.Stream; +import org.apache.maven.model.Build; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.testing.AbstractMojoTestCase; +import org.apache.maven.project.MavenProject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.openapitools.codegen.plugin.CodeGenMojo; + + +/** + * @author Thorsten Schlathoelter + */ +@ExtendWith(MockitoExtension.class) +@SuppressWarnings({"JUnitMalformedDeclaration", "JUnitMixedFramework"}) +class TestApiGeneratorMojoUnitTest extends AbstractMojoTestCase { + + private TestApiGeneratorMojo fixture; + + @Mock + private Build buildMock; + + @Mock + private MavenProject mavenProjectMock; + + @Mock + private MojoExecution mojoExecutionMock; + + @BeforeEach + void beforeEach() { + fixture = new TestApiGeneratorMojo(); + } + + static Stream replaceDynamicVarsInPattern() { + return Stream.of( + arguments("%PREFIX%-aa-%VERSION%", "MyPrefix", "1", false, "MyPrefix-aa-1"), + arguments("%PREFIX%-aa-%VERSION%", "MyPrefix", null, false, "MyPrefix-aa"), + arguments("%PREFIX%/aa/%VERSION%", "MyPrefix", "1", false, "MyPrefix/aa/1"), + arguments("%PREFIX%/aa/%VERSION%", "MyPrefix", null, false, "MyPrefix/aa"), + arguments("%PREFIX%.aa.%VERSION%", "MyPrefix", "1", true, "myprefix.aa.1"), + arguments("%PREFIX%.aa.%VERSION%", "MyPrefix", null, true, "myprefix.aa") + ); + } + + @ParameterizedTest + @MethodSource + void replaceDynamicVarsInPattern(String pattern, String prefix, String version, boolean toLowerCasePrefix, String expectedResult) { + + if (toLowerCasePrefix) { + assertThat( + replaceDynamicVarsToLowerCase(pattern, prefix, version)).isEqualTo(expectedResult); + } else { + assertThat( + replaceDynamicVars(pattern, prefix, version)).isEqualTo(expectedResult); + } + } + + static Stream configureMojo() { + return Stream.of( + arguments("DefaultConfigWithoutVersion", createMinimalApiConfig(null), + createMinimalCodeGenMojoParams( + "schema/xsd", + "org.citrusframework.automation.mydefaultprefix", + "org.citrusframework.automation.mydefaultprefix.model", + "org.citrusframework.automation.mydefaultprefix.api", + "http://www.citrusframework.org/citrus-test-schema/mydefaultprefix-api" + )), + arguments("DefaultConfigWithVersion", createMinimalApiConfig("v1"), + createMinimalCodeGenMojoParams( + "schema/xsd/v1", + "org.citrusframework.automation.mydefaultprefix.v1", + "org.citrusframework.automation.mydefaultprefix.v1.model", + "org.citrusframework.automation.mydefaultprefix.v1.api", + "http://www.citrusframework.org/citrus-test-schema/v1/mydefaultprefix-api" + )), + arguments("CustomConfigWithoutVersion", createFullApiConfig(null), + createCustomCodeGenMojoParams( + "schema/xsd", + "my.mycustomprefix.invoker.package", + "my.mycustomprefix.model.package", + "my.mycustomprefix.api.package", + "myNamespace/citrus-test-schema/mycustomprefix" + )), + arguments("CustomConfigWithVersion", createFullApiConfig("v1"), + createCustomCodeGenMojoParams( + "schema/xsd/v1", + "my.mycustomprefix.v1.invoker.package", + "my.mycustomprefix.v1.model.package", + "my.mycustomprefix.v1.api.package", + "myNamespace/citrus-test-schema/mycustomprefix/v1" + )) + ); + } + + @ParameterizedTest(name = "{0}") + @MethodSource + void configureMojo(String name, ApiConfig apiConfig, CodeGenMojoParams controlParams) throws MojoExecutionException { + doReturn("target").when(buildMock).getDirectory(); + doReturn(buildMock).when(mavenProjectMock).getBuild(); + fixture.setMavenProject(mavenProjectMock); + fixture.setMojoExecution(mojoExecutionMock); + + CodeGenMojo codeGenMojo = fixture.configureCodeGenMojo(apiConfig); + assertThat(getField(codeGenMojo, "project")).isEqualTo(mavenProjectMock); + assertThat(getField(codeGenMojo, "mojo")).isEqualTo(mojoExecutionMock); + assertThat(((File) getField(codeGenMojo, "output"))).hasName(controlParams.output); + assertThat(getField(codeGenMojo, "inputSpec")).isEqualTo(controlParams.source); + assertThat(getField(codeGenMojo, "generateSupportingFiles")).isEqualTo(TRUE); + assertThat(getField(codeGenMojo, "generatorName")).isEqualTo("java-citrus"); + + //noinspection unchecked + assertThat((Map)getField(codeGenMojo, "configOptions")) + .containsExactlyInAnyOrderEntriesOf(controlParams.configOptions); + } + + /** + * Create an {@link ApiConfig} with the minimal configuration, that is required. All other values will be chosen as defaults. + */ + @NotNull + private static ApiConfig createMinimalApiConfig(String version) { + ApiConfig apiConfig = new ApiConfig(); + apiConfig.setPrefix("MyDefaultPrefix"); + apiConfig.setSource("myDefaultSource"); + apiConfig.setVersion(version); + + return apiConfig; + } + + /** + * Create an {@link ApiConfig} with all possible configurations, no defaults. + */ + @NotNull + private static ApiConfig createFullApiConfig(String version) { + ApiConfig apiConfig = new ApiConfig(); + apiConfig.setPrefix("MyCustomPrefix"); + apiConfig.setSource("myCustomSource"); + apiConfig.setApiPackage("my.%PREFIX%.%VERSION%.api.package"); + apiConfig.setInvokerPackage("my.%PREFIX%.%VERSION%.invoker.package"); + apiConfig.setModelPackage("my.%PREFIX%.%VERSION%.model.package"); + apiConfig.setEndpoint("myEndpoint"); + apiConfig.setTargetXmlnsNamespace("myNamespace/citrus-test-schema/%PREFIX%/%VERSION%"); + apiConfig.setUseTags(false); + apiConfig.setType(ApiType.SOAP); + apiConfig.setVersion(version); + + return apiConfig; + } + + @NotNull + private static CodeGenMojoParams createMinimalCodeGenMojoParams(String schemaFolder, String invokerPackage, String modelPackage, String apiPackage, String targetXmlnsNamespace) { + + Map configOptionsControlMap = new HashMap<>(); + configOptionsControlMap.put("prefix", "MyDefaultPrefix"); + configOptionsControlMap.put("generatedSchemaFolder", schemaFolder); + configOptionsControlMap.put("invokerPackage", invokerPackage); + configOptionsControlMap.put("apiPackage", apiPackage); + configOptionsControlMap.put("modelPackage", modelPackage); + configOptionsControlMap.put("resourceFolder", "generated-test-resources"); + configOptionsControlMap.put("sourceFolder", "generated-test-sources"); + configOptionsControlMap.put("apiEndpoint", "mydefaultprefixEndpoint"); + configOptionsControlMap.put("targetXmlnsNamespace", targetXmlnsNamespace); + configOptionsControlMap.put("apiType", "REST"); + configOptionsControlMap.put("useTags", true); + + return new CodeGenMojoParams("target", "myDefaultSource", configOptionsControlMap); + } + + @NotNull + private static CodeGenMojoParams createCustomCodeGenMojoParams(String schemaFolder, String invokerPackage, String modelPackage, String apiPackage, String targetXmlnsNamespace) { + + Map configOptionsControlMap = new HashMap<>(); + configOptionsControlMap.put("prefix", "MyCustomPrefix"); + configOptionsControlMap.put("generatedSchemaFolder", schemaFolder); + configOptionsControlMap.put("invokerPackage", invokerPackage); + configOptionsControlMap.put("modelPackage", modelPackage); + configOptionsControlMap.put("apiPackage", apiPackage); + configOptionsControlMap.put("resourceFolder", "generated-test-resources"); + configOptionsControlMap.put("sourceFolder", "generated-test-sources"); + configOptionsControlMap.put("apiEndpoint", "myEndpoint"); + configOptionsControlMap.put("targetXmlnsNamespace", targetXmlnsNamespace); + configOptionsControlMap.put("apiType", "SOAP"); + configOptionsControlMap.put("useTags", false); + + return new CodeGenMojoParams("target", "myCustomSource", configOptionsControlMap); + } + + @Nested + class ApiConfigTest { + + private ApiConfig configFixture; + + @BeforeEach + void beforeEach() { + configFixture = new ApiConfig(); + } + + @Test + void apiPackagePathDefault() { + assertThat(configFixture.getApiPackage()).isEqualTo(DEFAULT_API_PACKAGE); + } + + @Test + void invokerPackagePathDefault() { + assertThat(configFixture.getInvokerPackage()).isEqualTo(DEFAULT_INVOKER_PACKAGE); + } + + @Test + void modelPackagePathDefault() { + assertThat(configFixture.getModelPackage()).isEqualTo(DEFAULT_MODEL_PACKAGE); + } + + @Test + void targetXmlnsNamespaceDefault() { + assertThat(configFixture.getTargetXmlnsNamespace()).isEqualTo(DEFAULT_TARGET_NAMESPACE_TEMPLATE); + } + + @Test + void endpointDefault() { + configFixture.setPrefix("MyPrefix"); + assertThat(configFixture.qualifiedEndpoint()).isEqualTo("myprefixEndpoint"); + } + + @Test + void apiTypeDefault() { + assertThat(configFixture.getType()).isEqualTo(DEFAULT_API_TYPE); + } + + @Test + void schemaFolderDefault() { + assertThat((String)getField(fixture, "schemaFolder")).isEqualTo(DEFAULT_SCHEMA_FOLDER_TEMPLATE); + } + + @Test + void sourceFolderDefault() { + assertThat((String) getField(fixture, "sourceFolder")).isEqualTo(DEFAULT_SOURCE_FOLDER); + } + + @Test + void resourceFolderDefault() { + assertThat((String) getField(fixture, "resourceFolder")).isEqualTo(DEFAULT_RESOURCE_FOLDER); + } + + @Test + void metaInfFolderDefault() { + assertThat((String) getField(fixture, "metaInfFolder")).isEqualTo(DEFAULT_META_INF_FOLDER); + } + } + + private record CodeGenMojoParams(String output, String source, Map configOptions) { + + } +} diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/stubs/CitrusOpenApiGeneratorMavenProjectStub.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/stubs/CitrusOpenApiGeneratorMavenProjectStub.java new file mode 100644 index 0000000000..94345c88ad --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/java/org/citrusframework/maven/plugin/stubs/CitrusOpenApiGeneratorMavenProjectStub.java @@ -0,0 +1,42 @@ +package org.citrusframework.maven.plugin.stubs; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import org.apache.maven.model.Build; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; +import org.codehaus.plexus.PlexusTestCase; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +public class CitrusOpenApiGeneratorMavenProjectStub extends MavenProjectStub { + + private final String config; + + public CitrusOpenApiGeneratorMavenProjectStub(String config) { + this.config = config; + initModel(); + initBuild(); + } + + private void initBuild() { + Build build = new Build(); + build.setDirectory(getBasedir() + "/target"); + build.setSourceDirectory(getBasedir() + "/src"); + setBuild(build); + } + + private void initModel() { + MavenXpp3Reader reader = new MavenXpp3Reader(); + try { + setModel(reader.read(new FileReader("pom.xml"))); + } catch (IOException | XmlPullParserException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public File getBasedir() { + return new File(PlexusTestCase.getBasedir() + "/target/" + config); + } +} diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-config.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-config.xml new file mode 100644 index 0000000000..97fcd85dd4 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-config.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + full-config + + + + + citrus-test-api-generator-maven-plugin + + + + org.mypackage.%PREFIX%.api + myEndpoint + org.mypackage.%PREFIX%.invoker + org.mypackage.%PREFIX%.model + Full + api/test-api.yml + "http://myNamespace" + + + generated-sources-mod + generated-resources-mod + myschema/xsd + main-mod/resources-mod/META-INF-MOD + + + + + create-test-api + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-with-version-config.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-with-version-config.xml new file mode 100644 index 0000000000..401ea5fa50 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-full-with-version-config.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + full-config + + + + + citrus-test-api-generator-maven-plugin + + + + org.mypackage.%PREFIX%.api + myEndpoint + org.mypackage.%PREFIX%.invoker + org.mypackage.%PREFIX%.model + FullWithVersion + api/test-api.yml + "http://myNamespace" + v1 + + + generated-sources-mod + generated-resources-mod + myschema/xsd + main-mod/resources-mod/META-INF-MOD + + + + + create-test-api + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-config.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-config.xml new file mode 100644 index 0000000000..ffb294db1d --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-config.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + minimal-config + + + + + citrus-test-api-generator-maven-plugin + + + + Minimal + api/test-api.yml + + + + + + + create-test-api + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-with-version-config.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-with-version-config.xml new file mode 100644 index 0000000000..90ee2d6e4d --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-with-version-config.xml @@ -0,0 +1,33 @@ + + 4.0.0 + + minimal-config + + + + + citrus-test-api-generator-maven-plugin + + + + MinimalWithVersion + api/test-api.yml + v2 + + + + + + + create-test-api + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-prefix.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-prefix.xml new file mode 100644 index 0000000000..fbdd07bd0d --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-prefix.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + missing-prefix + + + + + citrus-test-api-generator-maven-plugin + + + + mySource + + + + + + + create-test-api + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-source.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-source.xml new file mode 100644 index 0000000000..03f9f49074 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-missing-source.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + missing-source + + + + + citrus-test-api-generator-maven-plugin + + + + MissingSource + + + + + + + create-test-api + + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-multi-config.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-multi-config.xml new file mode 100644 index 0000000000..f71d499ba4 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-multi-config.xml @@ -0,0 +1,40 @@ + + 4.0.0 + + multi-config + + + + + citrus-test-api-generator-maven-plugin + + + + Multi1 + api/test-api.yml + + + Multi2 + api/test-api.yml + + + Multi3 + api/test-api.yml + + + + + + + create-test-api + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-soap-config.xml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-soap-config.xml new file mode 100644 index 0000000000..434de65370 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-soap-config.xml @@ -0,0 +1,33 @@ + + 4.0.0 + + soap-config + + + + + citrus-test-api-generator-maven-plugin + + + + Minimal + api/test-api.yml + SOAP + + + + + + + create-test-api + + + + + + + diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/test-api.yml b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/test-api.yml new file mode 100644 index 0000000000..eea3f01335 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/test-api.yml @@ -0,0 +1,125 @@ +openapi: 3.0.3 +info: + title: Schema Test API + version: 1.0.0 + description: | + A very simple test OpenAPI specification that is compliant with the random response generator (e.g. only contains responses of + media-type `application/json`). + +servers: + - url: http://localhost:9000/services/rest/ping/v1 + - url: http://localhost:9000/ping/v1 + +paths: + /ping/{id}: + put: + tags: + - ping + summary: Do the ping + operationId: doPing + parameters: + - name: id + in: path + description: Id to ping + required: true + explode: true + schema: + type: integer + format: int64 + - name: q1 + in: query + description: Some queryParameter + required: true + explode: true + schema: + type: integer + format: int64 + - name: api_key + in: header + required: true + schema: + type: string + requestBody: + description: ping data + content: + application/json: + schema: + $ref: '#/components/schemas/PingReqType' + required: true + responses: + 200: + description: successful operation + headers: + Ping-Time: + required: false + description: response time + schema: + type: integer + format: int64 + content: + application/json: + schema: + $ref: '#/components/schemas/PingRespType' + 201: + description: successful operation + headers: + Ping-Time: + required: false + description: response time + schema: + type: integer + format: int64 + content: + application/xml: + schema: + $ref: '#/components/schemas/PingRespType' + application/json: + schema: + $ref: '#/components/schemas/PingRespType' + 400: + description: Some error + content: + text/plain: + schema: + type: string + /pung/{id}: + get: + tags: + - pung + summary: Do the pung + operationId: doPung + parameters: + - name: id + in: path + description: Id to pung + required: true + explode: true + schema: + type: integer + format: int64 + responses: + 200: + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/PingRespType' + 400: + description: Invalid status value + content: {} +components: + schemas: + PingReqType: + type: object + properties: + id: + type: integer + format: int64 + PingRespType: + type: object + properties: + id: + type: integer + format: int64 + value: + type: string diff --git a/test-api-generator/pom.xml b/test-api-generator/pom.xml new file mode 100644 index 0000000000..f6298e3e11 --- /dev/null +++ b/test-api-generator/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + + org.citrusframework + citrus + 4.3.0-SNAPSHOT + ../pom.xml + + + citrus-test-api-generator + Citrus :: Test API Generator + Citrus Test API Generator + pom + + + true + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + + + citrus-test-api-generator-core + citrus-test-api-generator-maven-plugin + + + + diff --git a/validation/citrus-validation-text/src/main/java/org/citrusframework/validation/text/PlainTextMessageValidator.java b/validation/citrus-validation-text/src/main/java/org/citrusframework/validation/text/PlainTextMessageValidator.java index d7152db132..8fa005c685 100644 --- a/validation/citrus-validation-text/src/main/java/org/citrusframework/validation/text/PlainTextMessageValidator.java +++ b/validation/citrus-validation-text/src/main/java/org/citrusframework/validation/text/PlainTextMessageValidator.java @@ -16,9 +16,12 @@ package org.citrusframework.validation.text; +import static java.lang.Boolean.parseBoolean; +import static java.lang.Integer.parseInt; +import static org.citrusframework.message.MessagePayloadUtils.normalizeWhitespace; + import java.util.regex.Matcher; import java.util.regex.Pattern; - import org.citrusframework.CitrusSettings; import org.citrusframework.context.TestContext; import org.citrusframework.exceptions.ValidationException; @@ -29,9 +32,6 @@ import org.citrusframework.validation.context.ValidationContext; import org.citrusframework.validation.matcher.ValidationMatcherUtils; -import static java.lang.Boolean.parseBoolean; -import static java.lang.Integer.parseInt; - /** * Plain text validator using simple String comparison. * @@ -59,8 +59,8 @@ public void validateMessage(Message receivedMessage, Message controlMessage, Tes logger.debug("Start text message validation"); try { - String resultValue = normalizeWhitespace(receivedMessage.getPayload(String.class).trim()); - String controlValue = normalizeWhitespace(context.replaceDynamicContentInString(controlMessage.getPayload(String.class).trim())); + String resultValue = normalizeWhitespace(receivedMessage.getPayload(String.class).trim(), ignoreWhitespace, ignoreNewLineType); + String controlValue = normalizeWhitespace(context.replaceDynamicContentInString(controlMessage.getPayload(String.class).trim()), ignoreWhitespace, ignoreNewLineType); controlValue = processIgnoreStatements(controlValue, resultValue); controlValue = processVariableStatements(controlValue, resultValue, context); @@ -181,38 +181,6 @@ private void validateText(String receivedMessagePayload, String controlMessagePa } } - /** - * Normalize whitespace characters if appropriate. Based on system property settings this method normalizes - * new line characters exclusively or filters all whitespaces such as double whitespaces and new lines. - * - * @param payload - * @return - */ - private String normalizeWhitespace(String payload) { - if (ignoreWhitespace) { - StringBuilder result = new StringBuilder(); - boolean lastWasSpace = true; - for (int i = 0; i < payload.length(); i++) { - char c = payload.charAt(i); - if (Character.isWhitespace(c)) { - if (!lastWasSpace) { - result.append(' '); - } - lastWasSpace = true; - } else { - result.append(c); - lastWasSpace = false; - } - } - return result.toString().trim(); - } - - if (ignoreNewLineType) { - return payload.replaceAll("\\r(\\n)?", "\n"); - } - - return payload; - } @Override public boolean supportsMessageType(String messageType, Message message) { From 8139d9cf6b5d92a7bed7e7588e928a1e51fbc52d Mon Sep 17 00:00:00 2001 From: Thorsten Schlathoelter Date: Tue, 21 May 2024 06:24:16 +0200 Subject: [PATCH 02/16] chore: remove author annotations --- .../main/java/org/citrusframework/TestCaseRunnerProvider.java | 1 - .../org/citrusframework/variable/SegmentVariableExtractor.java | 2 -- .../variable/SegmentVariableExtractorRegistry.java | 2 -- .../citrusframework/variable/VariableExpressionIterator.java | 2 -- .../test/java/org/citrusframework/spi/mocks/FooWithParams.java | 3 --- .../test/java/org/citrusframework/spi/mocks/SingletonFoo.java | 3 --- .../main/java/org/citrusframework/TestCaseRunnerFactory.java | 1 - .../citrusframework/common/SpringXmlTestLoaderConfigurer.java | 1 - .../config/CitrusConversionServiceConfiguration.java | 1 - .../config/handler/CitrusTestCaseNamespaceHandler.java | 2 +- .../citrusframework/config/xml/BaseTestCaseMetaInfoParser.java | 1 - .../org/citrusframework/config/xml/BaseTestCaseParser.java | 2 +- .../main/java/org/citrusframework/io/CitrusResourceEditor.java | 1 - .../citrusframework/config/xml/CustomTestCaseParserTest.java | 3 --- .../integration/common/SpringXmlTestLoaderIT.java | 3 --- .../ws/integration/SendSoapMessageWithSchemaValidationIT.java | 1 - .../junit/integration/SpringXmlTestLoaderIT.java | 3 --- .../jupiter/integration/spring/SpringXmlTestLoader_IT.java | 3 --- .../src/main/java/org/citrusframework/testng/TestNGUtils.java | 3 --- .../citrusframework/openapi/generator/JavaCitrusCodegen.java | 3 --- .../org/citrusframework/maven/plugin/CodeGenMojoWrapper.java | 2 -- .../citrusframework/maven/plugin/SpringMetaFileGenerator.java | 2 -- .../org/citrusframework/maven/plugin/TestApiGeneratorMojo.java | 2 -- .../maven/plugin/TestApiGeneratorMojoIntegrationTest.java | 3 --- .../maven/plugin/TestApiGeneratorMojoUnitTest.java | 3 --- .../citrusframework/json/JsonPathSegmentVariableExtractor.java | 3 --- .../org/citrusframework/xml/XpathSegmentVariableExtractor.java | 3 --- 27 files changed, 2 insertions(+), 57 deletions(-) diff --git a/core/citrus-api/src/main/java/org/citrusframework/TestCaseRunnerProvider.java b/core/citrus-api/src/main/java/org/citrusframework/TestCaseRunnerProvider.java index 6d647477b6..b7b8646357 100644 --- a/core/citrus-api/src/main/java/org/citrusframework/TestCaseRunnerProvider.java +++ b/core/citrus-api/src/main/java/org/citrusframework/TestCaseRunnerProvider.java @@ -21,7 +21,6 @@ /** * Interface for providing TestCaseRunner. * - * @author Thorsten Schlathoelter * @since 4.0 */ public interface TestCaseRunnerProvider { diff --git a/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractor.java b/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractor.java index 0783e558aa..ce32fe3c06 100644 --- a/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractor.java +++ b/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractor.java @@ -20,8 +20,6 @@ /** * Class extracting values of segments of VariableExpressions. - * - * @author Thorsten Schlathoelter */ public interface SegmentVariableExtractor { diff --git a/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractorRegistry.java b/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractorRegistry.java index 095bee5e4c..0f4b6c22c8 100644 --- a/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractorRegistry.java +++ b/core/citrus-api/src/main/java/org/citrusframework/variable/SegmentVariableExtractorRegistry.java @@ -37,8 +37,6 @@ * the extractors managed by this registry in order to access variable content from the TestContext expressed by variable expressions. *

* Registry provides all known {@link SegmentVariableExtractor}s. - * - * @author Thorsten Schlathoelter */ public class SegmentVariableExtractorRegistry { diff --git a/core/citrus-api/src/main/java/org/citrusframework/variable/VariableExpressionIterator.java b/core/citrus-api/src/main/java/org/citrusframework/variable/VariableExpressionIterator.java index a955c1a6c3..01dc2b02bd 100644 --- a/core/citrus-api/src/main/java/org/citrusframework/variable/VariableExpressionIterator.java +++ b/core/citrus-api/src/main/java/org/citrusframework/variable/VariableExpressionIterator.java @@ -39,8 +39,6 @@ *

  • the third element of the persons property of the variable retrieved in the previous step
  • *
  • the first element of the firstnames property of the property retrieved in the previous step
  • * - * - * @author Thorsten Schlathoelter */ public class VariableExpressionIterator implements Iterator { diff --git a/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/FooWithParams.java b/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/FooWithParams.java index cd14f16866..d1abb46082 100644 --- a/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/FooWithParams.java +++ b/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/FooWithParams.java @@ -16,9 +16,6 @@ package org.citrusframework.spi.mocks; -/** - * @author Thorsten Schlathoelter - */ public class FooWithParams { public FooWithParams(int intParam, short shortParam, double doubleParam, float floatParam, diff --git a/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/SingletonFoo.java b/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/SingletonFoo.java index 451ea40325..b20b7c6acc 100644 --- a/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/SingletonFoo.java +++ b/core/citrus-api/src/test/java/org/citrusframework/spi/mocks/SingletonFoo.java @@ -16,9 +16,6 @@ package org.citrusframework.spi.mocks; -/** - * @author Thorsten Schlathoelter - */ public class SingletonFoo { public static final SingletonFoo INSTANCE = new SingletonFoo(); diff --git a/core/citrus-base/src/main/java/org/citrusframework/TestCaseRunnerFactory.java b/core/citrus-base/src/main/java/org/citrusframework/TestCaseRunnerFactory.java index 85e2c7fcb9..ed03364c69 100644 --- a/core/citrus-base/src/main/java/org/citrusframework/TestCaseRunnerFactory.java +++ b/core/citrus-base/src/main/java/org/citrusframework/TestCaseRunnerFactory.java @@ -35,7 +35,6 @@ * The specified file must define the type of {@link TestCaseRunnerProvider} responsible for * delivering the custom test case runner. * - * @author Thorsten Schlathoelter * @since 4.0 * @see TestCaseRunnerProvider */ diff --git a/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java b/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java index 1dc80fab73..e40464d6f1 100644 --- a/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java +++ b/core/citrus-spring/src/main/java/org/citrusframework/common/SpringXmlTestLoaderConfigurer.java @@ -26,7 +26,6 @@ * loading SpringXmlTest. * * - * @author Thorsten Schlathoelter * @since 4.0 * @see SpringXmlTestLoader */ diff --git a/core/citrus-spring/src/main/java/org/citrusframework/config/CitrusConversionServiceConfiguration.java b/core/citrus-spring/src/main/java/org/citrusframework/config/CitrusConversionServiceConfiguration.java index 1a3d5e6234..cc3762824a 100644 --- a/core/citrus-spring/src/main/java/org/citrusframework/config/CitrusConversionServiceConfiguration.java +++ b/core/citrus-spring/src/main/java/org/citrusframework/config/CitrusConversionServiceConfiguration.java @@ -27,7 +27,6 @@ * This configuration is necessary only when using Spring without Spring Boot. Spring Boot includes a standard conversion service * that automatically detects and uses relevant converters by default. * - * @author Thorsten Schlathoelter * @since 4.0 */ @Configuration diff --git a/core/citrus-spring/src/main/java/org/citrusframework/config/handler/CitrusTestCaseNamespaceHandler.java b/core/citrus-spring/src/main/java/org/citrusframework/config/handler/CitrusTestCaseNamespaceHandler.java index 060c108c86..cfaa6a7c7f 100644 --- a/core/citrus-spring/src/main/java/org/citrusframework/config/handler/CitrusTestCaseNamespaceHandler.java +++ b/core/citrus-spring/src/main/java/org/citrusframework/config/handler/CitrusTestCaseNamespaceHandler.java @@ -26,7 +26,7 @@ * Namespace handler registers bean definition parser * for Citrus testcase schema elements. * - * @author Christoph Deppisch, Thorsten Schlathoelter + * @author Christoph Deppisch * @since 2007 */ public class CitrusTestCaseNamespaceHandler extends NamespaceHandlerSupport { diff --git a/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseMetaInfoParser.java b/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseMetaInfoParser.java index b56eb3dc79..8652bb0a6b 100644 --- a/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseMetaInfoParser.java +++ b/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseMetaInfoParser.java @@ -35,7 +35,6 @@ * * @param * - * @author Thorsten Schlathoelter */ public abstract class BaseTestCaseMetaInfoParser implements BeanDefinitionParser { diff --git a/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseParser.java b/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseParser.java index 93d7bec510..60711635e9 100644 --- a/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseParser.java +++ b/core/citrus-spring/src/main/java/org/citrusframework/config/xml/BaseTestCaseParser.java @@ -39,7 +39,7 @@ * * @param * - * @author Christop Deppisch, Thorsten Schlathoelter + * @author Christop Deppisch */ @SuppressWarnings("PMD.AvoidDuplicateLiterals") public class BaseTestCaseParser implements BeanDefinitionParser { diff --git a/core/citrus-spring/src/main/java/org/citrusframework/io/CitrusResourceEditor.java b/core/citrus-spring/src/main/java/org/citrusframework/io/CitrusResourceEditor.java index 44f8c04203..baf7da0a26 100644 --- a/core/citrus-spring/src/main/java/org/citrusframework/io/CitrusResourceEditor.java +++ b/core/citrus-spring/src/main/java/org/citrusframework/io/CitrusResourceEditor.java @@ -33,7 +33,6 @@ * Delegates to a {@link Resources} to do the {@link Resource} creation. The implementation follows the * implementation of {@link org.springframework.core.io.ResourceEditor}. * - * @author Thorsten Schlathoelter * @since 4.0 */ public class CitrusResourceEditor extends PropertyEditorSupport { diff --git a/core/citrus-spring/src/test/java/org/citrusframework/config/xml/CustomTestCaseParserTest.java b/core/citrus-spring/src/test/java/org/citrusframework/config/xml/CustomTestCaseParserTest.java index 2a2fa2caa4..f2afae6854 100644 --- a/core/citrus-spring/src/test/java/org/citrusframework/config/xml/CustomTestCaseParserTest.java +++ b/core/citrus-spring/src/test/java/org/citrusframework/config/xml/CustomTestCaseParserTest.java @@ -31,9 +31,6 @@ import java.io.*; -/** - * @author Thorsten Schlatoelter - */ public class CustomTestCaseParserTest extends AbstractActionParserTest { @BeforeClass diff --git a/endpoints/citrus-spring-integration/src/test/java/org/citrusframework/integration/common/SpringXmlTestLoaderIT.java b/endpoints/citrus-spring-integration/src/test/java/org/citrusframework/integration/common/SpringXmlTestLoaderIT.java index a2f5768fcd..21ad578743 100644 --- a/endpoints/citrus-spring-integration/src/test/java/org/citrusframework/integration/common/SpringXmlTestLoaderIT.java +++ b/endpoints/citrus-spring-integration/src/test/java/org/citrusframework/integration/common/SpringXmlTestLoaderIT.java @@ -32,9 +32,6 @@ import org.testng.annotations.Test; import org.w3c.dom.Element; -/** - * @author Thorsten Schlathoelter - */ @SpringXmlTestLoaderConfiguration( parserConfigurations = { @BeanDefinitionParserConfiguration(name = "testcase", parser = SpringXmlTestLoaderIT.CustomTestCaseParser.class), diff --git a/endpoints/citrus-ws/src/test/java/org/citrusframework/ws/integration/SendSoapMessageWithSchemaValidationIT.java b/endpoints/citrus-ws/src/test/java/org/citrusframework/ws/integration/SendSoapMessageWithSchemaValidationIT.java index 9d3614d201..2c4934d156 100644 --- a/endpoints/citrus-ws/src/test/java/org/citrusframework/ws/integration/SendSoapMessageWithSchemaValidationIT.java +++ b/endpoints/citrus-ws/src/test/java/org/citrusframework/ws/integration/SendSoapMessageWithSchemaValidationIT.java @@ -22,7 +22,6 @@ import org.testng.annotations.Test; /** - * @author Thorsten Schlathoelter * @since 3.1.3 */ public class SendSoapMessageWithSchemaValidationIT extends TestNGCitrusSpringSupport { diff --git a/runtime/citrus-junit/src/test/java/org/citrusframework/junit/integration/SpringXmlTestLoaderIT.java b/runtime/citrus-junit/src/test/java/org/citrusframework/junit/integration/SpringXmlTestLoaderIT.java index ec6f829907..f86c179fb2 100644 --- a/runtime/citrus-junit/src/test/java/org/citrusframework/junit/integration/SpringXmlTestLoaderIT.java +++ b/runtime/citrus-junit/src/test/java/org/citrusframework/junit/integration/SpringXmlTestLoaderIT.java @@ -32,9 +32,6 @@ import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; -/** - * @author Thorsten Schlathoelter - */ @SpringXmlTestLoaderConfiguration( parserConfigurations = { @BeanDefinitionParserConfiguration(name = "testcase", parser = SpringXmlTestLoaderIT.CustomTestCaseParser.class), diff --git a/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java b/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java index 9567b9a83f..1058b107bb 100644 --- a/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java +++ b/runtime/citrus-junit5/src/test/java/org/citrusframework/junit/jupiter/integration/spring/SpringXmlTestLoader_IT.java @@ -39,9 +39,6 @@ import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; -/** - * @author Thorsten Schlathoelter - */ @CitrusSpringSupport @ContextConfiguration(classes = {CitrusSpringConfig.class, CustomConfiguration.class}) @SpringXmlTestLoaderConfiguration( diff --git a/runtime/citrus-testng/src/main/java/org/citrusframework/testng/TestNGUtils.java b/runtime/citrus-testng/src/main/java/org/citrusframework/testng/TestNGUtils.java index 79278dd0ea..2234f9ea93 100644 --- a/runtime/citrus-testng/src/main/java/org/citrusframework/testng/TestNGUtils.java +++ b/runtime/citrus-testng/src/main/java/org/citrusframework/testng/TestNGUtils.java @@ -18,9 +18,6 @@ import org.testng.SkipException; -/** - * @author Thorsten Schlathoelter - */ public class TestNGUtils { /** 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 7aa18331b5..b86f40dbe3 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 @@ -17,9 +17,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - * @author Thorsten Schlathoelter - */ public class JavaCitrusCodegen extends AbstractJavaCodegen { private static final Logger logger = LoggerFactory.getLogger(JavaCitrusCodegen.class); 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 ea92db1efb..dddd8da625 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 @@ -14,8 +14,6 @@ /** * Wrapper class that uses reflection to expose several properties of the {@link CodeGenMojo} for explicit assignment. - * - * @author Thorsten Schlathoelter */ public class CodeGenMojoWrapper extends CodeGenMojo { diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java index 23ae7b161f..b793b62346 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java @@ -26,8 +26,6 @@ * {@link TestApiGeneratorMojo#CITRUS_TEST_SCHEMA}. *

    * - * @author Thorsten Schlathoelter - * */ public class SpringMetaFileGenerator { diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java index d5b2b1cd53..fce0a54755 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java @@ -36,8 +36,6 @@ * of creating robust integration tests. *

    * - * @author Thorsten Schlathoelter - * */ @Mojo( name = "create-test-api", 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 d4bcc740b9..4cccffe386 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 @@ -38,9 +38,6 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -/** - * @author Thorsten Schlathoelter - */ class TestApiGeneratorMojoIntegrationTest extends AbstractMojoTestCase { public static final String OTHER_META_FILE_CONTENT = "somenamespace=somevalue"; 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 c63753525f..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 @@ -42,9 +42,6 @@ import org.openapitools.codegen.plugin.CodeGenMojo; -/** - * @author Thorsten Schlathoelter - */ @ExtendWith(MockitoExtension.class) @SuppressWarnings({"JUnitMalformedDeclaration", "JUnitMixedFramework"}) class TestApiGeneratorMojoUnitTest extends AbstractMojoTestCase { diff --git a/validation/citrus-validation-json/src/main/java/org/citrusframework/json/JsonPathSegmentVariableExtractor.java b/validation/citrus-validation-json/src/main/java/org/citrusframework/json/JsonPathSegmentVariableExtractor.java index d314480ca5..5111007448 100644 --- a/validation/citrus-validation-json/src/main/java/org/citrusframework/json/JsonPathSegmentVariableExtractor.java +++ b/validation/citrus-validation-json/src/main/java/org/citrusframework/json/JsonPathSegmentVariableExtractor.java @@ -24,9 +24,6 @@ import org.citrusframework.variable.SegmentVariableExtractorRegistry; import org.citrusframework.variable.VariableExpressionSegmentMatcher; -/** - * @author Thorsten Schlathoelter - */ public class JsonPathSegmentVariableExtractor extends SegmentVariableExtractorRegistry.AbstractSegmentVariableExtractor { @Override diff --git a/validation/citrus-validation-xml/src/main/java/org/citrusframework/xml/XpathSegmentVariableExtractor.java b/validation/citrus-validation-xml/src/main/java/org/citrusframework/xml/XpathSegmentVariableExtractor.java index 0e0ee46a6b..f4c89365d5 100644 --- a/validation/citrus-validation-xml/src/main/java/org/citrusframework/xml/XpathSegmentVariableExtractor.java +++ b/validation/citrus-validation-xml/src/main/java/org/citrusframework/xml/XpathSegmentVariableExtractor.java @@ -34,9 +34,6 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -/** - * @author Thorsten Schlathoelter - */ public class XpathSegmentVariableExtractor extends SegmentVariableExtractorRegistry.AbstractSegmentVariableExtractor { /** From 0b59743c1dd1d8ee97bc31463b139e3fbc685c70 Mon Sep 17 00:00:00 2001 From: Thorsten Schlathoelter Date: Tue, 21 May 2024 06:57:50 +0200 Subject: [PATCH 03/16] chore: add license to new files --- .../citrusframework/testapi/GeneratedApi.java | 16 ++++++++++++++++ .../testapi/GeneratedApiRequest.java | 16 ++++++++++++++++ .../ApiActionBuilderCustomizerService.java | 16 ++++++++++++++++ .../openapi/generator/JavaCitrusCodegen.java | 16 ++++++++++++++++ .../SimpleWsdlToOpenApiTransformer.java | 16 ++++++++++++++++ .../WsdlToOpenApiTransformationException.java | 16 ++++++++++++++++ .../main/resources/java-citrus/api.mustache | 16 ++++++++++++++++ .../resources/java-citrus/api_soap.mustache | 18 +++++++++++++++++- .../java-citrus/bean_configuration.mustache | 18 +++++++++++++++++- .../bean_definition_parser.mustache | 18 +++++++++++++++++- .../java-citrus/namespace_handler.mustache | 18 +++++++++++++++++- .../main/resources/java-citrus/schema.mustache | 16 ++++++++++++++++ .../resources/java-citrus/schema_soap.mustache | 16 ++++++++++++++++ .../resources/java-citrus/test_base.mustache | 16 ++++++++++++++++ .../java-citrus/test_base_soap.mustache | 18 +++++++++++++++++- .../maven/plugin/CodeGenMojoWrapper.java | 16 ++++++++++++++++ .../maven/plugin/SpringMetaFileGenerator.java | 16 ++++++++++++++++ .../maven/plugin/TestApiGeneratorMojo.java | 16 ++++++++++++++++ 18 files changed, 293 insertions(+), 5 deletions(-) diff --git a/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java index a3d6bfdad2..a37867188c 100644 --- a/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java +++ b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApi.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.testapi; import java.util.Map; diff --git a/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java index 5b519ac118..1b86cc29b0 100644 --- a/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java +++ b/core/citrus-api/src/main/java/org/citrusframework/testapi/GeneratedApiRequest.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.testapi; /** diff --git a/core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java b/core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java index 2b77d97f7b..3231b622af 100644 --- a/core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java +++ b/core/citrus-base/src/main/java/org/citrusframework/testapi/ApiActionBuilderCustomizerService.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.testapi; import org.citrusframework.TestAction; 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 b86f40dbe3..a1c0de6e4c 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 @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.openapi.generator; import static java.util.Arrays.asList; 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 68d7bf3579..fc7487aff4 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 @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.openapi.generator; import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java index fd845194bf..dd5946b512 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java +++ b/test-api-generator/citrus-test-api-generator-core/src/main/java/org/citrusframework/openapi/generator/exception/WsdlToOpenApiTransformationException.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.openapi.generator.exception; public class WsdlToOpenApiTransformationException extends Exception { 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 5d90d5f80c..d384833fec 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 @@ -1,3 +1,19 @@ +/* + * 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 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 d39d1aff12..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,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 d4b8dd2e01..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,20 @@ -/** +/* + * 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 * ================================================== 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 4e0957f1af..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,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 ad260e35ba..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,20 @@ -/** +/* + * 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 * ================================================== diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache index faf2807dd1..1beddbdebc 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema.mustache @@ -1,4 +1,20 @@ + + diff --git a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache index 877bdc1884..096cdc9392 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache +++ b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/schema_soap.mustache @@ -1,4 +1,20 @@ + + 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 f3566c6716..56a31fe7b3 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,3 +1,19 @@ +/* + * 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 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 051485041e..d688a453b8 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,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 dddd8da625..513f9ace80 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 @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.maven.plugin; import static org.citrusframework.openapi.generator.JavaCitrusCodegen.CODEGEN_NAME; diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java index b793b62346..2e1cdd2e0f 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/SpringMetaFileGenerator.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.maven.plugin; import static java.lang.String.format; diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java index fce0a54755..aeee254011 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java @@ -1,3 +1,19 @@ +/* + * 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. + */ + package org.citrusframework.maven.plugin; import static org.citrusframework.openapi.generator.JavaCitrusCodegen.API_ENDPOINT; From cd9aedacc4c3842503c86cbcb53575b1e145ee53 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Wed, 5 Jun 2024 08:50:06 +0200 Subject: [PATCH 04/16] chore: cleanup generator tests --- .../resources/java-citrus/test_base.mustache | 4 ++-- .../java-citrus/test_base_soap.mustache | 4 ++-- .../MultipartTestAbstractTestRequest.java | 18 +++++++++++++++++- .../MultipartTestBeanDefinitionParser.java | 18 +++++++++++++++++- .../MultipartTestNamespaceHandler.java | 18 +++++++++++++++++- .../request/MultiparttestControllerApi.java | 16 ++++++++++++++++ .../spring/MultipartTestBeanConfiguration.java | 18 +++++++++++++++++- .../citrus/PetStoreAbstractTestRequest.java | 18 +++++++++++++++++- .../citrus/PetStoreBeanDefinitionParser.java | 18 +++++++++++++++++- .../extension/PetStoreNamespaceHandler.java | 18 +++++++++++++++++- .../rest/petstore/request/PetApi.java | 16 ++++++++++++++++ .../rest/petstore/request/StoreApi.java | 16 ++++++++++++++++ .../rest/petstore/request/UserApi.java | 16 ++++++++++++++++ .../spring/PetStoreBeanConfiguration.java | 18 +++++++++++++++++- .../OpenApiFromWsdlAbstractTestRequest.java | 18 +++++++++++++++++- .../OpenApiFromWsdlBeanDefinitionParser.java | 18 +++++++++++++++++- .../OpenApiFromWsdlNamespaceHandler.java | 18 +++++++++++++++++- .../request/BookServiceSoapApi.java | 18 +++++++++++++++++- .../OpenApiFromWsdlBeanConfiguration.java | 18 +++++++++++++++++- 19 files changed, 289 insertions(+), 17 deletions(-) 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 56a31fe7b3..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 @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** + +/** * ================================================== * GENERATED CLASS, ALL CHANGES WILL BE LOST * ================================================== 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 d688a453b8..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 @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - /** + +/** * ================================================== * GENERATED CLASS, ALL CHANGES WILL BE LOST * ================================================== 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 index d35d8934bf..478a8e8742 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index d9730d0d42..1ca5480c9e 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index 0d81b2d521..9f36ba2472 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index 36d2bca5f4..bde720eb16 100644 --- 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 @@ -1,3 +1,19 @@ +/* + * 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 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 index 090a9bfb7e..d93b11f104 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index 9ff2150d21..1e9282b44b 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index 32920fb8ef..b52f5b5f42 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index af5b731084..84de90fb2c 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index a014b1c53f..0eb28542e6 100644 --- 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 @@ -1,3 +1,19 @@ +/* + * 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 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 index 406f97f2f9..e8062b4fb0 100644 --- 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 @@ -1,3 +1,19 @@ +/* + * 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 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 index 01f4558954..d52e4351f1 100644 --- 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 @@ -1,3 +1,19 @@ +/* + * 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 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 index cb89458c0b..bf6750e796 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index 8acda64ca3..4e493250fc 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index 1c72d58016..e59e1bc033 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index 04773ddb99..b844e884ca 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index dd84878034..f253418eec 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== 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 index e462cae9f8..919d03669b 100644 --- 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 @@ -1,4 +1,20 @@ -/** +/* + * 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 * ================================================== From dcde8e7b9d9bb5af32fba6f35f770a110fd11f7b Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Wed, 5 Jun 2024 08:56:46 +0200 Subject: [PATCH 05/16] chore: improve OpenApi example --- src/manual/connector-openapi.adoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/manual/connector-openapi.adoc b/src/manual/connector-openapi.adoc index b89f3889c2..c0e24a1c8a 100644 --- a/src/manual/connector-openapi.adoc +++ b/src/manual/connector-openapi.adoc @@ -130,6 +130,9 @@ private final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( @CitrusTest public void openApiClientTest() { + // Define variables to set request parameters. + // Note that the path-param in `petstore-v3.yaml` is named `petId` + variable("petId", "1001"); when(openapi(petstoreSpec) .client(httpClient) .send("getPetById")); @@ -146,6 +149,9 @@ public void openApiClientTest() { + + @@ -241,6 +247,9 @@ private final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( @CitrusTest public void openApiClientTest() { + // Define variables to set request parameters. + // Note that the path-param in `petstore-v3.yaml` is named `petId` + variable("petId", "1001"); when(openapi(petstoreSpec) .server(httpServer) .receive("addPet")); @@ -257,6 +266,9 @@ public void openApiClientTest() { + + From 8bcdbf9ffada32c197b095cbc20227dbe0bb27bc Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Wed, 5 Jun 2024 10:53:00 +0200 Subject: [PATCH 06/16] chore: add todo-readme --- test-api-generator/readme.md | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 test-api-generator/readme.md diff --git a/test-api-generator/readme.md b/test-api-generator/readme.md new file mode 100644 index 0000000000..9176328090 --- /dev/null +++ b/test-api-generator/readme.md @@ -0,0 +1,80 @@ +# TAT-1291 + +## Possible solution + +```java + +@CitrusTest +public void getPetById() { + // 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() + .getPetById() + .withPetId("1001") + .send() + .fork(true)); +} +``` + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` From 4668544eef73b978e1914c1a8251be9fb731bc22 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Mon, 10 Jun 2024 07:47:26 +0200 Subject: [PATCH 07/16] feature: first fluent java implementation (wip) --- .../OpenApiClientRequestActionBuilder.java | 1 + .../openapi/integration/OpenApiClientIT.java | 79 ++++++++++++ .../citrusframework/openapi/petstore/pet.json | 6 +- .../citrusframework/openapi/petstore/pet.xml | 15 +++ .../citrus-test-api-generator-core/pom.xml | 11 ++ .../generator/gen/OpenapiPetstore.java | 64 ++++++++++ .../generator/gen/OpenapiPetstoreTest.java | 120 ++++++++++++++++++ .../src/test/resources/apis/petstore.yaml | 10 +- test-api-generator/readme.md | 5 +- 9 files changed, 303 insertions(+), 8 deletions(-) create mode 100644 connectors/citrus-openapi/src/test/resources/org/citrusframework/openapi/petstore/pet.xml create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java 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 03f27805df..da387df437 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 @@ -75,6 +75,7 @@ public OpenApiClientRequestMessageBuilder(HttpMessage httpMessage, OpenApiSpecif @Override public Message build(TestContext context, String messageType) { + // TODO: TAT-1291 - make parameter substitution more explicit? OasDocument oasDocument = openApiSpec.getOpenApiDoc(context); OasOperation operation = null; OasPathItem pathItem = null; 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..6f61edfa68 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 @@ -30,6 +30,7 @@ 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; /** @@ -83,6 +84,84 @@ public void getPetById() { .receive("getPetById", HttpStatus.OK)); } + @CitrusTest + public void BUG_should_be_possible_to_switch_content_type__to_xml() { + variable("petId", "1001"); + + when(openapi(petstoreSpec) + .client(httpClient) + .send("getPetById") + .message() + .accept("application/xml") + .fork(true)); + + then(http().server(httpServer) + .receive() + .get("/pet/${petId}") + .message() + .accept("@contains('application/xml')@")); + + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.xml")) + .contentType("application/xml")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK) + .message() + // TODO XML bodies do not seem to work, even if there is just XML as "produces" in the spec + .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.xml")) + // TODO the type/contentType statements are useless, if there is another type in the spec. + // even if there are two. i.E: + // # this will always use JSON as type + // produces: + // - application/json + // - application/xml + .contentType("application/xml") + .type(XML)); + } + + @CitrusTest + public void BUG_should_only_validate_the_presence_of_required_properties() { + variable("petId", "1001"); + + when(openapi(petstoreSpec) + .client(httpClient) + .send("getPetById") + .message() + .accept("application/json") + .fork(true)); + + then(http().server(httpServer) + .receive() + .get("/pet/${petId}") + .message() + .accept("@contains('application/json')@")); + + then(http().server(httpServer) + .send() + .response(HttpStatus.OK) + .message() + // this should be valid, according to the spec-file + .body(""" + { + "category": {}, + "name": "", + "status": "sold" + } + """) + .contentType("application/json")); + + then(openapi(petstoreSpec) + .client(httpClient) + .receive("getPetById", HttpStatus.OK) + .message() + ); + } + @CitrusTest public void getAddPet() { variable("petId", "1001"); 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/test-api-generator/citrus-test-api-generator-core/pom.xml b/test-api-generator/citrus-test-api-generator-core/pom.xml index c74f91249a..9fc1ad4c55 100644 --- a/test-api-generator/citrus-test-api-generator-core/pom.xml +++ b/test-api-generator/citrus-test-api-generator-core/pom.xml @@ -79,6 +79,17 @@ ${project.version} test
    + + org.junit.jupiter + junit-jupiter + test + + + org.citrusframework + citrus-openapi + 4.3.0-SNAPSHOT + test + diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java new file mode 100644 index 0000000000..42f707e3f4 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java @@ -0,0 +1,64 @@ +package org.citrusframework.openapi.generator.gen; + +import org.citrusframework.TestCaseRunner; +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.OpenApiClientRequestActionBuilder; +import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder; + +import static org.citrusframework.spi.Resources.create; +import static org.springframework.http.HttpStatus.OK; + +// TODO move to mustache File +public class OpenapiPetstore { + private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( + create("src/test/resources/apis/petstore.yaml") + ); + + public static OpenapiPetstoreBuilder openapiPetstore(HttpClient httpClient, TestCaseRunner runner) { + return new OpenapiPetstoreBuilder(httpClient, runner); + } + + public static class OpenapiPetstoreBuilder { + + private final HttpClient httpClient; + private final TestCaseRunner runner; + + OpenapiPetstoreBuilder(HttpClient httpClient, TestCaseRunner runner) { + this.httpClient = httpClient; + // TODO the runner is only needed to set the request params as test-variables, which is ugly... + // see comment in `withPetId()` + this.runner = runner; + } + + public GetPetByIdBuilder getPetById() { + return new GetPetByIdBuilder(httpClient, petstoreSpec, runner); + } + + public static class GetPetByIdBuilder extends OpenApiClientActionBuilder { + public static final String OPERATION_ID = "getPetById"; + private final TestCaseRunner runner; + + public GetPetByIdBuilder(Endpoint httpClient, OpenApiSpecification specification, TestCaseRunner runner) { + super(httpClient, specification); + this.runner = runner; + } + + public GetPetByIdBuilder withPetId(String petId) { + // TODO? move that to super and make it more explicit to set params (e.g. not implicitly via testcase-variables) + runner.variable("petId", petId); + return this; + } + + public OpenApiClientRequestActionBuilder send() { + return send(OPERATION_ID); + } + + public OpenApiClientResponseActionBuilder receive() { + return receive(OPERATION_ID, OK); + } + } + } +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java new file mode 100644 index 0000000000..1ce15dd6de --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java @@ -0,0 +1,120 @@ +package org.citrusframework.openapi.generator.gen; + +import org.citrusframework.TestCaseRunner; +import org.citrusframework.annotations.CitrusResource; +import org.citrusframework.annotations.CitrusTest; +import org.citrusframework.config.CitrusSpringConfig; +import org.citrusframework.endpoint.EndpointConfiguration; +import org.citrusframework.http.client.HttpClient; +import org.citrusframework.http.client.HttpClientBuilder; +import org.citrusframework.http.client.HttpEndpointConfiguration; +import org.citrusframework.http.server.HttpServer; +import org.citrusframework.http.server.HttpServerBuilder; +import org.citrusframework.junit.jupiter.spring.CitrusSpringExtension; +import org.citrusframework.openapi.generator.gen.OpenapiPetstoreTest.Config; +import org.citrusframework.openapi.generator.rest.petstore.spring.PetStoreBeanConfiguration; +import org.citrusframework.spi.BindToRegistry; +import org.citrusframework.util.SocketUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +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 static org.citrusframework.http.actions.HttpActionBuilder.http; +import static org.citrusframework.message.MessageType.JSON; +import static org.citrusframework.openapi.generator.gen.OpenapiPetstore.openapiPetstore; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.http.HttpStatus.OK; + +@ExtendWith(CitrusSpringExtension.class) +@SpringBootTest(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, Config.class}) +class OpenapiPetstoreTest { + + @BeforeEach + public void beforeTest() { + } + + private final int port = SocketUtils.findAvailableTcpPort(8080); + + @BindToRegistry + private final HttpServer httpServer = new HttpServerBuilder() + .port(port) + .timeout(5000L) + .autoStart(true) + .defaultStatus(HttpStatus.NO_CONTENT) + .build(); + + @BindToRegistry + private final HttpClient httpClient = new HttpClientBuilder() + .requestUrl("http://localhost:%d".formatted(port)) + .build(); + + @Test + @CitrusTest + void testFluentGeneratedOpenapiAction(@CitrusResource TestCaseRunner runner) { + runner.$(openapiPetstore(httpClient, runner) + .getPetById() + .withPetId("1001") + .send() + // TODO? set `.fork(true)` in the `.send()` + .fork(true)); + + respondPet(runner); + + runner.$(openapiPetstore(httpClient, runner) + .getPetById() + .receive() + .message() + .type(JSON) + .contentType("application/json") + ); + } + + private void respondPet(TestCaseRunner runner) { + runner.$(http().server(httpServer) + .receive() + .get("/pet/1001") + .message() + .accept("@contains('application/json')@")); + runner.$(http().server(httpServer) + .send() + .response(OK) + .message() + .body(""" + { + "id": ${petId}, + "name": "citrus:randomEnumValue('hasso','cutie','fluffy')", + "category": { + "id": ${petId}, + "name": "citrus:randomEnumValue('dog', 'cat', 'fish')" + }, + "photoUrls": [ "http://localhost:8080/photos/${petId}" ], + "tags": [ + { + "id": ${petId}, + "name": "generated" + } + ], + "status": "citrus:randomEnumValue('available', 'pending', 'sold')" + } + """) + .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; + } + } +} 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..4c834d1e3b 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 @@ -155,15 +155,19 @@ paths: description: Returns a single pet operationId: getPetById produces: - - application/xml +# - application/xml - application/json parameters: - name: petId in: path description: ID of pet to return required: true - type: integer - format: int64 + # TODO: Should work for direct type/format and schema.type/schema.format + # type: integer + # format: int64 + schema: + type: integer + format: int64 responses: '200': description: successful operation diff --git a/test-api-generator/readme.md b/test-api-generator/readme.md index 9176328090..f4552f36b6 100644 --- a/test-api-generator/readme.md +++ b/test-api-generator/readme.md @@ -6,6 +6,7 @@ @CitrusTest public void getPetById() { +// variable("petId", "1001"); // native when(openapi(petstoreSpec) .client(httpClient) @@ -14,10 +15,10 @@ public void getPetById() { .fork(true)); // generated - TODO: to be implemented when(openapiPetstore() - .client() + .client(httpClient) .getPetById() .withPetId("1001") - .send() + .send() // maybe obsolete? .fork(true)); } ``` From 7d833ca9388375cb8284671399d4a7d3c70bf596 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Tue, 25 Jun 2024 08:36:24 +0200 Subject: [PATCH 08/16] feature: send typed fluent openapi builder works --- connectors/citrus-openapi/pom.xml | 6 + .../openapi/OpenApiTestDataGenerator.java | 254 +++++++++++------- .../actions/OpenApiClientActionBuilder.java | 44 ++- .../OpenApiClientRequestActionBuilder.java | 213 +++++++++------ .../openapi/integration/OpenApiClientIT.java | 147 +++++++++- .../openapi/yaml/OpenApiClientTest.java | 178 ++++++------ .../openapi/petstore/petstore-v3.json | 9 + .../openapi/petstore/petstore-v3.yaml | 7 + pom.xml | 4 +- .../generator/gen/OpenapiPetstore.java | 51 ++-- .../generator/gen/OpenapiPetstoreTest.java | 17 +- .../src/test/resources/apis/petstore.yaml | 14 + 12 files changed, 643 insertions(+), 301 deletions(-) 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 900897fe4b..87f0bc79c1 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.getMessageBuilder()::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 da387df437..6671bf3d33 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,113 +25,168 @@ 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); + // TODO remove? + public OpenApiClientRequestMessageBuilder getMessageBuilder() { + return messageBuilder; + } + + private final OpenApiClientRequestMessageBuilder messageBuilder; + + protected OpenApiClientRequestActionBuilder(OpenApiClientRequestMessageBuilder messageBuilder) { + super(messageBuilder, messageBuilder.httpMessage); + this.messageBuilder = messageBuilder; } - public OpenApiClientRequestActionBuilder(HttpMessage httpMessage, OpenApiSpecification openApiSpec, - String operationId) { - super(new OpenApiClientRequestMessageBuilder(httpMessage, openApiSpec, operationId), httpMessage); + public static OpenApiClientRequestActionBuilder create(OpenApiSpecification openApiSpec, String operationId) { + var messageBuilder = new OpenApiClientRequestMessageBuilder(new HttpMessage(), openApiSpec, operationId, Map.of()); + return new OpenApiClientRequestActionBuilder(messageBuilder); } - 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, + Map parameters + ) { super(httpMessage); this.openApiSpec = openApiSpec; this.operationId = operationId; this.httpMessage = httpMessage; + this.parameters.putAll(parameters); + } + + 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 (item == null) { + throw new CitrusRuntimeException( + "Unable to locate operation with id '%s' in OpenAPI specification %s" + .formatted(operationId, oasDocument.getAttributeNames()) + ); + } + return item; + } + } + + public OpenApiClientRequestMessageBuilder withParameter(String name, Object number) { + parameters.put(name, number); + return this; } @Override public Message build(TestContext context, String messageType) { // TODO: TAT-1291 - make parameter substitution more explicit? + context.addVariables(parameters); 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; - } - } - - if (operation == null) { - throw new CitrusRuntimeException("Unable to locate operation with id '%s' in OpenAPI specification %s".formatted(operationId, openApiSpec.getSpecUrl())); - } + 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 (operation.parameters != null) { - operation.parameters.stream() - .filter(param -> "header".equals(param.in)) - .filter(param -> (param.required != null && param.required) || context.getVariables().containsKey(param.getName())) - .forEach(param -> 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 -> httpMessage.queryParam(param.getName(), - OpenApiTestDataGenerator.createRandomValueExpression(param.getName(), (OasSchema) param.schema, context))); - } + return super.build(context, messageType); + } - Optional body = OasModelHelper.getRequestBodySchema(oasDocument, operation); - body.ifPresent(oasSchema -> httpMessage.setPayload(OpenApiTestDataGenerator.createOutboundPayload(oasSchema, - OasModelHelper.getSchemaDefinitions(oasDocument), openApiSpec))); - - 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/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java b/connectors/citrus-openapi/src/test/java/org/citrusframework/openapi/integration/OpenApiClientIT.java index 6f61edfa68..5ccf83d612 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,11 +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 @@ -60,16 +62,119 @@ 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() + // TODO bug? - cannot check correlationId + // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 + // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader + .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() + // TODO bug? - cannot check correlationId + // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 + // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader + // .header("correlationId", "@matches('\\w+')@") + ); + + // TODO does not work without..?! + 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/${petId}") + .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/1001") .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") .accept("@contains('application/json')@")); then(http().server(httpServer) @@ -84,7 +189,43 @@ public void getPetById() { .receive("getPetById", HttpStatus.OK)); } + /* TODO create issues + @CitrusTest + public void BUG_getPetById_paramsCanAlsoBeSetWithMessageBuilder() { + variable("petId", "1001"); + + when(openapi(petstoreSpec) + .client(httpClient) + .send("getPetById") + .fork(true) + .message() + // TODO Bug - if the params are already set on the message, they get overwritten + .queryParam("verbose", "false") + .header("correlationIds", "1234F5gXW") + ); + + then(http().server(httpServer) + .receive() + .get("/pet/1001") + .queryParam("verbose", "true") + .message() + .header("correlationIds", "1234F5gXW") + ); + + 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 + @Ignore public void BUG_should_be_possible_to_switch_content_type__to_xml() { variable("petId", "1001"); @@ -125,6 +266,7 @@ public void BUG_should_be_possible_to_switch_content_type__to_xml() { } @CitrusTest + @Ignore public void BUG_should_only_validate_the_presence_of_required_properties() { variable("petId", "1001"); @@ -161,9 +303,10 @@ public void BUG_should_only_validate_the_presence_of_required_properties() { .message() ); } + */ @CitrusTest - public void getAddPet() { + public void postAddPet() { variable("petId", "1001"); when(openapi(petstoreSpec) 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/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 981380096e..f60de64b62 100644 --- a/pom.xml +++ b/pom.xml @@ -273,8 +273,8 @@ 1.4.20 3.9.2 - false - false + ${skipTests} + ${skipTests} false diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java index 42f707e3f4..6ae9023ad4 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java @@ -8,6 +8,9 @@ import org.citrusframework.openapi.actions.OpenApiClientRequestActionBuilder; import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder; +import java.util.HashMap; +import java.util.Map; + import static org.citrusframework.spi.Resources.create; import static org.springframework.http.HttpStatus.OK; @@ -17,48 +20,66 @@ public class OpenapiPetstore { create("src/test/resources/apis/petstore.yaml") ); - public static OpenapiPetstoreBuilder openapiPetstore(HttpClient httpClient, TestCaseRunner runner) { - return new OpenapiPetstoreBuilder(httpClient, runner); + public static OpenapiPetstoreBuilder openapiPetstore(HttpClient httpClient) { + return new OpenapiPetstoreBuilder(httpClient); } public static class OpenapiPetstoreBuilder { private final HttpClient httpClient; - private final TestCaseRunner runner; - OpenapiPetstoreBuilder(HttpClient httpClient, TestCaseRunner runner) { + OpenapiPetstoreBuilder(HttpClient httpClient) { this.httpClient = httpClient; - // TODO the runner is only needed to set the request params as test-variables, which is ugly... - // see comment in `withPetId()` - this.runner = runner; } public GetPetByIdBuilder getPetById() { - return new GetPetByIdBuilder(httpClient, petstoreSpec, runner); + return new GetPetByIdBuilder(httpClient, petstoreSpec); } public static class GetPetByIdBuilder extends OpenApiClientActionBuilder { public static final String OPERATION_ID = "getPetById"; - private final TestCaseRunner runner; + private final Map variables = new HashMap<>(); - public GetPetByIdBuilder(Endpoint httpClient, OpenApiSpecification specification, TestCaseRunner runner) { + public GetPetByIdBuilder(Endpoint httpClient, OpenApiSpecification specification) { super(httpClient, specification); - this.runner = runner; } public GetPetByIdBuilder withPetId(String petId) { - // TODO? move that to super and make it more explicit to set params (e.g. not implicitly via testcase-variables) - runner.variable("petId", petId); + variables.put("petId", petId); + return this; + } + + public GetPetByIdBuilder withCorrelationIds(String correlationIds) { + variables.put("correlationIds", correlationIds); + return this; + } + + public GetPetByIdBuilder withVerbose(boolean verbose) { + variables.put("verbose", verbose); return this; } public OpenApiClientRequestActionBuilder send() { - return send(OPERATION_ID); + var openApiOperation = OpenApiOperationBuilder.operation(OPERATION_ID).withParameters(variables); + var send = send(openApiOperation); + send.fork(true); + return send; } - public OpenApiClientResponseActionBuilder receive() { + public OpenApiClientResponseActionBuilder receive(TestCaseRunner runner) { return receive(OPERATION_ID, OK); } + + public static class GetPetByIdMessageBuilder extends OpenApiClientRequestActionBuilder { + + protected GetPetByIdMessageBuilder(OpenApiClientRequestActionBuilder.OpenApiClientRequestMessageBuilder messageBuilder) { + super(messageBuilder); + } + + public GetPetByIdMessageBuilder create() { + return null; + } + } } } } diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java index 1ce15dd6de..c57ed41f3b 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java @@ -56,18 +56,19 @@ public void beforeTest() { @Test @CitrusTest void testFluentGeneratedOpenapiAction(@CitrusResource TestCaseRunner runner) { - runner.$(openapiPetstore(httpClient, runner) + runner.$(openapiPetstore(httpClient) .getPetById() - .withPetId("1001") + .withPetId("2002") + .withCorrelationIds("5599") + .withVerbose(true) .send() - // TODO? set `.fork(true)` in the `.send()` - .fork(true)); + ); respondPet(runner); - runner.$(openapiPetstore(httpClient, runner) + runner.$(openapiPetstore(httpClient) .getPetById() - .receive() + .receive(runner) .message() .type(JSON) .contentType("application/json") @@ -77,8 +78,10 @@ void testFluentGeneratedOpenapiAction(@CitrusResource TestCaseRunner runner) { private void respondPet(TestCaseRunner runner) { runner.$(http().server(httpServer) .receive() - .get("/pet/1001") + .get("/pet/2002") .message() + .queryParam("verbose", "true") + .header("correlationIds", "5599") .accept("@contains('application/json')@")); runner.$(http().server(httpServer) .send() 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 4c834d1e3b..6ef6cf4658 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 @@ -168,6 +168,20 @@ paths: schema: type: integer format: int64 + - + name: verbose + description: Output details + schema: + type: boolean + in: query + required: false + - + name: correlationIds + description: ID to trace a request + schema: + type: string + in: header + required: false responses: '200': description: successful operation From f15e09b297ddac301f85dcaa7935b58935b468c2 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Tue, 25 Jun 2024 08:47:17 +0200 Subject: [PATCH 09/16] feature: cleanup OpenApiClientRequestActionBuilder --- .../actions/OpenApiClientActionBuilder.java | 2 +- .../OpenApiClientRequestActionBuilder.java | 23 ++++++++----------- .../openapi/integration/OpenApiClientIT.java | 1 - 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientActionBuilder.java b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientActionBuilder.java index d03776b5b9..eff7dd8f4f 100644 --- a/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientActionBuilder.java +++ b/connectors/citrus-openapi/src/main/java/org/citrusframework/openapi/actions/OpenApiClientActionBuilder.java @@ -99,7 +99,7 @@ public OpenApiClientRequestActionBuilder send(OpenApiOperationBuilder openApiOpe builder.name("openapi:send-request"); builder.withReferenceResolver(referenceResolver); - openApiOperationBuilder.parameters.forEach(builder.getMessageBuilder()::withParameter); + openApiOperationBuilder.parameters.forEach(builder::withParameter); this.delegate = builder; return builder; 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 6671bf3d33..433324e5d1 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 @@ -46,11 +46,6 @@ */ public class OpenApiClientRequestActionBuilder extends HttpClientRequestActionBuilder { - // TODO remove? - public OpenApiClientRequestMessageBuilder getMessageBuilder() { - return messageBuilder; - } - private final OpenApiClientRequestMessageBuilder messageBuilder; protected OpenApiClientRequestActionBuilder(OpenApiClientRequestMessageBuilder messageBuilder) { @@ -59,10 +54,17 @@ protected OpenApiClientRequestActionBuilder(OpenApiClientRequestMessageBuilder m } public static OpenApiClientRequestActionBuilder create(OpenApiSpecification openApiSpec, String operationId) { - var messageBuilder = new OpenApiClientRequestMessageBuilder(new HttpMessage(), openApiSpec, operationId, Map.of()); + var messageBuilder = new OpenApiClientRequestMessageBuilder(new HttpMessage(), openApiSpec, operationId); return new OpenApiClientRequestActionBuilder(messageBuilder); } + + public OpenApiClientRequestActionBuilder withParameter(String name, Object number) { + messageBuilder.parameters.put(name, number); + return this; + } + + protected static class OpenApiClientRequestMessageBuilder extends HttpMessageBuilder { private final Map parameters = new HashMap<>(); @@ -74,14 +76,12 @@ protected static class OpenApiClientRequestMessageBuilder extends HttpMessageBui public OpenApiClientRequestMessageBuilder( HttpMessage httpMessage, OpenApiSpecification openApiSpec, - String operationId, - Map parameters + String operationId ) { super(httpMessage); this.openApiSpec = openApiSpec; this.operationId = operationId; this.httpMessage = httpMessage; - this.parameters.putAll(parameters); } private record OasItem( @@ -117,11 +117,6 @@ public static OasItem create(String operationId, OasDocument oasDocument) { } } - public OpenApiClientRequestMessageBuilder withParameter(String name, Object number) { - parameters.put(name, number); - return this; - } - @Override public Message build(TestContext context, String messageType) { // TODO: TAT-1291 - make parameter substitution more explicit? 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 5ccf83d612..27536a40b6 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 @@ -115,7 +115,6 @@ public void getPetById_requiredParamsShouldBeGeneratedIfNotProvided() { // .header("correlationId", "@matches('\\w+')@") ); - // TODO does not work without..?! variable("petId", "1001"); then(http().server(httpServer) .send() From 829cdb962ba40e5254946d76771e4143757e0383 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Tue, 25 Jun 2024 17:35:48 +0200 Subject: [PATCH 10/16] feature: improve receive action --- .../OpenApiClientResponseActionBuilder.java | 18 ++++- .../generator/gen/OpenapiPetstore.java | 80 +++++++++---------- .../generator/gen/OpenapiPetstoreTest.java | 18 +++-- .../src/test/resources/apis/petstore.yaml | 20 ++--- 4 files changed, 70 insertions(+), 66 deletions(-) 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/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java index 6ae9023ad4..16c166d7af 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java @@ -1,6 +1,5 @@ package org.citrusframework.openapi.generator.gen; -import org.citrusframework.TestCaseRunner; import org.citrusframework.endpoint.Endpoint; import org.citrusframework.http.client.HttpClient; import org.citrusframework.openapi.OpenApiSpecification; @@ -8,11 +7,9 @@ import org.citrusframework.openapi.actions.OpenApiClientRequestActionBuilder; import org.citrusframework.openapi.actions.OpenApiClientResponseActionBuilder; -import java.util.HashMap; -import java.util.Map; +import java.util.function.UnaryOperator; import static org.citrusframework.spi.Resources.create; -import static org.springframework.http.HttpStatus.OK; // TODO move to mustache File public class OpenapiPetstore { @@ -20,65 +17,62 @@ public class OpenapiPetstore { create("src/test/resources/apis/petstore.yaml") ); - public static OpenapiPetstoreBuilder openapiPetstore(HttpClient httpClient) { - return new OpenapiPetstoreBuilder(httpClient); + public static OpenapiPetstore openapiPetstore(HttpClient httpClient) { + return new OpenapiPetstore(httpClient); } - public static class OpenapiPetstoreBuilder { + private final HttpClient httpClient; - private final HttpClient httpClient; + private OpenapiPetstore(HttpClient httpClient) { + this.httpClient = httpClient; + } + + public GetPetByIdAction getPetById() { + return new GetPetByIdAction(httpClient, petstoreSpec); + } - OpenapiPetstoreBuilder(HttpClient httpClient) { - this.httpClient = httpClient; + public static class GetPetByIdAction extends OpenApiClientActionBuilder { + public static final String OPERATION_ID = "getPetById"; + + public GetPetByIdAction(Endpoint httpClient, OpenApiSpecification specification) { + super(httpClient, specification); } - public GetPetByIdBuilder getPetById() { - return new GetPetByIdBuilder(httpClient, petstoreSpec); + public OpenApiClientRequestActionBuilder send(UnaryOperator builderProvider) { + var builder = builderProvider.apply(new GetPetByIdRequest()); + var send = send(builder.build()); + send.fork(true); + return send; } - public static class GetPetByIdBuilder extends OpenApiClientActionBuilder { - public static final String OPERATION_ID = "getPetById"; - private final Map variables = new HashMap<>(); + public OpenApiClientResponseActionBuilder receive() { + return receive(OPERATION_ID, "200"); + } - public GetPetByIdBuilder(Endpoint httpClient, OpenApiSpecification specification) { - super(httpClient, specification); - } + public static class GetPetByIdRequest { + private final OpenApiOperationBuilder openApiOperation = OpenApiOperationBuilder.operation(OPERATION_ID); - public GetPetByIdBuilder withPetId(String petId) { - variables.put("petId", petId); - return this; + public static GetPetByIdRequest getPetByIdRequest() { + return new GetPetByIdRequest(); } - public GetPetByIdBuilder withCorrelationIds(String correlationIds) { - variables.put("correlationIds", correlationIds); + public GetPetByIdRequest withPetId(String petId) { + openApiOperation.withParameter("petId", petId); return this; } - public GetPetByIdBuilder withVerbose(boolean verbose) { - variables.put("verbose", verbose); + public GetPetByIdRequest withCorrelationIds(String correlationIds) { + openApiOperation.withParameter("correlationIds", correlationIds); return this; } - public OpenApiClientRequestActionBuilder send() { - var openApiOperation = OpenApiOperationBuilder.operation(OPERATION_ID).withParameters(variables); - var send = send(openApiOperation); - send.fork(true); - return send; - } - - public OpenApiClientResponseActionBuilder receive(TestCaseRunner runner) { - return receive(OPERATION_ID, OK); + public GetPetByIdRequest withVerbose(boolean verbose) { + openApiOperation.withParameter("verbose", verbose); + return this; } - public static class GetPetByIdMessageBuilder extends OpenApiClientRequestActionBuilder { - - protected GetPetByIdMessageBuilder(OpenApiClientRequestActionBuilder.OpenApiClientRequestMessageBuilder messageBuilder) { - super(messageBuilder); - } - - public GetPetByIdMessageBuilder create() { - return null; - } + public OpenApiOperationBuilder build() { + return openApiOperation; } } } diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java index c57ed41f3b..bda71a12b4 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java @@ -26,6 +26,7 @@ import static org.citrusframework.http.actions.HttpActionBuilder.http; import static org.citrusframework.message.MessageType.JSON; import static org.citrusframework.openapi.generator.gen.OpenapiPetstore.openapiPetstore; +import static org.citrusframework.validation.PathExpressionValidationContext.Builder.pathExpression; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.http.HttpStatus.OK; @@ -58,20 +59,23 @@ public void beforeTest() { void testFluentGeneratedOpenapiAction(@CitrusResource TestCaseRunner runner) { runner.$(openapiPetstore(httpClient) .getPetById() - .withPetId("2002") - .withCorrelationIds("5599") - .withVerbose(true) - .send() + .send(request -> request + .withPetId("2002") + .withCorrelationIds("5599") + .withVerbose(true)) ); respondPet(runner); runner.$(openapiPetstore(httpClient) .getPetById() - .receive(runner) + .receive() .message() - .type(JSON) - .contentType("application/json") + .validate(pathExpression() + .jsonPath("$.id", "2002") + .jsonPath("$.category.id", "2002") + .jsonPath("$.tags[0].name", "generated") + ) ); } 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 6ef6cf4658..01f4049413 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 @@ -162,26 +162,18 @@ paths: in: path description: ID of pet to return required: true - # TODO: Should work for direct type/format and schema.type/schema.format - # type: integer - # format: int64 - schema: - type: integer - format: int64 - - - name: verbose + type: integer + format: int64 + - name: verbose description: Output details - schema: - type: boolean in: query required: false - - - name: correlationIds + type: boolean + - name: correlationIds description: ID to trace a request - schema: - type: string in: header required: false + type: string responses: '200': description: successful operation From 3b5d3f548acb6baeac86db861ca91ae9dd7bfde5 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Fri, 5 Jul 2024 11:51:32 +0200 Subject: [PATCH 11/16] feature: put new openapi actions to mustache (WIP) --- .../openapi/OpenApiTestDataGenerator.java | 1 + src/manual/connector-openapi.adoc | 2 +- .../citrus-test-api-generator-core/pom.xml | 20 +- .../main/resources/java-citrus/api.mustache | 317 ++--- .../openapi/generator/GeneratedApiIT.java | 1071 ++++++++--------- .../openapi/generator/GetPetByIdIT.java | 224 ++++ .../openapi/generator/GetPetByIdTest.java | 254 ---- .../generator/JavaCitrusCodegenTest.java | 27 +- .../generator/SpringBeanConfigurationIT.java | 18 +- .../generator/gen/OpenapiPetstore.java | 77 +- .../src/test/resources/apis/petstore.yaml | 504 -------- .../rest/petstore/request/PetApi.java | 895 +------------- .../pom.xml | 9 + .../TestApiGeneratorMojoIntegrationTest.java | 166 +-- .../pom-minimal-petstore.xml | 32 + .../src/test/resources/api/petstore.yaml | 206 ++++ .../test/resources/expected/api/PetApi.java | 93 ++ 17 files changed, 1394 insertions(+), 2522 deletions(-) create mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdIT.java delete mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdTest.java create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/TestApiGeneratorMojoIntegrationTest/pom-minimal-petstore.xml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/api/petstore.yaml create mode 100644 test-api-generator/citrus-test-api-generator-maven-plugin/src/test/resources/expected/api/PetApi.java 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 87f0bc79c1..e5dc360ed4 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 @@ -313,6 +313,7 @@ public static String createValidationExpression( } /** + * TODO TAT-1291 this method does not respect optional/required properties * Create validation expression using functions according to schema type and format. * * @param schema diff --git a/src/manual/connector-openapi.adoc b/src/manual/connector-openapi.adoc index c0e24a1c8a..f3f9220ada 100644 --- a/src/manual/connector-openapi.adoc +++ b/src/manual/connector-openapi.adoc @@ -684,7 +684,7 @@ Further examples can be found here `org.citrusframework.openapi.generator.Genera To utilize the test API in Java, it's necessary to import the API configuration, that provides the respective request actions. The request to test can then be autowired, configured and autowired, as illustrated in the sample below. -Further examples can be found here `org.citrusframework.openapi.generator.GetPetByIdTest`. +Further examples can be found here `org.citrusframework.openapi.generator.GetPetByIdIT`. .Java DSL [source,java,indent=0,role="secondary"] 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 9fc1ad4c55..224c53c55a 100644 --- a/test-api-generator/citrus-test-api-generator-core/pom.xml +++ b/test-api-generator/citrus-test-api-generator-core/pom.xml @@ -41,12 +41,6 @@ ${project.version} - - org.assertj - assertj-core - ${assertj.version} - test - com.fasterxml.jackson.dataformat jackson-dataformat-yaml @@ -87,7 +81,13 @@ org.citrusframework citrus-openapi - 4.3.0-SNAPSHOT + ${project.version} + test + + + org.assertj + assertj-core + ${assertj.version} test @@ -158,7 +158,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 @@ -175,7 +175,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 @@ -196,7 +196,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/resources/java-citrus/api.mustache b/test-api-generator/citrus-test-api-generator-core/src/main/resources/java-citrus/api.mustache index d384833fec..59d38fef94 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 @@ -1,281 +1,108 @@ -/* - * 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 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 {{invokerPackage}}.citrus.{{prefix}}AbstractTestRequest; - -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 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; -@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") -public class {{classname}} implements GeneratedApi -{ +import static org.citrusframework.spi.Resources.create; - public static final {{classname}} INSTANCE = new {{classname}}(); +public class {{classname}} { + private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( + create("{{inputSpec}}") + ); - 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/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 0240db1100..dfa038e266 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,555 +72,550 @@ /** * 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) {} +// void testValidationFailure() { +// mockProducerAndConsumer(createReceiveMessage("{\"some\": \"payload\"}")); +// assertThatThrownBy( +// () -> executeTest("getPetByIdRequestTest", testContext)).hasCauseExactlyInstanceOf( +// ValidationException.class); +// } // -// @Override -// public synchronized void doAppend(ILoggingEvent eventObject) { -// logMessages.add(eventObject.getMessage()); -// super.doAppend(eventObject); +// @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); +// } // -// mockProducer(httpClient); +// @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; +// }; // -// Message receiveMessage = createReceiveMessage( -// FileUtils.readToString(Resources.create("org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), StandardCharsets.UTF_8) -// ); +// // sendAndValidateMessage("sendWithBodyLiteralTest", messageMatcher, AddPetRequest.class); +// } // -// mockConsumer(httpClient, testContext, receiveMessage); +// @Test +// void testSendWithExtraHeaders() { +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(httpMessage.getHeader("h1")).isEqualTo("v1"); +// assertThat(httpMessage.getHeader("h2")).isEqualTo("v2"); +// return true; +// }; // -// executeTest("getPetByIdRequestTest", testContext); +// // sendAndValidateMessage("sendWithExtraHeaderTest", messageMatcher, AddPetRequest.class); +// } // -// assertThat(logMessages.get(0)).isEqualTo("getPetById;GET;\"{}\";\"\";\"\""); +// @Test +// void testSendWithBodyLiteral() { +// ArgumentMatcher messageMatcher = message -> { +// HttpMessage httpMessage = (HttpMessage) message; +// assertThat(((String) httpMessage.getPayload()).trim()).isEqualTo("{\"id\": 13}"); +// return true; +// }; +// +// // sendAndValidateMessage("sendWithBodyLiteralTest", 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); +// } +// } +// +//// @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"}) +// public HttpClient applicationServiceClient() { +// HttpClient clientMock = mock(); +// EndpointConfiguration endpointConfigurationMock = mock(); +// when(clientMock.getEndpointConfiguration()).thenReturn(new HttpEndpointConfiguration()); +// when(endpointConfigurationMock.getTimeout()).thenReturn(5000L); +// return clientMock; +// } +// +// @Bean +// public ApiActionBuilderCustomizerService customizer() { +// return new ApiActionBuilderCustomizerService() { +// @Override +// public > T build( +// GeneratedApi generatedApi, TestAction action, TestContext context, T builder) { +// builder.getMessageBuilderSupport() +// .header("x-citrus-api-version", generatedApi.getApiVersion()); +// return builder; +// } +// }; +// } +// +// @Bean({"oas3", "testSchema"}) +// public SimpleJsonSchema testSchema() { +// JsonSchema schemaMock = mock(); +// SimpleJsonSchema jsonSchemaMock = mock(); +// +// when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); +// +// Set okReport = new HashSet<>(); +// when(schemaMock.validate(any())).thenReturn(okReport); +// return jsonSchemaMock; +// } +// +// @Bean +// public SimpleJsonSchema failingTestSchema() { +// JsonSchema schemaMock = mock(); +// SimpleJsonSchema jsonSchemaMock = mock(); +// +// when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); +// +// Set nokReport = new HashSet<>(); +// nokReport.add(new ValidationMessage.Builder().customMessage( +// "This is a simulated validation error message").build()); +// when(schemaMock.validate(any())).thenReturn(nokReport); +// return jsonSchemaMock; +// } // } - - /** - * 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; - } - - public static class Config { - - @Bean(name = {"applicationServiceClient", "multipartTestEndpoint", - "soapSampleStoreEndpoint", "petStoreEndpoint"}) - public HttpClient applicationServiceClient() { - HttpClient clientMock = mock(); - EndpointConfiguration endpointConfigurationMock = mock(); - when(clientMock.getEndpointConfiguration()).thenReturn(new HttpEndpointConfiguration()); - when(endpointConfigurationMock.getTimeout()).thenReturn(5000L); - return clientMock; - } - - @Bean - public ApiActionBuilderCustomizerService customizer() { - return new ApiActionBuilderCustomizerService() { - @Override - public > T build( - GeneratedApi generatedApi, TestAction action, TestContext context, T builder) { - builder.getMessageBuilderSupport() - .header("x-citrus-api-version", generatedApi.getApiVersion()); - return builder; - } - }; - } - - @Bean({"oas3", "testSchema"}) - public SimpleJsonSchema testSchema() { - JsonSchema schemaMock = mock(); - SimpleJsonSchema jsonSchemaMock = mock(); - - when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); - - Set okReport = new HashSet<>(); - when(schemaMock.validate(any())).thenReturn(okReport); - return jsonSchemaMock; - } - - @Bean - public SimpleJsonSchema failingTestSchema() { - JsonSchema schemaMock = mock(); - SimpleJsonSchema jsonSchemaMock = mock(); - - when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); - - Set nokReport = new HashSet<>(); - nokReport.add(new ValidationMessage.Builder().customMessage( - "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 new file mode 100644 index 0000000000..6c01c18ba1 --- /dev/null +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdIT.java @@ -0,0 +1,224 @@ +package org.citrusframework.openapi.generator; + +import org.citrusframework.TestCaseRunner; +import org.citrusframework.annotations.CitrusResource; +import org.citrusframework.annotations.CitrusTest; +import org.citrusframework.config.CitrusSpringConfig; +import org.citrusframework.http.client.HttpClient; +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.openapi.generator.rest.petstore.spring.PetStoreBeanConfiguration; +import org.citrusframework.spi.BindToRegistry; +import org.citrusframework.util.SocketUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.context.ContextConfiguration; + +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; + + +@CitrusSpringSupport +@ContextConfiguration(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class}) +class GetPetByIdIT { + + private final int port = SocketUtils.findAvailableTcpPort(8080); + +// private final MessageQueue inboundQueue = new DefaultMessageQueue("inboundQueue"); +// private final EndpointAdapter endpointAdapter = new DirectEndpointAdapter(direct() +// .synchronous() +// .timeout(5000L) +// .queue(inboundQueue) +// .build()); + + @BindToRegistry + private final HttpClient httpClient = new HttpClientBuilder() + .requestUrl("http://localhost:%d".formatted(port)) + .build(); + + // TODO TAT-1291 the GetPetByIdIT class gets instantiated twice, but the second time + // the httpServer, respectively the "endpoint", is not initialized correctly... + // why is the Test Class instantiated twice (once per testmethod?) anyway?! + @BindToRegistry + private final HttpServer httpServer = new HttpServerBuilder() + .port(port) + // .endpointAdapter(endpointAdapter) + .timeout(5000L) + .autoStart(true) + .defaultStatus(NO_CONTENT) + .build(); + + @BeforeEach + public void beforeTest() { + } + + @Test + @CitrusTest + void testByJsonPath(@CitrusResource TestCaseRunner runner) { + + runner.$( + openapiPetstore(httpClient) + .getPetById() + .send(request -> request + .withPetId(2002L) + .withCorrelationIds("5599") + .withVerbose(true) + ) + ); + + respondPet(runner); + + runner.$( + openapiPetstore(httpClient) + .getPetById() + .receive() + .message() + .validate( + pathExpression() + .jsonPath("$.name", "Snoopy") + .jsonPath("$.id", 2002) + ) + ); + } + + @Test + @CitrusTest + void testJsonFileBody(@CitrusResource TestCaseRunner runner) { + + runner.$( + openapiPetstore(httpClient) + .getPetById() + .send(request -> request + // TODO + // .withBodyFile("org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json") + .withPetId(2002L) + .withCorrelationIds("5599") + .withVerbose(true) + ) + ); + + respondPet(runner); + + runner.$( + openapiPetstore(httpClient) + .getPetById() + .receive() + .message() + .validate( + pathExpression() + .jsonPath("$.name", "Snoopy") + .jsonPath("$.id", 2002) + ) + ); + } + + 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)); + } + + /* @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 + ) + ); + } + + @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") + ); + } + + @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"); + }*/ +} diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdTest.java deleted file mode 100644 index 6421cee946..0000000000 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/GetPetByIdTest.java +++ /dev/null @@ -1,254 +0,0 @@ -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.message.Message; -import org.citrusframework.messaging.Producer; -import org.citrusframework.messaging.SelectiveConsumer; -import org.citrusframework.openapi.generator.GetPetByIdTest.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.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; - -@ExtendWith(CitrusSpringExtension.class) -@SpringBootTest(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, Config.class}) -class GetPetByIdTest { - - @Autowired - private GetPetByIdRequest getPetByIdRequest; - - @Autowired - @Qualifier("petStoreEndpoint") - private HttpClient httpClient; - - private String defaultResponse; - - @BeforeEach - public void beforeTest() throws IOException { - defaultResponse = readToString(Resources.create( - "org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"), - StandardCharsets.UTF_8) ; - - mockProducer(); - mockConsumer(); - } - - /** - * 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 - ) - ); - // 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 - ) - ); - } - - /** - * 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(); - - SelectiveConsumer consumer = mock(SelectiveConsumer.class); - when(httpClient.createConsumer()).thenReturn(consumer); - when(consumer.receive(any(), eq(5000L))).thenReturn(receiveMessage); - } - - 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; - } - - @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; - } - } -} 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 57f49cd861..d015f31abf 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 @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.citrusframework.openapi.generator.JavaCitrusCodegen.CODEGEN_NAME; +import static org.citrusframework.openapi.generator.PathUtils.getProjectPathOfClass; import java.io.File; import java.io.IOException; @@ -104,28 +105,14 @@ void areReservedWordsEscapedTest() throws IOException { } @Test - void arePathParamsFieldsPresent() throws IOException { - final CodegenConfigurator configurator = new CodegenConfigurator() - .setGeneratorName(CODEGEN_NAME) - .setInputSpec("src/test/resources/apis/petstore.yaml") - .setOutputDir("target/JavaCitrusCodegenTest/petstore"); + void arePathParamsFieldsPresent() { + var fixture = new JavaCitrusCodegen(); + String inputSpec = "src\\test\\resources\\apis\\petstore.yaml"; + fixture.setInputSpec(inputSpec); - final ClientOptInput clientOptInput = configurator.toClientOptInput(); - DefaultGenerator generator = new DefaultGenerator(); - List outputFiles = generator.opts(clientOptInput).generate(); + fixture.processOpts(); - 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); + assertThat(fixture.additionalProperties()).containsEntry("inputSpecRelative", "src/test/resources/apis/petstore.yaml"); } @Test diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java index 2f5dbf7179..83491b5d48 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/SpringBeanConfigurationIT.java @@ -9,7 +9,7 @@ import org.citrusframework.http.client.HttpClient; import org.citrusframework.http.client.HttpEndpointConfiguration; import org.citrusframework.junit.jupiter.spring.CitrusSpringSupport; -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.spring.PetStoreBeanConfiguration; import org.junit.jupiter.api.Test; import org.citrusframework.openapi.generator.SpringBeanConfigurationIT.ClientConfiguration; @@ -23,23 +23,25 @@ @ContextConfiguration(classes = {CitrusSpringConfig.class, ClientConfiguration.class, PetStoreBeanConfiguration.class}) class SpringBeanConfigurationIT { + // TODO TAT-1291 migrate tests + @Autowired private ApplicationContext applicationContext; @Test @CitrusTest void fromReferenceResolverIsPrototypeScoped(@CitrusResource TestContext testContext) { - var addPetRequest = testContext.getReferenceResolver().resolve(AddPetRequest.class); - assertThat(addPetRequest) - .isNotNull() - .isNotEqualTo(testContext.getReferenceResolver().resolve(AddPetRequest.class)); +// var addPetRequest = testContext.getReferenceResolver().resolve(AddPetRequest.class); +// assertThat(addPetRequest) +// .isNotNull() +// .isNotEqualTo(testContext.getReferenceResolver().resolve(AddPetRequest.class)); } @Test void fromSpringApplicationContextIsPrototypeScoped() { - assertThat(applicationContext.getBean(AddPetRequest.class)) - .isNotNull() - .isNotEqualTo(applicationContext.getBean(AddPetRequest.class)); +// assertThat(applicationContext.getBean(AddPetRequest.class)) +// .isNotNull() +// .isNotEqualTo(applicationContext.getBean(AddPetRequest.class)); } @TestConfiguration diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java index 16c166d7af..5ad19b5e25 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java @@ -4,6 +4,7 @@ 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; @@ -27,53 +28,63 @@ private OpenapiPetstore(HttpClient httpClient) { this.httpClient = httpClient; } - public GetPetByIdAction getPetById() { - return new GetPetByIdAction(httpClient, petstoreSpec); + public PetstoreAction getPetById() { + return petstoreAction(new GetPetByIdRequest()); } - public static class GetPetByIdAction extends OpenApiClientActionBuilder { - public static final String OPERATION_ID = "getPetById"; + private PetstoreAction petstoreAction(B requestBuilder) { + return new PetstoreAction<>(httpClient, petstoreSpec, requestBuilder); + } - public GetPetByIdAction(Endpoint httpClient, OpenApiSpecification specification) { - super(httpClient, specification); + public static class GetPetByIdRequest extends OperationRequestBuilder { + @Override + public String getOperationId() { + return "getPetById"; } - public OpenApiClientRequestActionBuilder send(UnaryOperator builderProvider) { - var builder = builderProvider.apply(new GetPetByIdRequest()); - var send = send(builder.build()); - send.fork(true); - return send; + public GetPetByIdRequest withPetId(String petId) { + openApiOperation.withParameter("petId", petId); + return this; } - public OpenApiClientResponseActionBuilder receive() { - return receive(OPERATION_ID, "200"); + public GetPetByIdRequest withCorrelationIds(String correlationIds) { + openApiOperation.withParameter("correlationIds", correlationIds); + return this; } - public static class GetPetByIdRequest { - private final OpenApiOperationBuilder openApiOperation = OpenApiOperationBuilder.operation(OPERATION_ID); + public GetPetByIdRequest withVerbose(boolean verbose) { + openApiOperation.withParameter("verbose", verbose); + return this; + } + } - public static GetPetByIdRequest getPetByIdRequest() { - return new GetPetByIdRequest(); - } + public static abstract class OperationRequestBuilder { + protected final OpenApiOperationBuilder openApiOperation = OpenApiOperationBuilder.operation(getOperationId()); - public GetPetByIdRequest withPetId(String petId) { - openApiOperation.withParameter("petId", petId); - return this; - } + public abstract String getOperationId(); - public GetPetByIdRequest withCorrelationIds(String correlationIds) { - openApiOperation.withParameter("correlationIds", correlationIds); - return this; - } + public OpenApiOperationBuilder build() { + return openApiOperation; + } + } - public GetPetByIdRequest withVerbose(boolean verbose) { - openApiOperation.withParameter("verbose", verbose); - return this; - } + public static class PetstoreAction extends OpenApiClientActionBuilder { + private final T operation; - public OpenApiOperationBuilder build() { - return openApiOperation; - } + 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/apis/petstore.yaml b/test-api-generator/citrus-test-api-generator-core/src/test/resources/apis/petstore.yaml index 01f4049413..0ff3414db5 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 @@ -20,133 +20,6 @@ tags: schemes: - http paths: - /pet: - post: - tags: - - pet - summary: Add a new pet to the store - description: '' - operationId: addPet - consumes: - - application/json - - application/xml - produces: - - application/xml - - application/json - parameters: - - in: body - name: body - description: Pet object that needs to be added to the store - required: true - schema: - $ref: '#/definitions/Pet' - responses: - '405': - description: Invalid input - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - put: - tags: - - pet - summary: Update an existing pet - description: '' - operationId: updatePet - consumes: - - application/json - - application/xml - produces: - - application/xml - - application/json - parameters: - - in: body - name: body - description: Pet object that needs to be added to the store - required: true - schema: - $ref: '#/definitions/Pet' - responses: - '400': - description: Invalid ID supplied - '404': - description: Pet not found - '405': - description: Validation exception - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - /pet/findByStatus: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - produces: - - application/xml - - application/json - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - type: array - items: - type: string - enum: - - available - - pending - - sold - default: available - collectionFormat: csv - responses: - '200': - description: successful operation - schema: - type: array - items: - $ref: '#/definitions/Pet' - '400': - description: Invalid status value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - /pet/findByTags: - get: - tags: - - pet - summary: Finds Pets by tags - description: 'Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.' - operationId: findPetsByTags - produces: - - application/xml - - application/json - parameters: - - name: tags - in: query - description: Tags to filter by - required: true - type: array - items: - type: string - collectionFormat: csv - responses: - '200': - description: successful operation - schema: - type: array - items: - $ref: '#/definitions/Pet' - '400': - description: Invalid tag value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - deprecated: true '/pet/{petId}': get: tags: @@ -186,383 +59,6 @@ paths: security: - api_key: [] - basicAuth: [] - post: - tags: - - pet - summary: Updates a pet in the store with form data - description: '' - operationId: updatePetWithForm - consumes: - - application/x-www-form-urlencoded - produces: - - application/xml - - application/json - parameters: - - name: petId - in: path - description: ID of pet that needs to be updated - required: true - type: integer - format: int64 - - name: name - in: formData - description: Updated name of the pet - required: false - type: string - - name: status - in: formData - description: Updated status of the pet - required: false - type: string - responses: - '405': - description: Invalid input - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - delete: - tags: - - pet - summary: Deletes a pet - description: '' - operationId: deletePet - produces: - - application/xml - - application/json - parameters: - - name: api_key - in: header - required: false - type: string - - name: petId - in: path - description: Pet id to delete - required: true - type: integer - format: int64 - responses: - '400': - description: Invalid pet value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - '/pet/{petId}/uploadImage': - post: - tags: - - pet - summary: uploads an image - description: '' - operationId: uploadFile - consumes: - - multipart/form-data - produces: - - application/json - parameters: - - name: petId - in: path - description: ID of pet to update - required: true - type: integer - format: int64 - - name: additionalMetadata - in: formData - description: Additional data to pass to server - required: false - type: string - - name: file - in: formData - description: file to upload - required: false - type: file - responses: - '200': - description: successful operation - schema: - $ref: '#/definitions/ApiResponse' - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' - /store/inventory: - get: - tags: - - store - summary: Returns pet inventories by status - description: Returns a map of status codes to quantities - operationId: getInventory - produces: - - application/json - parameters: [] - responses: - '200': - description: successful operation - schema: - type: object - additionalProperties: - type: integer - format: int32 - security: - - api_key: [] - /store/order: - post: - tags: - - store - summary: Place an order for a pet - description: '' - operationId: placeOrder - produces: - - application/xml - - application/json - parameters: - - in: body - name: body - description: order placed for purchasing the pet - required: true - schema: - $ref: '#/definitions/Order' - responses: - '200': - description: successful operation - schema: - $ref: '#/definitions/Order' - '400': - description: Invalid Order - '/store/order/{order_id}': - get: - tags: - - store - summary: Find purchase order by ID - description: 'For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions' - operationId: getOrderById - produces: - - application/xml - - application/json - parameters: - - name: order_id - in: path - description: ID of pet that needs to be fetched - required: true - type: integer - maximum: 5 - minimum: 1 - format: int64 - responses: - '200': - description: successful operation - schema: - $ref: '#/definitions/Order' - '400': - description: Invalid ID supplied - '404': - description: Order not found - delete: - tags: - - store - summary: Delete purchase order by ID - description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors - operationId: deleteOrder - produces: - - application/xml - - application/json - parameters: - - name: order_id - in: path - description: ID of the order that needs to be deleted - required: true - type: string - responses: - '400': - description: Invalid ID supplied - '404': - description: Order not found - /user: - post: - tags: - - user - summary: Create user - description: This can only be done by the logged in user. - operationId: createUser - produces: - - application/xml - - application/json - parameters: - - in: body - name: body - description: Created user object - required: true - schema: - $ref: '#/definitions/User' - responses: - default: - description: successful operation - /user/createWithArray: - post: - tags: - - user - summary: Creates list of users with given input array - description: '' - operationId: createUsersWithArrayInput - produces: - - application/xml - - application/json - parameters: - - in: body - name: body - description: List of user object - required: true - schema: - type: array - items: - $ref: '#/definitions/User' - responses: - default: - description: successful operation - /user/createWithList: - post: - tags: - - user - summary: Creates list of users with given input array - description: '' - operationId: createUsersWithListInput - produces: - - application/xml - - application/json - parameters: - - in: body - name: body - description: List of user object - required: true - schema: - type: array - items: - $ref: '#/definitions/User' - responses: - default: - description: successful operation - /user/login: - get: - tags: - - user - summary: Logs user into the system - description: '' - operationId: loginUser - produces: - - application/xml - - application/json - parameters: - - name: username - in: query - description: The user name for login - required: true - type: string - - name: password - in: query - description: The password for login in clear text - required: true - type: string - responses: - '200': - description: successful operation - schema: - type: string - headers: - X-Rate-Limit: - type: integer - format: int32 - description: calls per hour allowed by the user - X-Expires-After: - type: string - format: date-time - description: date in UTC when toekn expires - '400': - description: Invalid username/password supplied - /user/logout: - get: - tags: - - user - summary: Logs out current logged in user session - description: '' - operationId: logoutUser - produces: - - application/xml - - application/json - parameters: [] - responses: - default: - description: successful operation - '/user/{username}': - get: - tags: - - user - summary: Get user by user name - description: '' - operationId: getUserByName - produces: - - application/xml - - application/json - parameters: - - name: username - in: path - description: 'The name that needs to be fetched. Use user1 for testing.' - required: true - type: string - responses: - '200': - description: successful operation - schema: - $ref: '#/definitions/User' - '400': - description: Invalid username supplied - '404': - description: User not found - put: - tags: - - user - summary: Updated user - description: This can only be done by the logged in user. - operationId: updateUser - produces: - - application/xml - - application/json - parameters: - - name: username - in: path - description: name that need to be deleted - required: true - type: string - - in: body - name: body - description: Updated user object - required: true - schema: - $ref: '#/definitions/User' - responses: - '400': - description: Invalid user supplied - '404': - description: User not found - delete: - tags: - - user - summary: Delete user - description: This can only be done by the logged in user. - operationId: deleteUser - produces: - - application/xml - - application/json - parameters: - - name: username - in: path - description: The name that needs to be deleted - required: true - type: string - responses: - '400': - description: Invalid username supplied - '404': - description: User not found securityDefinitions: petstore_auth: type: oauth2 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 index 0eb28542e6..92a8b3fe96 100644 --- 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 @@ -1,878 +1,93 @@ -/* - * 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 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.io.IOException; -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import java.util.function.UnaryOperator; +import static org.citrusframework.spi.Resources.create; -@Generated(value = "org.citrusframework.openapi.generator.JavaCitrusCodegen") -public class PetApi implements GeneratedApi -{ +public class PetApi { + private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( + create("src/test/resources/apis/petstore.yaml") + ); - public static final PetApi INSTANCE = new PetApi(); - - public String getApiTitle() { - return "OpenAPI Petstore"; + public static PetApi openapiPetstore(HttpClient httpClient) { + return new PetApi(httpClient); } - public String getApiVersion() { - return "1.0.0"; - } + private final HttpClient httpClient; - public String getApiPrefix() { - return "PetStore"; + private PetApi(HttpClient httpClient) { + this.httpClient = httpClient; } - public Map getApiInfoExtensions() { - Map infoExtensionMap = new HashMap<>(); - infoExtensionMap.put("x-citrus-api-name", "petstore"); - infoExtensionMap.put("x-citrus-app", "PETS"); - return infoExtensionMap; + public PetstoreAction getPetById() { + return petstoreAction(new GetPetByIdRequest()); } - /** addPet (POST /pet) - Add a new pet to the store - - **/ - public static class AddPetRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet"; - private final Logger coverageLogger = LoggerFactory.getLogger(AddPetRequest.class); - - - public AddPetRequest() { - // 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() + ":addPetRequestType"); - } - - public String getOperationName() { - return "addPet"; - } - - public String getMethod() { - return "POST"; - } - - public String getPath() { - return "/pet"; - } - - /** - * 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, "addPet;POST;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - private String replacePathParams(String endpoint) { - - return endpoint; - } + private PetstoreAction petstoreAction(B requestBuilder) { + return new PetstoreAction<>(httpClient, petstoreSpec, requestBuilder); } - /** deletePet (DELETE /pet/{petId}) - Deletes a pet - - **/ - public static class DeletePetRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet/{petId}"; - private final Logger coverageLogger = LoggerFactory.getLogger(DeletePetRequest.class); - - private String petId; - - - public DeletePetRequest() { - // 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() + ":deletePetRequestType"); - } - - public String getOperationName() { - return "deletePet"; - } - - public String getMethod() { - return "DELETE"; - } - - public String getPath() { - return "/pet/{petId}"; - } - - /** - * 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, "deletePet;DELETE;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - public void setPetId(String petId) { - this.petId = petId; - } - - private String replacePathParams(String endpoint) { - endpoint = endpoint.replace("{" + "petId" + "}", petId); - return endpoint; - } - } - /** findPetsByStatus (GET /pet/findByStatus) - Finds Pets by status - - **/ - public static class FindPetsByStatusRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet/findByStatus"; - private final Logger coverageLogger = LoggerFactory.getLogger(FindPetsByStatusRequest.class); - - private String status; - - - public FindPetsByStatusRequest() { - // 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() + ":findPetsByStatusRequestType"); - } - - public String getOperationName() { - return "findPetsByStatus"; - } - - public String getMethod() { - return "GET"; - } - - public String getPath() { - return "/pet/findByStatus"; - } - - /** - * 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.status)) { - queryParams.put("status", context.replaceDynamicContentInString(this.status)); - httpClientRequestActionBuilder.queryParam("status", this.status); - } - - 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, "findPetsByStatus;GET;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - public void setStatus(String status) { - this.status = status; - } - - private String replacePathParams(String endpoint) { - - return endpoint; - } - } - /** findPetsByTags (GET /pet/findByTags) - Finds Pets by tags - - **/ - public static class FindPetsByTagsRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet/findByTags"; - private final Logger coverageLogger = LoggerFactory.getLogger(FindPetsByTagsRequest.class); - - private String tags; - - - public FindPetsByTagsRequest() { - // 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() + ":findPetsByTagsRequestType"); - } - - public String getOperationName() { - return "findPetsByTags"; - } - - public String getMethod() { - return "GET"; - } - - public String getPath() { - return "/pet/findByTags"; - } - - /** - * 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.tags)) { - queryParams.put("tags", context.replaceDynamicContentInString(this.tags)); - httpClientRequestActionBuilder.queryParam("tags", this.tags); - } - - 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, "findPetsByTags;GET;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - public void setTags(String tags) { - this.tags = tags; - } - - private String replacePathParams(String endpoint) { - - return endpoint; - } - } - /** getPetById (GET /pet/{petId}) - Find pet by ID - - **/ - public static class GetPetByIdRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet/{petId}"; - private final Logger coverageLogger = LoggerFactory.getLogger(GetPetByIdRequest.class); - - private String petId; - - - @Value("${" + "petStoreEndpoint.basic.username:#{null}}") - private String basicUsername; - @Value("${" + "petStoreEndpoint.basic.password:#{null}}") - private String basicPassword; - - - public GetPetByIdRequest() { - // 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() + ":getPetByIdRequestType"); - } - - public String getOperationName() { + /** + * getPetById (GET /pet/{petId}) + * Find pet by ID + **/ + public static class GetPetByIdRequest extends OperationRequestBuilder { + @Override + public String getOperationId() { return "getPetById"; } - public String getMethod() { - return "GET"; - } - - public String getPath() { - return "/pet/{petId}"; - } - - /** - * 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(",", "{", "}")); - - - if(basicUsername != null && basicPassword != null){ - messageBuilderSupport.header("Authorization", "Basic " + Base64.getEncoder().encodeToString((context.replaceDynamicContentInString(basicUsername)+":"+context.replaceDynamicContentInString(basicPassword)).getBytes())); - } - - httpClientRequestActionBuilder.withReferenceResolver(context.getReferenceResolver()); - httpClientRequestActionBuilder = customizeBuilder(INSTANCE, context, httpClientRequestActionBuilder); - - httpClientRequestActionBuilder.build().execute(context); - - coverageLogger.trace(coverageMarker, "getPetById;GET;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - public void setPetId(String petId) { - this.petId = petId; - } - - - public void setBasicUsername(String basicUsername) { - this.basicUsername = basicUsername; + public GetPetByIdRequest withPetId(String petId) { + openApiOperation.withParameter("petId", petId); + return this; } - public void setBasicPassword(String basicPassword) { - this.basicPassword = basicPassword; + public GetPetByIdRequest withCorrelationIds(String correlationIds) { + openApiOperation.withParameter("correlationIds", correlationIds); + return this; } - - private String replacePathParams(String endpoint) { - endpoint = endpoint.replace("{" + "petId" + "}", petId); - return endpoint; - } - } - /** updatePet (PUT /pet) - Update an existing pet - - **/ - public static class UpdatePetRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet"; - private final Logger coverageLogger = LoggerFactory.getLogger(UpdatePetRequest.class); - - - public UpdatePetRequest() { - // 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() + ":updatePetRequestType"); - } - - public String getOperationName() { - return "updatePet"; - } - - public String getMethod() { - return "PUT"; - } - - public String getPath() { - return "/pet"; - } - - /** - * 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, "updatePet;PUT;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - private String replacePathParams(String endpoint) { - - return endpoint; + public GetPetByIdRequest withVerbose(boolean verbose) { + openApiOperation.withParameter("verbose", verbose); + return this; } } - /** updatePetWithForm (POST /pet/{petId}) - Updates a pet in the store with form data - - **/ - public static class UpdatePetWithFormRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet/{petId}"; - private final Logger coverageLogger = LoggerFactory.getLogger(UpdatePetWithFormRequest.class); - - private String petId; - - - public UpdatePetWithFormRequest() { - // 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() + ":updatePetWithFormRequestType"); - } - public String getOperationName() { - return "updatePetWithForm"; - } + public static abstract class OperationRequestBuilder { + protected final OpenApiOperationBuilder openApiOperation = OpenApiOperationBuilder.operation(getOperationId()); - public String getMethod() { - return "POST"; - } + public abstract String getOperationId(); - public String getPath() { - return "/pet/{petId}"; - } - - /** - * 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, "updatePetWithForm;POST;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - public void setPetId(String petId) { - this.petId = petId; - } - - private String replacePathParams(String endpoint) { - endpoint = endpoint.replace("{" + "petId" + "}", petId); - return endpoint; + public OpenApiOperationBuilder build() { + return openApiOperation; } } - /** uploadFile (POST /pet/{petId}/uploadImage) - uploads an image - - **/ - public static class UploadFileRequest extends PetStoreAbstractTestRequest implements GeneratedApiRequest { - - private static final String ENDPOINT = "/pet/{petId}/uploadImage"; - private final Logger coverageLogger = LoggerFactory.getLogger(UploadFileRequest.class); - private String petId; + public static class PetstoreAction extends OpenApiClientActionBuilder { + private final T operation; - private String additionalMetadata; - - private String _file; - - - public UploadFileRequest() { - // 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() + ":uploadFileRequestType"); - } - - public String getOperationName() { - return "uploadFile"; - } - - public String getMethod() { - return "POST"; + private PetstoreAction(Endpoint httpClient, OpenApiSpecification specification, T operation) { + super(httpClient, specification); + this.operation = operation; } - public String getPath() { - return "/pet/{petId}/uploadImage"; + public OpenApiClientRequestActionBuilder send(UnaryOperator builderProvider) { + var builder = builderProvider.apply(operation); + var send = send(builder.build()); + send.fork(true); + return send; } - /** - * 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(additionalMetadata)) { - // first try to load from resource - ClassPathResource resource = null; - try { - resource = new ClassPathResource(additionalMetadata); - } - catch(Exception ignore) { - // Use plain text instead of resource - } - - if(resource != null && resource.exists()){ - multiValues.add("additionalMetadata", resource); - } else { - multiValues.add("additionalMetadata", additionalMetadata); - } - bodyLog += additionalMetadata.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") +","; - } - if (StringUtils.isNotBlank(_file)) { - multiValues.add("_file", new ClassPathResource(_file)); - bodyLog += _file.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, "uploadFile;POST;\"" + - query.replace("\n", "\\n").replace("\r", "\\r").replace("\"", "\"\"") + "\";\"" + - bodyLog); - } - - public void setPetId(String petId) { - this.petId = petId; - } - - public void setAdditionalMetadata(String additionalMetadata) { - this.additionalMetadata = additionalMetadata; - } - - public void set_file(String _file) { - this._file = _file; - } - - private String replacePathParams(String endpoint) { - endpoint = endpoint.replace("{" + "petId" + "}", petId); - return endpoint; + public OpenApiClientResponseActionBuilder receive() { + return receive(operation.getOperationId(), "200"); } } } 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 e92fd84d94..38f2cc887f 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 @@ -43,6 +43,15 @@ ${project.version} + + org.openapitools + openapi-generator + + + org.openapitools + openapi-generator-maven-plugin + ${openapi-generator-maven-plugin} + commons-io commons-io 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..231120d1d1 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 { + @MethodSource("executeMojoWithConfigurations") + 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()); @@ -212,22 +233,23 @@ private void assertApiType(ApiConfig apiConfig) throws IOException { switch (apiConfig.getType()) { case REST -> text = "HttpClient httpClient"; case SOAP -> text = "WebServiceClient wsClient"; - default -> throw new IllegalArgumentException(String.format("No apiTye set in ApiConfig. Expected one of %s", - stream(ApiType.values()).map(ApiType::toString).collect( - Collectors.joining()))); + default -> + throw new IllegalArgumentException(String.format("No apiTye set in ApiConfig. Expected one of %s", + stream(ApiType.values()).map(ApiType::toString).collect( + Collectors.joining()))); } assertThat(getContentOfFile(apiConfig, "AbstractTestRequest.java")).contains(text); } 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 +266,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 +279,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 +315,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/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"); + } + } +} From 276fb14296591e5fd9306cc08eff39f26367b188 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Fri, 5 Jul 2024 15:26:58 +0200 Subject: [PATCH 12/16] disable deprecated tests --- .../openapi/generator/GeneratedApiIT.java | 108 ++++++------ .../openapi/generator/GetPetByIdIT.java | 154 +++++------------- .../generator/JavaCitrusCodegenIT.java | 118 +++++++------- .../generator/JavaCitrusCodegenTest.java | 137 ++++++++-------- .../payloads/getPetByIdControlMessage1.json | 13 +- .../extension/PetStoreNamespaceHandler.java | 1 - .../spring/PetStoreBeanConfiguration.java | 1 - 7 files changed, 232 insertions(+), 300 deletions(-) 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 dfa038e266..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 @@ -77,14 +77,14 @@ //@ExtendWith({CitrusSpringExtension.class}) //@SpringBootTest(classes = {CitrusSpringConfig.class, GeneratedApiIT.Config.class}) //@TestPropertySource( -// properties = {"applicationServiceClient.basic.username=Max Mustermann", -// "applicationServiceClient.basic.password=Top secret"} +// properties = {"applicationServiceClient.basic.username=Max Mustermann", +// "applicationServiceClient.basic.password=Top secret"} //) class GeneratedApiIT { // TODO TAT-1291 migrate tests -// @Autowired + // @Autowired // private ApplicationContext applicationContext; // // @Autowired @@ -567,55 +567,55 @@ class GeneratedApiIT { // return receiveMessage; // } // -// public static class Config { -// -// @Bean(name = {"applicationServiceClient", "multipartTestEndpoint", -// "soapSampleStoreEndpoint", "petStoreEndpoint"}) -// public HttpClient applicationServiceClient() { -// HttpClient clientMock = mock(); -// EndpointConfiguration endpointConfigurationMock = mock(); -// when(clientMock.getEndpointConfiguration()).thenReturn(new HttpEndpointConfiguration()); -// when(endpointConfigurationMock.getTimeout()).thenReturn(5000L); -// return clientMock; -// } -// -// @Bean -// public ApiActionBuilderCustomizerService customizer() { -// return new ApiActionBuilderCustomizerService() { -// @Override -// public > T build( -// GeneratedApi generatedApi, TestAction action, TestContext context, T builder) { -// builder.getMessageBuilderSupport() -// .header("x-citrus-api-version", generatedApi.getApiVersion()); -// return builder; -// } -// }; -// } -// -// @Bean({"oas3", "testSchema"}) -// public SimpleJsonSchema testSchema() { -// JsonSchema schemaMock = mock(); -// SimpleJsonSchema jsonSchemaMock = mock(); -// -// when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); -// -// Set okReport = new HashSet<>(); -// when(schemaMock.validate(any())).thenReturn(okReport); -// return jsonSchemaMock; -// } -// -// @Bean -// public SimpleJsonSchema failingTestSchema() { -// JsonSchema schemaMock = mock(); -// SimpleJsonSchema jsonSchemaMock = mock(); -// -// when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); -// -// Set nokReport = new HashSet<>(); -// nokReport.add(new ValidationMessage.Builder().customMessage( -// "This is a simulated validation error message").build()); -// when(schemaMock.validate(any())).thenReturn(nokReport); -// return jsonSchemaMock; -// } -// } + public static class Config { + + @Bean(name = {"applicationServiceClient", "multipartTestEndpoint", + "soapSampleStoreEndpoint", "petStoreEndpoint"}) + public HttpClient applicationServiceClient() { + HttpClient clientMock = mock(); + EndpointConfiguration endpointConfigurationMock = mock(); + when(clientMock.getEndpointConfiguration()).thenReturn(new HttpEndpointConfiguration()); + when(endpointConfigurationMock.getTimeout()).thenReturn(5000L); + return clientMock; + } + + @Bean + public ApiActionBuilderCustomizerService customizer() { + return new ApiActionBuilderCustomizerService() { + @Override + public > T build( + GeneratedApi generatedApi, TestAction action, TestContext context, T builder) { + builder.getMessageBuilderSupport() + .header("x-citrus-api-version", generatedApi.getApiVersion()); + return builder; + } + }; + } + + @Bean({"oas3", "testSchema"}) + public SimpleJsonSchema testSchema() { + JsonSchema schemaMock = mock(); + SimpleJsonSchema jsonSchemaMock = mock(); + + when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); + + Set okReport = new HashSet<>(); + when(schemaMock.validate(any())).thenReturn(okReport); + return jsonSchemaMock; + } + + @Bean + public SimpleJsonSchema failingTestSchema() { + JsonSchema schemaMock = mock(); + SimpleJsonSchema jsonSchemaMock = mock(); + + when(jsonSchemaMock.getSchema()).thenReturn(schemaMock); + + Set nokReport = new HashSet<>(); + nokReport.add(new ValidationMessage.Builder().customMessage( + "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 6c01c18ba1..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 @@ -4,18 +4,24 @@ import org.citrusframework.annotations.CitrusResource; import org.citrusframework.annotations.CitrusTest; import org.citrusframework.config.CitrusSpringConfig; +import org.citrusframework.context.TestContext; import org.citrusframework.http.client.HttpClient; 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.openapi.generator.rest.petstore.spring.PetStoreBeanConfiguration; -import org.citrusframework.spi.BindToRegistry; import org.citrusframework.util.SocketUtils; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; import org.springframework.test.context.ContextConfiguration; +import java.io.File; + +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; @@ -25,38 +31,14 @@ @CitrusSpringSupport -@ContextConfiguration(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class}) +@ContextConfiguration(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, GetPetByIdIT.Config.class}) class GetPetByIdIT { - private final int port = SocketUtils.findAvailableTcpPort(8080); - -// private final MessageQueue inboundQueue = new DefaultMessageQueue("inboundQueue"); -// private final EndpointAdapter endpointAdapter = new DirectEndpointAdapter(direct() -// .synchronous() -// .timeout(5000L) -// .queue(inboundQueue) -// .build()); - - @BindToRegistry - private final HttpClient httpClient = new HttpClientBuilder() - .requestUrl("http://localhost:%d".formatted(port)) - .build(); - - // TODO TAT-1291 the GetPetByIdIT class gets instantiated twice, but the second time - // the httpServer, respectively the "endpoint", is not initialized correctly... - // why is the Test Class instantiated twice (once per testmethod?) anyway?! - @BindToRegistry - private final HttpServer httpServer = new HttpServerBuilder() - .port(port) - // .endpointAdapter(endpointAdapter) - .timeout(5000L) - .autoStart(true) - .defaultStatus(NO_CONTENT) - .build(); - - @BeforeEach - public void beforeTest() { - } + @Autowired + private HttpClient httpClient; + + @Autowired + private HttpServer httpServer; @Test @CitrusTest @@ -95,8 +77,6 @@ void testJsonFileBody(@CitrusResource TestCaseRunner runner) { openapiPetstore(httpClient) .getPetById() .send(request -> request - // TODO - // .withBodyFile("org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json") .withPetId(2002L) .withCorrelationIds("5599") .withVerbose(true) @@ -105,16 +85,17 @@ void testJsonFileBody(@CitrusResource TestCaseRunner runner) { respondPet(runner); + var expectedResponse = new File("src/test/resources/org/citrusframework/openapi/generator/GeneratedApiTest/payloads/getPetByIdControlMessage1.json"); runner.$( openapiPetstore(httpClient) .getPetById() .receive() .message() - .validate( - pathExpression() - .jsonPath("$.name", "Snoopy") - .jsonPath("$.id", 2002) - ) + .validate((Message message, TestContext context) -> { + assertThat(expectedResponse).exists().content().satisfies(expectedContent -> { + assertThat(message.getPayload(String.class)).isEqualToIgnoringWhitespace(expectedContent); + }); + }) ); } @@ -147,78 +128,27 @@ private void respondPet(TestCaseRunner runner) { .contentType("application/json").type(JSON)); } - /* @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 - ) - ); - } - - @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") - ); + @TestConfiguration + public static class Config { + + 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(); + } } - - @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"); - }*/ } 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 dd4f52ece7..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 @@ -31,63 +31,63 @@ */ class JavaCitrusCodegenIT { - 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()); - } +// 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 d015f31abf..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 @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.citrusframework.openapi.generator.JavaCitrusCodegen.CODEGEN_NAME; -import static org.citrusframework.openapi.generator.PathUtils.getProjectPathOfClass; import java.io.File; import java.io.IOException; @@ -82,72 +81,72 @@ void areAdditionalPropertiesProcessedTest() { assertThat(codegen.getTargetXmlnsNamespace()).isEqualTo(targetXmlnsNamespace); } - @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); - } +// @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/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/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 index 84de90fb2c..1382acba99 100644 --- 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 @@ -22,7 +22,6 @@ package org.citrusframework.openapi.generator.rest.petstore.citrus.extension; -import org.citrusframework.openapi.generator.rest.petstore.request.PetApi; 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; 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 index bf6750e796..66dce7320c 100644 --- 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 @@ -24,7 +24,6 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; -import org.citrusframework.openapi.generator.rest.petstore.request.PetApi; import org.citrusframework.openapi.generator.rest.petstore.request.StoreApi; import org.citrusframework.openapi.generator.rest.petstore.request.UserApi; import javax.annotation.processing.Generated; From 8ff1864ecf1b7a702908448fc25c3187e37a7fe6 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Fri, 5 Jul 2024 17:00:19 +0200 Subject: [PATCH 13/16] refactor --- .../citrus-test-api-generator-core/pom.xml | 6 ++ .../maven/plugin/TestApiGeneratorMojo.java | 80 ------------------- .../TestApiGeneratorMojoIntegrationTest.java | 9 +-- 3 files changed, 10 insertions(+), 85 deletions(-) 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 c00b9f1ce9..59aa261d1c 100644 --- a/test-api-generator/citrus-test-api-generator-core/pom.xml +++ b/test-api-generator/citrus-test-api-generator-core/pom.xml @@ -80,6 +80,12 @@ ${project.version} test + + org.citrusframework + citrus-openapi + ${project.version} + test + org.springframework.boot spring-boot-test diff --git a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java index e8a639a585..d37b4d8ffe 100644 --- a/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java +++ b/test-api-generator/citrus-test-api-generator-maven-plugin/src/main/java/org/citrusframework/maven/plugin/TestApiGeneratorMojo.java @@ -22,8 +22,6 @@ import static org.citrusframework.openapi.generator.JavaCitrusCodegen.API_TYPE; import static org.citrusframework.openapi.generator.JavaCitrusCodegen.PREFIX; import static org.citrusframework.openapi.generator.JavaCitrusCodegen.TARGET_XMLNS_NAMESPACE; -import static java.lang.String.format; -import static org.apache.commons.lang3.StringUtils.isBlank; import com.google.common.annotations.VisibleForTesting; import java.io.File; @@ -177,7 +175,6 @@ public String schemaFolder(ApiConfig apiConfig) { @Override public void execute() throws MojoExecutionException { - for (int index = 0; index < apis.size(); index++) { ApiConfig apiConfig = apis.get(index); validateApiConfig(index, apiConfig); @@ -222,7 +219,6 @@ private void requireNonBlankParameter(String name, int index, String parameterVa * Replace the placeholders '%PREFIX%' and '%VERSION%' in the given text. */ static String replaceDynamicVars(String text, String prefix, String version) { - if (text == null) { return null; } @@ -331,83 +327,10 @@ public static class ApiConfig { @Parameter(property = API_NAMESPACE_PROPERTY, defaultValue = DEFAULT_TARGET_NAMESPACE_TEMPLATE) private String targetXmlnsNamespace = DEFAULT_TARGET_NAMESPACE_TEMPLATE; - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - public String qualifiedEndpoint() { return DEFAULT_ENDPOINT.equals(endpoint) ? getPrefix().toLowerCase() + "Endpoint" : endpoint; } - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; - } - - public ApiType getType() { - return type; - } - - public void setType(ApiType type) { - this.type = type; - } - - public void setUseTags(boolean useTags) { - this.useTags = useTags; - } - - public String getInvokerPackage() { - return invokerPackage; - } - - public void setInvokerPackage(String invokerPackage) { - this.invokerPackage = invokerPackage; - } - - public String getApiPackage() { - return apiPackage; - } - - public void setApiPackage(String apiPackage) { - this.apiPackage = apiPackage; - } - - public String getModelPackage() { - return modelPackage; - } - - public void setModelPackage(String modelPackage) { - this.modelPackage = modelPackage; - } - - public String getTargetXmlnsNamespace() { - return targetXmlnsNamespace; - } - - public void setTargetXmlnsNamespace(String targetXmlnsNamespace) { - this.targetXmlnsNamespace = targetXmlnsNamespace; - } - Map toConfigOptionsProperties() { Map configOptionsProperties = new HashMap<>(); @@ -426,8 +349,5 @@ Map toConfigOptionsProperties() { return configOptionsProperties; } - } - - } 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 33be17b44e..39942a7bfd 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 @@ -96,7 +96,7 @@ public static Stream executeMojoWithConfigurations() { } @ParameterizedTest - @MethodSource("executeMojoWithConfigurations") + @MethodSource public void executeMojoWithConfigurations(String configName, Exception expectedException) throws Exception { @@ -237,10 +237,9 @@ private void assertApiType(ApiConfig apiConfig) throws IOException { switch (apiConfig.getType()) { case REST -> text = "HttpClient httpClient"; case SOAP -> text = "WebServiceClient wsClient"; - default -> - throw new IllegalArgumentException(String.format("No apiTye set in ApiConfig. Expected one of %s", - stream(ApiType.values()).map(ApiType::toString).collect( - Collectors.joining()))); + default -> throw new IllegalArgumentException(String.format("No apiTye set in ApiConfig. Expected one of %s", + stream(ApiType.values()).map(ApiType::toString).collect( + Collectors.joining()))); } assertThat(getContentOfFile(apiConfig, "AbstractTestRequest.java")).contains(text); } From 73f0d27c637f1b3e948f06084d5bf5ffa1d24ea3 Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Fri, 5 Jul 2024 17:17:53 +0200 Subject: [PATCH 14/16] update TODOs --- .../openapi/OpenApiTestDataGenerator.java | 2 +- .../OpenApiClientRequestActionBuilder.java | 2 +- .../openapi/integration/OpenApiClientIT.java | 53 +++++++++---------- .../generator/gen/OpenapiPetstore.java | 2 +- .../generator/gen/OpenapiPetstoreTest.java | 1 + 5 files changed, 29 insertions(+), 31 deletions(-) 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 6b31655892..ed690530a8 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 @@ -313,7 +313,7 @@ public static String createValidationExpression( } /** - * TODO TAT-1291 this method does not respect optional/required properties + * TODO BUG this method does not respect optional/required properties * Create validation expression using functions according to schema type and format. * * @param schema 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 433324e5d1..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 @@ -119,7 +119,7 @@ public static OasItem create(String operationId, OasDocument oasDocument) { @Override public Message build(TestContext context, String messageType) { - // TODO: TAT-1291 - make parameter substitution more explicit? + // TODO TAT-1291 - make parameter substitution more explicit context.addVariables(parameters); OasDocument oasDocument = openApiSpec.getOpenApiDoc(context); var item = OasItem.create(operationId, oasDocument); 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 27536a40b6..97c58b45c6 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 @@ -66,10 +66,10 @@ public void getPetById() { variable("correlationIds", "1234abcd"); when(openapi(petstoreSpec) - .client(httpClient) - .send("getPetById") - .fork(true) - .message() + .client(httpClient) + .send("getPetById") + .fork(true) + .message() ); then(http().server(httpServer) @@ -77,7 +77,7 @@ public void getPetById() { .get("/pet/1001") .queryParam("verbose", "true") .message() - // TODO bug? - cannot check correlationId + // TODO BUG? - cannot check correlationId // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader .header("correlationIds", "1234abcd") @@ -106,10 +106,10 @@ public void getPetById_requiredParamsShouldBeGeneratedIfNotProvided() { ); then(http().server(httpServer) - .receive() - .get("@matches('/pet/\\d+')@") - .message() - // TODO bug? - cannot check correlationId + .receive() + .get("@matches('/pet/\\d+')@") + .message() + // TODO BUG? - cannot check correlationId // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader // .header("correlationId", "@matches('\\w+')@") @@ -188,7 +188,7 @@ public void getPetById_generated() { .receive("getPetById", HttpStatus.OK)); } - /* TODO create issues + // TODO TAT-1291 create issues for Bugs @CitrusTest public void BUG_getPetById_paramsCanAlsoBeSetWithMessageBuilder() { variable("petId", "1001"); @@ -224,7 +224,6 @@ public void BUG_getPetById_paramsCanAlsoBeSetWithMessageBuilder() { } @CitrusTest - @Ignore public void BUG_should_be_possible_to_switch_content_type__to_xml() { variable("petId", "1001"); @@ -252,9 +251,9 @@ public void BUG_should_be_possible_to_switch_content_type__to_xml() { .client(httpClient) .receive("getPetById", HttpStatus.OK) .message() - // TODO XML bodies do not seem to work, even if there is just XML as "produces" in the spec + // TODO BUG XML bodies do not seem to work, even if there is just XML as "produces" in the spec .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.xml")) - // TODO the type/contentType statements are useless, if there is another type in the spec. + // TODO BUG the type/contentType statements are useless, if there is another type in the spec. // even if there are two. i.E: // # this will always use JSON as type // produces: @@ -265,7 +264,6 @@ public void BUG_should_be_possible_to_switch_content_type__to_xml() { } @CitrusTest - @Ignore public void BUG_should_only_validate_the_presence_of_required_properties() { variable("petId", "1001"); @@ -286,7 +284,7 @@ public void BUG_should_only_validate_the_presence_of_required_properties() { .send() .response(HttpStatus.OK) .message() - // this should be valid, according to the spec-file + // TODO BUG this should be valid, according to the spec-file .body(""" { "category": {}, @@ -302,7 +300,6 @@ public void BUG_should_only_validate_the_presence_of_required_properties() { .message() ); } - */ @CitrusTest public void postAddPet() { @@ -318,18 +315,18 @@ public void postAddPet() { .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/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java index 5ad19b5e25..e40075b73a 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java @@ -12,7 +12,7 @@ import static org.citrusframework.spi.Resources.create; -// TODO move to mustache File +// TODO TAT-1291 is in api.mustache File - delete this class public class OpenapiPetstore { private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( create("src/test/resources/apis/petstore.yaml") diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java index bda71a12b4..4577e64541 100644 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java +++ b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import static org.springframework.http.HttpStatus.OK; +// TODO TAT-1291 can this test be removed? in favor of GetPetByIdIT.java @ExtendWith(CitrusSpringExtension.class) @SpringBootTest(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, Config.class}) class OpenapiPetstoreTest { From 3517f7a869710e52a2b37927fd89eb2cb14f744a Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Mon, 15 Jul 2024 15:52:58 +0200 Subject: [PATCH 15/16] remove bug related todos created issues for that: - https://github.com/citrusframework/citrus/issues/1190 - https://github.com/citrusframework/citrus/issues/1189 --- .../openapi/integration/OpenApiClientIT.java | 113 ------------------ 1 file changed, 113 deletions(-) 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 97c58b45c6..f9831727b6 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 @@ -188,119 +188,6 @@ public void getPetById_generated() { .receive("getPetById", HttpStatus.OK)); } - // TODO TAT-1291 create issues for Bugs - @CitrusTest - public void BUG_getPetById_paramsCanAlsoBeSetWithMessageBuilder() { - variable("petId", "1001"); - - when(openapi(petstoreSpec) - .client(httpClient) - .send("getPetById") - .fork(true) - .message() - // TODO Bug - if the params are already set on the message, they get overwritten - .queryParam("verbose", "false") - .header("correlationIds", "1234F5gXW") - ); - - then(http().server(httpServer) - .receive() - .get("/pet/1001") - .queryParam("verbose", "true") - .message() - .header("correlationIds", "1234F5gXW") - ); - - 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 BUG_should_be_possible_to_switch_content_type__to_xml() { - variable("petId", "1001"); - - when(openapi(petstoreSpec) - .client(httpClient) - .send("getPetById") - .message() - .accept("application/xml") - .fork(true)); - - then(http().server(httpServer) - .receive() - .get("/pet/${petId}") - .message() - .accept("@contains('application/xml')@")); - - then(http().server(httpServer) - .send() - .response(HttpStatus.OK) - .message() - .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.xml")) - .contentType("application/xml")); - - then(openapi(petstoreSpec) - .client(httpClient) - .receive("getPetById", HttpStatus.OK) - .message() - // TODO BUG XML bodies do not seem to work, even if there is just XML as "produces" in the spec - .body(Resources.create("classpath:org/citrusframework/openapi/petstore/pet.xml")) - // TODO BUG the type/contentType statements are useless, if there is another type in the spec. - // even if there are two. i.E: - // # this will always use JSON as type - // produces: - // - application/json - // - application/xml - .contentType("application/xml") - .type(XML)); - } - - @CitrusTest - public void BUG_should_only_validate_the_presence_of_required_properties() { - variable("petId", "1001"); - - when(openapi(petstoreSpec) - .client(httpClient) - .send("getPetById") - .message() - .accept("application/json") - .fork(true)); - - then(http().server(httpServer) - .receive() - .get("/pet/${petId}") - .message() - .accept("@contains('application/json')@")); - - then(http().server(httpServer) - .send() - .response(HttpStatus.OK) - .message() - // TODO BUG this should be valid, according to the spec-file - .body(""" - { - "category": {}, - "name": "", - "status": "sold" - } - """) - .contentType("application/json")); - - then(openapi(petstoreSpec) - .client(httpClient) - .receive("getPetById", HttpStatus.OK) - .message() - ); - } - @CitrusTest public void postAddPet() { variable("petId", "1001"); From 54ec31aee4a6e85394340d983dd4539d1a4c02bf Mon Sep 17 00:00:00 2001 From: Sterchi Daniel Date: Wed, 17 Jul 2024 10:54:21 +0200 Subject: [PATCH 16/16] cleanup --- .../openapi/OpenApiTestDataGenerator.java | 1 - .../openapi/integration/OpenApiClientIT.java | 7 - .../openapi/generator/JavaCitrusCodegen.java | 56 -------- .../generator/gen/OpenapiPetstore.java | 90 ------------ .../generator/gen/OpenapiPetstoreTest.java | 128 ------------------ 5 files changed, 282 deletions(-) delete mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java delete mode 100644 test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java 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 ed690530a8..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 @@ -313,7 +313,6 @@ public static String createValidationExpression( } /** - * TODO BUG this method does not respect optional/required properties * Create validation expression using functions according to schema type and format. * * @param schema 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 f9831727b6..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 @@ -77,9 +77,6 @@ public void getPetById() { .get("/pet/1001") .queryParam("verbose", "true") .message() - // TODO BUG? - cannot check correlationId - // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 - // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader .header("correlationIds", "1234abcd") .accept("@contains('application/json')@") ); @@ -109,10 +106,6 @@ public void getPetById_requiredParamsShouldBeGeneratedIfNotProvided() { .receive() .get("@matches('/pet/\\d+')@") .message() - // TODO BUG? - cannot check correlationId - // see: org/citrusframework/validation/DefaultMessageHeaderValidator.java:68 - // see: org.citrusframework.message.MessageHeaderUtils.isSpringInternalHeader - // .header("correlationId", "@matches('\\w+')@") ); variable("petId", "1001"); 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 5228157320..ed63ee36f5 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 @@ -272,62 +272,6 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } - public void setApiPrefix(String apiPrefix) { - this.apiPrefix = apiPrefix; - } - - public String getHttpClient() { - return httpClient; - } - - public void setHttpClient(String httpClient) { - this.httpClient = httpClient; - } - - public String getHttpPathPrefix() { - return httpPathPrefix; - } - - public void setHttpPathPrefix(String httpPathPrefix) { - this.httpPathPrefix = httpPathPrefix; - } - - public String getOpenapiSchema() { - return openapiSchema; - } - - public void setOpenapiSchema(String openapiSchema) { - this.openapiSchema = openapiSchema; - } - - public String getResourceFolder() { - return resourceFolder; - } - - public void setResourceFolder(String resourceFolder) { - this.resourceFolder = resourceFolder; - } - - public String getGeneratedSchemaFolder() { - return generatedSchemaFolder; - } - - public void setGeneratedSchemaFolder(String generatedSchemaFolder) { - this.generatedSchemaFolder = generatedSchemaFolder; - } - - public String getTargetXmlnsNamespace() { - return targetXmlnsNamespace; - } - - public void setTargetXmlnsNamespace(String targetXmlnsNamespace) { - this.targetXmlnsNamespace = targetXmlnsNamespace; - } - - public String getApiPrefix() { - return apiPrefix; - } - private void addRestSupportingFiles(final String citrusFolder, String schemaFolder) { supportingFiles.add(new SupportingFile("schema.mustache", schemaFolder, apiPrefix.toLowerCase() + "-api.xsd")); supportingFiles.add(new SupportingFile("test_base.mustache", citrusFolder, apiPrefix + ABSTRACT_TEST_REQUEST_JAVA)); diff --git a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java deleted file mode 100644 index e40075b73a..0000000000 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstore.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.citrusframework.openapi.generator.gen; - -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; - -// TODO TAT-1291 is in api.mustache File - delete this class -public class OpenapiPetstore { - private static final OpenApiSpecification petstoreSpec = OpenApiSpecification.from( - create("src/test/resources/apis/petstore.yaml") - ); - - public static OpenapiPetstore openapiPetstore(HttpClient httpClient) { - return new OpenapiPetstore(httpClient); - } - - private final HttpClient httpClient; - - private OpenapiPetstore(HttpClient httpClient) { - this.httpClient = httpClient; - } - - public PetstoreAction getPetById() { - return petstoreAction(new GetPetByIdRequest()); - } - - private PetstoreAction petstoreAction(B requestBuilder) { - return new PetstoreAction<>(httpClient, petstoreSpec, requestBuilder); - } - - 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/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java b/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java deleted file mode 100644 index 4577e64541..0000000000 --- a/test-api-generator/citrus-test-api-generator-core/src/test/java/org/citrusframework/openapi/generator/gen/OpenapiPetstoreTest.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.citrusframework.openapi.generator.gen; - -import org.citrusframework.TestCaseRunner; -import org.citrusframework.annotations.CitrusResource; -import org.citrusframework.annotations.CitrusTest; -import org.citrusframework.config.CitrusSpringConfig; -import org.citrusframework.endpoint.EndpointConfiguration; -import org.citrusframework.http.client.HttpClient; -import org.citrusframework.http.client.HttpClientBuilder; -import org.citrusframework.http.client.HttpEndpointConfiguration; -import org.citrusframework.http.server.HttpServer; -import org.citrusframework.http.server.HttpServerBuilder; -import org.citrusframework.junit.jupiter.spring.CitrusSpringExtension; -import org.citrusframework.openapi.generator.gen.OpenapiPetstoreTest.Config; -import org.citrusframework.openapi.generator.rest.petstore.spring.PetStoreBeanConfiguration; -import org.citrusframework.spi.BindToRegistry; -import org.citrusframework.util.SocketUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -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 static org.citrusframework.http.actions.HttpActionBuilder.http; -import static org.citrusframework.message.MessageType.JSON; -import static org.citrusframework.openapi.generator.gen.OpenapiPetstore.openapiPetstore; -import static org.citrusframework.validation.PathExpressionValidationContext.Builder.pathExpression; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.springframework.http.HttpStatus.OK; - -// TODO TAT-1291 can this test be removed? in favor of GetPetByIdIT.java -@ExtendWith(CitrusSpringExtension.class) -@SpringBootTest(classes = {PetStoreBeanConfiguration.class, CitrusSpringConfig.class, Config.class}) -class OpenapiPetstoreTest { - - @BeforeEach - public void beforeTest() { - } - - private final int port = SocketUtils.findAvailableTcpPort(8080); - - @BindToRegistry - private final HttpServer httpServer = new HttpServerBuilder() - .port(port) - .timeout(5000L) - .autoStart(true) - .defaultStatus(HttpStatus.NO_CONTENT) - .build(); - - @BindToRegistry - private final HttpClient httpClient = new HttpClientBuilder() - .requestUrl("http://localhost:%d".formatted(port)) - .build(); - - @Test - @CitrusTest - void testFluentGeneratedOpenapiAction(@CitrusResource TestCaseRunner runner) { - runner.$(openapiPetstore(httpClient) - .getPetById() - .send(request -> request - .withPetId("2002") - .withCorrelationIds("5599") - .withVerbose(true)) - ); - - respondPet(runner); - - runner.$(openapiPetstore(httpClient) - .getPetById() - .receive() - .message() - .validate(pathExpression() - .jsonPath("$.id", "2002") - .jsonPath("$.category.id", "2002") - .jsonPath("$.tags[0].name", "generated") - ) - ); - } - - 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": "citrus:randomEnumValue('hasso','cutie','fluffy')", - "category": { - "id": ${petId}, - "name": "citrus:randomEnumValue('dog', 'cat', 'fish')" - }, - "photoUrls": [ "http://localhost:8080/photos/${petId}" ], - "tags": [ - { - "id": ${petId}, - "name": "generated" - } - ], - "status": "citrus:randomEnumValue('available', 'pending', 'sold')" - } - """) - .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; - } - } -}