You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2022/04/20 22:17:37 UTC

[camel-karavan] branch main updated: Generator REST DSL from OpenApi (#323)

This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


The following commit(s) were added to refs/heads/main by this push:
     new 017b2f5  Generator REST DSL from OpenApi (#323)
017b2f5 is described below

commit 017b2f59450206bbb62bf603aa9027c7032d6fbf
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Wed Apr 20 18:17:32 2022 -0400

    Generator REST DSL from OpenApi (#323)
    
    * Fix #322
    
    * Fix #322
---
 karavan-demo/openapi/open-api.json                | 1093 +++++++++++++++++++++
 karavan-designer/src/designer/KaravanDesigner.tsx |    3 +-
 karavan-vscode/package-lock.json                  |  105 +-
 karavan-vscode/package.json                       |   44 +-
 karavan-vscode/src/designerView.ts                |   37 +-
 karavan-vscode/src/extension.ts                   |   55 +-
 karavan-vscode/src/integrationView.ts             |   10 +-
 karavan-vscode/src/openapiView.ts                 |  112 +++
 karavan-vscode/src/utils.ts                       |   67 +-
 karavan-vscode/webview/App.tsx                    |    7 +-
 10 files changed, 1448 insertions(+), 85 deletions(-)

diff --git a/karavan-demo/openapi/open-api.json b/karavan-demo/openapi/open-api.json
new file mode 100644
index 0000000..8c1cf3a
--- /dev/null
+++ b/karavan-demo/openapi/open-api.json
@@ -0,0 +1,1093 @@
+{
+  "openapi": "3.0.2",
+  "info": {
+    "title": "Swagger 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.swagger.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"
+  }
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index 3a906b5..0360cc1 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -42,6 +42,7 @@ interface Props {
     yaml: string
     dark: boolean
     showStartHelp: boolean
+    tab?: string
 }
 
 interface State {
@@ -57,7 +58,7 @@ interface State {
 export class KaravanDesigner extends React.Component<Props, State> {
 
     public state: State = {
-        tab: 'routes',
+        tab: this.props.tab ? this.props.tab : 'routes',
         integration: this.props.yaml && CamelDefinitionYaml.yamlIsIntegration(this.props.yaml)
             ? CamelDefinitionYaml.yamlToIntegration(this.props.filename, this.props.yaml)
             : Integration.createNew(this.props.filename),
diff --git a/karavan-vscode/package-lock.json b/karavan-vscode/package-lock.json
index d5a82a7..47fa08d 100644
--- a/karavan-vscode/package-lock.json
+++ b/karavan-vscode/package-lock.json
@@ -20,6 +20,7 @@
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "rxjs": "7.5.2",
+        "shelljs": "^0.8.5",
         "uuid": "8.3.2"
       },
       "devDependencies": {
@@ -3281,8 +3282,7 @@
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
     },
     "node_modules/big-integer": {
       "version": "1.6.51",
@@ -3340,7 +3340,6 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
       "dependencies": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -3616,8 +3615,7 @@
     "node_modules/concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
     "node_modules/convert-source-map": {
       "version": "1.8.0",
@@ -4838,8 +4836,7 @@
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "node_modules/fsevents": {
       "version": "2.3.2",
@@ -4956,7 +4953,6 @@
       "version": "7.1.6",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
       "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
-      "dev": true,
       "dependencies": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -5230,7 +5226,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
       "dependencies": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -5239,8 +5234,7 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "node_modules/internal-slot": {
       "version": "1.0.3",
@@ -6047,7 +6041,6 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
       "dependencies": {
         "brace-expansion": "^1.1.7"
       },
@@ -6381,7 +6374,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
       "dependencies": {
         "wrappy": "1"
       }
@@ -6503,7 +6495,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -7263,6 +7254,41 @@
         "node": ">=8"
       }
     },
+    "node_modules/shelljs": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+      "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+      "dependencies": {
+        "glob": "^7.0.0",
+        "interpret": "^1.0.0",
+        "rechoir": "^0.6.2"
+      },
+      "bin": {
+        "shjs": "bin/shjs"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/shelljs/node_modules/interpret": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/shelljs/node_modules/rechoir": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+      "dependencies": {
+        "resolve": "^1.1.6"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
     "node_modules/side-channel": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -8632,8 +8658,7 @@
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
     "node_modules/y18n": {
       "version": "5.0.8",
@@ -10989,8 +11014,7 @@
     "balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
     },
     "big-integer": {
       "version": "1.6.51",
@@ -11036,7 +11060,6 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -11252,8 +11275,7 @@
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
     "convert-source-map": {
       "version": "1.8.0",
@@ -12173,8 +12195,7 @@
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "fsevents": {
       "version": "2.3.2",
@@ -12259,7 +12280,6 @@
       "version": "7.1.6",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
       "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
-      "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -12455,7 +12475,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -12464,8 +12483,7 @@
     "inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "internal-slot": {
       "version": "1.0.3",
@@ -13091,7 +13109,6 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
       "requires": {
         "brace-expansion": "^1.1.7"
       }
@@ -13341,7 +13358,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
       "requires": {
         "wrappy": "1"
       }
@@ -13426,8 +13442,7 @@
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
     },
     "path-key": {
       "version": "3.1.1",
@@ -13967,6 +13982,31 @@
       "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
       "dev": true
     },
+    "shelljs": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+      "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+      "requires": {
+        "glob": "^7.0.0",
+        "interpret": "^1.0.0",
+        "rechoir": "^0.6.2"
+      },
+      "dependencies": {
+        "interpret": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+          "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="
+        },
+        "rechoir": {
+          "version": "0.6.2",
+          "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+          "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+          "requires": {
+            "resolve": "^1.1.6"
+          }
+        }
+      }
+    },
     "side-channel": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -14980,8 +15020,7 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
     "y18n": {
       "version": "5.0.8",
diff --git a/karavan-vscode/package.json b/karavan-vscode/package.json
index 86eb137..e163ae3 100644
--- a/karavan-vscode/package.json
+++ b/karavan-vscode/package.json
@@ -39,14 +39,16 @@
     "kamel",
     "Tool",
     "Integration",
-    "Camel Yaml"
+    "Camel Yaml",
+    "Jbang"
   ],
   "activationEvents": [
     "onCommand:karavan.create-crd",
     "onCommand:karavan.create-yaml",
     "onCommand:karavan.open",
-    "onCommand:karavan.open-yaml",
+    "onCommand:karavan.open-file",
     "onCommand:karavan.jbang-run",
+    "onCommand:karavan.generate-rest",
     "onView:integrations",
     "onCommand:karavan.openKamelets",
     "onCommand:karavan.openComponents",
@@ -128,8 +130,8 @@
         }
       },
       {
-        "command": "karavan.open-yaml",
-        "title": "Karavan: Open YAML"
+        "command": "karavan.open-file",
+        "title": "Karavan: Open editor"
       },
       {
         "command": "karavan.jbang-run",
@@ -139,6 +141,10 @@
           "dark": "./icons/dark/run.svg"
         }
       },
+      {
+        "command": "karavan.generate-rest",
+        "title": "Karavan: Generate REST API"
+      },
       {
         "command": "integrations.refresh",
         "title": "Refresh",
@@ -147,6 +153,14 @@
           "dark": "icons/dark/refresh.svg"
         }
       },
+      {
+        "command": "openapi.refresh",
+        "title": "Refresh",
+        "icon": {
+          "light": "icons/light/refresh.svg",
+          "dark": "icons/dark/refresh.svg"
+        }
+      },
       {
         "command": "karavan.openKamelets",
         "title": "Open Kamelet Catalog"
@@ -181,6 +195,10 @@
         {
           "command": "karavan.jbang-run",
           "when": "resourceExtname == .yaml"
+        },
+        {
+          "command": "karavan.generate-rest",
+          "when": "resourceExtname == .json"
         }
       ],
       "editor/title": [
@@ -205,6 +223,11 @@
           "command": "karavan.create-crd",
           "when": "view == integrations",
           "group": "navigation"
+        },
+        {
+          "command": "openapi.refresh",
+          "when": "view == openapi",
+          "group": "navigation"
         }
       ],
       "view/item/context": [
@@ -214,7 +237,7 @@
           "group": "navigation_1_open@1"
         },
         {
-          "command": "karavan.open-yaml",
+          "command": "karavan.open-file",
           "when": "view == integrations && viewItem == 'integration'",
           "group": "navigation_1_open@2"
         },
@@ -222,6 +245,11 @@
           "command": "karavan.jbang-run",
           "when": "view == integrations && viewItem == 'integration'",
           "group": "navigation_2_run@1"
+        },
+        {
+          "command": "karavan.generate-rest",
+          "when": "view == openapi && viewItem == 'openapi'",
+          "group": "navigation_1_generate@1"
         }
       ]
     },
@@ -241,6 +269,11 @@
           "name": "Integrations",
           "visibility": "visible"
         },
+        {
+          "id": "openapi",
+          "name": "Open API",
+          "visibility": "visible"
+        },
         {
           "id": "help",
           "name": "Help & Feedback",
@@ -310,6 +343,7 @@
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "rxjs": "7.5.2",
+    "shelljs": "^0.8.5",
     "uuid": "8.3.2"
   }
 }
diff --git a/karavan-vscode/src/designerView.ts b/karavan-vscode/src/designerView.ts
index 6468d7e..d2e08b3 100644
--- a/karavan-vscode/src/designerView.ts
+++ b/karavan-vscode/src/designerView.ts
@@ -30,14 +30,14 @@ export class DesignerView {
 
     }
 
-    karavanOpen(fullPath: string) {
+    karavanOpen(fullPath: string, tab?: string) {
         const yaml = fs.readFileSync(path.resolve(fullPath)).toString('utf8');
         const filename = path.basename(fullPath);
         const relativePath = utils.getRalativePath(fullPath);
         const integration = utils.parceYaml(filename, yaml);
 
         if (integration[0]) {
-            this.openKaravanWebView(filename, relativePath, fullPath, integration[1]);
+            this.openKaravanWebView(filename, relativePath, fullPath, integration[1], tab);
         } else {
             vscode.window.showErrorMessage("File is not Camel Integration!")
         }
@@ -47,7 +47,7 @@ export class DesignerView {
         if (fullPath.startsWith('webview-panel/webview')) {
             const filename = Array.from(KARAVAN_PANELS.entries()).filter(({ 1: v }) => v.active).map(([k]) => k)[0];
             if (filename) {
-                utils.runCamelJbang(filename);
+                utils.camelJbangRun(filename);
             }
         } else {
             const yaml = fs.readFileSync(path.resolve(fullPath)).toString('utf8');
@@ -55,9 +55,9 @@ export class DesignerView {
             const filename = path.basename(fullPath);
             const integration = utils.parceYaml(filename, yaml);
             if (integration[0]) {
-                utils.runCamelJbang(relativePath);
+                utils.camelJbangRun(relativePath);
             } else {
-                vscode.window.showErrorMessage("File is not Camel-K Integration!")
+                vscode.window.showErrorMessage("File is not Camel Integration!")
             }
         }
     }
@@ -65,7 +65,7 @@ export class DesignerView {
     createIntegration(crd: boolean, rootPath?: string) {
         vscode.window
             .showInputBox({
-                title: crd ? "Create Camel-K Integration CRD" : "Create Camel Integration YAML",
+                title: crd ? "Create Camel Integration CRD" : "Create Camel Integration YAML",
                 ignoreFocusOut: true,
                 prompt: "Integration name",
                 validateInput: (text: string): string | undefined => {
@@ -91,7 +91,8 @@ export class DesignerView {
             });
     }
 
-    openKaravanWebView(filename: string, relativePath: string, fullPath: string,  yaml?: string) {
+    openKaravanWebView(filename: string, relativePath: string, fullPath: string,  yaml?: string, tab?: string) {
+        console.log("openKaravanWebView");
         if (!KARAVAN_PANELS.has(relativePath)) {
             // Karavan webview
             const panel = vscode.window.createWebviewPanel(
@@ -121,7 +122,7 @@ export class DesignerView {
                             utils.save(message.relativePath, message.yaml);
                             break;
                         case 'getData':
-                            this.sendData(panel, filename, relativePath, fullPath, message.reread === true, yaml);
+                            this.sendData(panel, filename, relativePath, fullPath, message.reread === true, yaml, tab);
                             break;
                         case 'disableStartHelp':
                             utils.disableStartHelp();
@@ -138,8 +139,9 @@ export class DesignerView {
 
             // Handle reopen
             panel.onDidChangeViewState((e: vscode.WebviewPanelOnDidChangeViewStateEvent) => {
-                if (e.webviewPanel.active) {
-                    e.webviewPanel.webview.postMessage({ command: 'activate' });
+                console.log(e);
+                if (e.webviewPanel.active || e.webviewPanel.reveal) {
+                    e.webviewPanel.webview.postMessage({ command: 'activate', tab: tab });
                 } else {
                     e.webviewPanel.webview.postMessage({ command: 'deactivate' });
                 }
@@ -148,16 +150,14 @@ export class DesignerView {
             KARAVAN_PANELS.set(relativePath, panel);
             vscode.commands.executeCommand("setContext", KARAVAN_LOADED, true);
         } else {
-            KARAVAN_PANELS.get(relativePath)?.reveal(undefined, true);
+            const panel = KARAVAN_PANELS.get(relativePath);
+            panel?.reveal(undefined, true);
+            panel?.webview.postMessage({ command: 'activate', tab: tab });
         }
     }
 
-    sendData(panel: vscode.WebviewPanel, filename: string, relativePath: string, fullPath: string, reread: boolean, yaml?: string) {
-        console.log(filename);
-        console.log(relativePath);
-        console.log(fullPath);
-        console.log(reread);
-        console.log(yaml);
+    sendData(panel: vscode.WebviewPanel, filename: string, relativePath: string, fullPath: string, reread: boolean, yaml?: string, tab?: string) {
+
         // Read and send Kamelets
         panel.webview.postMessage({ command: 'kamelets', kamelets: utils.readKamelets(this.context) });
 
@@ -172,9 +172,8 @@ export class DesignerView {
         if (reread){
             yaml = fs.readFileSync(path.resolve(fullPath)).toString('utf8');
         }
-        console.log(yaml);
         // Send integration
-        panel.webview.postMessage({ command: 'open', page: "designer", filename: filename, relativePath: relativePath, yaml: yaml });
+        panel.webview.postMessage({ command: 'open', page: "designer", filename: filename, relativePath: relativePath, yaml: yaml, tab: tab });
     }
 
 }
\ No newline at end of file
diff --git a/karavan-vscode/src/extension.ts b/karavan-vscode/src/extension.ts
index a871147..8395223 100644
--- a/karavan-vscode/src/extension.ts
+++ b/karavan-vscode/src/extension.ts
@@ -14,11 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import * as vscode from "vscode";
+import vscode, { window, commands, ExtensionContext } from 'vscode';
 import * as fs from "fs";
 import { DesignerView } from "./designerView";
 import {IntegrationView} from "./integrationView";
 import { HelpView } from "./helpView";
+import { selectFileName, inputFileName, OpenApiView, OpenApiItem } from "./openapiView";
 
 const KARAVAN_LOADED = "karavan:loaded";
 
@@ -44,8 +45,23 @@ export function activate(context: vscode.ExtensionContext) {
     const rootPath = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0))
 		? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined;
 
+     // Register views    
     const designer = new DesignerView(context, webviewContent, rootPath);
 
+    const integrationView = new IntegrationView(designer, rootPath);
+	vscode.window.registerTreeDataProvider('integrations', integrationView);    
+    vscode.commands.registerCommand('integrations.refresh', () => integrationView.refresh());
+
+    const openapiView = new OpenApiView(designer, rootPath);
+	vscode.window.registerTreeDataProvider('openapi', openapiView);    
+    vscode.commands.registerCommand('openapi.refresh', () => openapiView.refresh());
+
+    const helpView = new HelpView(context, webviewContent);
+	vscode.window.registerTreeDataProvider('help', helpView);    
+    vscode.commands.registerCommand('karavan.openKamelets', () => helpView.openKaravanWebView("kamelets"));
+    vscode.commands.registerCommand('karavan.openComponents', () => helpView.openKaravanWebView("components"));
+    vscode.commands.registerCommand('karavan.openEip', () => helpView.openKaravanWebView("eip"));
+
     // Create new Integration CRD command
     const createCrd = vscode.commands.registerCommand("karavan.create-crd", (...args: any[]) => {
         if (args.length > 0) designer.createIntegration(true, args[0].fsPath)
@@ -57,33 +73,36 @@ export function activate(context: vscode.ExtensionContext) {
     const createYaml = vscode.commands.registerCommand("karavan.create-yaml", (...args: any[]) => designer.createIntegration(false, args[0].fsPath));
     context.subscriptions.push(createYaml);
 
-    // Openintegration in designer
-    const open = vscode.commands.registerCommand("karavan.open", (...args: any[]) => designer.karavanOpen(args[0].fsPath));
+    // Open integration in designer command
+    const open = vscode.commands.registerCommand("karavan.open", (...args: any[]) => designer.karavanOpen(args[0].fsPath, args[0].tab));
     context.subscriptions.push(open);
 
-    // Open integration in YAML editor
-    const openYaml = vscode.commands.registerCommand("karavan.open-yaml", (...args: any[]) => {
+    // Open integration in editor command
+    const openFile = vscode.commands.registerCommand("karavan.open-file", (...args: any[]) => {
         let uri = vscode.Uri.file(args[0].fsPath);
         vscode.window.showTextDocument( uri, { preserveFocus: false, preview: false});
     });
-    context.subscriptions.push(openYaml);
+    context.subscriptions.push(openFile);
 
-    // Run Integration in designer
+    // Run Integration in designer command
     const run = vscode.commands.registerCommand("karavan.jbang-run", (...args: any[]) => designer.jbangRun(args[0].fsPath));
     context.subscriptions.push(run);
 
-    // Register views
-
-    const integrationView = new IntegrationView(designer, rootPath);
-	vscode.window.registerTreeDataProvider('integrations', integrationView);    
-    vscode.commands.registerCommand('integrations.refresh', () => integrationView.refresh());
-
-    const helpView = new HelpView(context, webviewContent);
-	vscode.window.registerTreeDataProvider('help', helpView);    
-    vscode.commands.registerCommand('karavan.openKamelets', () => helpView.openKaravanWebView("kamelets"));
-    vscode.commands.registerCommand('karavan.openComponents', () => helpView.openKaravanWebView("components"));
-    vscode.commands.registerCommand('karavan.openEip', () => helpView.openKaravanWebView("eip"));
+    // Generate RST API from OpenAPI specification command
+    const generateOptions = ["Create new CRD", "Create new YAML", "Add to existing file"];
+    const generateRest = commands.registerCommand('karavan.generate-rest', async (...args: any[]) => {
+        const openApi: OpenApiItem = args[0];
+        window.showQuickPick(generateOptions, {title:"Select REST Generator options", canPickMany: false}).then((value) => {
+            switch (value){
+                case generateOptions[0]: inputFileName(true, rootPath, openApi); break;
+                case generateOptions[1]: inputFileName(false, rootPath, openApi); break;
+                case generateOptions[2]: selectFileName(rootPath, openApi); break;
+            }
+        })
+	});
+    context.subscriptions.push(generateRest);
 
+    // Create issue command
     vscode.commands.registerCommand('karavan.reportIssue', () => {
         vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://github.com/apache/camel-karavan/issues/new?title=[VS+Code]New+report&template=issue_template.md'));
     });
diff --git a/karavan-vscode/src/integrationView.ts b/karavan-vscode/src/integrationView.ts
index 62bbf5b..e1188f4 100644
--- a/karavan-vscode/src/integrationView.ts
+++ b/karavan-vscode/src/integrationView.ts
@@ -36,13 +36,11 @@ export class IntegrationView implements vscode.TreeDataProvider<IntegrationItem>
 	getChildren(element?: IntegrationItem): vscode.ProviderResult<IntegrationItem[]> {
 		const integrations: IntegrationItem[] = [];
 		if (element === undefined && this.rootPath) {
-			utils.getYamlFiles(this.rootPath).forEach(f => {
+			utils.getIntegrationFiles(this.rootPath).forEach(f => {
 				const yaml = fs.readFileSync(path.resolve(f)).toString('utf8');
-				if (!f.startsWith(this.rootPath + path.sep + "target") && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
-					const filename = path.basename(f);
-					const i = CamelDefinitionYaml.yamlToIntegration(filename, yaml);
-					integrations.push(new IntegrationItem(i.metadata.name, f, i.crd ? "CRD" : "", i, { command: 'karavan.open', title: '', arguments: [{ fsPath: f }] }));
-				}
+				const filename = path.basename(f);
+				const i = CamelDefinitionYaml.yamlToIntegration(filename, yaml);
+				integrations.push(new IntegrationItem(i.metadata.name, f, i.crd ? "CRD" : "", i, { command: 'karavan.open', title: '', arguments: [{ fsPath: f }] }));
 			})
 		} else if (element && element.integration) {
 			element.integration.spec.flows?.forEach(f => {
diff --git a/karavan-vscode/src/openapiView.ts b/karavan-vscode/src/openapiView.ts
new file mode 100644
index 0000000..542b165
--- /dev/null
+++ b/karavan-vscode/src/openapiView.ts
@@ -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.
+ */
+import vscode, { window } from "vscode";
+import * as path from "path";
+import * as utils from "./utils";
+import * as fs from "fs";
+import { ThemeIcon } from "vscode";
+import { CamelDefinitionYaml } from "karavan-core/lib/api/CamelDefinitionYaml";
+import { DesignerView } from "./designerView";
+import { Integration } from "karavan-core/lib/model/IntegrationDefinition";
+
+export class OpenApiView implements vscode.TreeDataProvider<OpenApiItem> {
+
+	constructor(private designer: DesignerView, private rootPath: string | undefined) {
+
+	}
+	private _onDidChangeTreeData: vscode.EventEmitter<OpenApiItem | undefined | void> = new vscode.EventEmitter<OpenApiItem | undefined | void>();
+	readonly onDidChangeTreeData: vscode.Event<OpenApiItem | undefined | void> = this._onDidChangeTreeData.event;
+
+	getTreeItem(element: OpenApiItem): vscode.TreeItem | Thenable<vscode.TreeItem> {
+		return element;
+	}
+	getChildren(element?: OpenApiItem): vscode.ProviderResult<OpenApiItem[]> {
+		const openapis: OpenApiItem[] = [];
+		if (this.rootPath) {
+			utils.getJsonFiles(this.rootPath).forEach(f => {
+				const json = fs.readFileSync(path.resolve(f)).toString('utf8');
+				const api = JSON.parse(json);
+				if (api.openapi) {
+					const filename = path.basename(f);
+					openapis.push(new OpenApiItem(filename, f, api?.info?.title, { command: 'karavan.open-file', title: 'Open file', arguments: [{ fsPath: f }] }));
+				}
+			})
+		}
+		return Promise.resolve(openapis);
+	}
+
+	refresh(): void {
+		this._onDidChangeTreeData.fire();
+	}
+}
+
+export class OpenApiItem extends vscode.TreeItem {
+
+	constructor(
+		public readonly title: string,
+		public readonly fsPath: string,
+		public readonly description: string,
+		public readonly command?: vscode.Command
+	) {
+		super(title, vscode.TreeItemCollapsibleState.None);
+		this.tooltip = this.fsPath;
+	}
+
+	iconPath = new ThemeIcon("symbol-interface");
+
+	contextValue = "openapi";
+}
+
+/**
+ * Select file and add REST API
+ */
+export async function selectFileName(rootPath?: string, openApi?: OpenApiItem) {
+	if (rootPath && openApi?.fsPath) {
+		const files = utils.getIntegrationFiles(rootPath);
+		await window.showQuickPick(files, {
+			title: "Select Integration file to add REST API",
+			placeHolder: 'Select file',
+		}).then(filename => {
+			if (filename && openApi?.fsPath) {
+				utils.camelJbangGenerate(openApi.fsPath, filename, true, undefined);
+			}
+		});
+	}
+}
+
+/**
+ * Create new file and add REST API
+ */
+export async function inputFileName(crd: boolean, rootPath?: string, openApi?: OpenApiItem) {
+	vscode.window.showInputBox({
+		title: "Generate REST API from " + openApi?.title,
+		ignoreFocusOut: true,
+		prompt: "Integration file name",
+		validateInput: (text: string): string | undefined => {
+			if (!text || text.length === 0 || !text.endsWith(".yaml")) {
+				return 'Name should not be empty. Extension should be .yaml';
+			} else {
+				return undefined;
+			}
+		}
+	}).then(filename => {
+		if (filename && openApi?.fsPath) {
+			const fullPath = rootPath + path.sep + filename;
+			utils.camelJbangGenerate(openApi.fsPath, fullPath, false,  crd);
+		}
+	});
+}
\ No newline at end of file
diff --git a/karavan-vscode/src/utils.ts b/karavan-vscode/src/utils.ts
index f6649fe..659da44 100644
--- a/karavan-vscode/src/utils.ts
+++ b/karavan-vscode/src/utils.ts
@@ -17,6 +17,7 @@
 import * as vscode from "vscode";
 import * as fs from "fs";
 import * as path from "path";
+import * as shell from 'shelljs';
 import { CamelDefinitionYaml } from "karavan-core/lib/api/CamelDefinitionYaml";
 
 const TERMINALS: Map<string, vscode.Terminal> = new Map<string, vscode.Terminal>();
@@ -72,7 +73,7 @@ export function disableStartHelp(){
     config.update("Karavan.showStartHelp", false);
 }
 
-export function runCamelJbang(filename: string) {
+export function camelJbangRun(filename: string) {
     const version = vscode.workspace.getConfiguration().get("camel.version");
     const maxMessages: number = vscode.workspace.getConfiguration().get("camel.maxMessages") || -1;
     const loggingLevel = vscode.workspace.getConfiguration().get("camel.loggingLevel");
@@ -127,4 +128,68 @@ export function getYamlFiles(baseDir: string): string[] {
     return result;
 }
 
+export function getJsonFiles(baseDir: string): string[] {
+    const result:string[] = [];
+    getAllFiles(baseDir, []).filter(f => f.endsWith(".json")).forEach(f => {
+        result.push(f);
+    })
+    return result;
+}
+
+export function getIntegrationFiles(baseDir: string): string[]{
+    return getYamlFiles(baseDir).filter(f => {
+        const yaml = fs.readFileSync(path.resolve(f)).toString('utf8');
+        return !f.startsWith(baseDir + path.sep + "target") && CamelDefinitionYaml.yamlIsIntegration(yaml);
+    });
+}
+
+export function camelJbangGenerate(openApiFullPath: string, fullPath: string,  add: boolean, crd?: boolean) {
+    const version = vscode.workspace.getConfiguration().get("camel.version");
+    const command = "jbang -Dcamel.jbang.version=" + version + " camel@apache/camel generate rest -i " + openApiFullPath;
+    const jbang = shell.which('jbang');
+    if (jbang){
+        shell.config.execPath = String(jbang);
+        shell.exec(command, { async: true }, (code, stdout, stderr) => {
+            console.log('Exit code:', code);
+            if (code === 0){
+                // console.log('Program output:', stdout);
+                const filename = path.basename(fullPath);
+                let yaml;
+                if (add) {
+                    const camelYaml = fs.readFileSync(path.resolve(fullPath)).toString('utf8');
+                    yaml = createYaml(filename, stdout, camelYaml, undefined);
+                } else {
+                    yaml = createYaml(filename, stdout, undefined, crd);
+                }
+                const uriFile: vscode.Uri = vscode.Uri.file(fullPath);
+                fs.writeFile(uriFile.fsPath, yaml, err => {
+                    if (err) vscode.window.showErrorMessage("Error: " + err?.message);
+                    else {
+                        vscode.commands.executeCommand('integrations.refresh')
+                        .then(x => vscode.commands.executeCommand('karavan.open', {fsPath: fullPath, tab: 'rest'}));
+                    }
+                });
+            } else {
+                vscode.window.showErrorMessage(stderr);
+            }
+        });
+    }
+}
+
+export function createYaml(filename: string, restYaml: string, camelYaml?: string, crd?: boolean): string {
+    if (camelYaml) {
+        const i = CamelDefinitionYaml.yamlToIntegration(filename, camelYaml);
+        const rest = CamelDefinitionYaml.yamlToIntegration(filename, restYaml);
+        i.spec.flows = i.spec.flows?.filter(f => f.dslName !== 'RestDefinition');
+        i.spec.flows?.push(...rest.spec.flows || []);
+        return CamelDefinitionYaml.integrationToYaml(i);
+    } else if (crd === true){
+        const i = CamelDefinitionYaml.yamlToIntegration(filename, restYaml);
+        i.crd = true;
+        return CamelDefinitionYaml.integrationToYaml(i);
+    } else {
+        return restYaml;
+    }
+}
+
 
diff --git a/karavan-vscode/webview/App.tsx b/karavan-vscode/webview/App.tsx
index 5d468b0..b9b418a 100644
--- a/karavan-vscode/webview/App.tsx
+++ b/karavan-vscode/webview/App.tsx
@@ -42,6 +42,7 @@ interface State {
   showStartHelp: boolean
   page: "designer" | "kamelets" | "components" | "eip"
   active: boolean
+  tab?: string
 }
 
 class App extends React.Component<Props, State> {
@@ -101,12 +102,13 @@ class App extends React.Component<Props, State> {
             relativePath: message.relativePath,
             key: Math.random().toString(),
             loaded: true,
-            active: true
+            active: true, 
+            tab: message.tab
           });
         }
         break;
       case 'activate':
-        this.setState({ loaded: false, filename: '', key: '', active: true });
+        this.setState({ loaded: false, filename: '', key: '', active: true, tab: message.tab });
         vscode.postMessage({ command: 'getData', reread: true });
         break;
       case 'deactivate':
@@ -146,6 +148,7 @@ class App extends React.Component<Props, State> {
             yaml={this.state.yaml}
             onSave={(filename, yaml, propertyOnly) => this.save(filename, yaml, propertyOnly)}
             onDisableHelp={this.disableStartHelp}
+            tab={this.state.tab}
             dark={this.props.dark} />
         }
         {this.state.loaded && this.state.page === "kamelets" && <KameletsPage dark={this.props.dark} />}