You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2022/03/17 09:52:03 UTC

[camel-kamelets] 01/02: chore: Add Kamelet rest-openapi-sink

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

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

commit f2263a4f562654a489ad7c7941c49e4404938c98
Author: Christoph Deppisch <cd...@redhat.com>
AuthorDate: Wed Mar 16 20:26:13 2022 +0100

    chore: Add Kamelet rest-openapi-sink
    
    New Kamelet loads OpenAPI specification from (http/https URL) and calls an operation on the HTTP service. The request is generated respecting the rules given in the OpenAPI specification (e.g. path parameters, Content-Type).
---
 .github/workflows/yaks-tests.yaml                  |   1 +
 kamelets/rest-openapi-sink.kamelet.yaml            |  57 ++++
 .../kamelets/rest-openapi-sink.kamelet.yaml        |  57 ++++
 test/rest-openapi-sink/openapi.json                | 289 +++++++++++++++++++++
 test/rest-openapi-sink/pet.json                    |   1 +
 .../rest-openapi-sink-binding.yaml                 |  29 +++
 test/rest-openapi-sink/rest-openapi-sink.feature   |  52 ++++
 test/rest-openapi-sink/yaks-config.yaml            |  35 +++
 8 files changed, 521 insertions(+)

diff --git a/.github/workflows/yaks-tests.yaml b/.github/workflows/yaks-tests.yaml
index 754f566..da874b9 100644
--- a/.github/workflows/yaks-tests.yaml
+++ b/.github/workflows/yaks-tests.yaml
@@ -102,6 +102,7 @@ jobs:
         yaks run test/mail-sink $YAKS_RUN_OPTIONS
         yaks run test/timer-source $YAKS_RUN_OPTIONS
         yaks run test/earthquake-source $YAKS_RUN_OPTIONS
+        yaks run test/rest-openapi-sink $YAKS_RUN_OPTIONS
     - name: YAKS Report
       if: failure()
       run: |
diff --git a/kamelets/rest-openapi-sink.kamelet.yaml b/kamelets/rest-openapi-sink.kamelet.yaml
new file mode 100644
index 0000000..35ee46e
--- /dev/null
+++ b/kamelets/rest-openapi-sink.kamelet.yaml
@@ -0,0 +1,57 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+apiVersion: camel.apache.org/v1alpha1
+kind: Kamelet
+metadata:
+  name: rest-openapi-sink
+  annotations:
+    camel.apache.org/kamelet.support.level: "Preview"
+    camel.apache.org/catalog.version: "main-SNAPSHOT"
+    camel.apache.org/kamelet.icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAlvUlEQVR4nOydd1xT19vAT3YgCSEM2VtEEVABERXFvWqXVbHaOuqve7nbam1tq7bWLldbq9bVum1t3VtxsEQFBFQ2yAwJISRkJ+9HLy9SNDfJzT33JprvX7fk5Jyn8uXec895zjl0g8EAHDjAGyrZATh4MnGI5QAKDrEcQMEhlgMoOMRyAAWHWA6g4BDLARQcYjmAAp3sABwQQVNT0/Xr1wvyC8rKymtrayUSiVqtBgCw2WyBQODr6xMaGtYzKrJPnz5cLheXFimOkfcnGJVKdfbsuZMnT+bfyjfnF02n02Jj48aMHT1w4EA63aqbjkOsJxOVSvXXwb/379/X3CzF8PUuXbpMe2Xa2LFjaDQatgAcYj2BpKdnrF2ztr6+3sp6Qk [...]
+    camel.apache.org/provider: "Apache Software Foundation"
+    camel.apache.org/kamelet.group: "HTTP"
+  labels:
+    camel.apache.org/kamelet.type: sink
+spec:
+  definition:
+    title: REST OpenAPI Sink
+    description: Load OpenAPI specification from URI and call an operation on the HTTP service. The request is generated respecting the rules given in the OpenAPI specification (e.g. path parameters, Content-Type).
+    required:
+    - specification
+    - operation
+    type: object
+    properties:
+      specification:
+        title: Specification URI
+        description: URI to the OpenApi specification file
+        type: string
+        example: "https://api.example.com/openapi.json"
+        pattern: "^(http|https|file|classpath)://.*"
+      operation:
+        title: Operation ID
+        description: The operation to call
+        type: string
+  dependencies:
+  - "camel:rest-openapi"
+  - "camel:http"
+  - "camel:kamelet"
+  - "camel:core"
+  template:
+    from:
+      uri: kamelet:source
+      steps:
+      - to: "rest-openapi:{{specification}}#{{operation}}"
diff --git a/library/camel-kamelets/src/main/resources/kamelets/rest-openapi-sink.kamelet.yaml b/library/camel-kamelets/src/main/resources/kamelets/rest-openapi-sink.kamelet.yaml
new file mode 100644
index 0000000..35ee46e
--- /dev/null
+++ b/library/camel-kamelets/src/main/resources/kamelets/rest-openapi-sink.kamelet.yaml
@@ -0,0 +1,57 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+apiVersion: camel.apache.org/v1alpha1
+kind: Kamelet
+metadata:
+  name: rest-openapi-sink
+  annotations:
+    camel.apache.org/kamelet.support.level: "Preview"
+    camel.apache.org/catalog.version: "main-SNAPSHOT"
+    camel.apache.org/kamelet.icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAlvUlEQVR4nOydd1xT19vAT3YgCSEM2VtEEVABERXFvWqXVbHaOuqve7nbam1tq7bWLldbq9bVum1t3VtxsEQFBFQ2yAwJISRkJ+9HLy9SNDfJzT33JprvX7fk5Jyn8uXec895zjl0g8EAHDjAGyrZATh4MnGI5QAKDrEcQMEhlgMoOMRyAAWHWA6g4BDLARQcYjmAAp3sABwQQVNT0/Xr1wvyC8rKymtrayUSiVqtBgCw2WyBQODr6xMaGtYzKrJPnz5cLheXFimOkfcnGJVKdfbsuZMnT+bfyjfnF02n02Jj48aMHT1w4EA63aqbjkOsJxOVSvXXwb/379/X3CzF8PUuXbpMe2Xa2LFjaDQatgAcYj2BpKdnrF2ztr6+3sp6Qk [...]
+    camel.apache.org/provider: "Apache Software Foundation"
+    camel.apache.org/kamelet.group: "HTTP"
+  labels:
+    camel.apache.org/kamelet.type: sink
+spec:
+  definition:
+    title: REST OpenAPI Sink
+    description: Load OpenAPI specification from URI and call an operation on the HTTP service. The request is generated respecting the rules given in the OpenAPI specification (e.g. path parameters, Content-Type).
+    required:
+    - specification
+    - operation
+    type: object
+    properties:
+      specification:
+        title: Specification URI
+        description: URI to the OpenApi specification file
+        type: string
+        example: "https://api.example.com/openapi.json"
+        pattern: "^(http|https|file|classpath)://.*"
+      operation:
+        title: Operation ID
+        description: The operation to call
+        type: string
+  dependencies:
+  - "camel:rest-openapi"
+  - "camel:http"
+  - "camel:kamelet"
+  - "camel:core"
+  template:
+    from:
+      uri: kamelet:source
+      steps:
+      - to: "rest-openapi:{{specification}}#{{operation}}"
diff --git a/test/rest-openapi-sink/openapi.json b/test/rest-openapi-sink/openapi.json
new file mode 100644
index 0000000..cae57a7
--- /dev/null
+++ b/test/rest-openapi-sink/openapi.json
@@ -0,0 +1,289 @@
+{
+    "openapi": "3.0.2",
+    "info": {
+        "title": "Swagger Petstore",
+        "version": "1.0.1",
+        "description": "This is a sample server Petstore server.",
+        "license": {
+            "name": "Apache 2.0",
+            "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+        }
+    },
+    "servers": [
+        {
+            "url": "http://test-service/petstore"
+        }
+    ],
+    "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": {
+                    "204": {
+                        "description": "No content"
+                    },
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    },
+                    "405": {
+                        "description": "Validation exception"
+                    }
+                },
+                "operationId": "updatePet",
+                "summary": "Update an existing pet",
+                "description": ""
+            },
+            "post": {
+                "requestBody": {
+                    "description": "Pet object that needs to be added to the store",
+                    "content": {
+                        "text/plain": {
+                            "schema": {
+                                "$ref": "#/components/schemas/Pet"
+                            }
+                        },
+                        "application/json": {
+                            "schema": {
+                                "$ref": "#/components/schemas/Pet"
+                            }
+                        },
+                        "application/xml": {
+                            "schema": {
+                                "$ref": "#/components/schemas/Pet"
+                            }
+                        }
+                    },
+                    "required": true
+                },
+                "tags": [
+                    "pet"
+                ],
+                "responses": {
+                    "201": {
+                        "description": "Created"
+                    },
+                    "405": {
+                        "description": "Invalid input"
+                    }
+                },
+                "operationId": "addPet",
+                "summary": "Add a new pet to the store",
+                "description": ""
+            }
+        },
+        "/pet/{petId}": {
+            "get": {
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "description": "ID of pet to return",
+                        "schema": {
+                            "format": "int64",
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    },
+                    {
+                        "name": "verbose",
+                        "description": "Output details",
+                        "schema": {
+                            "type": "boolean"
+                        },
+                        "in": "query",
+                        "required": false
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "content": {
+                            "application/json": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Pet"
+                                }
+                            },
+                            "application/xml": {
+                                "schema": {
+                                    "$ref": "#/components/schemas/Pet"
+                                }
+                            }
+                        },
+                        "description": "successful operation"
+                    },
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    }
+                },
+                "operationId": "getPetById",
+                "summary": "Find pet by ID",
+                "description": "Returns a single pet"
+            },
+            "delete": {
+                "tags": [
+                    "pet"
+                ],
+                "parameters": [
+                    {
+                        "name": "petId",
+                        "description": "Pet id to delete",
+                        "schema": {
+                            "format": "int64",
+                            "type": "integer"
+                        },
+                        "in": "path",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "204": {
+                        "description": "No content"
+                    },
+                    "400": {
+                        "description": "Invalid ID supplied"
+                    },
+                    "404": {
+                        "description": "Pet not found"
+                    }
+                },
+                "operationId": "deletePet",
+                "summary": "Deletes a pet",
+                "description": ""
+            }
+        }
+    },
+    "components": {
+        "schemas": {
+            "Category": {
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "name": {
+                        "type": "string"
+                    }
+                },
+                "xml": {
+                    "name": "Category"
+                }
+            },
+            "Tag": {
+                "type": "object",
+                "properties": {
+                    "id": {
+                        "format": "int64",
+                        "type": "integer"
+                    },
+                    "name": {
+                        "type": "string"
+                    }
+                },
+                "xml": {
+                    "name": "Tag"
+                }
+            },
+            "Pet": {
+                "required": [
+                    "category",
+                    "name",
+                    "status"
+                ],
+                "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"
+                    }
+                }
+            }
+        }
+    },
+    "tags": [
+        {
+            "name": "pet",
+            "description": "Everything about your Pets"
+        }
+    ]
+}
diff --git a/test/rest-openapi-sink/pet.json b/test/rest-openapi-sink/pet.json
new file mode 100644
index 0000000..1e9db09
--- /dev/null
+++ b/test/rest-openapi-sink/pet.json
@@ -0,0 +1 @@
+{ "id": ${petId}, "name": "fluffy", "category": {"id": ${petId}, "name": "dog"}, "photoUrls": [ "petstore/v3/photos/${petId}" ], "tags": [{"id": ${petId}, "name": "generated"}], "status": "available"}
diff --git a/test/rest-openapi-sink/rest-openapi-sink-binding.yaml b/test/rest-openapi-sink/rest-openapi-sink-binding.yaml
new file mode 100644
index 0000000..1f814cd
--- /dev/null
+++ b/test/rest-openapi-sink/rest-openapi-sink-binding.yaml
@@ -0,0 +1,29 @@
+apiVersion: camel.apache.org/v1alpha1
+kind: KameletBinding
+metadata:
+  name: rest-openapi-sink-binding
+spec:
+  source:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1alpha1
+      name: timer-source
+    properties:
+      period: 60000
+      message: '${pet}'
+  steps:
+    - ref:
+        kind: Kamelet
+        apiVersion: camel.apache.org/v1alpha1
+        name: insert-header-action
+      properties:
+        name: petId
+        value: ${petId}
+  sink:
+    ref:
+      kind: Kamelet
+      apiVersion: camel.apache.org/v1alpha1
+      name: rest-openapi-sink
+    properties:
+      specification: http://test-service.${YAKS_NAMESPACE}/petstore/openapi.json
+      operation: ${operation}
diff --git a/test/rest-openapi-sink/rest-openapi-sink.feature b/test/rest-openapi-sink/rest-openapi-sink.feature
new file mode 100644
index 0000000..64a73d3
--- /dev/null
+++ b/test/rest-openapi-sink/rest-openapi-sink.feature
@@ -0,0 +1,52 @@
+Feature: REST OpenAPI Kamelet sink
+
+  Background:
+    Given HTTP server timeout is 60000 ms
+    Given HTTP server "test-service"
+    Given variable petId is "1000"
+    Given load variable pet.json
+
+  Scenario: Create Http server
+    Given create Kubernetes service test-service
+
+  Scenario: Create Kamelet binding for addPet
+    Given Camel K resource polling configuration
+      | maxAttempts          | 200   |
+      | delayBetweenAttempts | 2000  |
+    Given variable operation is "addPet"
+    When load KameletBinding rest-openapi-sink-binding.yaml
+    Then Camel K integration rest-openapi-sink-binding should be running
+
+  Scenario: Provide OpenAPI specification to Camel K integration
+    When receive GET /petstore/openapi.json
+    Then HTTP request header Content-Type is "application/json"
+    Then HTTP response body: citrus:readFile(classpath:openapi.json)
+    Then send HTTP 200 OK
+
+  Scenario: Verify proper addPet request message sent
+    Given expect HTTP request body: citrus:readFile(classpath:openapi.json)
+    And HTTP request header Content-Type is "application/json"
+    When receive POST /petstore/pet
+    And send HTTP 201 CREATED
+
+  Scenario: Remove resources
+    Given delete KameletBinding rest-openapi-sink-binding
+
+  Scenario: Create Kamelet binding for deletePet
+    Given variable operation is "deletePet"
+    When load KameletBinding rest-openapi-sink-binding.yaml
+    Then Camel K integration rest-openapi-sink-binding should be running
+
+  Scenario: Provide OpenAPI specification to Camel K integration
+    When receive GET /petstore/openapi.json
+    Then HTTP request header Content-Type is "application/json"
+    Then HTTP response body: citrus:readFile(classpath:openapi.json)
+    Then send HTTP 200 OK
+
+  Scenario: Verify proper deletePet request message sent
+    When receive DELETE /petstore/pet/${petId}
+    And send HTTP 204 NO_CONTENT
+
+  Scenario: Remove resources
+    Given delete KameletBinding rest-openapi-sink-binding
+    And delete Kubernetes service test-service
diff --git a/test/rest-openapi-sink/yaks-config.yaml b/test/rest-openapi-sink/yaks-config.yaml
new file mode 100644
index 0000000..9e2b89a
--- /dev/null
+++ b/test/rest-openapi-sink/yaks-config.yaml
@@ -0,0 +1,35 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+config:
+  namespace:
+    temporary: false
+  runtime:
+    env:
+      - name: YAKS_CAMELK_AUTO_REMOVE_RESOURCES
+        value: false
+      - name: YAKS_KUBERNETES_AUTO_REMOVE_RESOURCES
+        value: false
+    resources:
+      - pet.json
+      - openapi.json
+      - rest-openapi-sink-binding.yaml
+  dump:
+    enabled: true
+    failedOnly: true
+    includes:
+      - app=camel-k