diff --git a/client/deployment/pom.xml b/client/deployment/pom.xml index 04eccecbd..a995eb63d 100644 --- a/client/deployment/pom.xml +++ b/client/deployment/pom.xml @@ -14,7 +14,6 @@ 7.12.0 2.0.17 4.3.1 - 2.1.25 diff --git a/docs/modules/ROOT/assets/images/moqu-devui-card-framework.png b/docs/modules/ROOT/assets/images/moqu-devui-card-framework.png new file mode 100644 index 000000000..e9b7d0e40 Binary files /dev/null and b/docs/modules/ROOT/assets/images/moqu-devui-card-framework.png differ diff --git a/docs/modules/ROOT/assets/images/table-wiremock.png b/docs/modules/ROOT/assets/images/table-wiremock.png new file mode 100644 index 000000000..7113b186b Binary files /dev/null and b/docs/modules/ROOT/assets/images/table-wiremock.png differ diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 57338522b..5fdf5662d 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -1,2 +1,3 @@ * xref:client.adoc[Client Generator] -* xref:server.adoc[Server Generator] \ No newline at end of file +* xref:server.adoc[Server Generator] +* xref:moqu.adoc[Moqu Wiremock Generator] \ No newline at end of file diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock.adoc new file mode 100644 index 000000000..fe7a5ce9e --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock.adoc @@ -0,0 +1,32 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir]] [.property-path]##link:#quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir[`quarkus.openapi-generator.moqu.resource-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.moqu.resource-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Path to the Moqu (relative to the project). + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`openapi` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.adoc new file mode 100644 index 000000000..7a6b1b4da --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.adoc @@ -0,0 +1,32 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-moqu-wiremock_quarkus-resource-dir]] [.property-path]##link:#quarkus-openapi-generator-moqu-wiremock_quarkus-resource-dir[`quarkus.resource-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.resource-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Path to the Moqu (relative to the project). + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_RESOURCE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_RESOURCE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`openapi` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.openapi-generator.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.openapi-generator.adoc new file mode 100644 index 000000000..fe7a5ce9e --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator-moqu-wiremock_quarkus.openapi-generator.adoc @@ -0,0 +1,32 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir]] [.property-path]##link:#quarkus-openapi-generator-moqu-wiremock_quarkus-openapi-generator-moqu-resource-dir[`quarkus.openapi-generator.moqu.resource-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.openapi-generator.moqu.resource-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Path to the Moqu (relative to the project). + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPENAPI_GENERATOR_MOQU_RESOURCE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +|`openapi` + +|=== + diff --git a/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.adoc b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.adoc new file mode 100644 index 000000000..73cce0732 --- /dev/null +++ b/docs/modules/ROOT/pages/includes/quarkus-openapi-generator_quarkus.adoc @@ -0,0 +1,1199 @@ +[.configuration-legend] +icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime +[.configuration-reference.searchable, cols="80,.^10,.^10"] +|=== + +h|[.header-title]##Configuration property## +h|Type +h|Default + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-skip-form-model[`quarkus.skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-type-mappings-type-mappings[`quarkus.type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Type Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be used for a given OAS datatype (the keys of this map) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-import-mappings-import-mappings[`quarkus.import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Import Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given OAS datatype (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-schema-mappings-schema-mappings[`quarkus.schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-model-type-annotations[`quarkus.additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-enum-type-unexpected-member[`quarkus.additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-api-type-annotations[`quarkus.additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-additional-request-args[`quarkus.additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-return-response[`quarkus.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-enable-security-generation[`quarkus.enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-open-api-normalizer-normalizer[`quarkus.open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-mutiny[`quarkus.mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-mutiny-return-response[`quarkus.mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-generate-part-filename[`quarkus.generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-part-filename-value[`quarkus.part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-use-field-name-in-part-filename[`quarkus.use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-use-bean-validation[`quarkus.use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-generate-apis[`quarkus.generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-generate-models[`quarkus.generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-equals-hashcode[`quarkus.equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.equals-hashcode+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-verbose]] [.property-path]##link:#quarkus-openapi-generator_quarkus-verbose[`quarkus.verbose`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.verbose+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to log the internal generator codegen process in the default output or not. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_VERBOSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_VERBOSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`false` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-input-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-input-base-dir[`quarkus.input-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.input-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where OpenAPI files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_INPUT_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_INPUT_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-template-base-dir]] [.property-path]##link:#quarkus-openapi-generator_quarkus-template-base-dir[`quarkus.template-base-dir`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.template-base-dir+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to change the directory where template files must be found. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_TEMPLATE_BASE_DIR+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_TEMPLATE_BASE_DIR+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-validatespec]] [.property-path]##link:#quarkus-openapi-generator_quarkus-validatespec[`quarkus.validateSpec`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.validateSpec+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in an error. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_VALIDATESPEC+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_VALIDATESPEC+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +|`true` + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-include]] [.property-path]##link:#quarkus-openapi-generator_quarkus-include[`quarkus.include`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.include+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to specify files for which generation should be executed only + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_INCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_INCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-exclude]] [.property-path]##link:#quarkus-openapi-generator_quarkus-exclude[`quarkus.exclude`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.exclude+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Option to exclude file from generation + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_EXCLUDE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_EXCLUDE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-default-security-scheme]] [.property-path]##link:#quarkus-openapi-generator_quarkus-default-security-scheme[`quarkus.default-security-scheme`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.default-security-scheme+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Create security for the referenced security scheme + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_DEFAULT_SECURITY_SCHEME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_DEFAULT_SECURITY_SCHEME+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-skip-form-model]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-skip-form-model[`quarkus.spec."spec-item".skip-form-model`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".skip-form-model+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Whether to skip the generation of models for form parameters + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__SKIP_FORM_MODEL+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-type-mappings-type-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-type-mappings-type-mappings[`quarkus.spec."spec-item".type-mappings."type-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".type-mappings."type-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Type Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be used for a given OAS datatype (the keys of this map) + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__TYPE_MAPPINGS__TYPE_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-import-mappings-import-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-import-mappings-import-mappings[`quarkus.spec."spec-item".import-mappings."import-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".import-mappings."import-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Import Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given OAS datatype (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__IMPORT_MAPPINGS__IMPORT_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-schema-mappings-schema-mappings]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-schema-mappings-schema-mappings[`quarkus.spec."spec-item".schema-mappings."schema-mappings"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".schema-mappings."schema-mappings"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Schema Mapping is an OpenAPI Generator configuration specifying which Java types (the values) should be imported when a given schema type (the keys of this map) is used + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__SCHEMA_MAPPINGS__SCHEMA_MAPPINGS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-model-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-model-type-annotations[`quarkus.spec."spec-item".additional-model-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-model-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated model files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_MODEL_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-enum-type-unexpected-member]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-enum-type-unexpected-member[`quarkus.spec."spec-item".additional-enum-type-unexpected-member`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-enum-type-unexpected-member+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the enums should have an `UNEXPECTED` member to convey values that cannot be parsed. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_ENUM_TYPE_UNEXPECTED_MEMBER+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-api-type-annotations]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-api-type-annotations[`quarkus.spec."spec-item".additional-api-type-annotations`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-api-type-annotations+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +The specified annotations will be added to the generated api files + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_API_TYPE_ANNOTATIONS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-additional-request-args]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-additional-request-args[`quarkus.spec."spec-item".additional-request-args`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".additional-request-args+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Add custom/additional HTTP Headers or other args to every request + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ADDITIONAL_REQUEST_ARGS+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-return-response[`quarkus.spec."spec-item".return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if the methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-enable-security-generation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-enable-security-generation[`quarkus.spec."spec-item".enable-security-generation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".enable-security-generation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines if security support classes should be generated + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__ENABLE_SECURITY_GENERATION+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-open-api-normalizer-normalizer]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-open-api-normalizer-normalizer[`quarkus.spec."spec-item".open-api-normalizer."normalizer"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".open-api-normalizer."normalizer"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the normalizer options. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__OPEN_API_NORMALIZER__NORMALIZER_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-mutiny]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-mutiny[`quarkus.spec."spec-item".mutiny`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".mutiny+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable SmallRye Mutiny support. If you set this to `true`, all return types will be wrapped in `io.smallrye.mutiny.Uni`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MUTINY+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MUTINY+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-return-response]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-return-response[`quarkus.spec."spec-item".mutiny.return-response`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".mutiny.return-response+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines with SmallRye Mutiny enabled if methods should return `jakarta.ws.rs.core.Response` or a model. Default is `false`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_RETURN_RESPONSE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-mutiny-operation-ids-mutiny-multi-operation-ids[`quarkus.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".mutiny.operation-ids."mutiny-multi-operation-ids"+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Handles the return type for each operation, depending on the configuration. The following cases are supported: + +1. If `mutiny` is enabled and the operation ID is specified to return `Multi`: - The return type will be wrapped in `io.smallrye.mutiny.Multi`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Multi`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Multi`. - Otherwise, it will return `io.smallrye.mutiny.Multi`. + +2. If `mutiny` is enabled and the operation ID is specified to return `Uni`: - The return type will be wrapped in `io.smallrye.mutiny.Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni`. + +3. If `mutiny` is enabled but no specific operation ID is configured for `Multi` or `Uni`: - The return type defaults to `Uni`. - If `mutiny.return-response` is enabled, the return type will be `io.smallrye.mutiny.Uni`. - If the operation has a void return type, it will return `io.smallrye.mutiny.Uni`. - Otherwise, it will return `io.smallrye.mutiny.Uni``. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MUTINY_OPERATION_IDS__MUTINY_MULTI_OPERATION_IDS_+++` +endif::add-copy-button-to-env-var[] +-- +|Map +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-generate-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-generate-part-filename[`quarkus.spec."spec-item".generate-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".generate-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the `PartFilename` (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) annotation should be generated for MultipartForm POJOs. By setting to `false`, the annotation will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-part-filename-value]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-part-filename-value[`quarkus.spec."spec-item".part-filename-value`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".part-filename-value+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines the filename for a part in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. In case no value is set, the default one is `File` or `file`, depending on the `CommonItemConfig++#++useFieldNameInPartFilename` configuration. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__PART_FILENAME_VALUE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-use-field-name-in-part-filename]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-use-field-name-in-part-filename[`quarkus.spec."spec-item".use-field-name-in-part-filename`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".use-field-name-in-part-filename+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Defines, whether the filename should also include the property name in case the `PartFilename` annotation (`org.jboss.resteasy.reactive.PartFilename` or `org.jboss.resteasy.annotations.providers.multipart.PartFilename`) is generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__USE_FIELD_NAME_IN_PART_FILENAME+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-use-bean-validation]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-use-bean-validation[`quarkus.spec."spec-item".use-bean-validation`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".use-bean-validation+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable bean validation. If you set this to `true`, validation annotations are added to generated sources E.g. `@Size`. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__USE_BEAN_VALIDATION+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-generate-apis]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-generate-apis[`quarkus.spec."spec-item".generate-apis`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".generate-apis+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of APIs. If you set this to `false`, APIs will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_APIS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_APIS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-generate-models]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-generate-models[`quarkus.spec."spec-item".generate-models`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".generate-models+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of models. If you set this to `false`, models will not be generated. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_MODELS+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__GENERATE_MODELS+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-equals-hashcode]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-equals-hashcode[`quarkus.spec."spec-item".equals-hashcode`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".equals-hashcode+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Enable the generation of equals and hashcode in models. If you set this to `false`, the models will not have equals and hashcode. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__EQUALS_HASHCODE+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-base-package]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-base-package[`quarkus.spec."spec-item".base-package`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".base-package+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Base package for where the generated code for the given OpenAPI specification will be added. + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__BASE_PACKAGE+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__BASE_PACKAGE+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-api-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-api-name-suffix[`quarkus.spec."spec-item".api-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".api-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated api classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__API_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-model-name-suffix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-model-name-suffix[`quarkus.spec."spec-item".model-name-suffix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".model-name-suffix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Suffix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_SUFFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-model-name-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-model-name-prefix[`quarkus.spec."spec-item".model-name-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".model-name-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Prefix name for generated model classes + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__MODEL_NAME_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix[`quarkus.spec."spec-item".remove-operation-id-prefix`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".remove-operation-id-prefix+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX+++` +endif::add-copy-button-to-env-var[] +-- +|boolean +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-delimiter]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-delimiter[`quarkus.spec."spec-item".remove-operation-id-prefix-delimiter`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".remove-operation-id-prefix-delimiter+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_DELIMITER+++` +endif::add-copy-button-to-env-var[] +-- +|string +| + +a|icon:lock[title=Fixed at build time] [[quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-count]] [.property-path]##link:#quarkus-openapi-generator_quarkus-spec-spec-item-remove-operation-id-prefix-count[`quarkus.spec."spec-item".remove-operation-id-prefix-count`]## +ifdef::add-copy-button-to-config-props[] +config_property_copy_button:+++quarkus.spec."spec-item".remove-operation-id-prefix-count+++[] +endif::add-copy-button-to-config-props[] + + +[.description] +-- +Remove operation id prefix + + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_SPEC__SPEC_ITEM__REMOVE_OPERATION_ID_PREFIX_COUNT+++` +endif::add-copy-button-to-env-var[] +-- +|int +| + +|=== + diff --git a/docs/modules/ROOT/pages/moqu.adoc b/docs/modules/ROOT/pages/moqu.adoc new file mode 100644 index 000000000..846cd9bbc --- /dev/null +++ b/docs/modules/ROOT/pages/moqu.adoc @@ -0,0 +1,206 @@ += Quarkus - Open API Generator - Moqu + +include::./includes/attributes.adoc[] + +The **OpenAPI Generator Moqu extension** converts an OpenAPI specification into a mock representation. This mock can then be mapped to the link:https://wiremock.org/[WireMock] for further use, providing a way to simulate APIs for testing and development purposes. + +[NOTE] +==== +Currently, this extension supports only link:https://wiremock.org/[WireMock] definitions. +==== + +[[getting-started]] +== Getting Started + +[source,xml] +---- + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu + {project-version} + +---- + +Now, create the following OpenAPI specification file under your `src/resources/openapi` directory: + +[source,yaml] +.src/main/openapi/hello.yaml +---- +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Get framework by ID +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: ["999-SNAPSHOT", "3.15.1"] + supportsJava: + type: boolean + example: true + contributors: + type: integer + example: 1000 + rules: + type: object + example: + hello: world +---- + +Execute now your application on Dev mode, and access the Dev UI for getting you wiremock stubbing: + +image::moqu-devui-card-framework.png[] + +Click on `Moqu Wiremock`, you will se a table containing all wiremock definitions: + +image::table-wiremock.png[] + +Now, you can `see` or `download` the Wiremock stubbing. + +== Request matching + +The Moqu extension uses the request and response examples defined in the OpenAPI Specification to determine the appropriate response for a specific request, creating a corresponding request/response pair. + +Example: + +[source,yaml] +---- +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{id}: + get: + description: Get user by ID + parameters: + - name: id + in: path + required: true + schema: + type: number + examples: + john: <1> + value: 1 <2> + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: <3> + value: + '{"id": 1, "name": "John Doe"}' +---- + +<1> Defines an example named `john` for request +<2> Maps the request for path `/users/1` should use the response named as `john` +<3> Defines an example named `john` for response + + +In other words, if the user accesses `/users/1`, the response will be the one mapped for the `john` example in response. + +The Wiremock definition using the OpenAPI specification above, looks something like this: + +[source,json] +---- +{ + "mappings": [ + { + "request": { + "method": "GET", + "url": "/users/1" + }, + "response": { + "status": 200, + "body": "{\"name\":\"John\",\"age\": 80}", + "headers": {} + } + } + ] +} +---- + +=== Response as Schema + +You can use the `$ref` to reference a schema for mapping a response: + +[source,yaml] +---- +paths: + "/users/{id}": + get: + parameters: + - name: id + in: path + examples: + alice: + value: 1 + responses: + 200: + content: + "application/json": + examples: + alice: + $ref: "#/components/schemas/User" + description: Ok +components: + schemas: + User: + type: object + properties: + name: + type: string + example: "Alice" + age: + type: number + example: 80 +---- + +The Wiremock definition using the OpenAPI specification above, looks something like this: + +[source,json] +---- +{ + "mappings": [ + { + "request": { + "method": "GET", + "url": "/users/1" + }, + "response": { + "status": 200, + "body": "{\"name\":\"Alice\",\"age\":80}", + "headers": {} + } + } + ] +} +---- \ No newline at end of file diff --git a/moqu/core/pom.xml b/moqu/core/pom.xml new file mode 100644 index 000000000..5164dff74 --- /dev/null +++ b/moqu/core/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-parent + 3.0.0-lts-SNAPSHOT + + + quarkus-openapi-generator-moqu-core + Quarkus :: Openapi Generator :: Moqu :: Core + + + 2.16.1 + + + + + io.swagger.parser.v3 + swagger-parser + ${version.io.swagger.parser} + + + org.assertj + assertj-core + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + commons-io + commons-io + ${commons.io.version} + + + org.jboss.logmanager + jboss-logmanager + + + diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/Moqu.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/Moqu.java new file mode 100644 index 000000000..1d0024cad --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/Moqu.java @@ -0,0 +1,37 @@ +package io.quarkiverse.openapi.moqu; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import io.quarkiverse.openapi.moqu.model.RequestResponsePair; + +/** + * Represents a collection of request-response pairs, providing methods to access + * these pairs in an immutable list. + */ +public class Moqu { + + private List requestResponsePairs = new ArrayList<>(); + + /** + * Constructs a {@code Moqu} instance with the provided list of request-response pairs. + * + * @param requestResponsePairs the list of {@link RequestResponsePair} objects to initialize + * the collection. Must not be {@code null}. + * @throws NullPointerException if {@code requestResponsePairs} is null. + */ + public Moqu(List requestResponsePairs) { + this.requestResponsePairs = Objects.requireNonNull(requestResponsePairs); + } + + /** + * Returns an unmodifiable list of request-response pairs. + * + * @return an immutable list of {@link RequestResponsePair}. + */ + public List getRequestResponsePairs() { + return Collections.unmodifiableList(requestResponsePairs); + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquImporter.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquImporter.java new file mode 100644 index 000000000..16d63747d --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquImporter.java @@ -0,0 +1,17 @@ +package io.quarkiverse.openapi.moqu; + +/** + * {@link MoquImporter} aims to convert a specification into a {@link Moqu} model. + * It provides a method to parse the content, typically from an OpenAPI specification, + * and generate a corresponding {@link Moqu} instance. + */ +public interface MoquImporter { + + /** + * Parses the provided OpenAPI content and generates a new {@link Moqu} instance. + * + * @param content the OpenAPI content as a string, which will be parsed into a {@link Moqu} model. + * @return a new {@link Moqu} instance based on the provided content. + */ + Moqu parse(String content); +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquMapper.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquMapper.java new file mode 100644 index 000000000..53ed02bbe --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/MoquMapper.java @@ -0,0 +1,19 @@ +package io.quarkiverse.openapi.moqu; + +import java.util.List; + +/** + * A generic interface for mapping a {@link Moqu} instance to a list of objects of type {@code T}. + * + * @param the type of objects to which the {@link Moqu} instance will be mapped. + */ +public interface MoquMapper { + + /** + * Maps the given {@link Moqu} instance to a list of objects of type {@code T}. + * + * @param moqu the {@link Moqu} instance to be mapped. + * @return a list of mapped objects of type {@code T}. + */ + List map(Moqu moqu); +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporter.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporter.java new file mode 100644 index 000000000..a969d8582 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporter.java @@ -0,0 +1,253 @@ +package io.quarkiverse.openapi.moqu; + +import static io.swagger.v3.parser.util.SchemaTypeUtil.INTEGER_TYPE; +import static io.swagger.v3.parser.util.SchemaTypeUtil.OBJECT_TYPE; +import static io.swagger.v3.parser.util.SchemaTypeUtil.STRING_TYPE; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Strings; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import io.quarkiverse.openapi.moqu.model.Header; +import io.quarkiverse.openapi.moqu.model.Request; +import io.quarkiverse.openapi.moqu.model.RequestResponsePair; +import io.quarkiverse.openapi.moqu.model.Response; +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.examples.Example; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.SwaggerParseResult; + +public class OpenAPIMoquImporter implements MoquImporter { + + private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIMoquImporter.class); + private static final String HTTP_HEADER_ACCEPT = "Accept"; + private static final String REFERENCE_PREFIX = "#/components/schemas/"; + + @Override + public Moqu parse(String content) { + + SwaggerParseResult swaggerParseResult = new OpenAPIV3Parser().readContents(content); + + if (LOGGER.isDebugEnabled()) { + for (String message : swaggerParseResult.getMessages()) { + LOGGER.debug("[context:SwaggerParseResult] {}", message); + } + } + + OpenAPI openAPI = swaggerParseResult.getOpenAPI(); + + if (Objects.isNull(openAPI)) { + throw new IllegalArgumentException("Cannot parse OpenAPI V3 content: " + content); + } + + return new Moqu( + getRequestResponsePairs(openAPI)); + } + + private List getRequestResponsePairs(OpenAPI openAPI) { + Map requestResponsePairs = new HashMap<>(); + + Map localSchemas = getSchemas(openAPI); + + Set> entries = Optional.ofNullable(openAPI.getPaths()) + .orElseThrow(IllegalArgumentException::new) + .entrySet(); + + for (Map.Entry entry : entries) { + + for (Map.Entry httpMethodOperation : entry.getValue().readOperationsMap() + .entrySet()) { + + if (!Objects.isNull(httpMethodOperation.getValue().getResponses())) { + + Set> statusApiResponses = httpMethodOperation.getValue().getResponses() + .entrySet(); + + for (Map.Entry statusApiResponse : statusApiResponses) { + + if (Objects.isNull(statusApiResponse.getValue())) { + continue; + } + + Map> examplesOnPath = extractParameters(httpMethodOperation.getValue(), + ParameterType.PATH); + + requestResponsePairs.putAll(getContentRequestResponsePairs(statusApiResponse, examplesOnPath, + httpMethodOperation.getKey(), entry.getKey(), localSchemas)); + } + } + } + } + + return requestResponsePairs.entrySet().stream().map(entry -> new RequestResponsePair(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + private Map getSchemas(OpenAPI openAPI) { + if (openAPI.getComponents() == null) { + return Map.of(); + } + return Objects.requireNonNullElse(openAPI.getComponents().getSchemas(), Map.of()); + } + + private int tryGetStatusCode(Map.Entry statusApiResponse) { + try { + return Integer.parseInt(statusApiResponse.getKey()); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid status code: " + statusApiResponse.getKey()); + } + } + + private Map> extractParameters(Operation operation, ParameterType parameterType) { + List parameters = Optional.ofNullable(operation.getParameters()).orElse(Collections.emptyList()); + Map> finalParameters = new HashMap<>(); + + for (Parameter parameter : parameters) { + if (isEligibleForExtraction(parameter, parameterType)) { + + Set exampleNames = parameter.getExamples().keySet(); + for (String exampleName : exampleNames) { + + Example example = parameter.getExamples().get(exampleName); + + Object object = example.getValue(); + String value = resolveContent(object); + finalParameters.computeIfAbsent(exampleName, + k -> ArrayListMultimap.create()).put(parameter.getName(), value); + } + } + } + + return finalParameters; + } + + private boolean isEligibleForExtraction(Parameter parameter, ParameterType type) { + return parameter.getIn().equals(type.value()) && !Objects.isNull(parameter.getExamples()); + } + + private Map getContentRequestResponsePairs(Map.Entry statusApiResponse, + Map> parametersOnPath, PathItem.HttpMethod httpMethod, String url, + Map localSchemas) { + Map requestResponseMap = new HashMap<>(); + + ApiResponse apiResponse = statusApiResponse.getValue(); + + int statusCode = tryGetStatusCode(statusApiResponse); + + for (Map.Entry entry : apiResponse.getContent().entrySet()) { + String contentType = entry.getKey(); + MediaType mediaType = entry.getValue(); + Map examples = Optional.ofNullable(mediaType.getExamples()).orElse(Collections.emptyMap()); + + examples.forEach((exampleName, example) -> { + + String content = resolveContent(localSchemas, example); + + Response response = new Response( + exampleName, + mediaType, + statusCode, + content, + List.of()); + + Multimap onPath = parametersOnPath.get(exampleName); + List reqParams = new ArrayList<>(); + + if (onPath != null) { + for (Map.Entry paramEntry : onPath.entries()) { + io.quarkiverse.openapi.moqu.model.Parameter parameter = new io.quarkiverse.openapi.moqu.model.Parameter( + paramEntry.getKey(), + paramEntry.getValue(), + ParameterType.PATH); + reqParams.add(parameter); + } + } + + List parameters = reqParams.stream() + .filter(reqParam -> reqParam.where().equals(ParameterType.PATH)).toList(); + String finalUrl = resolveUrlParameters(url, parameters); + Request request = new Request( + finalUrl, + httpMethod.name(), + exampleName, + new Header(HTTP_HEADER_ACCEPT, List.of(contentType)), + reqParams); + requestResponseMap.put(request, response); + }); + } + + return requestResponseMap; + } + + private String resolveContent(Map localSchemas, Example example) { + if (!Strings.isNullOrEmpty(example.get$ref())) { + return resolveRef(example.get$ref(), localSchemas); + } else { + return resolveContent(example.getValue()); + } + } + + private String resolveUrlParameters(String url, List parameters) { + for (io.quarkiverse.openapi.moqu.model.Parameter parameter : parameters) { + String placeholder = "{%s}".formatted(parameter.key()); + url = url.replace(placeholder, parameter.value()); + } + return url; + } + + private String resolveRef(String ref, Map localSchemas) { + if (!ref.startsWith(REFERENCE_PREFIX)) { + throw new IllegalArgumentException( + "There is no support for external $ref schemas. Please, configure the %s as local schema" + .formatted(ref)); + } + + String refName = ref.substring(REFERENCE_PREFIX.length(), ref.length()); + + Schema schema = localSchemas.get(refName); + + if (schema == null) { + throw new IllegalArgumentException("Schema not found: " + refName); + } + + return generateResponseBodyFromRefSchema(schema); + } + + private String resolveContent(Object object) { + if (object instanceof String) { + return (String) object; + } + if (object instanceof Integer) { + return String.valueOf((Integer) object); + } + throw new IllegalArgumentException("Object is not a String"); + } + + private static String generateResponseBodyFromRefSchema(final Schema schema) { + String schemaType = Optional.ofNullable(schema.getType()).orElse(OBJECT_TYPE); + return switch (schemaType) { + case STRING_TYPE, INTEGER_TYPE -> (String) schema.getExample(); + case OBJECT_TYPE -> SchemaReader.readObjectExample(schema); + default -> ""; + }; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/ParameterType.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/ParameterType.java new file mode 100644 index 000000000..0c053f55b --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/ParameterType.java @@ -0,0 +1,43 @@ +package io.quarkiverse.openapi.moqu; + +/** + * Enum representing the type of a parameter in an HTTP request, indicating its location. + * The parameter can be part of the path, query string, or headers. + */ +public enum ParameterType { + + /** + * Indicates that the parameter is part of the URL path. + */ + PATH("path"), + + /** + * Indicates that the parameter is part of the query string. + */ + QUERY("query"), + + /** + * Indicates that the parameter is part of the HTTP headers. + */ + HEADER("header"); + + private final String value; + + /** + * Constructs a {@code ParameterType} with the given string value representing the parameter location. + * + * @param value the string value corresponding to the parameter type. + */ + ParameterType(String value) { + this.value = value; + } + + /** + * Returns the string value associated with this {@code ParameterType}. + * + * @return the string representation of the parameter type. + */ + public String value() { + return this.value; + } +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/SchemaReader.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/SchemaReader.java new file mode 100644 index 000000000..7b330f0e5 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/SchemaReader.java @@ -0,0 +1,64 @@ +package io.quarkiverse.openapi.moqu; + +import static io.swagger.v3.parser.util.SchemaTypeUtil.OBJECT_TYPE; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import io.quarkiverse.openapi.moqu.marshall.ObjectMapperFactory; +import io.swagger.v3.oas.models.media.Schema; + +/** + * Utility class for reading schema examples and converting them into JSON representations. + * This class provides methods to extract example data from a given schema and serialize it + * into JSON format. + */ +public class SchemaReader { + + static String EMPTY_JSON_OBJECT = "{}"; + + /** + * Reads the example object from the provided schema and converts it to a JSON string. + * + * @param schema the schema from which to extract the example object. + * @return a JSON string representation of the example object, or an empty JSON object + * if an error occurs during processing. + */ + static String readObjectExample(Schema schema) { + try { + Map map = mapObjectExample(schema); + return ObjectMapperFactory.getInstance().writeValueAsString(map); + } catch (JsonProcessingException e) { + return EMPTY_JSON_OBJECT; + } + } + + /** + * Recursively maps the properties of the provided schema to a map. + * + * @param schema the schema from which to map properties. + * @return a map representing the example properties of the schema. + */ + private static Map mapObjectExample(Schema schema) { + Map currentRoot = new HashMap<>(); + + Optional.ofNullable(schema.getProperties()) + .orElse(Map.of()) + .forEach((key, value) -> { + if (value.getType().equals(OBJECT_TYPE)) { + if (value.getExample() != null) { + currentRoot.put(key, value.getExample()); + } else { + currentRoot.put(key, mapObjectExample(value)); + } + } else { + currentRoot.put(key, value.getExample()); + } + }); + + return currentRoot; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/marshall/ObjectMapperFactory.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/marshall/ObjectMapperFactory.java new file mode 100644 index 000000000..68957a738 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/marshall/ObjectMapperFactory.java @@ -0,0 +1,15 @@ +package io.quarkiverse.openapi.moqu.marshall; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Responsible for providing a Single of {@link ObjectMapper} instance. + */ +public class ObjectMapperFactory { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static ObjectMapper getInstance() { + return objectMapper; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Header.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Header.java new file mode 100644 index 000000000..476bd63ab --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Header.java @@ -0,0 +1,12 @@ +package io.quarkiverse.openapi.moqu.model; + +import java.util.List; + +/** + * Represents an HTTP header with a name and a set of associated values. + * + * @param name the name of the HTTP header (e.g., "Accept", "Content-Type"). + * @param value the set of values associated with the header, allowing multiple values (e.g., "application/json", "text/html"). + */ +public record Header(String name, List value) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Operation.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Operation.java new file mode 100644 index 000000000..bac321819 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Operation.java @@ -0,0 +1,10 @@ +package io.quarkiverse.openapi.moqu.model; + +/** + * Represents an HTTP operation. + *

+ * + * @param httpMethod the HTTP verb used for the current {@link Operation}. + */ +public record Operation(String httpMethod) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Parameter.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Parameter.java new file mode 100644 index 000000000..ee87f789b --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Parameter.java @@ -0,0 +1,14 @@ +package io.quarkiverse.openapi.moqu.model; + +import io.quarkiverse.openapi.moqu.ParameterType; + +/** + * Represents an HTTP request parameter with a key, value, and location indicating where the parameter is used. + * + * @param key the key of the parameter (e.g., "id", "query"). + * @param value the value of the parameter associated with the key. + * @param where the location of the parameter in the request (e.g., query string, path, header), defined by + * {@link ParameterType}. + */ +public record Parameter(String key, String value, ParameterType where) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Request.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Request.java new file mode 100644 index 000000000..ff1b8fe8e --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Request.java @@ -0,0 +1,16 @@ +package io.quarkiverse.openapi.moqu.model; + +import java.util.Collection; + +/** + * Represents an HTTP request with essential details such as URL, HTTP method, + * example name, accepted header, and parameters. + * + * @param url the URL to which the request is sent. + * @param httpMethod the HTTP method (GET, POST, PUT, DELETE, etc.) used for the request. + * @param exampleName the name of the example associated with the request. + * @param accept the "Accept" header, which specifies the expected response format. + * @param parameters the list of parameters to be included in the request. + */ +public record Request(String url, String httpMethod, String exampleName, Header accept, Collection parameters) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/RequestResponsePair.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/RequestResponsePair.java new file mode 100644 index 000000000..214cb92de --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/RequestResponsePair.java @@ -0,0 +1,10 @@ +package io.quarkiverse.openapi.moqu.model; + +/** + * Represents a pair of an HTTP request and its corresponding response. + * + * @param request the HTTP request that was sent. + * @param response the HTTP response received for the given request. + */ +public record RequestResponsePair(Request request, Response response) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Response.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Response.java new file mode 100644 index 000000000..532345b9f --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/model/Response.java @@ -0,0 +1,20 @@ +package io.quarkiverse.openapi.moqu.model; + +import java.util.List; + +import io.swagger.v3.oas.models.media.MediaType; + +/** + * Represents an HTTP response with details such as the example name, media type, + * status code, content, and headers. + * + * @param exampleName the name of the example associated with this response. + * @param mediaType the media type of the response content (e.g., application/json, text/html), + * represented by {@link MediaType}. + * @param statusCode the HTTP status code of the response (e.g., 200, 404). + * @param content the body of the response as a string. + * @param headers the list of headers included in the response. + */ +public record Response(String exampleName, MediaType mediaType, int statusCode, + String content, List

headers) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapper.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapper.java new file mode 100644 index 000000000..83bf92e58 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapper.java @@ -0,0 +1,41 @@ +package io.quarkiverse.openapi.moqu.wiremock.mapper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.MoquMapper; +import io.quarkiverse.openapi.moqu.model.Request; +import io.quarkiverse.openapi.moqu.model.RequestResponsePair; +import io.quarkiverse.openapi.moqu.model.Response; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockRequest; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockResponse; + +public class WiremockMapper implements MoquMapper { + + @Override + public List map(Moqu moqu) { + ArrayList definitions = new ArrayList<>(); + for (RequestResponsePair pair : moqu.getRequestResponsePairs()) { + + Request mockRequest = pair.request(); + Response mockResponse = pair.response(); + + WiremockRequest request = new WiremockRequest(mockRequest.httpMethod(), mockRequest.url()); + + Map headers = new HashMap<>(); + + mockResponse.headers().forEach(item -> headers.put(item.name(), item.value())); + + WiremockResponse response = new WiremockResponse(mockResponse.statusCode(), mockResponse.content(), headers); + + definitions.add(new WiremockMapping( + request, response)); + } + + return definitions; + } +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockMapping.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockMapping.java new file mode 100644 index 000000000..bf703d7f9 --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockMapping.java @@ -0,0 +1,4 @@ +package io.quarkiverse.openapi.moqu.wiremock.model; + +public record WiremockMapping(WiremockRequest request, WiremockResponse response) { +} \ No newline at end of file diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockRequest.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockRequest.java new file mode 100644 index 000000000..3d663e79a --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockRequest.java @@ -0,0 +1,6 @@ +package io.quarkiverse.openapi.moqu.wiremock.model; + +public record WiremockRequest( + String method, + String url) { +} diff --git a/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockResponse.java b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockResponse.java new file mode 100644 index 000000000..bfbcb6eef --- /dev/null +++ b/moqu/core/src/main/java/io/quarkiverse/openapi/moqu/wiremock/model/WiremockResponse.java @@ -0,0 +1,8 @@ +package io.quarkiverse.openapi.moqu.wiremock.model; + +import java.util.Map; + +public record WiremockResponse(Integer status, + String body, + Map headers) { +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporterTest.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporterTest.java new file mode 100644 index 000000000..8267b1ddd --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/OpenAPIMoquImporterTest.java @@ -0,0 +1,181 @@ +package io.quarkiverse.openapi.moqu; + +import static io.quarkiverse.openapi.moqu.TestUtils.readContentFromFile; + +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.openapi.moqu.marshall.ObjectMapperFactory; + +class OpenAPIMoquImporterTest { + + private final MoquImporter sut = new OpenAPIMoquImporter(); + + @Test + @DisplayName("Should create a new definition from OpenAPI specification") + void shouldCreateANewDefinitionFromOpenAPISpecification() { + // act + String content = readContentFromFile("wiremock/one_example_in_the_same_path.yml"); + Moqu moqu = sut.parse(content); + + // assert + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().accept().name()).isEqualTo("Accept"); + softly.assertThat(requestResponsePair.request().accept().value()).contains("application/json"); + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("john"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + softly.assertThat(requestResponsePair.request().parameters()).anySatisfy(parameters -> { + softly.assertThat(parameters.key()).isEqualTo("id"); + softly.assertThat(parameters.value()).isEqualTo("1"); + }); + }); + }); + } + + @Test + @DisplayName("Should throws exception when the OpenAPI is invalid") + void shouldThrowsExceptionWhenTheOpenAPIIsInvalid() { + // act, assert + Assertions.assertThrows(IllegalArgumentException.class, () -> { + sut.parse(""" + openapi: 3.0.3 + info: + version: 999-SNAPSHOT + """); + }); + } + + @Test + @DisplayName("Should handle OpenAPI with two path params") + void shouldHandleOpenAPIWithTwoPathParams() { + + // act + String content = readContentFromFile("wiremock/two_examples_in_the_same_path.yml"); + Moqu moqu = sut.parse(content); + + // assert + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(2); + softly.assertThat(moqu.getRequestResponsePairs()).allSatisfy(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().accept().name()).isEqualTo("Accept"); + softly.assertThat(requestResponsePair.request().accept().value()).contains("application/json"); + }); + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("john"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + }); + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("mary"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + }); + }); + } + + @Test + @DisplayName("Should generate a response from ref") + void shouldGenerateAResponseFromRef() { + String content = readContentFromFile("wiremock/response_from_ref.yml"); + + Moqu moqu = sut.parse(content); + + // assert + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + softly.assertThat(requestResponsePair.request().accept().name()).isEqualTo("Accept"); + softly.assertThat(requestResponsePair.request().accept().value()).contains("application/json"); + softly.assertThat(requestResponsePair.request().exampleName()).isEqualTo("quarkus"); + softly.assertThat(requestResponsePair.request().parameters()).hasSize(1); + softly.assertThat(requestResponsePair.request().parameters()).anySatisfy(parameters -> { + softly.assertThat(parameters.key()).isEqualTo("id"); + softly.assertThat(parameters.value()).isEqualTo("1"); + }); + }); + }); + } + + @Test + @DisplayName("Should generate a response from ref as array") + void shouldGenerateAResponseFromRefAsArray() { + String content = readContentFromFile("wiremock/response_from_ref_array.yml"); + Moqu moqu = sut.parse(content); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(2); + + softly.assertThat(map.get("supportsJava")).isEqualTo(true); + + }); + }); + } + + @Test + @DisplayName("Should generate a response from $ref and with no $ref") + void shouldGenerateAResponseFromRefAndNoRef() { + String content = readContentFromFile("wiremock/response_from_ref_and_noref.yml"); + Moqu moqu = sut.parse(content); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(2); + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(2); + }); + + softly.assertThat(moqu.getRequestResponsePairs()).anySatisfy(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(1); + }); + }); + } + + @Test + @DisplayName("Should generate a full OpenAPI specification") + void shouldGenerateAFullResponse() { + String content = readContentFromFile("wiremock/full.yml"); + Moqu moqu = sut.parse(content); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(moqu.getRequestResponsePairs()).isNotEmpty(); + softly.assertThat(moqu.getRequestResponsePairs()).hasSize(1); + softly.assertThat(moqu.getRequestResponsePairs().get(0)).satisfies(requestResponsePair -> { + Map map = ObjectMapperFactory.getInstance().readValue( + requestResponsePair.response() + .content(), + Map.class); + softly.assertThat((List) map.get("versions")) + .hasSize(2); + + softly.assertThat(map.get("supportsJava")).isEqualTo(true); + + softly.assertThat(map.get("contributors")).isEqualTo(1000); + + softly.assertThat(((Map) map.get("rules")).get("hello")).isEqualTo("world"); + }); + }); + } +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/TestUtils.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/TestUtils.java new file mode 100644 index 000000000..2f8b1b571 --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/TestUtils.java @@ -0,0 +1,20 @@ +package io.quarkiverse.openapi.moqu; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + +public class TestUtils { + + public static String readContentFromFile(String resourcePath) { + URL url = Thread.currentThread().getContextClassLoader().getResource((resourcePath)); + assert url != null; + try { + return Files.readString(Path.of(url.toURI())); + } catch (IOException | URISyntaxException e) { + return null; + } + } +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapperTest.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapperTest.java new file mode 100644 index 000000000..9a8611bab --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockMapperTest.java @@ -0,0 +1,65 @@ +package io.quarkiverse.openapi.moqu.wiremock.mapper; + +import static io.quarkiverse.openapi.moqu.TestUtils.readContentFromFile; + +import java.util.List; + +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.OpenAPIMoquImporter; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; + +class WiremockMapperTest { + + private final OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + private final WiremockMapper sut = new WiremockMapper(); + + @Test + @DisplayName("Should map one Wiremock definition") + void shouldMapOneWiremockDefinition() { + String content = readContentFromFile("wiremock/mapper/should_map_one_wiremock_definition.yml"); + + Moqu moqu = importer.parse(content); + + List definitions = sut.map(moqu); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).isNotEmpty(); + softly.assertThat(definitions).hasSize(1); + softly.assertThat(definitions).anySatisfy(definition -> { + softly.assertThat(definition.request().method()).isEqualTo("GET"); + softly.assertThat(definition.request().url()).isEqualTo("/users/1"); + softly.assertThat(definition.response().body()).isEqualTo("{\"id\": 1, \"name\": \"John Doe\"}"); + }); + }); + } + + @Test + @DisplayName("Should map two Wiremock definitions") + void shouldMapTwoWiremockDefinitions() { + String content = readContentFromFile("wiremock/mapper/should_map_two_wiremock_definition.yml"); + + Moqu mock = importer.parse(content); + + List definitions = sut.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).isNotEmpty(); + softly.assertThat(definitions).hasSize(2); + softly.assertThat(definitions).anySatisfy(definition -> { + softly.assertThat(definition.request().method()).isEqualTo("GET"); + softly.assertThat(definition.request().url()).isEqualTo("/users/1"); + softly.assertThat(definition.response().body()).isEqualTo("{\"id\": 1, \"name\": \"John Doe\"}"); + }); + + softly.assertThat(definitions).anySatisfy(definition -> { + softly.assertThat(definition.request().method()).isEqualTo("GET"); + softly.assertThat(definition.request().url()).isEqualTo("/users/2"); + softly.assertThat(definition.response().body()).isEqualTo("{\"id\": 2, \"name\": \"Mary Doe\"}"); + }); + }); + } +} diff --git a/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockPathParamTest.java b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockPathParamTest.java new file mode 100644 index 000000000..abff5f59a --- /dev/null +++ b/moqu/core/src/test/java/io/quarkiverse/openapi/moqu/wiremock/mapper/WiremockPathParamTest.java @@ -0,0 +1,164 @@ +package io.quarkiverse.openapi.moqu.wiremock.mapper; + +import java.util.List; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.OpenAPIMoquImporter; +import io.quarkiverse.openapi.moqu.TestUtils; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; + +public class WiremockPathParamTest { + + @Test + @DisplayName("Should convert a OpenAPI with a single path param correctly") + void shouldMapOneWiremockDefinition() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_one_path_param.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(1); + WiremockMapping definition = definitions.get(0); + + softly.assertThat(definition).satisfies(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()).isEqualTo("{\"name\": \"Quarkus\"}"); + }); + }); + } + + @Test + @DisplayName("Should convert with a two OpenAPI#paths each one with one path param") + void shouldMapTwoWiremockDefinitions() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_two_params_but_different_path.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(2); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()).isEqualTo("{\"name\": \"John Doe\"}"); + }); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/frameworks/quarkus"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"description\": \"Quarkus, build time augmentation toolkit\"}"); + }); + }); + } + + @Test + @DisplayName("Should convert with a combination of path param") + void shouldConvertWithACombinationOfPathParam() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_two_path_params_combination.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(2); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1/books/80"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"name\": \"Book for John\", \"chapters\": 8}"); + }); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/2/books/70"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"name\": \"Book for Mary\", \"chapters\": 10}"); + }); + }); + } + + @Test + @DisplayName("Should convert with a combination but only one with example") + void shouldConvertPathParamCombinationOnlyOneWithExample() { + + String content = TestUtils.readContentFromFile("wiremock/path_param_two_path_params_only_one_with_example.yml"); + if (content == null) { + Assertions.fail("Was not possible to read the file!"); + } + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + + Moqu mock = importer.parse(content); + + WiremockMapper wiremockMapper = new WiremockMapper(); + + List definitions = wiremockMapper.map(mock); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(definitions).hasSize(1); + + softly.assertThat(definitions).anySatisfy(wiremockDefinition -> { + // request + softly.assertThat(wiremockDefinition.request().url()).isEqualTo("/users/1/books/{bookId}"); + softly.assertThat(wiremockDefinition.request().method()).isEqualTo("GET"); + // response + softly.assertThat(wiremockDefinition.response().status()).isEqualTo(200); + softly.assertThat(wiremockDefinition.response().body()) + .isEqualTo("{\"name\": \"Book for John\", \"chapters\": 8}"); + }); + }); + } + +} diff --git a/moqu/core/src/test/resources/wiremock/full.yml b/moqu/core/src/test/resources/wiremock/full.yml new file mode 100644 index 000000000..d91f48923 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/full.yml @@ -0,0 +1,45 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: ["999-SNAPSHOT", "3.15.1"] + supportsJava: + type: boolean + example: true + contributors: + type: integer + example: 1000 + rules: + type: object + example: + hello: world + diff --git a/moqu/core/src/test/resources/wiremock/mapper/should_map_one_wiremock_definition.yml b/moqu/core/src/test/resources/wiremock/mapper/should_map_one_wiremock_definition.yml new file mode 100644 index 000000000..92ea0200c --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/mapper/should_map_one_wiremock_definition.yml @@ -0,0 +1,28 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{userId}: + get: + description: Get user by ID + parameters: + - name: userId + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/mapper/should_map_two_wiremock_definition.yml b/moqu/core/src/test/resources/wiremock/mapper/should_map_two_wiremock_definition.yml new file mode 100644 index 000000000..2dabf118f --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/mapper/should_map_two_wiremock_definition.yml @@ -0,0 +1,33 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{userId}: + get: + description: Get user by ID + parameters: + - name: userId + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + mary: + value: 2 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' + mary: + value: + '{"id": 2, "name": "Mary Doe"}' \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/one_example_in_the_same_path.yml b/moqu/core/src/test/resources/wiremock/one_example_in_the_same_path.yml new file mode 100644 index 000000000..eac9598b5 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/one_example_in_the_same_path.yml @@ -0,0 +1,28 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{id}: + get: + description: Get user by ID + parameters: + - name: id + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/path_param_one_path_param.yml b/moqu/core/src/test/resources/wiremock/path_param_one_path_param.yml new file mode 100644 index 000000000..a87227d57 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_one_path_param.yml @@ -0,0 +1,23 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}": + get: + parameters: + - name: userId + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + value: '{"name": "Quarkus"}' + description: Ok diff --git a/moqu/core/src/test/resources/wiremock/path_param_two_params_but_different_path.yml b/moqu/core/src/test/resources/wiremock/path_param_two_params_but_different_path.yml new file mode 100644 index 000000000..26eb2dba8 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_two_params_but_different_path.yml @@ -0,0 +1,39 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}": + get: + parameters: + - name: userId + in: path + examples: + john: + value: 1 + responses: + 200: + content: + "application/json": + examples: + john: + value: '{"name": "John Doe"}' + description: Ok + "/frameworks/{name}": + get: + parameters: + - name: name + in: path + examples: + quarkus: + value: quarkus + responses: + 200: + content: + "application/json": + examples: + quarkus: + value: '{"description": "Quarkus, build time augmentation toolkit"}' + description: Ok diff --git a/moqu/core/src/test/resources/wiremock/path_param_two_path_params_combination.yml b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_combination.yml new file mode 100644 index 000000000..140eec479 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_combination.yml @@ -0,0 +1,35 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}/books/{bookId}": + get: + parameters: + - name: userId + in: path + examples: + john: + value: 1 + mary: + value: 2 + - name: bookId + in: path + examples: + john: + value: 80 + mary: + value: 70 + + responses: + 200: + content: + "application/json": + examples: + john: + value: '{"name": "Book for John", "chapters": 8}' + mary: + value: '{"name": "Book for Mary", "chapters": 10}' + description: Ok diff --git a/moqu/core/src/test/resources/wiremock/path_param_two_path_params_only_one_with_example.yml b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_only_one_with_example.yml new file mode 100644 index 000000000..3b1f159a0 --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/path_param_two_path_params_only_one_with_example.yml @@ -0,0 +1,25 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{userId}/books/{bookId}": + get: + parameters: + - name: userId + in: path + examples: + john: + value: 1 + - name: bookId + in: path + responses: + 200: + content: + "application/json": + examples: + john: + value: '{"name": "Book for John", "chapters": 8}' + description: Ok \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/response_from_ref.yml b/moqu/core/src/test/resources/wiremock/response_from_ref.yml new file mode 100644 index 000000000..4e62b37fb --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/response_from_ref.yml @@ -0,0 +1,31 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" \ No newline at end of file diff --git a/moqu/core/src/test/resources/wiremock/response_from_ref_and_noref.yml b/moqu/core/src/test/resources/wiremock/response_from_ref_and_noref.yml new file mode 100644 index 000000000..b9537febc --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/response_from_ref_and_noref.yml @@ -0,0 +1,38 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + vertx: + value: 2 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + vertx: + value: '{ "name": "Vert.x", "versions": ["999-SNAPSHOT"]}' + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: [ "999-SNAPSHOT", "3.15.1" ] diff --git a/moqu/core/src/test/resources/wiremock/response_from_ref_array.yml b/moqu/core/src/test/resources/wiremock/response_from_ref_array.yml new file mode 100644 index 000000000..8707012bd --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/response_from_ref_array.yml @@ -0,0 +1,37 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/frameworks/{id}": + get: + parameters: + - name: id + in: path + examples: + quarkus: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/Framework" + description: Ok +components: + schemas: + Framework: + type: object + properties: + name: + type: string + example: "Quarkus" + versions: + type: array + example: ["999-SNAPSHOT", "3.15.1"] + supportsJava: + type: boolean + example: true diff --git a/moqu/core/src/test/resources/wiremock/two_examples_in_the_same_path.yml b/moqu/core/src/test/resources/wiremock/two_examples_in_the_same_path.yml new file mode 100644 index 000000000..2dabf118f --- /dev/null +++ b/moqu/core/src/test/resources/wiremock/two_examples_in_the_same_path.yml @@ -0,0 +1,33 @@ +openapi: 3.0.3 +info: + title: "Users API" + version: 1.0.0-alpha +servers: + - url: http://localhost:8888 +paths: + /users/{userId}: + get: + description: Get user by ID + parameters: + - name: userId + in: path + required: true + schema: + type: number + examples: + john: + value: 1 + mary: + value: 2 + responses: + "200": + description: Ok + content: + "application/json": + examples: + john: + value: + '{"id": 1, "name": "John Doe"}' + mary: + value: + '{"id": 2, "name": "Mary Doe"}' \ No newline at end of file diff --git a/moqu/deployment/pom.xml b/moqu/deployment/pom.xml new file mode 100644 index 000000000..c1f53c8eb --- /dev/null +++ b/moqu/deployment/pom.xml @@ -0,0 +1,66 @@ + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-parent + 3.0.0-lts-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-moqu-wiremock-deployment + Quarkus - Openapi Generator - Moqu - Wiremock - Deployment + + + + io.quarkus + quarkus-core-deployment + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-wiremock + ${project.version} + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-core + ${project.version} + + + + io.quarkus + quarkus-junit5-internal + + + + io.quarkus + quarkus-vertx-http-deployment + + + + io.rest-assured + rest-assured + test + + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquProjectProcessor.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquProjectProcessor.java new file mode 100644 index 000000000..94ed8ff79 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquProjectProcessor.java @@ -0,0 +1,102 @@ +package io.quarkiverse.openapi.generator; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +import org.jboss.logging.Logger; + +import io.quarkiverse.openapi.generator.items.MoquBuildItem; +import io.quarkiverse.openapi.generator.items.MoquProjectBuildItem; +import io.quarkiverse.openapi.generator.moqu.MoquConfig; +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkiverse.openapi.moqu.OpenAPIMoquImporter; +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.runtime.util.ClassPathUtils; + +public class MoquProjectProcessor { + + private static final Logger LOGGER = Logger.getLogger(MoquProjectProcessor.class); + + private static final Set SUPPORTED_EXTENSIONS = Set.of("yaml", "yml", "json"); + + @BuildStep + MoquProjectBuildItem generate(MoquConfig config) { + try { + + HashMap filesMap = new HashMap<>(); + ClassPathUtils.consumeAsPaths(config.resourceDir(), path -> { + try { + boolean directory = Files.isDirectory(path); + if (directory) { + try (Stream pathStream = Files.find(path, Integer.MAX_VALUE, + (p, a) -> Files.isRegularFile(p) && SUPPORTED_EXTENSIONS.contains( + getExtension(p.getFileName().toString())))) { + + pathStream.forEach(p -> { + try { + String filename = p.getFileName().toString(); + + MoquProjectBuildItem.File moquFile = new MoquProjectBuildItem.File( + removeExtension(filename), getExtension(filename), Files.readString(p)); + + filesMap.put(filename, moquFile); + + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + return new MoquProjectBuildItem(filesMap); + + } catch (IOException e) { + LOGGER.error("Was not possible to scan Moqu project files.", e); + throw new RuntimeException(e); + } + } + + @BuildStep(onlyIf = { IsDevelopment.class }) + void consume(Optional moquProject, + BuildProducer moquMocks) { + + OpenAPIMoquImporter importer = new OpenAPIMoquImporter(); + moquProject.ifPresent(project -> { + for (Map.Entry spec : project.specs().entrySet()) { + + MoquProjectBuildItem.File moquFile = spec.getValue(); + + Moqu moqu = importer.parse(moquFile.content()); + + moquMocks.produce(new MoquBuildItem( + moquFile.filename(), + moquFile.extension(), + moqu)); + } + }); + } + + public static String getExtension(String path) { + Objects.requireNonNull(path, "path is required"); + final int i = path.lastIndexOf("."); + return i > 0 ? path.substring(i + 1) : null; + } + + public static String removeExtension(String path) { + Objects.requireNonNull(path, "path is required"); + final int i = path.lastIndexOf("."); + return i > 0 ? path.substring(0, i) : path; + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquWiremockProcessor.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquWiremockProcessor.java new file mode 100644 index 000000000..086f65f88 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/MoquWiremockProcessor.java @@ -0,0 +1,12 @@ +package io.quarkiverse.openapi.generator; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +public class MoquWiremockProcessor { + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem("moqu-wiremock"); + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquModel.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquModel.java new file mode 100644 index 000000000..38cc5c795 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquModel.java @@ -0,0 +1,5 @@ +package io.quarkiverse.openapi.generator.devui; + +public record MoquModel(String name, String link) { + +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquWiremockDevUIProcessor.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquWiremockDevUIProcessor.java new file mode 100644 index 000000000..0514701af --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/devui/MoquWiremockDevUIProcessor.java @@ -0,0 +1,81 @@ +package io.quarkiverse.openapi.generator.devui; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.quarkiverse.openapi.generator.items.MoquBuildItem; +import io.quarkiverse.openapi.generator.moqu.recorder.MoquRoutesRecorder; +import io.quarkiverse.openapi.moqu.marshall.ObjectMapperFactory; +import io.quarkiverse.openapi.moqu.wiremock.mapper.WiremockMapper; +import io.quarkiverse.openapi.moqu.wiremock.model.WiremockMapping; +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.devui.spi.page.CardPageBuildItem; +import io.quarkus.devui.spi.page.Page; +import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; +import io.quarkus.vertx.http.deployment.RouteBuildItem; + +public class MoquWiremockDevUIProcessor { + + private static final String MAPPINGS_KEY = "mappings"; + private static final String WIREMOCK_MAPPINGS_JSON = "/wiremock-mappings.json"; + + @BuildStep(onlyIf = IsDevelopment.class) + @Record(ExecutionTime.RUNTIME_INIT) + void generateWiremock(List mocks, NonApplicationRootPathBuildItem nonApplicationRootPath, + BuildProducer routes, + MoquRoutesRecorder recorder) { + + WiremockMapper wiremockMapper = new WiremockMapper(); + ObjectMapper objMapper = ObjectMapperFactory.getInstance(); + + for (MoquBuildItem mock : mocks) { + List wiremockMappings = wiremockMapper.map(mock.getMoqu()); + try { + String json = objMapper.writeValueAsString(Map.of( + MAPPINGS_KEY, wiremockMappings)); + + String uri = mock.prefixUri(nonApplicationRootPath.resolvePath("moqu")) + .concat(WIREMOCK_MAPPINGS_JSON); + + routes.produce(nonApplicationRootPath.routeBuilder() + .routeFunction(uri, recorder.handleFile(json)) + .displayOnNotFoundPage() + .build()); + + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + } + + @BuildStep(onlyIf = { IsDevelopment.class }) + CardPageBuildItem cardPageBuildItem( + List moquMocks, + NonApplicationRootPathBuildItem nonApplicationRootPath) { + CardPageBuildItem cardPageBuildItem = new CardPageBuildItem(); + + List models = moquMocks.stream() + .map(m -> new MoquModel(m.getFullFilename(), m.prefixUri( + nonApplicationRootPath.resolvePath("moqu")) + .concat("/wiremock-mappings.json"))) + .toList(); + + cardPageBuildItem.addBuildTimeData("mocks", models); + + cardPageBuildItem.addPage( + Page.webComponentPageBuilder() + .title("Moqu Wiremock") + .icon("font-awesome-solid:server") + .componentLink("qwc-moqu.js") + .staticLabel(String.valueOf(moquMocks.size()))); + + return cardPageBuildItem; + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquBuildItem.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquBuildItem.java new file mode 100644 index 000000000..a6285f734 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquBuildItem.java @@ -0,0 +1,37 @@ +package io.quarkiverse.openapi.generator.items; + +import io.quarkiverse.openapi.moqu.Moqu; +import io.quarkus.builder.item.MultiBuildItem; + +public final class MoquBuildItem extends MultiBuildItem { + + private final String filename; + private final String extension; + private final Moqu moqu; + + public MoquBuildItem(String filename, String extension, Moqu moqu) { + this.filename = filename; + this.extension = extension; + this.moqu = moqu; + } + + public String getFilename() { + return filename; + } + + public String getExtension() { + return extension; + } + + public Moqu getMoqu() { + return moqu; + } + + public String getFullFilename() { + return filename + "." + extension; + } + + public String prefixUri(String basePath) { + return String.format("%s/%s/%s", basePath, extension, filename); + } +} diff --git a/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquProjectBuildItem.java b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquProjectBuildItem.java new file mode 100644 index 000000000..ea8de60a5 --- /dev/null +++ b/moqu/deployment/src/main/java/io/quarkiverse/openapi/generator/items/MoquProjectBuildItem.java @@ -0,0 +1,22 @@ +package io.quarkiverse.openapi.generator.items; + +import java.util.Collections; +import java.util.Map; + +import io.quarkus.builder.item.SimpleBuildItem; + +public final class MoquProjectBuildItem extends SimpleBuildItem { + + private final Map specs; + + public MoquProjectBuildItem(Map specs) { + this.specs = specs; + } + + public Map specs() { + return Collections.unmodifiableMap(specs); + } + + public record File(String filename, String extension, String content) { + } +} diff --git a/moqu/deployment/src/main/resources/dev-ui/qwc-moqu.js b/moqu/deployment/src/main/resources/dev-ui/qwc-moqu.js new file mode 100644 index 000000000..31d42cded --- /dev/null +++ b/moqu/deployment/src/main/resources/dev-ui/qwc-moqu.js @@ -0,0 +1,96 @@ +import {LitElement, html, css} from 'lit'; +import {columnBodyRenderer} from '@vaadin/grid/lit.js'; +import {mocks} from 'build-time-data'; +import '@vaadin/grid'; +import '@vaadin/vertical-layout'; +import '@vaadin/icon'; + +/** + * This component shows the Moqu mocks + */ +export class QwcMoqu extends LitElement { + + static styles = css` + .arctable { + height: 100%; + padding-bottom: 10px; + } + + .moqu-icon { + font-size: small; + color: var(--lumo-contrast-50pct); + cursor: pointer; + } + `; + + static properties = { + _mocks: {state: true} + }; + + constructor() { + super(); + this._mocks = mocks; + } + + render() { + if (this._mocks) { + return this._renderMockList(); + } else { + return html`No mocks found`; + } + } + + _renderMockList() { + return html` + + + + + + + + + `; + } + + _nameRenderer(mock) { + return html` + + ${mock.name} + + `; + } + + _linkDownloadRenderer(mock) { + return html` + + + + + + `; + } + + _linkSeeRenderer(mock) { + return html` + + + + + + `; + } + + +} + +customElements.define('qwc-moqu', QwcMoqu); diff --git a/moqu/deployment/src/test/java/io/quarkiverse/openapi/generator/MoquProjectProcessorTest.java b/moqu/deployment/src/test/java/io/quarkiverse/openapi/generator/MoquProjectProcessorTest.java new file mode 100644 index 000000000..76347c237 --- /dev/null +++ b/moqu/deployment/src/test/java/io/quarkiverse/openapi/generator/MoquProjectProcessorTest.java @@ -0,0 +1,47 @@ +package io.quarkiverse.openapi.generator; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; + +public class MoquProjectProcessorTest { + + @RegisterExtension + static final QuarkusDevModeTest unitTest = new QuarkusDevModeTest() + .withApplicationRoot(javaArchive -> javaArchive + .addAsResource("api.yaml", "openapi/openapi.yaml") + .addAsResource("apiv2.json", "openapi/api.json")); + + @Test + void testModeAsSee() { + RestAssured.given() + .when().get("/q/moqu/yaml/openapi/wiremock-mappings.json?mode=see") + .then() + .statusCode(200) + .body(Matchers.containsString("Alice")) + .log().ifError(); + } + + @Test + void testModeAsDownload() { + RestAssured.given() + .when().get("/q/moqu/yaml/openapi/wiremock-mappings.json") + .then() + .statusCode(200) + .body(Matchers.containsString("Alice")) + .log().ifError(); + } + + @Test + void testModeAsDownloadUsingJson() { + RestAssured.given() + .when().get("/q/moqu/json/api/wiremock-mappings.json") + .then() + .statusCode(200) + .body(Matchers.containsString("Alice")) + .log().ifError(); + } +} diff --git a/moqu/deployment/src/test/resources/api.yaml b/moqu/deployment/src/test/resources/api.yaml new file mode 100644 index 000000000..802ebe7e6 --- /dev/null +++ b/moqu/deployment/src/test/resources/api.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.3 +servers: + - url: http://localhost:8888 +info: + version: 999-SNAPSHOT + title: Method GET one path param +paths: + "/users/{id}": + get: + parameters: + - name: id + in: path + examples: + alice: + value: 1 + responses: + 200: + content: + "application/json": + examples: + quarkus: + $ref: "#/components/schemas/User" + description: Ok +components: + schemas: + User: + type: object + properties: + name: + type: string + example: "Alice" + age: + type: number + example: 80 + diff --git a/moqu/deployment/src/test/resources/apiv2.json b/moqu/deployment/src/test/resources/apiv2.json new file mode 100644 index 000000000..4bbe37c02 --- /dev/null +++ b/moqu/deployment/src/test/resources/apiv2.json @@ -0,0 +1,60 @@ +{ + "openapi": "3.0.3", + "servers": [ + { + "url": "http://localhost:8888" + } + ], + "info": { + "version": "999-SNAPSHOT", + "title": "Method GET one path param" + }, + "paths": { + "/users/{id}": { + "get": { + "parameters": [ + { + "name": "id", + "in": "path", + "examples": { + "alice": { + "value": 1 + } + } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "examples": { + "quarkus": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Alice" + }, + "age": { + "type": "number", + "example": 80 + } + } + } + } + } +} diff --git a/moqu/pom.xml b/moqu/pom.xml new file mode 100644 index 000000000..e0fae16e0 --- /dev/null +++ b/moqu/pom.xml @@ -0,0 +1,19 @@ + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-parent + 3.0.0-lts-SNAPSHOT + ../pom.xml + + 4.0.0 + pom + quarkus-openapi-generator-moqu-parent + Quarkus - Openapi Generator - Moqu - Parent + + + core + deployment + runtime + + diff --git a/moqu/runtime/pom.xml b/moqu/runtime/pom.xml new file mode 100644 index 000000000..227d687b5 --- /dev/null +++ b/moqu/runtime/pom.xml @@ -0,0 +1,77 @@ + + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-parent + 3.0.0-lts-SNAPSHOT + + 4.0.0 + + quarkus-openapi-generator-moqu-wiremock + Quarkus - Openapi Generator - Moqu - Wiremock + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-moqu-core + ${project.version} + + + + io.quarkus + quarkus-core + + + + io.quarkus + quarkus-vertx-http + + + + io.quarkus + quarkus-junit5 + test + + + + io.rest-assured + rest-assured + test + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + true + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/MoquConfig.java b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/MoquConfig.java new file mode 100644 index 000000000..ee9912ee8 --- /dev/null +++ b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/MoquConfig.java @@ -0,0 +1,19 @@ +package io.quarkiverse.openapi.generator.moqu; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigMapping(prefix = "quarkus.openapi-generator.moqu") +@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +public interface MoquConfig { + + String DEFAULT_RESOURCE_DIR = "openapi"; + + /** + * Path to the Moqu (relative to the project). + */ + @WithDefault(DEFAULT_RESOURCE_DIR) + String resourceDir(); +} diff --git a/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/recorder/MoquRoutesRecorder.java b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/recorder/MoquRoutesRecorder.java new file mode 100644 index 000000000..86d211c01 --- /dev/null +++ b/moqu/runtime/src/main/java/io/quarkiverse/openapi/generator/moqu/recorder/MoquRoutesRecorder.java @@ -0,0 +1,47 @@ +package io.quarkiverse.openapi.generator.moqu.recorder; + +import java.util.function.Consumer; + +import io.quarkus.runtime.annotations.Recorder; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.RoutingContext; + +@Recorder +public class MoquRoutesRecorder { + + public Consumer handleFile(String content) { + return new Consumer() { + @Override + public void accept(Route route) { + route.method(HttpMethod.GET); + route.handler(new Handler() { + @Override + public void handle(RoutingContext routingContext) { + HttpServerResponse response = routingContext.response(); + HttpServerRequest request = routingContext.request(); + + String mode = request.getParam("mode"); + if (mode != null && mode.equalsIgnoreCase("see")) { + response.putHeader("Content-Type", "application/json; charset=utf-8"); + response.end(Buffer.buffer(content)); + } else { + setForDownloading(response, content); + } + } + + }); + } + }; + } + + private void setForDownloading(HttpServerResponse response, String content) { + response.putHeader("Content-Type", "application/octet-stream"); + response.putHeader("Content-Disposition", "attachment; filename=wiremock-mappings.json"); + response.end(Buffer.buffer(content)); + } +} diff --git a/moqu/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/moqu/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 000000000..c9383283a --- /dev/null +++ b/moqu/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,11 @@ +name: "OpenAPI Generator - Moqu - Wiremock Generator" +artifact: ${project.groupId}:${project.artifactId}:${project.version} +description: The OpenAPI Generator Moqu Wiremock extension converts an OpenAPI specification into a Wiremock definition. +metadata: + keywords: + - "openapi" + - "openapi-generator" + - "wiremock" + categories: + - "web" + status: "preview" diff --git a/pom.xml b/pom.xml index d7f47cf0d..756f5eb5f 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,8 @@ client server + docs + moqu :git:git@github.com:quarkiverse/quarkus-openapi-generator.git