You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ff...@apache.org on 2019/12/19 22:42:40 UTC
[camel] branch master updated: [CAMEL-14315]add
openapi-rest-dsl-generator
This is an automated email from the ASF dual-hosted git repository.
ffang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new 946a24f [CAMEL-14315]add openapi-rest-dsl-generator
946a24f is described below
commit 946a24fc574576131f80bfa3a279b5f4fef55180
Author: Freeman Fang <fr...@gmail.com>
AuthorDate: Thu Dec 19 17:36:35 2019 -0500
[CAMEL-14315]add openapi-rest-dsl-generator
---
docs/components/modules/ROOT/nav.adoc | 1 -
.../ROOT/pages/aws-translate-component.adoc | 4 +-
.../openapi-rest-dsl-generator/openapi-spec.json | 1093 ++++++++++++++++++++
tooling/openapi-rest-dsl-generator/openapi-v2.json | 1042 +++++++++++++++++++
tooling/openapi-rest-dsl-generator/pom.xml | 94 ++
.../generator/openapi/AppendableGenerator.java | 38 +
.../camel/generator/openapi/CodeEmitter.java | 25 +
.../generator/openapi/DestinationGenerator.java | 26 +
.../generator/openapi/DirectToOperationId.java | 38 +
.../camel/generator/openapi/FilerGenerator.java | 40 +
.../openapi/MethodBodySourceCodeEmitter.java | 166 +++
.../camel/generator/openapi/OperationFilter.java | 45 +
.../camel/generator/openapi/OperationVisitor.java | 239 +++++
.../camel/generator/openapi/PathGenerator.java | 39 +
.../camel/generator/openapi/PathVisitor.java | 98 ++
.../generator/openapi/RestDefinitionEmitter.java | 99 ++
.../openapi/RestDslDefinitionGenerator.java | 40 +
.../camel/generator/openapi/RestDslGenerator.java | 203 ++++
.../openapi/RestDslSourceCodeGenerator.java | 207 ++++
.../generator/openapi/RestDslXmlGenerator.java | 114 ++
.../SpringBootProjectSourceCodeGenerator.java | 107 ++
.../openapi/MethodBodySourceCodeEmitterTest.java | 56 +
.../PathSpringBootProjectSourceGeneratorTest.java | 46 +
.../openapi/RestDefinitionEmitterTest.java | 71 ++
.../generator/openapi/RestDslGeneratorTest.java | 122 +++
.../generator/openapi/RestDslGeneratorV3Test.java | 118 +++
.../openapi/RestDslSourceCodeGeneratorTest.java | 109 ++
.../openapi/RestDslSourceCodeGeneratorV3Test.java | 112 ++
.../generator/openapi/RestDslXmlGeneratorTest.java | 117 +++
.../openapi/RestDslXmlGeneratorV3Test.java | 116 +++
.../src/test/resources/MyRestRoute.txt | 294 ++++++
.../src/test/resources/MyRestRouteFilter.txt | 78 ++
.../src/test/resources/MyRestRouteFilterV3.txt | 76 ++
.../src/test/resources/MyRestRouteV3.txt | 288 ++++++
.../src/test/resources/OpenApiPetstore.txt | 291 ++++++
.../resources/OpenApiPetstoreWithRestComponent.txt | 294 ++++++
.../OpenApiPetstoreWithRestComponentXml.txt | 101 ++
.../src/test/resources/OpenApiPetstoreXml.txt | 100 ++
.../src/test/resources/OpenApiV3Petstore.txt | 285 +++++
.../OpenApiV3PetstoreWithRestComponent.txt | 288 ++++++
.../OpenApiV3PetstoreWithRestComponentXml.txt | 99 ++
.../src/test/resources/OpenApiV3PetstoreXml.txt | 99 ++
.../test/resources/SpringBootRestController.txt | 24 +
.../src/test/resources/log4j2.properties | 28 +
tooling/pom.xml | 1 +
45 files changed, 6969 insertions(+), 2 deletions(-)
diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc
index fab718f..ee5e565 100644
--- a/docs/components/modules/ROOT/nav.adoc
+++ b/docs/components/modules/ROOT/nav.adoc
@@ -42,7 +42,6 @@
* xref:aws-sqs-component.adoc[AWS Simple Queue Service Component]
* xref:aws-swf-component.adoc[AWS Simple Workflow Component]
* xref:aws-translate-component.adoc[AWS Translate Component]
-* xref:aws-translate-component.adoc[AWS Translate Component]
* xref:aws-xray.adoc[AWS XRay Component]
* xref:azure-blob-component.adoc[Azure Storage Blob Service Component]
* xref:azure-queue-component.adoc[Azure Storage Queue Service Component]
diff --git a/docs/components/modules/ROOT/pages/aws-translate-component.adoc b/docs/components/modules/ROOT/pages/aws-translate-component.adoc
index 9c667da..82505c6 100644
--- a/docs/components/modules/ROOT/pages/aws-translate-component.adoc
+++ b/docs/components/modules/ROOT/pages/aws-translate-component.adoc
@@ -1,6 +1,6 @@
[[aws-translate-component]]
= AWS Translate Component
-:page-source: components/camel-aws-translate/bin/src/main/docs/aws-translate-component.adoc
+:page-source: components/camel-aws-translate/src/main/docs/aws-translate-component.adoc
*Since Camel 3.0*
@@ -153,6 +153,8 @@ the https://aws.amazon.com/translate/[Amazon Translate] service.
|`CamelAwsTranslateTargetLanguage` |`String` |The text target language
+|`CamelAwsTranslateTerminologyNames` |`String` |The terminologies to use
+
|`CamelAwsTranslateOperation` |`String` |The operation to perform
|=======================================================================
diff --git a/tooling/openapi-rest-dsl-generator/openapi-spec.json b/tooling/openapi-rest-dsl-generator/openapi-spec.json
new file mode 100644
index 0000000..099a353
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/openapi-spec.json
@@ -0,0 +1,1093 @@
+{
+ "openapi": "3.0.2",
+ "info": {
+ "title": "OpenApi Petstore",
+ "version": "1.0.0",
+ "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
+ "termsOfService": "http://swagger.io/terms/",
+ "contact": {
+ "email": "apiteam@swagger.io"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "servers": [
+ {
+ "url": "http://petstore.openapi.io/api/v3"
+ }
+ ],
+ "paths": {
+ "/pet": {
+ "put": {
+ "requestBody": {
+ "description": "Pet object that needs to be added to the store",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "pet"
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ },
+ "405": {
+ "description": "Validation exception"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "operationId": "updatePet",
+ "summary": "Update an existing pet",
+ "description": ""
+ },
+ "post": {
+ "requestBody": {
+ "description": "Pet object that needs to be added to the store",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "pet"
+ ],
+ "parameters": [
+ {
+ "name": "verbose",
+ "description": "Verbose data",
+ "schema": {
+ "default": false,
+ "type": "boolean"
+ },
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "operationId": "addPet",
+ "summary": "Add a new pet to the store",
+ "description": ""
+ }
+ },
+ "/pet/findByStatus": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "parameters": [
+ {
+ "style": "form",
+ "explode": true,
+ "name": "status",
+ "description": "Status values that need to be considered for filter",
+ "schema": {
+ "type": "array",
+ "items": {
+ "default": "available",
+ "enum": [
+ "available",
+ "pending",
+ "sold"
+ ],
+ "type": "string"
+ }
+ },
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ },
+ "description": "successful operation"
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "operationId": "findPetsByStatus",
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings"
+ }
+ },
+ "/pet/findByTags": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "parameters": [
+ {
+ "style": "form",
+ "explode": true,
+ "name": "tags",
+ "description": "Tags to filter by",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ },
+ "description": "successful operation"
+ },
+ "400": {
+ "description": "Invalid tag value"
+ }
+ },
+ "deprecated": true,
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "operationId": "findPetsByTags",
+ "summary": "Finds Pets by tags",
+ "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing."
+ }
+ },
+ "/pet/{petId}": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "description": "ID of pet to return",
+ "schema": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ },
+ "description": "successful operation"
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ }
+ ],
+ "operationId": "getPetById",
+ "summary": "Find pet by ID",
+ "description": "Returns a single pet"
+ },
+ "post": {
+ "requestBody": {
+ "content": {
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "description": "Updated name of the pet",
+ "type": "string"
+ },
+ "status": {
+ "description": "Updated status of the pet",
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "pet"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "description": "ID of pet that needs to be updated",
+ "schema": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "operationId": "updatePetWithForm",
+ "summary": "Updates a pet in the store with form data",
+ "description": ""
+ },
+ "delete": {
+ "tags": [
+ "pet"
+ ],
+ "parameters": [
+ {
+ "name": "api_key",
+ "schema": {
+ "type": "string"
+ },
+ "in": "header",
+ "required": false
+ },
+ {
+ "name": "petId",
+ "description": "Pet id to delete",
+ "schema": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "operationId": "deletePet",
+ "summary": "Deletes a pet",
+ "description": ""
+ }
+ },
+ "/pet/{petId}/uploadImage": {
+ "post": {
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "additionalMetadata": {
+ "description": "Additional data to pass to server",
+ "type": "string"
+ },
+ "file": {
+ "format": "binary",
+ "description": "file to upload",
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "pet"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "description": "ID of pet to update",
+ "schema": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ApiResponse"
+ }
+ }
+ },
+ "description": "successful operation"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "operationId": "uploadFile",
+ "summary": "uploads an image",
+ "description": ""
+ }
+ },
+ "/store/inventory": {
+ "get": {
+ "tags": [
+ "store"
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "format": "int32",
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "description": "successful operation"
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ }
+ ],
+ "operationId": "getInventory",
+ "summary": "Returns pet inventories by status",
+ "description": "Returns a map of status codes to quantities"
+ }
+ },
+ "/store/order": {
+ "post": {
+ "requestBody": {
+ "description": "order placed for purchasing the pet",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "store"
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ }
+ },
+ "description": "successful operation"
+ },
+ "400": {
+ "description": "Invalid Order"
+ }
+ },
+ "operationId": "placeOrder",
+ "summary": "Place an order for a pet",
+ "description": ""
+ }
+ },
+ "/store/order/{orderId}": {
+ "get": {
+ "tags": [
+ "store"
+ ],
+ "parameters": [
+ {
+ "name": "orderId",
+ "description": "ID of pet that needs to be fetched",
+ "schema": {
+ "format": "int64",
+ "maximum": 10,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Order"
+ }
+ }
+ },
+ "description": "successful operation"
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ },
+ "operationId": "getOrderById",
+ "summary": "Find purchase order by ID",
+ "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions"
+ },
+ "delete": {
+ "tags": [
+ "store"
+ ],
+ "parameters": [
+ {
+ "name": "orderId",
+ "description": "ID of the order that needs to be deleted",
+ "schema": {
+ "format": "int64",
+ "minimum": 1,
+ "type": "integer"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ },
+ "operationId": "deleteOrder",
+ "summary": "Delete purchase order by ID",
+ "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors"
+ }
+ },
+ "/user": {
+ "post": {
+ "requestBody": {
+ "description": "Created user object",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "user"
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ },
+ "operationId": "createUser",
+ "summary": "Create user",
+ "description": "This can only be done by the logged in user."
+ }
+ },
+ "/user/createWithArray": {
+ "post": {
+ "requestBody": {
+ "description": "List of user object",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "user"
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ },
+ "operationId": "createUsersWithArrayInput",
+ "summary": "Creates list of users with given input array",
+ "description": ""
+ }
+ },
+ "/user/createWithList": {
+ "post": {
+ "requestBody": {
+ "description": "List of user object",
+ "content": {
+ "*/*": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "user"
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ },
+ "operationId": "createUsersWithListInput",
+ "summary": "Creates list of users with given input array",
+ "description": ""
+ }
+ },
+ "/user/login": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "description": "The user name for login",
+ "schema": {
+ "type": "string"
+ },
+ "in": "query",
+ "required": true
+ },
+ {
+ "name": "password",
+ "description": "The password for login in clear text",
+ "schema": {
+ "type": "string"
+ },
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "headers": {
+ "X-Rate-Limit": {
+ "schema": {
+ "format": "int32",
+ "type": "integer"
+ },
+ "description": "calls per hour allowed by the user"
+ },
+ "X-Expires-After": {
+ "schema": {
+ "format": "date-time",
+ "type": "string"
+ },
+ "description": "date in UTC when token expires"
+ }
+ },
+ "content": {
+ "application/xml": {
+ "schema": {
+ "type": "string"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "description": "successful operation"
+ },
+ "400": {
+ "description": "Invalid username/password supplied"
+ }
+ },
+ "operationId": "loginUser",
+ "summary": "Logs user into the system",
+ "description": ""
+ }
+ },
+ "/user/logout": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ },
+ "operationId": "logoutUser",
+ "summary": "Logs out current logged in user session",
+ "description": ""
+ }
+ },
+ "/user/{username}": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "description": "The name that needs to be fetched. Use user1 for testing. ",
+ "schema": {
+ "type": "string"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "content": {
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ },
+ "description": "successful operation"
+ },
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "operationId": "getUserByName",
+ "summary": "Get user by user name",
+ "description": ""
+ },
+ "put": {
+ "requestBody": {
+ "description": "Updated user object",
+ "content": {
+ "*/*": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ },
+ "required": true
+ },
+ "tags": [
+ "user"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "description": "name that need to be updated",
+ "schema": {
+ "type": "string"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid user supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "operationId": "updateUser",
+ "summary": "Updated user",
+ "description": "This can only be done by the logged in user."
+ },
+ "delete": {
+ "tags": [
+ "user"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "description": "The name that needs to be deleted",
+ "schema": {
+ "type": "string"
+ },
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ },
+ "operationId": "deleteUser",
+ "summary": "Delete user",
+ "description": "This can only be done by the logged in user."
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Order": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "petId": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "quantity": {
+ "format": "int32",
+ "type": "integer"
+ },
+ "shipDate": {
+ "format": "date-time",
+ "type": "string"
+ },
+ "status": {
+ "description": "Order Status",
+ "enum": [
+ "placed",
+ "approved",
+ "delivered"
+ ],
+ "type": "string"
+ },
+ "complete": {
+ "default": false,
+ "type": "boolean"
+ }
+ },
+ "xml": {
+ "name": "Order"
+ }
+ },
+ "Category": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Category"
+ }
+ },
+ "User": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "username": {
+ "type": "string"
+ },
+ "firstName": {
+ "type": "string"
+ },
+ "lastName": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string"
+ },
+ "phone": {
+ "type": "string"
+ },
+ "userStatus": {
+ "format": "int32",
+ "description": "User Status",
+ "type": "integer"
+ }
+ },
+ "xml": {
+ "name": "User"
+ }
+ },
+ "Tag": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Tag"
+ }
+ },
+ "Pet": {
+ "required": [
+ "name",
+ "photoUrls"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "format": "int64",
+ "type": "integer"
+ },
+ "category": {
+ "$ref": "#/components/schemas/Category"
+ },
+ "name": {
+ "type": "string",
+ "example": "doggie"
+ },
+ "photoUrls": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "xml": {
+ "name": "photoUrl",
+ "wrapped": true
+ }
+ },
+ "tags": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Tag"
+ },
+ "xml": {
+ "name": "tag",
+ "wrapped": true
+ }
+ },
+ "status": {
+ "description": "pet status in the store",
+ "enum": [
+ "available",
+ "pending",
+ "sold"
+ ],
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Pet"
+ }
+ },
+ "ApiResponse": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "format": "int32",
+ "type": "integer"
+ },
+ "type": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "securitySchemes": {
+ "petstore_auth": {
+ "flows": {
+ "implicit": {
+ "authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
+ "scopes": {
+ "write:pets": "modify pets in your account",
+ "read:pets": "read your pets"
+ }
+ }
+ },
+ "type": "oauth2"
+ },
+ "api_key": {
+ "type": "apiKey",
+ "name": "api_key",
+ "in": "header"
+ }
+ }
+ },
+ "tags": [
+ {
+ "name": "pet",
+ "description": "Everything about your Pets",
+ "externalDocs": {
+ "description": "Find out more",
+ "url": "http://swagger.io"
+ }
+ },
+ {
+ "name": "store",
+ "description": "Access to Petstore orders"
+ },
+ {
+ "name": "user",
+ "description": "Operations about user",
+ "externalDocs": {
+ "description": "Find out more about our store",
+ "url": "http://swagger.io"
+ }
+ }
+ ],
+ "externalDocs": {
+ "description": "Find out more about Swagger",
+ "url": "http://swagger.io"
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/openapi-v2.json b/tooling/openapi-rest-dsl-generator/openapi-v2.json
new file mode 100644
index 0000000..8d762a8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/openapi-v2.json
@@ -0,0 +1,1042 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
+ "version": "1.0.0",
+ "title": "OpenApi Petstore",
+ "termsOfService": "http://swagger.io/terms/",
+ "contact": {
+ "email": "apiteam@swagger.io"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "host": "petstore.openapi.io",
+ "basePath": "/v2",
+ "tags": [
+ {
+ "name": "pet",
+ "description": "Everything about your Pets",
+ "externalDocs": {
+ "description": "Find out more",
+ "url": "http://swagger.io"
+ }
+ },
+ {
+ "name": "store",
+ "description": "Access to Petstore orders"
+ },
+ {
+ "name": "user",
+ "description": "Operations about user",
+ "externalDocs": {
+ "description": "Find out more about our store",
+ "url": "http://swagger.io"
+ }
+ }
+ ],
+ "schemes": [
+ "http"
+ ],
+ "paths": {
+ "/pet": {
+ "post": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Add a new pet to the store",
+ "description": "",
+ "operationId": "addPet",
+ "consumes": [
+ "application/json",
+ "application/xml"
+ ],
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Pet object that needs to be added to the store",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ {
+ "name": "verbose",
+ "in": "query",
+ "description": "Verbose data",
+ "type": "boolean",
+ "default": false
+ }
+ ],
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ },
+ "put": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Update an existing pet",
+ "description": "",
+ "operationId": "updatePet",
+ "consumes": [
+ "application/json",
+ "application/xml"
+ ],
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Pet object that needs to be added to the store",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Pet"
+ }
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ },
+ "405": {
+ "description": "Validation exception"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/pet/findByStatus": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Finds Pets by status",
+ "description": "Multiple status values can be provided with comma separated strings",
+ "operationId": "findPetsByStatus",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "status",
+ "in": "query",
+ "description": "Status values that need to be considered for filter",
+ "required": true,
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": [
+ "available",
+ "pending",
+ "sold"
+ ],
+ "default": "available"
+ },
+ "collectionFormat": "multi"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid status value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/pet/findByTags": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Finds Pets by tags",
+ "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
+ "operationId": "findPetsByTags",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "tags",
+ "in": "query",
+ "description": "Tags to filter by",
+ "required": true,
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "collectionFormat": "multi"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Pet"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid tag value"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ],
+ "deprecated": true
+ }
+ },
+ "/pet/{petId}": {
+ "get": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Find pet by ID",
+ "description": "Returns a single pet",
+ "operationId": "getPetById",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet to return",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/Pet"
+ }
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ }
+ ]
+ },
+ "post": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Updates a pet in the store with form data",
+ "description": "",
+ "operationId": "updatePetWithForm",
+ "consumes": [
+ "application/x-www-form-urlencoded"
+ ],
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet that needs to be updated",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ },
+ {
+ "name": "name",
+ "in": "formData",
+ "description": "Updated name of the pet",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "status",
+ "in": "formData",
+ "description": "Updated status of the pet",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "405": {
+ "description": "Invalid input"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ },
+ "delete": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "Deletes a pet",
+ "description": "",
+ "operationId": "deletePet",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "api_key",
+ "in": "header",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "Pet id to delete",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Pet not found"
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/pet/{petId}/uploadImage": {
+ "post": {
+ "tags": [
+ "pet"
+ ],
+ "summary": "uploads an image",
+ "description": "",
+ "operationId": "uploadFile",
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "petId",
+ "in": "path",
+ "description": "ID of pet to update",
+ "required": true,
+ "type": "integer",
+ "format": "int64"
+ },
+ {
+ "name": "additionalMetadata",
+ "in": "formData",
+ "description": "Additional data to pass to server",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "file",
+ "in": "formData",
+ "description": "file to upload",
+ "required": false,
+ "type": "file"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/ApiResponse"
+ }
+ }
+ },
+ "security": [
+ {
+ "petstore_auth": [
+ "write:pets",
+ "read:pets"
+ ]
+ }
+ ]
+ }
+ },
+ "/store/inventory": {
+ "get": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Returns pet inventories by status",
+ "description": "Returns a map of status codes to quantities",
+ "operationId": "getInventory",
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "api_key": []
+ }
+ ]
+ }
+ },
+ "/store/order": {
+ "post": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Place an order for a pet",
+ "description": "",
+ "operationId": "placeOrder",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "order placed for purchasing the pet",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Order"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/Order"
+ }
+ },
+ "400": {
+ "description": "Invalid Order"
+ }
+ }
+ }
+ },
+ "/store/order/{orderId}": {
+ "get": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Find purchase order by ID",
+ "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions",
+ "operationId": "getOrderById",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "orderId",
+ "in": "path",
+ "description": "ID of pet that needs to be fetched",
+ "required": true,
+ "type": "integer",
+ "maximum": 10.0,
+ "minimum": 1.0,
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/Order"
+ }
+ },
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "store"
+ ],
+ "summary": "Delete purchase order by ID",
+ "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors",
+ "operationId": "deleteOrder",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "orderId",
+ "in": "path",
+ "description": "ID of the order that needs to be deleted",
+ "required": true,
+ "type": "integer",
+ "minimum": 1.0,
+ "format": "int64"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid ID supplied"
+ },
+ "404": {
+ "description": "Order not found"
+ }
+ }
+ }
+ },
+ "/user": {
+ "post": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Create user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "createUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Created user object",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/createWithArray": {
+ "post": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Creates list of users with given input array",
+ "description": "",
+ "operationId": "createUsersWithArrayInput",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "List of user object",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/createWithList": {
+ "post": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Creates list of users with given input array",
+ "description": "",
+ "operationId": "createUsersWithListInput",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "in": "body",
+ "name": "body",
+ "description": "List of user object",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/login": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Logs user into the system",
+ "description": "",
+ "operationId": "loginUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "query",
+ "description": "The user name for login",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "password",
+ "in": "query",
+ "description": "The password for login in clear text",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "type": "string"
+ },
+ "headers": {
+ "X-Rate-Limit": {
+ "type": "integer",
+ "format": "int32",
+ "description": "calls per hour allowed by the user"
+ },
+ "X-Expires-After": {
+ "type": "string",
+ "format": "date-time",
+ "description": "date in UTC when token expires"
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid username/password supplied"
+ }
+ }
+ }
+ },
+ "/user/logout": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Logs out current logged in user session",
+ "description": "",
+ "operationId": "logoutUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [],
+ "responses": {
+ "default": {
+ "description": "successful operation"
+ }
+ }
+ }
+ },
+ "/user/{username}": {
+ "get": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Get user by user name",
+ "description": "",
+ "operationId": "getUserByName",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "The name that needs to be fetched. Use user1 for testing. ",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ },
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ },
+ "put": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Updated user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "updateUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "name that need to be updated",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "in": "body",
+ "name": "body",
+ "description": "Updated user object",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/User"
+ }
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid user supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "user"
+ ],
+ "summary": "Delete user",
+ "description": "This can only be done by the logged in user.",
+ "operationId": "deleteUser",
+ "produces": [
+ "application/xml",
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "username",
+ "in": "path",
+ "description": "The name that needs to be deleted",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Invalid username supplied"
+ },
+ "404": {
+ "description": "User not found"
+ }
+ }
+ }
+ }
+ },
+ "securityDefinitions": {
+ "petstore_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
+ "flow": "implicit",
+ "scopes": {
+ "write:pets": "modify pets in your account",
+ "read:pets": "read your pets"
+ }
+ },
+ "api_key": {
+ "type": "apiKey",
+ "name": "api_key",
+ "in": "header"
+ }
+ },
+ "definitions": {
+ "Order": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "petId": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "quantity": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "shipDate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "status": {
+ "type": "string",
+ "description": "Order Status",
+ "enum": [
+ "placed",
+ "approved",
+ "delivered"
+ ]
+ },
+ "complete": {
+ "type": "boolean",
+ "default": false
+ }
+ },
+ "xml": {
+ "name": "Order"
+ }
+ },
+ "Category": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Category"
+ }
+ },
+ "User": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "username": {
+ "type": "string"
+ },
+ "firstName": {
+ "type": "string"
+ },
+ "lastName": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string"
+ },
+ "phone": {
+ "type": "string"
+ },
+ "userStatus": {
+ "type": "integer",
+ "format": "int32",
+ "description": "User Status"
+ }
+ },
+ "xml": {
+ "name": "User"
+ }
+ },
+ "Tag": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "xml": {
+ "name": "Tag"
+ }
+ },
+ "Pet": {
+ "type": "object",
+ "required": [
+ "name",
+ "photoUrls"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "category": {
+ "$ref": "#/definitions/Category"
+ },
+ "name": {
+ "type": "string",
+ "example": "doggie"
+ },
+ "photoUrls": {
+ "type": "array",
+ "xml": {
+ "name": "photoUrl",
+ "wrapped": true
+ },
+ "items": {
+ "type": "string"
+ }
+ },
+ "tags": {
+ "type": "array",
+ "xml": {
+ "name": "tag",
+ "wrapped": true
+ },
+ "items": {
+ "$ref": "#/definitions/Tag"
+ }
+ },
+ "status": {
+ "type": "string",
+ "description": "pet status in the store",
+ "enum": [
+ "available",
+ "pending",
+ "sold"
+ ]
+ }
+ },
+ "xml": {
+ "name": "Pet"
+ }
+ },
+ "ApiResponse": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "type": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "externalDocs": {
+ "description": "Find out more about Swagger",
+ "url": "http://swagger.io"
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/pom.xml b/tooling/openapi-rest-dsl-generator/pom.xml
new file mode 100644
index 0000000..f2d3b28
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>tooling</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-openapi-rest-dsl-generator</artifactId>
+
+ <name>Camel :: Tooling :: OpenApi REST DSL Generator</name>
+ <description>Generator of REST DSL route definitions from OpenAPI specification</description>
+
+ <properties>
+ <camel.osgi.import.pkg>
+ org.apache.camel.*;${camel.osgi.import.camel.version},
+ *
+ </camel.osgi.import.pkg>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup</groupId>
+ <artifactId>javapoet</artifactId>
+ <version>${javapoet-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.apicurio</groupId>
+ <artifactId>apicurio-data-models</artifactId>
+ <version>1.0.16.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>${commons-lang3-version}</version>
+ </dependency>
+
+ <!-- test -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj-version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/AppendableGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/AppendableGenerator.java
new file mode 100644
index 0000000..4aabcd8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/AppendableGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.IOException;
+
+import com.squareup.javapoet.JavaFile;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+
+
+final class AppendableGenerator extends RestDslSourceCodeGenerator<Appendable> {
+
+ AppendableGenerator(final OasDocument openapi) {
+ super(openapi);
+ }
+
+ @Override
+ public void generate(final Appendable destination) throws IOException {
+ final JavaFile javaFile = generateSourceCode();
+
+ javaFile.writeTo(destination);
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/CodeEmitter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/CodeEmitter.java
new file mode 100644
index 0000000..47ac904
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/CodeEmitter.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+interface CodeEmitter<T> {
+
+ CodeEmitter<T> emit(String method, Object... args);
+
+ T result();
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DestinationGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DestinationGenerator.java
new file mode 100644
index 0000000..acdd138
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DestinationGenerator.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+
+@FunctionalInterface
+public interface DestinationGenerator {
+
+ String generateDestinationFor(OasOperation operation);
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DirectToOperationId.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DirectToOperationId.java
new file mode 100644
index 0000000..bab373b
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/DirectToOperationId.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+
+
+public final class DirectToOperationId implements DestinationGenerator {
+
+ private final AtomicInteger directRouteCount = new AtomicInteger(0);
+
+ @Override
+ public String generateDestinationFor(final OasOperation operation) {
+ return "direct:" + Optional.ofNullable(operation.operationId).orElseGet(this::generateDirectName);
+ }
+
+ String generateDirectName() {
+ return "rest" + directRouteCount.incrementAndGet();
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/FilerGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/FilerGenerator.java
new file mode 100644
index 0000000..ead7341
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/FilerGenerator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.IOException;
+
+import javax.annotation.processing.Filer;
+
+import com.squareup.javapoet.JavaFile;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+
+
+final class FilerGenerator extends RestDslSourceCodeGenerator<Filer> {
+
+ FilerGenerator(final OasDocument openapi) {
+ super(openapi);
+ }
+
+ @Override
+ public void generate(final Filer destination) throws IOException {
+ final JavaFile javaFile = generateSourceCode();
+
+ javaFile.writeTo(destination);
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitter.java
new file mode 100644
index 0000000..8b2524b
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitter.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+import com.squareup.javapoet.MethodSpec;
+
+import static org.apache.commons.lang3.ClassUtils.isPrimitiveOrWrapper;
+
+class MethodBodySourceCodeEmitter implements CodeEmitter<MethodSpec> {
+
+ private final MethodSpec.Builder builder;
+
+ private boolean first = true;
+
+ private final Deque<Integer> indentIntentStack = new ArrayDeque<>();
+
+ private final Deque<Integer> indentStack = new ArrayDeque<>();
+
+ MethodBodySourceCodeEmitter(final MethodSpec.Builder builder) {
+ this.builder = builder;
+ indentStack.push(0);
+ }
+
+ @Override
+ public CodeEmitter<MethodSpec> emit(final String method, final Object... args) {
+ final boolean hasArgs = args != null && args.length > 0;
+
+ final int indent = indentLevelOf(method);
+
+ if (!first) {
+ builder.addCode("\n");
+ }
+
+ builder.addCode(String.join("", Collections.nCopies(indentStack.peek(), "$<")));
+ builder.addCode(String.join("", Collections.nCopies(indent, "$>")));
+
+ if (!first) {
+ builder.addCode(".");
+ }
+
+ indentStack.push(indent);
+
+ if (hasArgs) {
+ builder.addCode("$L(" + invocationLiteralsFor(args) + ")", extend(method, argumentsFor(args)));
+ } else {
+ builder.addCode("$L()", method);
+ }
+
+ first = false;
+
+ return this;
+ }
+
+ @Override
+ public MethodSpec result() {
+ builder.addCode(String.join("", Collections.nCopies(indentStack.peek(), "$<")));
+ builder.addCode(";\n");
+ return builder.build();
+ }
+
+ Object[] argumentsFor(final Object[] args) {
+ final List<Object> arguments = new ArrayList<>(args.length);
+
+ for (final Object arg : args) {
+ if (isPrimitiveOrWrapper(arg.getClass())) {
+ arguments.add(arg);
+ } else if (arg instanceof String) {
+ arguments.add(arg);
+ } else if (arg instanceof Enum) {
+ arguments.add(arg.getClass());
+ arguments.add(arg);
+ } else if (arg instanceof String[]) {
+ arguments.add(Arrays.stream((String[]) arg).collect(Collectors.joining(",")));
+ }
+ }
+
+ return arguments.toArray(new Object[arguments.size()]);
+ }
+
+ Object[] extend(final Object first, final Object... others) {
+ if (others == null || others.length == 0) {
+ return new Object[] {first};
+ }
+
+ final Object[] ret = new Object[1 + others.length];
+
+ ret[0] = first;
+ System.arraycopy(others, 0, ret, 1, others.length);
+
+ return ret;
+ }
+
+ int indentLevelOf(final String method) {
+ switch (method) {
+ case "rest":
+ return 0;
+ case "post":
+ case "get":
+ case "put":
+ case "patch":
+ case "delete":
+ case "head":
+ case "options":
+ return 1;
+ case "param":
+ indentIntentStack.push(3);
+ return 2;
+ case "endParam":
+ indentIntentStack.pop();
+ return 2;
+ case "route":
+ indentIntentStack.push(3);
+ return 2;
+ case "endRest":
+ indentIntentStack.pop();
+ return 2;
+ default:
+ if (indentIntentStack.isEmpty()) {
+ return 2;
+ }
+ return indentIntentStack.peek();
+ }
+ }
+
+ String invocationLiteralsFor(final Object[] args) {
+ final StringJoiner literals = new StringJoiner(",");
+
+ for (final Object arg : args) {
+ if (isPrimitiveOrWrapper(arg.getClass())) {
+ literals.add("$L");
+ } else if (arg instanceof String) {
+ literals.add("$S");
+ } else if (arg instanceof Enum) {
+ literals.add("$T.$L");
+ } else if (arg instanceof String[]) {
+ literals.add("$S");
+ }
+ }
+
+ return literals.toString();
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationFilter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationFilter.java
new file mode 100644
index 0000000..82371f6
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.util.Arrays;
+
+import org.apache.camel.support.PatternHelper;
+
+class OperationFilter {
+
+ // operation names to include separated by comma (wildcards can be used, eg find*)
+ private String includes;
+
+ public String getIncludes() {
+ return includes;
+ }
+
+ public void setIncludes(String includes) {
+ this.includes = includes;
+ }
+
+ boolean accept(String name) {
+ boolean match = true;
+
+ if (includes != null) {
+ String[] patterns = includes.split(",");
+ match = Arrays.stream(patterns).anyMatch(pattern -> PatternHelper.matchPattern(name, pattern));
+ }
+ return match;
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationVisitor.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationVisitor.java
new file mode 100644
index 0000000..368f6f7
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/OperationVisitor.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+import io.apicurio.datamodels.openapi.models.OasParameter;
+import io.apicurio.datamodels.openapi.models.OasResponse;
+import io.apicurio.datamodels.openapi.models.OasSchema;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Items;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Operation;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Parameter;
+import io.apicurio.datamodels.openapi.v3.models.Oas30MediaType;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Operation;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Parameter;
+import io.apicurio.datamodels.openapi.v3.models.Oas30RequestBody;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Response;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Schema;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Schema.Oas30ItemsSchema;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+import org.apache.camel.util.ObjectHelper;
+
+class OperationVisitor<T> {
+
+ private final DestinationGenerator destinationGenerator;
+
+ private final CodeEmitter<T> emitter;
+
+ private final OperationFilter filter;
+
+ private final String path;
+
+ OperationVisitor(final CodeEmitter<T> emitter, final OperationFilter filter, final String path, final DestinationGenerator destinationGenerator) {
+ this.emitter = emitter;
+ this.filter = filter;
+ this.path = path;
+ this.destinationGenerator = destinationGenerator;
+ }
+
+ List<String> asStringList(final List<?> values) {
+ if (values == null || values.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final List<String> stringList = new ArrayList<>();
+ values.forEach(v -> stringList.add(String.valueOf(v)));
+
+ return stringList;
+ }
+
+ CodeEmitter<T> emit(final OasParameter parameter) {
+ emitter.emit("param");
+ emit("name", parameter.getName());
+ final String parameterType = parameter.in;
+ if (ObjectHelper.isNotEmpty(parameterType)) {
+ emit("type", RestParamType.valueOf(parameterType));
+ }
+ if (!parameterType.equals("body")) {
+ if (parameter instanceof Oas20Parameter) {
+ final Oas20Parameter serializableParameter = (Oas20Parameter)parameter;
+
+ final String dataType = serializableParameter.type;
+ emit("dataType", dataType);
+ emit("allowableValues", asStringList(serializableParameter.enum_));
+ final String collectionFormat = serializableParameter.collectionFormat;
+ if (ObjectHelper.isNotEmpty(collectionFormat)) {
+ emit("collectionFormat", CollectionFormat.valueOf(collectionFormat));
+ }
+ if (ObjectHelper.isNotEmpty(serializableParameter.default_)) {
+ String value = serializableParameter.default_.toString();
+ emit("defaultValue", value);
+ }
+
+ final Oas20Items items = serializableParameter.items;
+ if ("array".equals(dataType) && items != null) {
+ emit("arrayType", items.type);
+ }
+ } else if (parameter instanceof Oas30Parameter) {
+ final Oas30Parameter serializableParameter = (Oas30Parameter)parameter;
+ Oas30Schema schema = (Oas30Schema)serializableParameter.schema;
+ if (schema != null) {
+ final String dataType = schema.type;
+ if (ObjectHelper.isNotEmpty(dataType)) {
+ emit("dataType", dataType);
+ }
+ emit("allowableValues", asStringList(schema.enum_));
+ final String collectionFormat = serializableParameter.style;
+ if (ObjectHelper.isNotEmpty(collectionFormat)) {
+ if (collectionFormat.equals("form")) {
+ if (serializableParameter.explode) {
+ emit("collectionFormat", CollectionFormat.multi);
+ } else {
+ emit("collectionFormat", CollectionFormat.csv);
+ }
+ }
+ }
+ if (ObjectHelper.isNotEmpty(schema.default_)) {
+ String value = schema.default_.toString();
+ emit("defaultValue", value);
+ }
+
+ if ("array".equals(dataType) && schema.items != null
+ && schema.items instanceof Oas30ItemsSchema) {
+ emit("arrayType", ((Oas30ItemsSchema)schema.items).type);
+ }
+ }
+ }
+ }
+ if (parameter.required != null) {
+ emit("required", parameter.required);
+ } else {
+ emit("required", Boolean.FALSE);
+ }
+ emit("description", parameter.description);
+ emitter.emit("endParam");
+
+ return emitter;
+ }
+
+ CodeEmitter<T> emit(final String method, final List<String> values) {
+ if (values == null || values.isEmpty()) {
+ return emitter;
+ }
+
+ return emitter.emit(method, new Object[] {values.toArray(new String[values.size()])});
+ }
+
+ CodeEmitter<T> emit(final String method, final Object value) {
+ if (ObjectHelper.isEmpty(value)) {
+ return emitter;
+ }
+
+ return emitter.emit(method, value);
+ }
+
+ void visit(final PathVisitor.HttpMethod method, final OasOperation operation) {
+ if (filter.accept(operation.operationId)) {
+ final String methodName = method.name().toLowerCase();
+ emitter.emit(methodName, path);
+
+ emit("id", operation.operationId);
+ emit("description", operation.description);
+ List<String> operationLevelConsumes = new ArrayList<String>();
+ if (operation instanceof Oas20Operation) {
+ operationLevelConsumes = ((Oas20Operation)operation).consumes;
+ } else if (operation instanceof Oas30Operation) {
+ Oas30Operation oas30Operation = (Oas30Operation)operation;
+ if (oas30Operation.requestBody != null
+ && oas30Operation.requestBody.content != null) {
+ for (String ct : oas30Operation.requestBody.content.keySet()) {
+ operationLevelConsumes.add(ct);
+ }
+ }
+
+ }
+ emit("consumes", operationLevelConsumes);
+ List<String> operationLevelProduces = new ArrayList<String>();
+ if (operation instanceof Oas20Operation) {
+ operationLevelProduces = ((Oas20Operation)operation).produces;
+ } else if (operation instanceof Oas30Operation) {
+ Oas30Operation oas30Operation = (Oas30Operation)operation;
+ if (oas30Operation.responses != null) {
+ for (OasResponse response : oas30Operation.responses.getResponses()) {
+ Oas30Response oas30Response = (Oas30Response)response;
+ for (String ct : oas30Response.content.keySet()) {
+ operationLevelProduces.add(ct);
+ }
+ }
+ }
+ }
+ emit("produces", operationLevelProduces);
+
+ if (operation.getParameters() != null) {
+ operation.getParameters().forEach(parameter -> {
+ emit(parameter);
+ });
+ }
+ if (operation instanceof Oas30Operation) {
+ emitOas30Operation((Oas30Operation)operation);
+ }
+
+ emitter.emit("to", destinationGenerator.generateDestinationFor(operation));
+ }
+ }
+
+ private CodeEmitter<T> emitOas30Operation(Oas30Operation operation) {
+
+ if (operation.requestBody != null) {
+ boolean foundForm = false;
+ Oas30RequestBody requestBody = operation.requestBody;
+ for (Entry<String, Oas30MediaType> entry : requestBody.content.entrySet()) {
+ String ct = entry.getKey();
+ Oas30MediaType mediaType = entry.getValue();
+ if (ct.contains("form") && mediaType.schema.properties != null) {
+ for (Entry<String, OasSchema> entrySchema : mediaType.schema.properties.entrySet()) {
+ foundForm = true;
+ emitter.emit("param");
+ emit("name", entrySchema.getKey());
+ emit("type", RestParamType.formData);
+ emit("dataType", entrySchema.getValue().type);
+ emit("required", requestBody.required);
+ emit("description", entrySchema.getValue().description);
+ emitter.emit("endParam");
+ }
+ }
+ }
+ if (!foundForm) {
+ emitter.emit("param");
+ emit("name", "body");
+ emit("type", RestParamType.valueOf("body"));
+ emit("required", Boolean.TRUE);
+ emit("description", requestBody.description);
+ emitter.emit("endParam");
+ }
+ }
+
+ return emitter;
+
+ }
+}
\ No newline at end of file
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathGenerator.java
new file mode 100644
index 0000000..1844295
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import com.squareup.javapoet.JavaFile;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+
+
+final class PathGenerator extends RestDslSourceCodeGenerator<Path> {
+
+ PathGenerator(final OasDocument openapi) {
+ super(openapi);
+ }
+
+ @Override
+ public void generate(final Path destination) throws IOException {
+ final JavaFile javaFile = generateSourceCode();
+
+ javaFile.writeTo(destination);
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathVisitor.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathVisitor.java
new file mode 100644
index 0000000..53d01c8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/PathVisitor.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import io.apicurio.datamodels.openapi.models.OasOperation;
+import io.apicurio.datamodels.openapi.models.OasPathItem;
+import org.apache.camel.util.ObjectHelper;
+
+
+
+class PathVisitor<T> {
+
+ private final DestinationGenerator destinationGenerator;
+
+ private final CodeEmitter<T> emitter;
+ private final OperationFilter filter;
+
+ PathVisitor(final String basePath, final CodeEmitter<T> emitter, OperationFilter filter, final DestinationGenerator destinationGenerator) {
+ this.emitter = emitter;
+ this.filter = filter;
+ this.destinationGenerator = destinationGenerator;
+
+ if (ObjectHelper.isEmpty(basePath)) {
+ emitter.emit("rest");
+ } else {
+ emitter.emit("rest", basePath);
+ }
+ }
+
+ void visit(final String path, final OasPathItem definition) {
+ final OperationVisitor<T> restDslOperation = new OperationVisitor<>(emitter, filter, path, destinationGenerator);
+
+ getOperationMap(definition).forEach(restDslOperation::visit);
+ }
+
+ void visit(final OasPathItem definition) {
+ final OperationVisitor<T> restDslOperation = new OperationVisitor<>(emitter, filter, definition.getPath(), destinationGenerator);
+
+ getOperationMap(definition).forEach(restDslOperation::visit);
+ }
+
+ private Map<HttpMethod, OasOperation> getOperationMap(OasPathItem path) {
+ Map<HttpMethod, OasOperation> result = new LinkedHashMap<HttpMethod, OasOperation>();
+
+ if (path.get != null) {
+ result.put(HttpMethod.GET, path.get);
+ }
+ if (path.put != null) {
+ result.put(HttpMethod.PUT, path.put);
+ }
+ if (path.post != null) {
+ result.put(HttpMethod.POST, path.post);
+ }
+ if (path.delete != null) {
+ result.put(HttpMethod.DELETE, path.delete);
+ }
+ if (path.patch != null) {
+ result.put(HttpMethod.PATCH, path.patch);
+ }
+ if (path.head != null) {
+ result.put(HttpMethod.HEAD, path.head);
+ }
+ if (path.options != null) {
+ result.put(HttpMethod.OPTIONS, path.options);
+ }
+
+ return result;
+ }
+
+ public enum HttpMethod {
+ POST,
+ GET,
+ PUT,
+ PATCH,
+ DELETE,
+ HEAD,
+ OPTIONS
+ }
+
+}
\ No newline at end of file
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDefinitionEmitter.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDefinitionEmitter.java
new file mode 100644
index 0000000..955647b
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDefinitionEmitter.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+
+class RestDefinitionEmitter implements CodeEmitter<RestsDefinition> {
+
+ private final RestsDefinition definition;
+
+ private Object variable;
+
+ RestDefinitionEmitter(final CamelContext context) {
+ definition = new RestsDefinition();
+ variable = definition;
+ }
+
+ @Override
+ public CodeEmitter<RestsDefinition> emit(final String method, final Object... args) {
+ try {
+ final Class<? extends Object> type = variable.getClass();
+
+ final Object[] arguments = argumentsFor(args);
+
+ final Method declaredMethod = type.getMethod(method, parameterTypesOf(arguments));
+
+ variable = declaredMethod.invoke(variable, arguments);
+ } catch (final Throwable e) {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ return this;
+ }
+
+ @Override
+ public RestsDefinition result() {
+ return definition;
+ }
+
+ Object[] argumentsFor(final Object[] args) {
+ final List<Object> arguments = new ArrayList<>(args.length);
+
+ for (final Object arg : args) {
+ if (arg instanceof String[]) {
+ arguments.add(Arrays.stream((String[]) arg).collect(Collectors.joining(",")));
+ } else {
+ arguments.add(arg);
+ }
+ }
+
+ return arguments.toArray(new Object[arguments.size()]);
+ }
+
+ Class<?>[] parameterTypesOf(final Object[] args) {
+ final Class<?>[] parameterTypes = new Class<?>[args.length];
+
+ for (int i = 0; i < args.length; i++) {
+ parameterTypes[i] = args[i].getClass();
+ }
+
+ return parameterTypes;
+ }
+
+ Class<?>[] typesOf(final Object[] args) {
+ final Class<?>[] types = new Class<?>[args.length];
+
+ for (int i = 0; i < types.length; i++) {
+ types[i] = args[i].getClass();
+ }
+
+ return types;
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslDefinitionGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslDefinitionGenerator.java
new file mode 100644
index 0000000..01210c8
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslDefinitionGenerator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+
+
+public final class RestDslDefinitionGenerator extends RestDslGenerator<RestDslDefinitionGenerator> {
+
+ RestDslDefinitionGenerator(final OasDocument openapi) {
+ super(openapi);
+ }
+
+ public RestsDefinition generate(final CamelContext context) {
+ final RestDefinitionEmitter emitter = new RestDefinitionEmitter(context);
+ String basePath = RestDslGenerator.getBasePathFromOasDocument(openapi);
+ final PathVisitor<RestsDefinition> restDslStatement = new PathVisitor<>(basePath, emitter, filter, destinationGenerator());
+
+ openapi.paths.getPathItems().forEach(restDslStatement::visit);
+
+ return emitter.result();
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslGenerator.java
new file mode 100644
index 0000000..65b6034
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslGenerator.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+
+import javax.annotation.processing.Filer;
+
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Document;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Document;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+import static org.apache.camel.util.ObjectHelper.notNull;
+
+/**
+ * Source code and {@link RestsDefinition} generator that generates Camel REST
+ * DSL implementations from OpenAPI specifications.
+ */
+public abstract class RestDslGenerator<G> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RestDslGenerator.class);
+ final OasDocument openapi;
+
+
+ DestinationGenerator destinationGenerator = new DirectToOperationId();
+ OperationFilter filter = new OperationFilter();
+ String restComponent;
+ String restContextPath;
+ String apiContextPath;
+ boolean springComponent;
+ boolean springBootProject;
+
+ RestDslGenerator(final OasDocument openapi) {
+ this.openapi = notNull(openapi, "openapi");
+ }
+
+ public G withDestinationGenerator(final DestinationGenerator directRouteGenerator) {
+ notNull(directRouteGenerator, "directRouteGenerator");
+ this.destinationGenerator = directRouteGenerator;
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ DestinationGenerator destinationGenerator() {
+ return destinationGenerator;
+ }
+
+ public G withOperationFilter(OperationFilter filter) {
+ this.filter = filter;
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ public G withOperationFilter(String include) {
+ this.filter.setIncludes(include);
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ public G withRestComponent(String restComponent) {
+ this.restComponent = restComponent;
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ public G withRestContextPath(String contextPath) {
+ this.restContextPath = contextPath;
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ public G withApiContextPath(String contextPath) {
+ this.apiContextPath = contextPath;
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ public G asSpringComponent() {
+ this.springComponent = true;
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ public G asSpringBootProject() {
+ this.springBootProject = true;
+
+ @SuppressWarnings("unchecked")
+ final G that = (G) this;
+
+ return that;
+ }
+
+ public static RestDslSourceCodeGenerator<Appendable> toAppendable(final OasDocument openapi) {
+ return new AppendableGenerator(openapi);
+ }
+
+ public static RestDslDefinitionGenerator toDefinition(final OasDocument openapi) {
+ return new RestDslDefinitionGenerator(openapi);
+ }
+
+ public static RestDslXmlGenerator toXml(final OasDocument openapi) {
+ return new RestDslXmlGenerator(openapi);
+ }
+
+ public static RestDslSourceCodeGenerator<Filer> toFiler(final OasDocument openapi) {
+ return new FilerGenerator(openapi);
+ }
+
+ public static RestDslSourceCodeGenerator<Path> toPath(final OasDocument openapi) {
+ return new PathGenerator(openapi);
+ }
+
+ public static String getHostFromOasDocument(final OasDocument openapi) {
+ String host = null;
+ if (openapi instanceof Oas20Document) {
+ host = ((Oas20Document)openapi).host;
+ } else if (openapi instanceof Oas30Document) {
+ if (((Oas30Document)openapi).getServers() != null
+ && ((Oas30Document)openapi).getServers().get(0) != null) {
+ try {
+ URL serverUrl = new URL(((Oas30Document)openapi).getServers().get(0).url);
+ host = serverUrl.getHost();
+
+ } catch (MalformedURLException e) {
+ LOG.info("error when parsing OpenApi 3.0 doc server url", e);
+ }
+ }
+ }
+ return host;
+
+ }
+
+ public static String getBasePathFromOasDocument(final OasDocument openapi) {
+ String basePath = null;
+ if (openapi instanceof Oas20Document) {
+ basePath = ((Oas20Document)openapi).basePath;
+ } else if (openapi instanceof Oas30Document) {
+ if (((Oas30Document)openapi).getServers() != null
+ && ((Oas30Document)openapi).getServers().get(0) != null) {
+ try {
+ URL serverUrl = new URL(((Oas30Document)openapi).getServers().get(0).url);
+ basePath = serverUrl.getPath();
+ if (basePath.indexOf("//") == 0) {
+ //strip off the first "/" if double "/" exists
+ basePath = basePath.substring(1);
+ }
+ if ("/".equals(basePath)) {
+ basePath = "";
+ }
+
+ } catch (MalformedURLException e) {
+ //not a valid whole url, just the basePath
+ basePath = ((Oas30Document)openapi).getServers().get(0).url;
+ }
+ }
+
+ }
+ return basePath;
+
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslSourceCodeGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslSourceCodeGenerator.java
new file mode 100644
index 0000000..70d9aa7
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslSourceCodeGenerator.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.function.Function;
+import java.util.stream.Collector;
+
+import javax.annotation.Generated;
+import javax.lang.model.element.Modifier;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import io.apicurio.datamodels.openapi.models.OasInfo;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.util.ObjectHelper;
+
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+/**
+ * Generates Java source code
+ */
+public abstract class RestDslSourceCodeGenerator<T> extends RestDslGenerator<RestDslSourceCodeGenerator<T>> {
+ static final String DEFAULT_CLASS_NAME = "RestDslRoute";
+
+ static final String DEFAULT_PACKAGE_NAME = "rest.dsl.generated";
+
+ private static final String DEFAULT_INDENT = " ";
+
+ private Function<OasDocument, String> classNameGenerator = RestDslSourceCodeGenerator::generateClassName;
+
+ private Instant generated = Instant.now();
+
+ private String indent = DEFAULT_INDENT;
+
+ private Function<OasDocument, String> packageNameGenerator = RestDslSourceCodeGenerator::generatePackageName;
+
+ private boolean sourceCodeTimestamps;
+
+ RestDslSourceCodeGenerator(final OasDocument openapi) {
+ super(openapi);
+ }
+
+ public abstract void generate(T destination) throws IOException;
+
+ public RestDslSourceCodeGenerator<T> withClassName(final String className) {
+ notEmpty(className, "className");
+ this.classNameGenerator = s -> className;
+
+ return this;
+ }
+
+ public RestDslSourceCodeGenerator<T> withIndent(final String indent) {
+ this.indent = ObjectHelper.notNull(indent, "indent");
+
+ return this;
+ }
+
+ public RestDslSourceCodeGenerator<T> withoutSourceCodeTimestamps() {
+ sourceCodeTimestamps = false;
+
+ return this;
+ }
+
+ public RestDslSourceCodeGenerator<T> withPackageName(final String packageName) {
+ notEmpty(packageName, "packageName");
+ this.packageNameGenerator = s -> packageName;
+
+ return this;
+ }
+
+ public RestDslSourceCodeGenerator<T> withSourceCodeTimestamps() {
+ sourceCodeTimestamps = true;
+
+ return this;
+ }
+
+ MethodSpec generateConfigureMethod(final OasDocument openapi) {
+ final MethodSpec.Builder configure = MethodSpec.methodBuilder("configure").addModifiers(Modifier.PUBLIC)
+ .returns(void.class).addJavadoc("Defines Apache Camel routes using REST DSL fluent API.\n");
+
+ final MethodBodySourceCodeEmitter emitter = new MethodBodySourceCodeEmitter(configure);
+
+ if (restComponent != null) {
+ configure.addCode("\n");
+ configure.addCode("restConfiguration().component(\"" + restComponent + "\")");
+ if (restContextPath != null) {
+ configure.addCode(".contextPath(\"" + restContextPath + "\")");
+ }
+ if (ObjectHelper.isNotEmpty(apiContextPath)) {
+ configure.addCode(".apiContextPath(\"" + apiContextPath + "\")");
+ }
+ configure.addCode(";\n\n");
+ }
+
+ String basePath = RestDslGenerator.getBasePathFromOasDocument(openapi);
+
+ final PathVisitor<MethodSpec> restDslStatement = new PathVisitor<>(basePath, emitter, filter, destinationGenerator());
+ openapi.paths.getItems().forEach(restDslStatement::visit);
+ return emitter.result();
+ }
+
+ Instant generated() {
+ return generated;
+ }
+
+ JavaFile generateSourceCode() {
+ final MethodSpec methodSpec = generateConfigureMethod(openapi);
+
+ final String classNameToUse = classNameGenerator.apply(openapi);
+
+ final AnnotationSpec.Builder generatedAnnotation = AnnotationSpec.builder(Generated.class).addMember("value",
+ "$S", getClass().getName());
+ if (sourceCodeTimestamps) {
+ generatedAnnotation.addMember("date", "$S", generated());
+ }
+
+ TypeSpec.Builder builder = TypeSpec.classBuilder(classNameToUse).superclass(RouteBuilder.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(methodSpec)
+ .addAnnotation(generatedAnnotation.build())
+ .addJavadoc("Generated from OpenApi specification by Camel REST DSL generator.\n");
+ if (springComponent) {
+ final AnnotationSpec.Builder springAnnotation = AnnotationSpec.builder(ClassName.bestGuess("org.springframework.stereotype.Component"));
+ builder.addAnnotation(springAnnotation.build());
+ }
+ TypeSpec generatedRouteBuilder = builder.build();
+
+ final String packageNameToUse = packageNameGenerator.apply(openapi);
+
+ return JavaFile.builder(packageNameToUse, generatedRouteBuilder).indent(indent).build();
+ }
+
+ RestDslSourceCodeGenerator<T> withGeneratedTime(final Instant generated) {
+ this.generated = generated;
+
+ return this;
+ }
+
+ static String generateClassName(final OasDocument openapi) {
+ final OasInfo info = (OasInfo)openapi.info;
+ if (info == null) {
+ return DEFAULT_CLASS_NAME;
+ }
+
+ final String title = info.title;
+ if (title == null) {
+ return DEFAULT_CLASS_NAME;
+ }
+
+ final String className = title.chars().filter(Character::isJavaIdentifierPart).filter(c -> c < 'z').boxed()
+ .collect(Collector.of(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append,
+ StringBuilder::toString));
+
+ if (className.isEmpty() || !Character.isJavaIdentifierStart(className.charAt(0))) {
+ return DEFAULT_CLASS_NAME;
+ }
+
+ return className;
+ }
+
+ static String generatePackageName(final OasDocument openapi) {
+ String host = RestDslGenerator.getHostFromOasDocument(openapi);
+
+
+ if (ObjectHelper.isNotEmpty(host)) {
+ final StringBuilder packageName = new StringBuilder();
+
+ final String hostWithoutPort = host.replaceFirst(":.*", "");
+
+ if ("localhost".equalsIgnoreCase(hostWithoutPort)) {
+ return DEFAULT_PACKAGE_NAME;
+ }
+
+ final String[] parts = hostWithoutPort.split("\\.");
+
+ for (int i = parts.length - 1; i >= 0; i--) {
+ packageName.append(parts[i]);
+ if (i != 0) {
+ packageName.append('.');
+ }
+ }
+
+ return packageName.toString();
+ }
+
+ return DEFAULT_PACKAGE_NAME;
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslXmlGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslXmlGenerator.java
new file mode 100644
index 0000000..f139b61
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/RestDslXmlGenerator.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.xml.sax.InputSource;
+
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.model.ModelHelper;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.util.ObjectHelper;
+
+
+
+public class RestDslXmlGenerator extends RestDslGenerator<RestDslXmlGenerator> {
+
+ private boolean blueprint;
+
+ RestDslXmlGenerator(final OasDocument openapi) {
+ super(openapi);
+ }
+
+ public String generate(final CamelContext context) throws Exception {
+ final RestDefinitionEmitter emitter = new RestDefinitionEmitter(context);
+ String basePath = RestDslGenerator.getBasePathFromOasDocument(openapi);
+ final PathVisitor<RestsDefinition> restDslStatement = new PathVisitor<>(basePath, emitter, filter,
+ destinationGenerator());
+
+ openapi.paths.getPathItems().forEach(restDslStatement::visit);
+
+ final RestsDefinition rests = emitter.result();
+ final String xml = ModelHelper.dumpModelAsXml(context, rests);
+
+ final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ builderFactory.setNamespaceAware(true);
+
+ final DocumentBuilder builder = builderFactory.newDocumentBuilder();
+
+ final Document document = builder.parse(new InputSource(new StringReader(xml)));
+
+ final Element root = document.getDocumentElement();
+
+ if (blueprint) {
+ document.renameNode(root, "http://camel.apache.org/schema/blueprint", root.getTagName());
+ }
+
+ // remove all customId attributes as we do not want them in the output
+ final NodeList elements = document.getElementsByTagName("*");
+ for (int i = 0; i < elements.getLength(); i++) {
+ final Element element = (Element) elements.item(i);
+ element.removeAttribute("customId");
+ }
+
+ if (restComponent != null) {
+ final Element configuration = document.createElement("restConfiguration");
+ configuration.setAttribute("component", restComponent);
+
+ if (restContextPath != null) {
+ configuration.setAttribute("contextPath", restContextPath);
+ }
+
+ if (ObjectHelper.isNotEmpty(apiContextPath)) {
+ configuration.setAttribute("apiContextPath", apiContextPath);
+ }
+
+ root.insertBefore(configuration, root.getFirstChild());
+ }
+
+ final TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
+ final Transformer transformer = transformerFactory.newTransformer();
+
+ final StringWriter writer = new StringWriter();
+ transformer.transform(new DOMSource(document), new StreamResult(writer));
+
+ return writer.toString();
+ }
+
+ public RestDslXmlGenerator withBlueprint() {
+ blueprint = true;
+ return this;
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/SpringBootProjectSourceCodeGenerator.java b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/SpringBootProjectSourceCodeGenerator.java
new file mode 100644
index 0000000..dfd2604
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/main/java/org/apache/camel/generator/openapi/SpringBootProjectSourceCodeGenerator.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import javax.annotation.Generated;
+import javax.lang.model.element.Modifier;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import org.apache.camel.util.ObjectHelper;
+
+import static org.apache.camel.util.StringHelper.notEmpty;
+
+public class SpringBootProjectSourceCodeGenerator {
+
+ private static final String DEFAULT_INDENT = " ";
+
+ private String indent = DEFAULT_INDENT;
+
+ private String packageName;
+
+ public void generate(Path destination) throws IOException {
+ final JavaFile javaFile = generateSourceCode();
+
+ javaFile.writeTo(destination);
+ }
+
+ public SpringBootProjectSourceCodeGenerator withIndent(final String indent) {
+ this.indent = ObjectHelper.notNull(indent, "indent");
+ return this;
+ }
+
+ public SpringBootProjectSourceCodeGenerator withPackageName(final String packageName) {
+ notEmpty(packageName, "packageName");
+ this.packageName = packageName;
+ return this;
+ }
+
+ JavaFile generateSourceCode() {
+ notEmpty(packageName, "packageName");
+
+ final MethodSpec methodSpec = generateRestMethod();
+
+ final String classNameToUse = "CamelRestController";
+
+ final AnnotationSpec.Builder generatedAnnotation = AnnotationSpec.builder(Generated.class).addMember("value",
+ "$S", getClass().getName());
+ final AnnotationSpec.Builder restAnnotation = AnnotationSpec.builder(ClassName.bestGuess("org.springframework.web.bind.annotation.RestController"));
+
+ TypeSpec.Builder builder = TypeSpec.classBuilder(classNameToUse)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(methodSpec)
+ .addAnnotation(generatedAnnotation.build())
+ .addAnnotation(restAnnotation.build())
+ .addJavadoc("Forward requests to the Camel servlet so it can service REST requests.\n");
+ TypeSpec generatedRestController = builder.build();
+
+ return JavaFile.builder(packageName, generatedRestController).indent(indent).build();
+ }
+
+ MethodSpec generateRestMethod() {
+ ClassName req = ClassName.bestGuess("javax.servlet.http.HttpServletRequest");
+ ClassName res = ClassName.bestGuess("javax.servlet.http.HttpServletResponse");
+
+ final AnnotationSpec.Builder reqAnnotation = AnnotationSpec.builder(ClassName.bestGuess("org.springframework.web.bind.annotation.RequestMapping"))
+ .addMember("value", "\"/**\"");
+
+ final MethodSpec.Builder forward = MethodSpec.methodBuilder("camelServlet").addModifiers(Modifier.PUBLIC)
+ .addParameter(req, "request")
+ .addParameter(res, "response")
+ .addAnnotation(reqAnnotation.build())
+ .returns(void.class);
+
+ forward.addCode("try {\n");
+ forward.addCode(" String path = request.getRequestURI();\n");
+ forward.addCode(" request.getServletContext().getRequestDispatcher(\"/camel/\" + path).forward(request, response);\n");
+ forward.addCode("} catch (Exception e) {\n");
+ forward.addCode(" response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\n");
+ forward.addCode("}\n");
+
+ return forward.build();
+ }
+
+ public static SpringBootProjectSourceCodeGenerator generator() {
+ return new SpringBootProjectSourceCodeGenerator();
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitterTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitterTest.java
new file mode 100644
index 0000000..6d3cdaf
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/MethodBodySourceCodeEmitterTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.MethodSpec.Builder;
+import org.apache.camel.model.rest.RestParamType;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MethodBodySourceCodeEmitterTest {
+
+ @Test
+ public void shouldGenerateSourceCode() {
+ final Builder method = MethodSpec.methodBuilder("configure");
+
+ final MethodBodySourceCodeEmitter emitter = new MethodBodySourceCodeEmitter(method);
+
+ emitter.emit("rest");
+ emitter.emit("put", "/pet");
+ emitter.emit("consumes", new Object[] {new String[] {"application/json", "application/xml"}});
+ emitter.emit("produces", new Object[] {new String[] {"application/xml", "application/json"}});
+ emitter.emit("param");
+ emitter.emit("name", "body");
+ emitter.emit("type", RestParamType.body);
+ emitter.emit("required", true);
+ emitter.emit("endParam");
+
+ assertThat(emitter.result().toString()).isEqualTo("void configure() {\n"//
+ + " rest()\n"//
+ + " .put(\"/pet\")\n"//
+ + " .consumes(\"application/json,application/xml\")\n"//
+ + " .produces(\"application/xml,application/json\")\n"//
+ + " .param()\n"//
+ + " .name(\"body\")\n"//
+ + " .type(org.apache.camel.model.rest.RestParamType.body)\n"//
+ + " .required(true)\n"//
+ + " .endParam();\n"//
+ + "}\n");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/PathSpringBootProjectSourceGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/PathSpringBootProjectSourceGeneratorTest.java
new file mode 100644
index 0000000..e7d6a46
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/PathSpringBootProjectSourceGeneratorTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PathSpringBootProjectSourceGeneratorTest {
+
+ @Test
+ public void shouldGenerateSourceCodeWithDefaults() throws IOException, URISyntaxException {
+ Path path = new File("target/generated-sources").toPath();
+ SpringBootProjectSourceCodeGenerator.generator().withPackageName("com.foo").generate(path);
+ final String generatedContent = new String(Files.readAllBytes(Paths.get("target/generated-sources/com/foo/CamelRestController.java")), StandardCharsets.UTF_8);
+
+ final URI file = PathSpringBootProjectSourceGeneratorTest.class.getResource("/SpringBootRestController.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(generatedContent).isEqualTo(expectedContent);
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDefinitionEmitterTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDefinitionEmitterTest.java
new file mode 100644
index 0000000..53d9f12
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDefinitionEmitterTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.util.List;
+
+
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.model.rest.RestOperationParamDefinition;
+import org.apache.camel.model.rest.RestParamType;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.apache.camel.model.rest.VerbDefinition;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDefinitionEmitterTest {
+ @Test
+ public void shouldGenerateObjects() {
+ final DefaultCamelContext context = new DefaultCamelContext();
+
+ final RestDefinitionEmitter emitter = new RestDefinitionEmitter(context);
+
+ emitter.emit("rest");
+ emitter.emit("put", "/pet");
+ emitter.emit("consumes", new Object[] {new String[] {"application/json", "application/xml"}});
+ emitter.emit("produces", new Object[] {new String[] {"application/xml", "application/json"}});
+ emitter.emit("param");
+ emitter.emit("name", "body");
+ emitter.emit("type", RestParamType.body);
+ emitter.emit("required", true);
+ emitter.emit("endParam");
+
+ final RestsDefinition result = emitter.result();
+ final List<RestDefinition> rests = result.getRests();
+ assertThat(rests).hasSize(1);
+
+ final RestDefinition rest = rests.get(0);
+ final List<VerbDefinition> verbs = rest.getVerbs();
+ assertThat(verbs).hasSize(1);
+
+ final VerbDefinition definition = verbs.get(0);
+ assertThat(definition.asVerb()).isEqualTo("put");
+ assertThat(definition.getUri()).isEqualTo("/pet");
+ assertThat(definition.getConsumes()).isEqualTo("application/json,application/xml");
+ assertThat(definition.getProduces()).isEqualTo("application/xml,application/json");
+
+ final List<RestOperationParamDefinition> params = definition.getParams();
+ assertThat(params).hasSize(1);
+
+ final RestOperationParamDefinition param = params.get(0);
+ assertThat(param.getName()).isEqualTo("body");
+ assertThat(param.getType()).isEqualTo(RestParamType.body);
+ assertThat(param.getRequired()).isEqualTo(true);
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorTest.java
new file mode 100644
index 0000000..6e8913d
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Instant;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslGeneratorTest {
+
+ static OasDocument openapi;
+
+ final Instant generated = Instant.parse("2017-10-17T00:00:00.000Z");
+
+
+ @BeforeClass
+ public static void readOpenApiDoc() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ FileInputStream fis = new FileInputStream(new File("openapi-v2.json"));
+ JsonNode node = mapper.readTree(fis);
+ openapi = (OasDocument)Library.readDocument(node);
+ }
+
+ @Test
+ public void shouldCreateDefinitions() {
+ final CamelContext context = new DefaultCamelContext();
+
+ final RestsDefinition definition = RestDslGenerator.toDefinition(openapi).generate(context);
+ assertThat(definition).isNotNull();
+ assertThat(definition.getRests()).hasSize(1);
+ assertThat(definition.getRests().get(0).getPath()).isEqualTo("/v2");
+
+
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithDefaults() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).generate(code);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstore.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(code.toString()).isEqualTo(expectedContent);
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithRestComponent() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withRestComponent("servlet").withRestContextPath("/").generate(code);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstoreWithRestComponent.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(code.toString()).isEqualTo(expectedContent);
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithOptions() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+ .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+ .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/MyRestRoute.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(code.toString()).isEqualTo(expectedContent);
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithFilter() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+ .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+ .withOperationFilter("find*,deletePet,updatePet")
+ .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/MyRestRouteFilter.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(code.toString()).isEqualTo(expectedContent);
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorV3Test.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorV3Test.java
new file mode 100644
index 0000000..f31460e
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslGeneratorV3Test.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Instant;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.model.rest.RestsDefinition;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslGeneratorV3Test {
+
+ static OasDocument openapi;
+
+ final Instant generated = Instant.parse("2017-10-17T00:00:00.000Z");
+
+
+ @BeforeClass
+ public static void readOpenApiDoc() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ FileInputStream fis = new FileInputStream(new File("openapi-spec.json"));
+ JsonNode node = mapper.readTree(fis);
+ openapi = (OasDocument)Library.readDocument(node);
+ }
+
+ @Test
+ public void shouldCreateDefinitions() {
+ final CamelContext context = new DefaultCamelContext();
+ final RestsDefinition definition = RestDslGenerator.toDefinition(openapi).generate(context);
+ assertThat(definition).isNotNull();
+ assertThat(definition.getRests()).hasSize(1);
+ assertThat(definition.getRests().get(0).getPath()).isEqualTo("/api/v3");
+
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithDefaults() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).generate(code);
+
+ final URI file = RestDslGeneratorV3Test.class.getResource("/OpenApiV3Petstore.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+ assertThat(code.toString()).isEqualTo(expectedContent);
+
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithRestComponent() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withRestComponent("servlet").withRestContextPath("/").generate(code);
+
+ final URI file = RestDslGeneratorV3Test.class.getResource("/OpenApiV3PetstoreWithRestComponent.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(code.toString()).isEqualTo(expectedContent);
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithOptions() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+ .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+ .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+ final URI file = RestDslGeneratorV3Test.class.getResource("/MyRestRouteV3.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+ assertThat(code.toString()).isEqualTo(expectedContent);
+ }
+
+ @Test
+ public void shouldGenerateSourceCodeWithFilter() throws IOException, URISyntaxException {
+ final StringBuilder code = new StringBuilder();
+
+ RestDslGenerator.toAppendable(openapi).withGeneratedTime(generated).withClassName("MyRestRoute")
+ .withPackageName("com.example").withIndent("\t").withSourceCodeTimestamps()
+ .withOperationFilter("find*,deletePet,updatePet")
+ .withDestinationGenerator(o -> "direct:rest-" + o.operationId).generate(code);
+
+ final URI file = RestDslGeneratorV3Test.class.getResource("/MyRestRouteFilterV3.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+ assertThat(code.toString()).isEqualTo(expectedContent);
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorTest.java
new file mode 100644
index 0000000..77530cc
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import io.apicurio.datamodels.openapi.models.OasInfo;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Document;
+import io.apicurio.datamodels.openapi.v2.models.Oas20Info;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslSourceCodeGeneratorTest {
+
+ @Test
+ public void shouldCreatePackageNamesFromHostnames() {
+ final Oas20Document openapi = new Oas20Document();
+ openapi.host = "api.example.org";
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+ }
+
+ @Test
+ public void shouldCreatePackageNamesFromHostnamesWithPorts() {
+ final Oas20Document openapi = new Oas20Document();
+ openapi.host = "api.example.org:8080";
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+ }
+
+ @Test
+ public void shouldGenerateClassNameFromTitle() {
+ final Oas20Document openapi = new Oas20Document();
+ OasInfo info = new Oas20Info();
+ info.title = "Example API";
+ openapi.info = info;
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI");
+ }
+
+ @Test
+ public void shouldGenerateClassNameFromTitleWithNonValidJavaIdentifiers() {
+ final Oas20Document openapi = new Oas20Document();
+ OasInfo info = new Oas20Info();
+ info.title = "Example-API 2.0";
+ openapi.info = info;
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI20");
+ }
+
+ @Test
+ public void shouldUseDefaultClassNameIfInfoOrTitleIsNotPresent() {
+ final Oas20Document openapi = new Oas20Document();
+
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+ openapi.info = new Oas20Info();
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultClassNameIfTitleContainsOnlyNonValidJavaIdentifiers() {
+ final Oas20Document openapi = new Oas20Document();
+ OasInfo info = new Oas20Info();
+ info.title = "\\%/4";
+ openapi.info = info;
+
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultPackageNameForLocalhost() {
+ final Oas20Document openapi = new Oas20Document();
+ openapi.host = "localhost";
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultPackageNameForLocalhostWithPort() {
+ final Oas20Document openapi = new Oas20Document();
+ openapi.host = "localhost:8080";
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultPackageNameIfNoHostIsSpecified() {
+ final Oas20Document openapi = new Oas20Document();
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorV3Test.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorV3Test.java
new file mode 100644
index 0000000..faf8b81
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslSourceCodeGeneratorV3Test.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+
+import io.apicurio.datamodels.openapi.models.OasInfo;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Document;
+import io.apicurio.datamodels.openapi.v3.models.Oas30Info;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+public class RestDslSourceCodeGeneratorV3Test {
+
+ @Test
+ public void shouldCreatePackageNamesFromHostnames() {
+ final Oas30Document openapi = new Oas30Document();
+ openapi.addServer("http://api.example.org/", "test server url");
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+ }
+
+ @Test
+ public void shouldCreatePackageNamesFromHostnamesWithPorts() {
+ final Oas30Document openapi = new Oas30Document();
+ openapi.addServer("http://api.example.org:8080/", "test server url");
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi)).isEqualTo("org.example.api");
+ }
+
+ @Test
+ public void shouldGenerateClassNameFromTitle() {
+ final Oas30Document openapi = new Oas30Document();
+ OasInfo info = new Oas30Info();
+ info.title = "Example API";
+ openapi.info = info;
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI");
+ }
+
+ @Test
+ public void shouldGenerateClassNameFromTitleWithNonValidJavaIdentifiers() {
+ final Oas30Document openapi = new Oas30Document();
+ OasInfo info = new Oas30Info();
+ info.title = "Example-API 2.0";
+ openapi.info = info;
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi)).isEqualTo("ExampleAPI20");
+ }
+
+ @Test
+ public void shouldUseDefaultClassNameIfInfoOrTitleIsNotPresent() {
+ final Oas30Document openapi = new Oas30Document();
+
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+ openapi.info = new Oas30Info();
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultClassNameIfTitleContainsOnlyNonValidJavaIdentifiers() {
+ final Oas30Document openapi = new Oas30Document();
+ OasInfo info = new Oas30Info();
+ info.title = "\\%/4";
+ openapi.info = info;
+
+ assertThat(RestDslSourceCodeGenerator.generateClassName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_CLASS_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultPackageNameForLocalhost() {
+ final Oas30Document openapi = new Oas30Document();
+ openapi.addServer("http://localhost", "test server url");
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultPackageNameForLocalhostWithPort() {
+ final Oas30Document openapi = new Oas30Document();
+ openapi.addServer("http://localhost:8080", "test server url");
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+ }
+
+ @Test
+ public void shouldUseDefaultPackageNameIfNoHostIsSpecified() {
+ final Oas30Document openapi = new Oas30Document();
+
+ assertThat(RestDslSourceCodeGenerator.generatePackageName(openapi))
+ .isEqualTo(RestDslSourceCodeGenerator.DEFAULT_PACKAGE_NAME);
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorTest.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorTest.java
new file mode 100644
index 0000000..ad4ab36
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.StringReader;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.xml.sax.InputSource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslXmlGeneratorTest {
+
+ static OasDocument openapi;
+
+ @BeforeClass
+ public static void readOpenApiDoc() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ FileInputStream fis = new FileInputStream(new File("openapi-v2.json"));
+ JsonNode node = mapper.readTree(fis);
+ openapi = (OasDocument)Library.readDocument(node);
+ }
+
+ @Test
+ public void shouldGenerateBlueprintXml() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).withBlueprint().generate(context);
+ assertThat(xml).isNotEmpty();
+ assertThat(xml.contains("http://camel.apache.org/schema/blueprint"));
+ }
+
+ @Test
+ public void shouldGenerateXml() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).generate(context);
+ assertThat(xml).isNotEmpty();
+ assertThat(xml.contains("http://camel.apache.org/schema/spring"));
+ }
+
+ @Test
+ public void shouldGenerateXmlWithDefaultnamespace() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+ final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setNamespaceAware(true);
+
+ final DocumentBuilder builder = builderFactory.newDocumentBuilder();
+
+ final Document document = builder.parse(new InputSource(new StringReader(xml)));
+
+ assertThat(document.isDefaultNamespace("http://camel.apache.org/schema/spring")).isTrue();
+ }
+
+ @Test
+ public void shouldGenerateXmlWithDefaults() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstoreXml.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(xml).isXmlEqualTo(expectedContent);
+ }
+
+ @Test
+ public void shouldGenerateXmlWithRestComponent() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).withRestComponent("servlet").withRestContextPath("/foo")
+ .generate(context);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/OpenApiPetstoreWithRestComponentXml.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(xml).isXmlEqualTo(expectedContent);
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorV3Test.java b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorV3Test.java
new file mode 100644
index 0000000..616d370
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/java/org/apache/camel/generator/openapi/RestDslXmlGeneratorV3Test.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.generator.openapi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.StringReader;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.xml.sax.InputSource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.apicurio.datamodels.Library;
+import io.apicurio.datamodels.openapi.models.OasDocument;
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RestDslXmlGeneratorV3Test {
+
+ static OasDocument openapi;
+
+ @BeforeClass
+ public static void readOpenApiDoc() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ FileInputStream fis = new FileInputStream(new File("openapi-spec.json"));
+ JsonNode node = mapper.readTree(fis);
+ openapi = (OasDocument)Library.readDocument(node);
+ }
+
+ @Test
+ public void shouldGenerateBlueprintXml() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).withBlueprint().generate(context);
+ assertThat(xml).isNotEmpty();
+ assertThat(xml.contains("http://camel.apache.org/schema/blueprint"));
+ }
+
+ @Test
+ public void shouldGenerateXml() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).generate(context);
+ assertThat(xml).isNotEmpty();
+ assertThat(xml.contains("http://camel.apache.org/schema/spring"));
+ }
+
+ @Test
+ public void shouldGenerateXmlWithDefaultnamespace() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+ final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setNamespaceAware(true);
+
+ final DocumentBuilder builder = builderFactory.newDocumentBuilder();
+
+ final Document document = builder.parse(new InputSource(new StringReader(xml)));
+
+ assertThat(document.isDefaultNamespace("http://camel.apache.org/schema/spring")).isTrue();
+ }
+
+ @Test
+ public void shouldGenerateXmlWithDefaults() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).generate(context);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/OpenApiV3PetstoreXml.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+
+ assertThat(xml).isXmlEqualTo(expectedContent);
+ }
+
+ @Test
+ public void shouldGenerateXmlWithRestComponent() throws Exception {
+ final CamelContext context = new DefaultCamelContext();
+
+ final String xml = RestDslGenerator.toXml(openapi).withRestComponent("servlet").withRestContextPath("/foo")
+ .generate(context);
+
+ final URI file = RestDslGeneratorTest.class.getResource("/OpenApiV3PetstoreWithRestComponentXml.txt").toURI();
+ final String expectedContent = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+ assertThat(xml).isXmlEqualTo(expectedContent);
+ }
+
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRoute.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRoute.txt
new file mode 100644
index 0000000..d08f3b5
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRoute.txt
@@ -0,0 +1,294 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+ value = "org.apache.camel.generator.openapi.AppendableGenerator",
+ date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+ rest("/v2")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:rest-updatePet")
+ .post("/pet")
+ .id("addPet")
+ .consumes("application/json,application/xml")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .param()
+ .name("verbose")
+ .type(RestParamType.query)
+ .dataType("boolean")
+ .defaultValue("false")
+ .required(false)
+ .description("Verbose data")
+ .endParam()
+ .to("direct:rest-addPet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:rest-findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:rest-findPetsByTags")
+ .get("/pet/{petId}")
+ .id("getPetById")
+ .description("Returns a single pet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to return")
+ .endParam()
+ .to("direct:rest-getPetById")
+ .post("/pet/{petId}")
+ .id("updatePetWithForm")
+ .consumes("application/x-www-form-urlencoded")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be updated")
+ .endParam()
+ .param()
+ .name("name")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Updated name of the pet")
+ .endParam()
+ .param()
+ .name("status")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Updated status of the pet")
+ .endParam()
+ .to("direct:rest-updatePetWithForm")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:rest-deletePet")
+ .post("/pet/{petId}/uploadImage")
+ .id("uploadFile")
+ .consumes("multipart/form-data")
+ .produces("application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to update")
+ .endParam()
+ .param()
+ .name("additionalMetadata")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Additional data to pass to server")
+ .endParam()
+ .param()
+ .name("file")
+ .type(RestParamType.formData)
+ .dataType("file")
+ .required(false)
+ .description("file to upload")
+ .endParam()
+ .to("direct:rest-uploadFile")
+ .get("/store/inventory")
+ .id("getInventory")
+ .description("Returns a map of status codes to quantities")
+ .produces("application/json")
+ .to("direct:rest-getInventory")
+ .post("/store/order")
+ .id("placeOrder")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("order placed for purchasing the pet")
+ .endParam()
+ .to("direct:rest-placeOrder")
+ .get("/store/order/{orderId}")
+ .id("getOrderById")
+ .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be fetched")
+ .endParam()
+ .to("direct:rest-getOrderById")
+ .delete("/store/order/{orderId}")
+ .id("deleteOrder")
+ .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of the order that needs to be deleted")
+ .endParam()
+ .to("direct:rest-deleteOrder")
+ .post("/user")
+ .id("createUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Created user object")
+ .endParam()
+ .to("direct:rest-createUser")
+ .post("/user/createWithArray")
+ .id("createUsersWithArrayInput")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:rest-createUsersWithArrayInput")
+ .post("/user/createWithList")
+ .id("createUsersWithListInput")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:rest-createUsersWithListInput")
+ .get("/user/login")
+ .id("loginUser")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The user name for login")
+ .endParam()
+ .param()
+ .name("password")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The password for login in clear text")
+ .endParam()
+ .to("direct:rest-loginUser")
+ .get("/user/logout")
+ .id("logoutUser")
+ .produces("application/xml,application/json")
+ .to("direct:rest-logoutUser")
+ .get("/user/{username}")
+ .id("getUserByName")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be fetched. Use user1 for testing. ")
+ .endParam()
+ .to("direct:rest-getUserByName")
+ .put("/user/{username}")
+ .id("updateUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("name that need to be updated")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Updated user object")
+ .endParam()
+ .to("direct:rest-updateUser")
+ .delete("/user/{username}")
+ .id("deleteUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be deleted")
+ .endParam()
+ .to("direct:rest-deleteUser");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilter.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilter.txt
new file mode 100644
index 0000000..27547a6
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilter.txt
@@ -0,0 +1,78 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+ value = "org.apache.camel.generator.openapi.AppendableGenerator",
+ date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+ rest("/v2")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:rest-updatePet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:rest-findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:rest-findPetsByTags")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:rest-deletePet");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilterV3.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilterV3.txt
new file mode 100644
index 0000000..473210d
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteFilterV3.txt
@@ -0,0 +1,76 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+ value = "org.apache.camel.generator.openapi.AppendableGenerator",
+ date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+ rest("/api/v3")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:rest-updatePet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:rest-findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:rest-findPetsByTags")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:rest-deletePet");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteV3.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteV3.txt
new file mode 100644
index 0000000..1ec2154
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/MyRestRouteV3.txt
@@ -0,0 +1,288 @@
+package com.example;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated(
+ value = "org.apache.camel.generator.openapi.AppendableGenerator",
+ date = "2017-10-17T00:00:00Z"
+)
+public final class MyRestRoute extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+ rest("/api/v3")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:rest-updatePet")
+ .post("/pet")
+ .id("addPet")
+ .consumes("application/json,application/xml")
+ .param()
+ .name("verbose")
+ .type(RestParamType.query)
+ .dataType("boolean")
+ .defaultValue("false")
+ .required(false)
+ .description("Verbose data")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:rest-addPet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:rest-findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:rest-findPetsByTags")
+ .get("/pet/{petId}")
+ .id("getPetById")
+ .description("Returns a single pet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to return")
+ .endParam()
+ .to("direct:rest-getPetById")
+ .post("/pet/{petId}")
+ .id("updatePetWithForm")
+ .consumes("application/x-www-form-urlencoded")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be updated")
+ .endParam()
+ .param()
+ .name("name")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Updated name of the pet")
+ .endParam()
+ .param()
+ .name("status")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Updated status of the pet")
+ .endParam()
+ .to("direct:rest-updatePetWithForm")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:rest-deletePet")
+ .post("/pet/{petId}/uploadImage")
+ .id("uploadFile")
+ .consumes("multipart/form-data")
+ .produces("application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to update")
+ .endParam()
+ .param()
+ .name("additionalMetadata")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Additional data to pass to server")
+ .endParam()
+ .param()
+ .name("file")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("file to upload")
+ .endParam()
+ .to("direct:rest-uploadFile")
+ .get("/store/inventory")
+ .id("getInventory")
+ .description("Returns a map of status codes to quantities")
+ .produces("application/json")
+ .to("direct:rest-getInventory")
+ .post("/store/order")
+ .id("placeOrder")
+ .consumes("*/*")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("order placed for purchasing the pet")
+ .endParam()
+ .to("direct:rest-placeOrder")
+ .get("/store/order/{orderId}")
+ .id("getOrderById")
+ .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be fetched")
+ .endParam()
+ .to("direct:rest-getOrderById")
+ .delete("/store/order/{orderId}")
+ .id("deleteOrder")
+ .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of the order that needs to be deleted")
+ .endParam()
+ .to("direct:rest-deleteOrder")
+ .post("/user")
+ .id("createUser")
+ .description("This can only be done by the logged in user.")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Created user object")
+ .endParam()
+ .to("direct:rest-createUser")
+ .post("/user/createWithArray")
+ .id("createUsersWithArrayInput")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:rest-createUsersWithArrayInput")
+ .post("/user/createWithList")
+ .id("createUsersWithListInput")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:rest-createUsersWithListInput")
+ .get("/user/login")
+ .id("loginUser")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The user name for login")
+ .endParam()
+ .param()
+ .name("password")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The password for login in clear text")
+ .endParam()
+ .to("direct:rest-loginUser")
+ .get("/user/logout")
+ .id("logoutUser")
+ .to("direct:rest-logoutUser")
+ .get("/user/{username}")
+ .id("getUserByName")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be fetched. Use user1 for testing. ")
+ .endParam()
+ .to("direct:rest-getUserByName")
+ .put("/user/{username}")
+ .id("updateUser")
+ .description("This can only be done by the logged in user.")
+ .consumes("*/*")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("name that need to be updated")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Updated user object")
+ .endParam()
+ .to("direct:rest-updateUser")
+ .delete("/user/{username}")
+ .id("deleteUser")
+ .description("This can only be done by the logged in user.")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be deleted")
+ .endParam()
+ .to("direct:rest-deleteUser");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstore.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstore.txt
new file mode 100644
index 0000000..c3334f7
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstore.txt
@@ -0,0 +1,291 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+ rest("/v2")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:updatePet")
+ .post("/pet")
+ .id("addPet")
+ .consumes("application/json,application/xml")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .param()
+ .name("verbose")
+ .type(RestParamType.query)
+ .dataType("boolean")
+ .defaultValue("false")
+ .required(false)
+ .description("Verbose data")
+ .endParam()
+ .to("direct:addPet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:findPetsByTags")
+ .get("/pet/{petId}")
+ .id("getPetById")
+ .description("Returns a single pet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to return")
+ .endParam()
+ .to("direct:getPetById")
+ .post("/pet/{petId}")
+ .id("updatePetWithForm")
+ .consumes("application/x-www-form-urlencoded")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be updated")
+ .endParam()
+ .param()
+ .name("name")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Updated name of the pet")
+ .endParam()
+ .param()
+ .name("status")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Updated status of the pet")
+ .endParam()
+ .to("direct:updatePetWithForm")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:deletePet")
+ .post("/pet/{petId}/uploadImage")
+ .id("uploadFile")
+ .consumes("multipart/form-data")
+ .produces("application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to update")
+ .endParam()
+ .param()
+ .name("additionalMetadata")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Additional data to pass to server")
+ .endParam()
+ .param()
+ .name("file")
+ .type(RestParamType.formData)
+ .dataType("file")
+ .required(false)
+ .description("file to upload")
+ .endParam()
+ .to("direct:uploadFile")
+ .get("/store/inventory")
+ .id("getInventory")
+ .description("Returns a map of status codes to quantities")
+ .produces("application/json")
+ .to("direct:getInventory")
+ .post("/store/order")
+ .id("placeOrder")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("order placed for purchasing the pet")
+ .endParam()
+ .to("direct:placeOrder")
+ .get("/store/order/{orderId}")
+ .id("getOrderById")
+ .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be fetched")
+ .endParam()
+ .to("direct:getOrderById")
+ .delete("/store/order/{orderId}")
+ .id("deleteOrder")
+ .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of the order that needs to be deleted")
+ .endParam()
+ .to("direct:deleteOrder")
+ .post("/user")
+ .id("createUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Created user object")
+ .endParam()
+ .to("direct:createUser")
+ .post("/user/createWithArray")
+ .id("createUsersWithArrayInput")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithArrayInput")
+ .post("/user/createWithList")
+ .id("createUsersWithListInput")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithListInput")
+ .get("/user/login")
+ .id("loginUser")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The user name for login")
+ .endParam()
+ .param()
+ .name("password")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The password for login in clear text")
+ .endParam()
+ .to("direct:loginUser")
+ .get("/user/logout")
+ .id("logoutUser")
+ .produces("application/xml,application/json")
+ .to("direct:logoutUser")
+ .get("/user/{username}")
+ .id("getUserByName")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be fetched. Use user1 for testing. ")
+ .endParam()
+ .to("direct:getUserByName")
+ .put("/user/{username}")
+ .id("updateUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("name that need to be updated")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Updated user object")
+ .endParam()
+ .to("direct:updateUser")
+ .delete("/user/{username}")
+ .id("deleteUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be deleted")
+ .endParam()
+ .to("direct:deleteUser");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponent.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponent.txt
new file mode 100644
index 0000000..657025f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponent.txt
@@ -0,0 +1,294 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+
+ restConfiguration().component("servlet").contextPath("/");
+
+ rest("/v2")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:updatePet")
+ .post("/pet")
+ .id("addPet")
+ .consumes("application/json,application/xml")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .param()
+ .name("verbose")
+ .type(RestParamType.query)
+ .dataType("boolean")
+ .defaultValue("false")
+ .required(false)
+ .description("Verbose data")
+ .endParam()
+ .to("direct:addPet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:findPetsByTags")
+ .get("/pet/{petId}")
+ .id("getPetById")
+ .description("Returns a single pet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to return")
+ .endParam()
+ .to("direct:getPetById")
+ .post("/pet/{petId}")
+ .id("updatePetWithForm")
+ .consumes("application/x-www-form-urlencoded")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be updated")
+ .endParam()
+ .param()
+ .name("name")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Updated name of the pet")
+ .endParam()
+ .param()
+ .name("status")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Updated status of the pet")
+ .endParam()
+ .to("direct:updatePetWithForm")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:deletePet")
+ .post("/pet/{petId}/uploadImage")
+ .id("uploadFile")
+ .consumes("multipart/form-data")
+ .produces("application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to update")
+ .endParam()
+ .param()
+ .name("additionalMetadata")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(false)
+ .description("Additional data to pass to server")
+ .endParam()
+ .param()
+ .name("file")
+ .type(RestParamType.formData)
+ .dataType("file")
+ .required(false)
+ .description("file to upload")
+ .endParam()
+ .to("direct:uploadFile")
+ .get("/store/inventory")
+ .id("getInventory")
+ .description("Returns a map of status codes to quantities")
+ .produces("application/json")
+ .to("direct:getInventory")
+ .post("/store/order")
+ .id("placeOrder")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("order placed for purchasing the pet")
+ .endParam()
+ .to("direct:placeOrder")
+ .get("/store/order/{orderId}")
+ .id("getOrderById")
+ .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be fetched")
+ .endParam()
+ .to("direct:getOrderById")
+ .delete("/store/order/{orderId}")
+ .id("deleteOrder")
+ .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of the order that needs to be deleted")
+ .endParam()
+ .to("direct:deleteOrder")
+ .post("/user")
+ .id("createUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Created user object")
+ .endParam()
+ .to("direct:createUser")
+ .post("/user/createWithArray")
+ .id("createUsersWithArrayInput")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithArrayInput")
+ .post("/user/createWithList")
+ .id("createUsersWithListInput")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithListInput")
+ .get("/user/login")
+ .id("loginUser")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The user name for login")
+ .endParam()
+ .param()
+ .name("password")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The password for login in clear text")
+ .endParam()
+ .to("direct:loginUser")
+ .get("/user/logout")
+ .id("logoutUser")
+ .produces("application/xml,application/json")
+ .to("direct:logoutUser")
+ .get("/user/{username}")
+ .id("getUserByName")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be fetched. Use user1 for testing. ")
+ .endParam()
+ .to("direct:getUserByName")
+ .put("/user/{username}")
+ .id("updateUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("name that need to be updated")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Updated user object")
+ .endParam()
+ .to("direct:updateUser")
+ .delete("/user/{username}")
+ .id("deleteUser")
+ .description("This can only be done by the logged in user.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be deleted")
+ .endParam()
+ .to("direct:deleteUser");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponentXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponentXml.txt
new file mode 100644
index 0000000..d2ffa0f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreWithRestComponentXml.txt
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<rests xmlns="http://camel.apache.org/schema/spring">
+ <restConfiguration component="servlet" contextPath="/foo"/>
+ <rest path="/v2">
+ <put consumes="application/json,application/xml" id="updatePet" produces="application/xml,application/json" uri="/pet">
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <to uri="direct:updatePet"/>
+ </put>
+ <post consumes="application/json,application/xml" id="addPet" produces="application/xml,application/json" uri="/pet">
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+ <to uri="direct:addPet"/>
+ </post>
+ <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+ <description>Multiple status values can be provided with comma separated strings</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+ <to uri="direct:findPetsByStatus"/>
+ </get>
+ <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+ <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+ <to uri="direct:findPetsByTags"/>
+ </get>
+ <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+ <description>Returns a single pet</description>
+ <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+ <to uri="direct:getPetById"/>
+ </get>
+ <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" produces="application/xml,application/json" uri="/pet/{petId}">
+ <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Updated name of the pet" name="name" required="false" type="formData"/>
+ <param dataType="string" description="Updated status of the pet" name="status" required="false" type="formData"/>
+ <to uri="direct:updatePetWithForm"/>
+ </post>
+ <delete id="deletePet" produces="application/xml,application/json" uri="/pet/{petId}">
+ <param dataType="string" name="api_key" required="false" type="header"/>
+ <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+ <to uri="direct:deletePet"/>
+ </delete>
+ <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+ <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="false" type="formData"/>
+ <param dataType="file" description="file to upload" name="file" required="false" type="formData"/>
+ <to uri="direct:uploadFile"/>
+ </post>
+ <get id="getInventory" produces="application/json" uri="/store/inventory">
+ <description>Returns a map of status codes to quantities</description>
+ <to uri="direct:getInventory"/>
+ </get>
+ <post id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+ <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+ <to uri="direct:placeOrder"/>
+ </post>
+ <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions</description>
+ <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+ <to uri="direct:getOrderById"/>
+ </get>
+ <delete id="deleteOrder" produces="application/xml,application/json" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+ <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+ <to uri="direct:deleteOrder"/>
+ </delete>
+ <post id="createUser" produces="application/xml,application/json" uri="/user">
+ <description>This can only be done by the logged in user.</description>
+ <param description="Created user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUser"/>
+ </post>
+ <post id="createUsersWithArrayInput" produces="application/xml,application/json" uri="/user/createWithArray">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithArrayInput"/>
+ </post>
+ <post id="createUsersWithListInput" produces="application/xml,application/json" uri="/user/createWithList">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithListInput"/>
+ </post>
+ <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+ <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+ <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+ <to uri="direct:loginUser"/>
+ </get>
+ <get id="logoutUser" produces="application/xml,application/json" uri="/user/logout">
+ <to uri="direct:logoutUser"/>
+ </get>
+ <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+ <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+ <to uri="direct:getUserByName"/>
+ </get>
+ <put id="updateUser" produces="application/xml,application/json" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+ <param description="Updated user object" name="body" required="true" type="body"/>
+ <to uri="direct:updateUser"/>
+ </put>
+ <delete id="deleteUser" produces="application/xml,application/json" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+ <to uri="direct:deleteUser"/>
+ </delete>
+ </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreXml.txt
new file mode 100644
index 0000000..8d68e03
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiPetstoreXml.txt
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<rests xmlns="http://camel.apache.org/schema/spring">
+ <rest path="/v2">
+ <put consumes="application/json,application/xml" id="updatePet" produces="application/xml,application/json" uri="/pet">
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <to uri="direct:updatePet"/>
+ </put>
+ <post consumes="application/json,application/xml" id="addPet" produces="application/xml,application/json" uri="/pet">
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+ <to uri="direct:addPet"/>
+ </post>
+ <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+ <description>Multiple status values can be provided with comma separated strings</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+ <to uri="direct:findPetsByStatus"/>
+ </get>
+ <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+ <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+ <to uri="direct:findPetsByTags"/>
+ </get>
+ <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+ <description>Returns a single pet</description>
+ <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+ <to uri="direct:getPetById"/>
+ </get>
+ <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" produces="application/xml,application/json" uri="/pet/{petId}">
+ <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Updated name of the pet" name="name" required="false" type="formData"/>
+ <param dataType="string" description="Updated status of the pet" name="status" required="false" type="formData"/>
+ <to uri="direct:updatePetWithForm"/>
+ </post>
+ <delete id="deletePet" produces="application/xml,application/json" uri="/pet/{petId}">
+ <param dataType="string" name="api_key" required="false" type="header"/>
+ <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+ <to uri="direct:deletePet"/>
+ </delete>
+ <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+ <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="false" type="formData"/>
+ <param dataType="file" description="file to upload" name="file" required="false" type="formData"/>
+ <to uri="direct:uploadFile"/>
+ </post>
+ <get id="getInventory" produces="application/json" uri="/store/inventory">
+ <description>Returns a map of status codes to quantities</description>
+ <to uri="direct:getInventory"/>
+ </get>
+ <post id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+ <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+ <to uri="direct:placeOrder"/>
+ </post>
+ <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions</description>
+ <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+ <to uri="direct:getOrderById"/>
+ </get>
+ <delete id="deleteOrder" produces="application/xml,application/json" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+ <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+ <to uri="direct:deleteOrder"/>
+ </delete>
+ <post id="createUser" produces="application/xml,application/json" uri="/user">
+ <description>This can only be done by the logged in user.</description>
+ <param description="Created user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUser"/>
+ </post>
+ <post id="createUsersWithArrayInput" produces="application/xml,application/json" uri="/user/createWithArray">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithArrayInput"/>
+ </post>
+ <post id="createUsersWithListInput" produces="application/xml,application/json" uri="/user/createWithList">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithListInput"/>
+ </post>
+ <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+ <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+ <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+ <to uri="direct:loginUser"/>
+ </get>
+ <get id="logoutUser" produces="application/xml,application/json" uri="/user/logout">
+ <to uri="direct:logoutUser"/>
+ </get>
+ <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+ <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+ <to uri="direct:getUserByName"/>
+ </get>
+ <put id="updateUser" produces="application/xml,application/json" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+ <param description="Updated user object" name="body" required="true" type="body"/>
+ <to uri="direct:updateUser"/>
+ </put>
+ <delete id="deleteUser" produces="application/xml,application/json" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+ <to uri="direct:deleteUser"/>
+ </delete>
+ </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3Petstore.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3Petstore.txt
new file mode 100644
index 0000000..239990e
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3Petstore.txt
@@ -0,0 +1,285 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+ rest("/api/v3")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:updatePet")
+ .post("/pet")
+ .id("addPet")
+ .consumes("application/json,application/xml")
+ .param()
+ .name("verbose")
+ .type(RestParamType.query)
+ .dataType("boolean")
+ .defaultValue("false")
+ .required(false)
+ .description("Verbose data")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:addPet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:findPetsByTags")
+ .get("/pet/{petId}")
+ .id("getPetById")
+ .description("Returns a single pet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to return")
+ .endParam()
+ .to("direct:getPetById")
+ .post("/pet/{petId}")
+ .id("updatePetWithForm")
+ .consumes("application/x-www-form-urlencoded")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be updated")
+ .endParam()
+ .param()
+ .name("name")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Updated name of the pet")
+ .endParam()
+ .param()
+ .name("status")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Updated status of the pet")
+ .endParam()
+ .to("direct:updatePetWithForm")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:deletePet")
+ .post("/pet/{petId}/uploadImage")
+ .id("uploadFile")
+ .consumes("multipart/form-data")
+ .produces("application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to update")
+ .endParam()
+ .param()
+ .name("additionalMetadata")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Additional data to pass to server")
+ .endParam()
+ .param()
+ .name("file")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("file to upload")
+ .endParam()
+ .to("direct:uploadFile")
+ .get("/store/inventory")
+ .id("getInventory")
+ .description("Returns a map of status codes to quantities")
+ .produces("application/json")
+ .to("direct:getInventory")
+ .post("/store/order")
+ .id("placeOrder")
+ .consumes("*/*")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("order placed for purchasing the pet")
+ .endParam()
+ .to("direct:placeOrder")
+ .get("/store/order/{orderId}")
+ .id("getOrderById")
+ .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be fetched")
+ .endParam()
+ .to("direct:getOrderById")
+ .delete("/store/order/{orderId}")
+ .id("deleteOrder")
+ .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of the order that needs to be deleted")
+ .endParam()
+ .to("direct:deleteOrder")
+ .post("/user")
+ .id("createUser")
+ .description("This can only be done by the logged in user.")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Created user object")
+ .endParam()
+ .to("direct:createUser")
+ .post("/user/createWithArray")
+ .id("createUsersWithArrayInput")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithArrayInput")
+ .post("/user/createWithList")
+ .id("createUsersWithListInput")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithListInput")
+ .get("/user/login")
+ .id("loginUser")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The user name for login")
+ .endParam()
+ .param()
+ .name("password")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The password for login in clear text")
+ .endParam()
+ .to("direct:loginUser")
+ .get("/user/logout")
+ .id("logoutUser")
+ .to("direct:logoutUser")
+ .get("/user/{username}")
+ .id("getUserByName")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be fetched. Use user1 for testing. ")
+ .endParam()
+ .to("direct:getUserByName")
+ .put("/user/{username}")
+ .id("updateUser")
+ .description("This can only be done by the logged in user.")
+ .consumes("*/*")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("name that need to be updated")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Updated user object")
+ .endParam()
+ .to("direct:updateUser")
+ .delete("/user/{username}")
+ .id("deleteUser")
+ .description("This can only be done by the logged in user.")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be deleted")
+ .endParam()
+ .to("direct:deleteUser");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponent.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponent.txt
new file mode 100644
index 0000000..614fd01
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponent.txt
@@ -0,0 +1,288 @@
+package io.openapi.petstore;
+
+import javax.annotation.Generated;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.CollectionFormat;
+import org.apache.camel.model.rest.RestParamType;
+
+/**
+ * Generated from OpenApi specification by Camel REST DSL generator.
+ */
+@Generated("org.apache.camel.generator.openapi.AppendableGenerator")
+public final class OpenApiPetstore extends RouteBuilder {
+ /**
+ * Defines Apache Camel routes using REST DSL fluent API.
+ */
+ public void configure() {
+
+ restConfiguration().component("servlet").contextPath("/");
+
+ rest("/api/v3")
+ .put("/pet")
+ .id("updatePet")
+ .consumes("application/json,application/xml")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:updatePet")
+ .post("/pet")
+ .id("addPet")
+ .consumes("application/json,application/xml")
+ .param()
+ .name("verbose")
+ .type(RestParamType.query)
+ .dataType("boolean")
+ .defaultValue("false")
+ .required(false)
+ .description("Verbose data")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Pet object that needs to be added to the store")
+ .endParam()
+ .to("direct:addPet")
+ .get("/pet/findByStatus")
+ .id("findPetsByStatus")
+ .description("Multiple status values can be provided with comma separated strings")
+ .produces("application/xml,application/json")
+ .param()
+ .name("status")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Status values that need to be considered for filter")
+ .endParam()
+ .to("direct:findPetsByStatus")
+ .get("/pet/findByTags")
+ .id("findPetsByTags")
+ .description("Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.")
+ .produces("application/xml,application/json")
+ .param()
+ .name("tags")
+ .type(RestParamType.query)
+ .dataType("array")
+ .collectionFormat(CollectionFormat.multi)
+ .arrayType("string")
+ .required(true)
+ .description("Tags to filter by")
+ .endParam()
+ .to("direct:findPetsByTags")
+ .get("/pet/{petId}")
+ .id("getPetById")
+ .description("Returns a single pet")
+ .produces("application/xml,application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to return")
+ .endParam()
+ .to("direct:getPetById")
+ .post("/pet/{petId}")
+ .id("updatePetWithForm")
+ .consumes("application/x-www-form-urlencoded")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be updated")
+ .endParam()
+ .param()
+ .name("name")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Updated name of the pet")
+ .endParam()
+ .param()
+ .name("status")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Updated status of the pet")
+ .endParam()
+ .to("direct:updatePetWithForm")
+ .delete("/pet/{petId}")
+ .id("deletePet")
+ .param()
+ .name("api_key")
+ .type(RestParamType.header)
+ .dataType("string")
+ .required(false)
+ .endParam()
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("Pet id to delete")
+ .endParam()
+ .to("direct:deletePet")
+ .post("/pet/{petId}/uploadImage")
+ .id("uploadFile")
+ .consumes("multipart/form-data")
+ .produces("application/json")
+ .param()
+ .name("petId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet to update")
+ .endParam()
+ .param()
+ .name("additionalMetadata")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("Additional data to pass to server")
+ .endParam()
+ .param()
+ .name("file")
+ .type(RestParamType.formData)
+ .dataType("string")
+ .required(true)
+ .description("file to upload")
+ .endParam()
+ .to("direct:uploadFile")
+ .get("/store/inventory")
+ .id("getInventory")
+ .description("Returns a map of status codes to quantities")
+ .produces("application/json")
+ .to("direct:getInventory")
+ .post("/store/order")
+ .id("placeOrder")
+ .consumes("*/*")
+ .produces("application/xml,application/json")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("order placed for purchasing the pet")
+ .endParam()
+ .to("direct:placeOrder")
+ .get("/store/order/{orderId}")
+ .id("getOrderById")
+ .description("For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions")
+ .produces("application/xml,application/json")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of pet that needs to be fetched")
+ .endParam()
+ .to("direct:getOrderById")
+ .delete("/store/order/{orderId}")
+ .id("deleteOrder")
+ .description("For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors")
+ .param()
+ .name("orderId")
+ .type(RestParamType.path)
+ .dataType("integer")
+ .required(true)
+ .description("ID of the order that needs to be deleted")
+ .endParam()
+ .to("direct:deleteOrder")
+ .post("/user")
+ .id("createUser")
+ .description("This can only be done by the logged in user.")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Created user object")
+ .endParam()
+ .to("direct:createUser")
+ .post("/user/createWithArray")
+ .id("createUsersWithArrayInput")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithArrayInput")
+ .post("/user/createWithList")
+ .id("createUsersWithListInput")
+ .consumes("*/*")
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("List of user object")
+ .endParam()
+ .to("direct:createUsersWithListInput")
+ .get("/user/login")
+ .id("loginUser")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The user name for login")
+ .endParam()
+ .param()
+ .name("password")
+ .type(RestParamType.query)
+ .dataType("string")
+ .required(true)
+ .description("The password for login in clear text")
+ .endParam()
+ .to("direct:loginUser")
+ .get("/user/logout")
+ .id("logoutUser")
+ .to("direct:logoutUser")
+ .get("/user/{username}")
+ .id("getUserByName")
+ .produces("application/xml,application/json")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be fetched. Use user1 for testing. ")
+ .endParam()
+ .to("direct:getUserByName")
+ .put("/user/{username}")
+ .id("updateUser")
+ .description("This can only be done by the logged in user.")
+ .consumes("*/*")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("name that need to be updated")
+ .endParam()
+ .param()
+ .name("body")
+ .type(RestParamType.body)
+ .required(true)
+ .description("Updated user object")
+ .endParam()
+ .to("direct:updateUser")
+ .delete("/user/{username}")
+ .id("deleteUser")
+ .description("This can only be done by the logged in user.")
+ .param()
+ .name("username")
+ .type(RestParamType.path)
+ .dataType("string")
+ .required(true)
+ .description("The name that needs to be deleted")
+ .endParam()
+ .to("direct:deleteUser");
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponentXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponentXml.txt
new file mode 100644
index 0000000..abd42a6
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreWithRestComponentXml.txt
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?><rests xmlns="http://camel.apache.org/schema/spring"><restConfiguration component="servlet" contextPath="/foo"/>
+ <rest path="/api/v3">
+ <put consumes="application/json,application/xml" id="updatePet" uri="/pet">
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <to uri="direct:updatePet"/>
+ </put>
+ <post consumes="application/json,application/xml" id="addPet" uri="/pet">
+ <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <to uri="direct:addPet"/>
+ </post>
+ <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+ <description>Multiple status values can be provided with comma separated strings</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+ <to uri="direct:findPetsByStatus"/>
+ </get>
+ <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+ <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+ <to uri="direct:findPetsByTags"/>
+ </get>
+ <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+ <description>Returns a single pet</description>
+ <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+ <to uri="direct:getPetById"/>
+ </get>
+ <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" uri="/pet/{petId}">
+ <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Updated name of the pet" name="name" required="true" type="formData"/>
+ <param dataType="string" description="Updated status of the pet" name="status" required="true" type="formData"/>
+ <to uri="direct:updatePetWithForm"/>
+ </post>
+ <delete id="deletePet" uri="/pet/{petId}">
+ <param dataType="string" name="api_key" required="false" type="header"/>
+ <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+ <to uri="direct:deletePet"/>
+ </delete>
+ <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+ <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="true" type="formData"/>
+ <param dataType="string" description="file to upload" name="file" required="true" type="formData"/>
+ <to uri="direct:uploadFile"/>
+ </post>
+ <get id="getInventory" produces="application/json" uri="/store/inventory">
+ <description>Returns a map of status codes to quantities</description>
+ <to uri="direct:getInventory"/>
+ </get>
+ <post consumes="*/*" id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+ <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+ <to uri="direct:placeOrder"/>
+ </post>
+ <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions</description>
+ <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+ <to uri="direct:getOrderById"/>
+ </get>
+ <delete id="deleteOrder" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+ <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+ <to uri="direct:deleteOrder"/>
+ </delete>
+ <post consumes="*/*" id="createUser" uri="/user">
+ <description>This can only be done by the logged in user.</description>
+ <param description="Created user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUser"/>
+ </post>
+ <post consumes="*/*" id="createUsersWithArrayInput" uri="/user/createWithArray">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithArrayInput"/>
+ </post>
+ <post consumes="*/*" id="createUsersWithListInput" uri="/user/createWithList">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithListInput"/>
+ </post>
+ <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+ <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+ <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+ <to uri="direct:loginUser"/>
+ </get>
+ <get id="logoutUser" uri="/user/logout">
+ <to uri="direct:logoutUser"/>
+ </get>
+ <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+ <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+ <to uri="direct:getUserByName"/>
+ </get>
+ <put consumes="*/*" id="updateUser" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+ <param description="Updated user object" name="body" required="true" type="body"/>
+ <to uri="direct:updateUser"/>
+ </put>
+ <delete id="deleteUser" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+ <to uri="direct:deleteUser"/>
+ </delete>
+ </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreXml.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreXml.txt
new file mode 100644
index 0000000..ebb8da1
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/OpenApiV3PetstoreXml.txt
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?><rests xmlns="http://camel.apache.org/schema/spring">
+ <rest path="/api/v3">
+ <put consumes="application/json,application/xml" id="updatePet" uri="/pet">
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <to uri="direct:updatePet"/>
+ </put>
+ <post consumes="application/json,application/xml" id="addPet" uri="/pet">
+ <param dataType="boolean" defaultValue="false" description="Verbose data" name="verbose" required="false" type="query"/>
+ <param description="Pet object that needs to be added to the store" name="body" required="true" type="body"/>
+ <to uri="direct:addPet"/>
+ </post>
+ <get id="findPetsByStatus" produces="application/xml,application/json" uri="/pet/findByStatus">
+ <description>Multiple status values can be provided with comma separated strings</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Status values that need to be considered for filter" name="status" required="true" type="query"/>
+ <to uri="direct:findPetsByStatus"/>
+ </get>
+ <get id="findPetsByTags" produces="application/xml,application/json" uri="/pet/findByTags">
+ <description>Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.</description>
+ <param arrayType="string" collectionFormat="multi" dataType="array" description="Tags to filter by" name="tags" required="true" type="query"/>
+ <to uri="direct:findPetsByTags"/>
+ </get>
+ <get id="getPetById" produces="application/xml,application/json" uri="/pet/{petId}">
+ <description>Returns a single pet</description>
+ <param dataType="integer" description="ID of pet to return" name="petId" required="true" type="path"/>
+ <to uri="direct:getPetById"/>
+ </get>
+ <post consumes="application/x-www-form-urlencoded" id="updatePetWithForm" uri="/pet/{petId}">
+ <param dataType="integer" description="ID of pet that needs to be updated" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Updated name of the pet" name="name" required="true" type="formData"/>
+ <param dataType="string" description="Updated status of the pet" name="status" required="true" type="formData"/>
+ <to uri="direct:updatePetWithForm"/>
+ </post>
+ <delete id="deletePet" uri="/pet/{petId}">
+ <param dataType="string" name="api_key" required="false" type="header"/>
+ <param dataType="integer" description="Pet id to delete" name="petId" required="true" type="path"/>
+ <to uri="direct:deletePet"/>
+ </delete>
+ <post consumes="multipart/form-data" id="uploadFile" produces="application/json" uri="/pet/{petId}/uploadImage">
+ <param dataType="integer" description="ID of pet to update" name="petId" required="true" type="path"/>
+ <param dataType="string" description="Additional data to pass to server" name="additionalMetadata" required="true" type="formData"/>
+ <param dataType="string" description="file to upload" name="file" required="true" type="formData"/>
+ <to uri="direct:uploadFile"/>
+ </post>
+ <get id="getInventory" produces="application/json" uri="/store/inventory">
+ <description>Returns a map of status codes to quantities</description>
+ <to uri="direct:getInventory"/>
+ </get>
+ <post consumes="*/*" id="placeOrder" produces="application/xml,application/json" uri="/store/order">
+ <param description="order placed for purchasing the pet" name="body" required="true" type="body"/>
+ <to uri="direct:placeOrder"/>
+ </post>
+ <get id="getOrderById" produces="application/xml,application/json" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions</description>
+ <param dataType="integer" description="ID of pet that needs to be fetched" name="orderId" required="true" type="path"/>
+ <to uri="direct:getOrderById"/>
+ </get>
+ <delete id="deleteOrder" uri="/store/order/{orderId}">
+ <description>For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors</description>
+ <param dataType="integer" description="ID of the order that needs to be deleted" name="orderId" required="true" type="path"/>
+ <to uri="direct:deleteOrder"/>
+ </delete>
+ <post consumes="*/*" id="createUser" uri="/user">
+ <description>This can only be done by the logged in user.</description>
+ <param description="Created user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUser"/>
+ </post>
+ <post consumes="*/*" id="createUsersWithArrayInput" uri="/user/createWithArray">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithArrayInput"/>
+ </post>
+ <post consumes="*/*" id="createUsersWithListInput" uri="/user/createWithList">
+ <param description="List of user object" name="body" required="true" type="body"/>
+ <to uri="direct:createUsersWithListInput"/>
+ </post>
+ <get id="loginUser" produces="application/xml,application/json" uri="/user/login">
+ <param dataType="string" description="The user name for login" name="username" required="true" type="query"/>
+ <param dataType="string" description="The password for login in clear text" name="password" required="true" type="query"/>
+ <to uri="direct:loginUser"/>
+ </get>
+ <get id="logoutUser" uri="/user/logout">
+ <to uri="direct:logoutUser"/>
+ </get>
+ <get id="getUserByName" produces="application/xml,application/json" uri="/user/{username}">
+ <param dataType="string" description="The name that needs to be fetched. Use user1 for testing. " name="username" required="true" type="path"/>
+ <to uri="direct:getUserByName"/>
+ </get>
+ <put consumes="*/*" id="updateUser" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="name that need to be updated" name="username" required="true" type="path"/>
+ <param description="Updated user object" name="body" required="true" type="body"/>
+ <to uri="direct:updateUser"/>
+ </put>
+ <delete id="deleteUser" uri="/user/{username}">
+ <description>This can only be done by the logged in user.</description>
+ <param dataType="string" description="The name that needs to be deleted" name="username" required="true" type="path"/>
+ <to uri="direct:deleteUser"/>
+ </delete>
+ </rest>
+</rests>
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/SpringBootRestController.txt b/tooling/openapi-rest-dsl-generator/src/test/resources/SpringBootRestController.txt
new file mode 100644
index 0000000..0eb0f2f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/SpringBootRestController.txt
@@ -0,0 +1,24 @@
+package com.foo;
+
+import javax.annotation.Generated;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Forward requests to the Camel servlet so it can service REST requests.
+ */
+@Generated("org.apache.camel.generator.openapi.SpringBootProjectSourceCodeGenerator")
+@RestController
+public final class CamelRestController {
+ @RequestMapping("/**")
+ public void camelServlet(HttpServletRequest request, HttpServletResponse response) {
+ try {
+ String path = request.getRequestURI();
+ request.getServletContext().getRequestDispatcher("/camel/" + path).forward(request, response);
+ } catch (Exception e) {
+ response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+}
diff --git a/tooling/openapi-rest-dsl-generator/src/test/resources/log4j2.properties b/tooling/openapi-rest-dsl-generator/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..be6f29f
--- /dev/null
+++ b/tooling/openapi-rest-dsl-generator/src/test/resources/log4j2.properties
@@ -0,0 +1,28 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-openapi-rest-dsl-generator.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/tooling/pom.xml b/tooling/pom.xml
index 76b1a9d..52eb485 100644
--- a/tooling/pom.xml
+++ b/tooling/pom.xml
@@ -41,6 +41,7 @@
<module>camel-util-json</module>
<module>camel-bundle-plugin</module>
<module>swagger-rest-dsl-generator</module>
+ <module>openapi-rest-dsl-generator</module>
<module>maven</module>
</modules>