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} />}