This is an automated email from the ASF dual-hosted git repository.
treblereel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new b3d25948766 kie-issues#2718: [kn-plugin-workflow] Minify the openAPI
spec files to trim components/schemas (#2749)
b3d25948766 is described below
commit b3d259487660d3604ad238b3fe1c4fa7077d0f00
Author: Dmitrii Tikhomirov <[email protected]>
AuthorDate: Wed Jan 8 09:39:36 2025 -0800
kie-issues#2718: [kn-plugin-workflow] Minify the openAPI spec files to trim
components/schemas (#2749)
Co-authored-by: Dmitrii Tikhomirov <[email protected]>
---
.../pkg/specs/openapi_minifier.go | 78 ++++-
.../pkg/specs/openapi_minifier_test.go | 226 ++++++++++++---
.../kn-plugin-workflow/pkg/specs/ref_collector.go | 190 ++++++++++++
.../pkg/specs/testdata/refs/emptyworkflow.sw.yaml | 29 ++
.../testdata/refs/openapi-subflow34.expected.yaml | 115 ++++++++
.../pkg/specs/testdata/refs/openapi.expected.yaml | 186 ++++++++++++
.../pkg/specs/testdata/refs/openapi.yaml | 320 +++++++++++++++++++++
.../pkg/specs/testdata/refs/openapi1.expected.yaml | 42 +++
.../pkg/specs/testdata/refs/openapi1.yaml | 75 +++++
.../pkg/specs/testdata/refs/openapi2.expected.yaml | 36 +++
.../pkg/specs/testdata/refs/openapi2.yaml | 64 +++++
.../pkg/specs/testdata/refs/subflow1.sw.yaml | 21 ++
.../pkg/specs/testdata/refs/subflow2.sw.yaml | 21 ++
.../pkg/specs/testdata/refs/subflow3.sw.yaml | 21 ++
.../pkg/specs/testdata/refs/subflow4.sw.yaml | 21 ++
.../pkg/specs/testdata/refs/workflow.sw.yaml | 38 +++
.../pkg/specs/testdata/refs/workflow1.sw.yaml | 34 +++
17 files changed, 1456 insertions(+), 61 deletions(-)
diff --git a/packages/kn-plugin-workflow/pkg/specs/openapi_minifier.go
b/packages/kn-plugin-workflow/pkg/specs/openapi_minifier.go
index 4fe7ef0747c..0d3f1a6e0e2 100644
--- a/packages/kn-plugin-workflow/pkg/specs/openapi_minifier.go
+++ b/packages/kn-plugin-workflow/pkg/specs/openapi_minifier.go
@@ -27,6 +27,7 @@ import (
"os"
"path"
"path/filepath"
+ "reflect"
"strings"
"github.com/apache/incubator-kie-tools/packages/kn-plugin-workflow/pkg/common"
@@ -158,22 +159,9 @@ func (m *OpenApiMinifier) minifySpecsFile(specFileName
string, operations sets.S
return "", fmt.Errorf("❌ ERROR: failed to read OpenAPI
document: %w", err)
}
- doc, err := openapi3.NewLoader().LoadFromData(data)
+ doc, err := m.removeUnusedNodes(data, specFile, operations)
if err != nil {
- return "", fmt.Errorf("❌ ERROR: failed to load OpenAPI
document: %w", err)
- }
- if doc.Paths == nil {
- return "", fmt.Errorf("OpenAPI document %s has no paths",
specFileName)
- }
- for key, value := range doc.Paths.Map() {
- for method, operation := range value.Operations() {
- if !operations.Has(operation.OperationID) {
- value.SetOperation(method, nil)
- }
- }
- if isPathItemEmpty(value) {
- doc.Paths.Delete(key)
- }
+ return "", err
}
minifiedFile, err := m.writeMinifiedFileToDisk(specFile, doc)
@@ -194,6 +182,66 @@ func (m *OpenApiMinifier) minifySpecsFile(specFileName
string, operations sets.S
return minifiedFile, nil
}
+func (m *OpenApiMinifier) removeUnusedNodes(data []byte, specFileName string,
operations sets.Set[string]) (*openapi3.T, error) {
+ doc, err := openapi3.NewLoader().LoadFromData(data)
+
+ collector, err := newCollector(specFileName)
+ if err != nil {
+ return nil, err
+ }
+
+ keep, err := collector.collect(operations)
+ if err != nil {
+ return nil, err
+ }
+
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: failed to load OpenAPI
document: %w", err)
+ }
+ if doc.Paths == nil {
+ return nil, fmt.Errorf("OpenAPI document %s has no paths",
specFileName)
+ }
+ for key, value := range doc.Paths.Map() {
+ for method, operation := range value.Operations() {
+ if !operations.Has(operation.OperationID) {
+ value.SetOperation(method, nil)
+ }
+ }
+ if isPathItemEmpty(value) {
+ doc.Paths.Delete(key)
+ }
+ }
+
+ if doc.Components != nil {
+ // note we have to skip securitySchemes, because it aren't
referenced by operation directly via $ref
+ components := map[string]interface{}{
+ "schemas": doc.Components.Schemas,
+ "headers": doc.Components.Headers,
+ "parameters": doc.Components.Parameters,
+ "responses": doc.Components.Responses,
+ "requestBodies": doc.Components.RequestBodies,
+ "examples": doc.Components.Examples,
+ "links": doc.Components.Links,
+ "callbacks": doc.Components.Callbacks,
+ }
+
+ for key, componentMap := range components {
+ if componentMap == nil {
+ continue
+ }
+
+ componentValue := reflect.ValueOf(componentMap)
+ for _, name := range componentValue.MapKeys() {
+ nameStr := name.String()
+ if !keep["components"][key].Has(nameStr) {
+ componentValue.SetMapIndex(name,
reflect.Value{})
+ }
+ }
+ }
+ }
+ return doc, nil
+}
+
func (m *OpenApiMinifier) findWorkflowFile() error {
file, err := common.FindSonataFlowFile(workflowExtensionsType)
if err != nil {
diff --git a/packages/kn-plugin-workflow/pkg/specs/openapi_minifier_test.go
b/packages/kn-plugin-workflow/pkg/specs/openapi_minifier_test.go
index b3be4bfc93e..05ba5d7335b 100644
--- a/packages/kn-plugin-workflow/pkg/specs/openapi_minifier_test.go
+++ b/packages/kn-plugin-workflow/pkg/specs/openapi_minifier_test.go
@@ -24,6 +24,7 @@ import (
"io"
"os"
"path"
+ "reflect"
"strings"
"testing"
@@ -36,6 +37,7 @@ import (
type spec struct {
file string
+ expected string
initial int
minified int
}
@@ -51,30 +53,30 @@ type minifyTest struct {
func TestOpenAPIMinify(t *testing.T) {
tests := []minifyTest{
{
- workflowFile: "testdata/workflow.sw.yaml",
// 4 functions, 2 of them are ref to the same openapi spec
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 3}}, // 5 operations, 3 must left
+ workflowFile: "testdata/workflow.sw.yaml",
// 4 functions, 2 of them are ref to the same
openapi spec
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 3}}, // 5 operations, 3
must left
specsDir: "specs",
subflowsDir: "subflows",
},
{
- workflowFile: "testdata/workflow.sw.json",
// 4 functions, 2 of them are ref to the same openapi spec
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 3}}, // 5 operations, 3 must left
+ workflowFile: "testdata/workflow.sw.json",
// 4 functions, 2 of them are ref to the same
openapi spec
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 3}}, // 5 operations, 3
must left
specsDir: "specs",
subflowsDir: "subflows",
},
{
- workflowFile:
"testdata/workflow-json-openapi.sw.json", // 4 functions, 2 of them
are ref to the same openapi spec
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi-json.json", 5, 3}}, // 5 operations, 3 must left
+ workflowFile:
"testdata/workflow-json-openapi.sw.json", //
4 functions, 2 of them are ref to the same openapi spec
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi-json.json", initial: 5, minified: 3}}, // 5 operations,
3 must left
specsDir: "specs",
subflowsDir: "subflows",
},
{
workflowFile: "testdata/workflow2.sw.yaml", // 4
functions, 1 per openapi spec file
openapiSpecFiles: []spec{
- {"testdata/flink1-openapi.yaml", 3, 1},
- {"testdata/flink2-openapi.yaml", 3, 1},
- {"testdata/flink3-openapi.yaml", 3, 1},
- {"testdata/flink4-openapi.yaml", 3, 1}},
+ {file: "testdata/flink1-openapi.yaml", initial:
3, minified: 1},
+ {file: "testdata/flink2-openapi.yaml", initial:
3, minified: 1},
+ {file: "testdata/flink3-openapi.yaml", initial:
3, minified: 1},
+ {file: "testdata/flink4-openapi.yaml", initial:
3, minified: 1}},
specsDir: "specs",
subflowsDir: "subflows",
},
@@ -86,26 +88,26 @@ func TestOpenAPIMinify(t *testing.T) {
},
{
workflowFile: "testdata/workflow-empty.sw.yaml", //
check all operations are removed
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 0}},
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 0}},
specsDir: "specs",
subflowsDir: "subflows",
},
{
workflowFile:
"testdata/workflow-mySpecsDir.sw.yaml", // check all operations are removed,
with different specs dir
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 3}},
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 3}},
specsDir: "mySpecsDir",
subflowsDir: "subflows",
},
{
workflowFile:
"testdata/workflow-mySpecsDir-one-finction.sw.yaml", // check all operations
are removed, with different specs dir
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 2}},
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 2}},
specsDir: "mySpecsDir",
subflowsDir: "subflows",
subflows:
[]string{"testdata/subflow-mySpecsDir.sw.yaml"},
},
{
workflowFile: "testdata/workflow-empty.sw.yaml", //
check all operations are removed, with different subflow dir
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 0}},
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 0}},
specsDir: "mySpecsDir",
subflowsDir: "subflows",
},
@@ -117,35 +119,35 @@ func TestOpenAPIMinify(t *testing.T) {
},
{
workflowFile: "testdata/workflow-empty2.sw.yaml",
// check functions is on subflow
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 2}},
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 2}},
specsDir: "specs",
subflowsDir: "subflows",
subflows: []string{"testdata/subflow.sw.yaml"},
},
{
workflowFile: "testdata/workflow-empty2.sw.yaml",
// check functions is on subflow, with different subflow and specs dirs
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 2}},
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 2}},
specsDir: "mySpecsDir",
subflowsDir: "mySubFlowDir",
subflows:
[]string{"testdata/subflow-mySpecsDir.sw.yaml"},
},
{
- workflowFile: "testdata/workflow-greeting.sw.yaml",
// check we can process subflows with the same file name but different
extensions
- openapiSpecFiles: []spec{{"testdata/greetingAPI.yaml",
3, 1}},
+ workflowFile: "testdata/workflow-greeting.sw.yaml",
// check we can process subflows with the same file name but different
extensions
+ openapiSpecFiles: []spec{{file:
"testdata/greetingAPI.yaml", initial: 3, minified: 1}},
specsDir: "specs",
subflowsDir: "custom_subflows",
subflows: []string{"testdata/hello.sw.json",
"testdata/hello.sw.yaml"}, // 2 subflows, 1 of them has a function that uses
the greetingAPI.yaml
},
{
- workflowFile: "testdata/workflow-greeting.sw.yaml",
// check we can process subflows with the same file name but different
extensions
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 2}},
+ workflowFile: "testdata/workflow-greeting.sw.yaml",
// check we can process subflows with the same file name but different
extensions
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 2}},
specsDir: "custom_specs",
subflowsDir: "custom_subflows",
subflows:
[]string{"testdata/subflow-custom.sw.json", "testdata/subflow-custom.sw.yaml"},
// 2 subflows, each one has a function that uses the flink-openapi.yaml
},
{
- workflowFile:
"testdata/workflow-subflow-custom.sw.yaml", // workflow with a function that
uses a subflow with a function that uses the flink-openapi.yaml
- openapiSpecFiles:
[]spec{{"testdata/flink-openapi.yaml", 5, 3}},
+ workflowFile:
"testdata/workflow-subflow-custom.sw.yaml", // workflow with a function that
uses a subflow with a function that uses the flink-openapi.yaml
+ openapiSpecFiles: []spec{{file:
"testdata/flink-openapi.yaml", initial: 5, minified: 3}},
specsDir: "custom_specs",
subflowsDir: "custom_subflows",
subflows:
[]string{"testdata/subflow-custom.sw.json", "testdata/subflow-custom.sw.yaml"},
// 2 subflows, each one has a function that uses the flink-openapi.yaml
@@ -159,30 +161,8 @@ func TestOpenAPIMinify(t *testing.T) {
for _, test := range tests {
t.Run(test.workflowFile, func(t *testing.T) {
- if err := os.Mkdir(test.specsDir, 0755); err != nil {
- t.Fatalf("Error creating specs directory: %v",
err)
- }
- defer os.RemoveAll(test.specsDir)
- if err := copyFile(test.workflowFile,
path.Base(test.workflowFile)); err != nil {
- t.Fatalf("Error copying workflow file: %v", err)
- }
- defer os.Remove(path.Base(test.workflowFile))
- if len(test.subflows) > 0 {
- if err := os.Mkdir(test.subflowsDir, 0755); err
!= nil {
- t.Fatalf("Error creating subflows
directory: %v", err)
- }
- defer os.RemoveAll(test.subflowsDir)
- for _, subflow := range test.subflows {
- if err := copyFile(subflow,
path.Join(test.subflowsDir, path.Base(subflow))); err != nil {
- t.Fatalf("Error copying subflow
file: %v", err)
- }
- }
- }
- for _, openapiSpecFile := range test.openapiSpecFiles {
- if err := copyFile(openapiSpecFile.file,
path.Join(test.specsDir, path.Base(openapiSpecFile.file))); err != nil {
- t.Fatalf("Error copying openapi spec
file: %v", err)
- }
- }
+ prepareStructure(t, test)
+ defer cleanUp(t, test)
minifiedfiles, err := NewMinifier(&OpenApiMinifierOpts{
SpecsDir: path.Join(current, test.specsDir),
@@ -197,6 +177,160 @@ func TestOpenAPIMinify(t *testing.T) {
}
}
+// These tests contain openapi specs with $ref to other specs
+func TestOpenAPIMinifyRefs(t *testing.T) {
+ tests := []minifyTest{
+ {
+ workflowFile: "testdata/refs/workflow.sw.yaml",
+ openapiSpecFiles: []spec{{file:
"testdata/refs/openapi.yaml", expected: "testdata/refs/openapi.expected.yaml",
initial: 5, minified: 3}},
+ specsDir: "specs",
+ subflowsDir: "subflows",
+ },
+ {
+ workflowFile: "testdata/refs/workflow.sw.yaml",
+ openapiSpecFiles: []spec{{file:
"testdata/refs/openapi.yaml", expected: "testdata/refs/openapi.expected.yaml",
initial: 5, minified: 3}},
+ specsDir: "my_specs",
+ subflowsDir: "subflows",
+ },
+ {
+ workflowFile: "testdata/refs/emptyworkflow.sw.yaml",
+ openapiSpecFiles: []spec{{file:
"testdata/refs/openapi1.yaml", expected:
"testdata/refs/openapi1.expected.yaml", initial: 1, minified: 1},
+ {file:
"testdata/refs/openapi2.yaml", expected:
"testdata/refs/openapi2.expected.yaml", initial: 1, minified: 1}},
+ specsDir: "specs",
+ subflowsDir: "subflows",
+ },
+ {
+ workflowFile: "testdata/refs/emptyworkflow.sw.yaml",
+ openapiSpecFiles: []spec{{file:
"testdata/refs/openapi1.yaml", expected:
"testdata/refs/openapi1.expected.yaml", initial: 1, minified: 1},
+ {file:
"testdata/refs/openapi2.yaml", expected:
"testdata/refs/openapi2.expected.yaml", initial: 1, minified: 1}},
+ specsDir: "my_specs",
+ subflowsDir: "subflows",
+ },
+ {
+ workflowFile: "testdata/refs/emptyworkflow.sw.yaml",
+ openapiSpecFiles: []spec{{file:
"testdata/refs/openapi1.yaml", expected:
"testdata/refs/openapi1.expected.yaml", initial: 1, minified: 1},
+ {file:
"testdata/refs/openapi2.yaml", expected:
"testdata/refs/openapi2.expected.yaml", initial: 1, minified: 1}},
+ specsDir: "my_specs",
+ subflowsDir: "custom_specs",
+ subflows:
[]string{"testdata/refs//subflow2.sw.yaml", "testdata/refs/subflow2.sw.yaml"},
// 2 subflows, each one has a function that uses the flink-openapi.yaml
+ },
+ {
+ workflowFile: "testdata/refs/emptyworkflow.sw.yaml",
+ openapiSpecFiles: []spec{{file:
"testdata/refs/openapi.yaml", expected:
"testdata/refs/openapi-subflow34.expected.yaml", initial: 5, minified: 2}},
+ specsDir: "specs",
+ subflowsDir: "custom_specs",
+ subflows:
[]string{"testdata/refs//subflow3.sw.yaml", "testdata/refs/subflow4.sw.yaml"},
// 2 subflows, each one has a function that uses the flink-openapi.yaml
+ },
+ }
+
+ current, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("Error getting current directory: %v", err)
+ }
+
+ for _, test := range tests {
+ t.Run(test.workflowFile, func(t *testing.T) {
+ prepareStructure(t, test)
+ defer cleanUp(t, test)
+
+ minifiedfiles, err := NewMinifier(&OpenApiMinifierOpts{
+ SpecsDir: path.Join(current, test.specsDir),
+ SubflowsDir: path.Join(current,
test.subflowsDir),
+ }).Minify()
+
+ if err != nil {
+ t.Fatalf("Error minifying openapi specs: %v",
err)
+ }
+ testFiles := map[string]spec{}
+
+ for _, spec := range test.openapiSpecFiles {
+ testFiles[path.Base(spec.file)] = spec
+ }
+
+ for k, v := range minifiedfiles {
+ expected := testFiles[k].expected
+ assert.Nil(t, validateOpenAPISpec(v), "Minified
file %s is not a valid OpenAPI spec", v)
+ assert.True(t, compareYAMLFiles(t, v,
expected), "Minified file %s is not equal to the expected file %s", v, expected)
+ }
+ })
+ }
+}
+
+func validateOpenAPISpec(filePath string) error {
+ loader := openapi3.NewLoader()
+ doc, err := loader.LoadFromFile(filePath)
+ if err != nil {
+ return fmt.Errorf("failed to load OpenAPI spec from file: %v",
err)
+ }
+
+ if err := doc.Validate(loader.Context); err != nil {
+ return fmt.Errorf("OpenAPI spec is invalid: %v", err)
+ }
+ return nil
+}
+
+func compareYAMLFiles(t *testing.T, file1Path, file2Path string) bool {
+ data1, err := os.ReadFile(file1Path)
+ if err != nil {
+ t.Fatalf("failed to read file %s: %v", file1Path, err)
+ }
+
+ data2, err := os.ReadFile(file2Path)
+ if err != nil {
+ t.Fatalf("failed to read file %s: %v", file2Path, err)
+ }
+
+ var obj1, obj2 interface{}
+ if err := yaml.Unmarshal(data1, &obj1); err != nil {
+ t.Fatalf("failed to unmarshal file %s: %v", file1Path, err)
+ }
+ if err := yaml.Unmarshal(data2, &obj2); err != nil {
+ t.Fatalf("failed to unmarshal file %s: %v", file2Path, err)
+ }
+
+ return reflect.DeepEqual(obj1, obj2)
+}
+
+func prepareStructure(t *testing.T, test minifyTest) {
+ if err := os.Mkdir(test.specsDir, 0755); err != nil {
+ t.Fatalf("Error creating specs directory: %v", err)
+ }
+ if err := copyFile(test.workflowFile, path.Base(test.workflowFile));
err != nil {
+ t.Fatalf("Error copying workflow file: %v", err)
+ }
+ if len(test.subflows) > 0 {
+ if err := os.Mkdir(test.subflowsDir, 0755); err != nil {
+ t.Fatalf("Error creating subflows directory: %v", err)
+ }
+ for _, subflow := range test.subflows {
+ if err := copyFile(subflow, path.Join(test.subflowsDir,
path.Base(subflow))); err != nil {
+ t.Fatalf("Error copying subflow file: %v", err)
+ }
+ }
+ }
+ for _, openapiSpecFile := range test.openapiSpecFiles {
+ if err := copyFile(openapiSpecFile.file,
path.Join(test.specsDir, path.Base(openapiSpecFile.file))); err != nil {
+ t.Fatalf("Error copying openapi spec file: %v", err)
+ }
+ }
+}
+
+func cleanUp(t *testing.T, test minifyTest) {
+ err := os.Remove(path.Base(test.workflowFile))
+ if err != nil {
+ t.Fatalf("Error removing workflow file: %v", err)
+ }
+ err = os.RemoveAll(test.specsDir)
+ if err != nil {
+ t.Fatalf("Error removing specs directory: %v", err)
+ }
+ err = os.RemoveAll(test.subflowsDir)
+ if err != nil {
+ t.Fatalf("Error removing subflows directory: %v", err)
+ }
+
+}
+
// checkInitial checks the initial number of operations in the openapi specs
func checkInitial(t *testing.T, test minifyTest) {
for _, spec := range test.openapiSpecFiles {
diff --git a/packages/kn-plugin-workflow/pkg/specs/ref_collector.go
b/packages/kn-plugin-workflow/pkg/specs/ref_collector.go
new file mode 100644
index 00000000000..90011103c2b
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/ref_collector.go
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package specs
+
+import (
+ "fmt"
+ "gopkg.in/yaml.v3"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "os"
+ "strings"
+)
+
+type collector struct {
+ filename string
+ refs sets.Set[string]
+ doc map[string]any
+}
+
+type node struct {
+ section string
+ subsection string
+ object string
+}
+
+func newCollector(file string) (*collector, error) {
+ data, err := os.ReadFile(file)
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: failed to read OpenAPI spec
file %s: %w", file, err)
+ }
+
+ m := make(map[string]any)
+ err = yaml.Unmarshal(data, &m)
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: failed to unmarshal OpenAPI
spec file %s: %w", file, err)
+ }
+
+ return &collector{filename: file, doc: m, refs: sets.Set[string]{}}, nil
+}
+
+func (c *collector) collect(operations sets.Set[string])
(map[string]map[string]sets.Set[string], error) {
+ for operation := range operations {
+ operationNode, err := c.findByOperationId(operation, c.doc)
+ if err != nil {
+ return nil, err
+ }
+ mapEntry(operationNode.(map[string]interface{}), c.refs)
+ }
+ visited, err := c.collectDependentRefs()
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: failed to collect dependent
refs in OpenAPI spec file %s: %w", c.filename, err)
+ }
+
+ preserve := map[string]map[string]sets.Set[string]{}
+ for ref := range visited {
+ node, err := c.parseRef(ref)
+ if err != nil {
+ return nil, fmt.Errorf("❌ ERROR: failed to parse ref at
OpenAPI spec file %s: %w", c.filename, err)
+ }
+ if preserve[node.section] == nil {
+ preserve[node.section] = map[string]sets.Set[string]{}
+ }
+ if preserve[node.section][node.subsection] == nil {
+ preserve[node.section][node.subsection] =
sets.Set[string]{}
+ }
+ preserve[node.section][node.subsection].Insert(node.object)
+ }
+ return preserve, nil
+}
+
+func (c *collector) collectDependentRefs() (sets.Set[string], error) {
+ var visited = sets.Set[string]{}
+ for c.refs.Len() > 0 {
+ operation, _ := c.refs.PopAny()
+ if !visited.Has(operation) {
+ visited.Insert(operation)
+ var current = sets.Set[string]{}
+ node, err := c.findByRefObject(operation, c.doc)
+ if err != nil {
+ return nil, err
+ }
+ mapEntry(node, current)
+ for current.Len() > 0 {
+ operation, _ := current.PopAny()
+ if !visited.Has(operation) {
+ c.refs.Insert(operation)
+ }
+ }
+ }
+ }
+ return visited, nil
+}
+
+func (c *collector) parseRef(ref string) (node, error) {
+ if !strings.HasPrefix(ref, "#/") {
+ return node{}, fmt.Errorf("invalid $ref: %s, must start with #/
at OpenAPI spec file %s", ref, c.filename)
+ }
+ parts := strings.Split(ref, "/")
+ if len(parts) < 4 {
+ return node{}, fmt.Errorf("invalid $ref %s at OpenAPI spec file
%s", ref, c.filename)
+ }
+ return node{section: parts[1], subsection: parts[2], object: parts[3]},
nil
+}
+
+func (c *collector) findByRefObject(ref string, m map[string]interface{})
(map[string]interface{}, error) {
+ parsedRef, err := c.parseRef(ref)
+ if err != nil {
+ return nil, err
+ }
+ section, ok := m[parsedRef.section].(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("OpenAPI spec file %s has no such
section: %s", c.filename, ref)
+ }
+ subsection, ok := section[parsedRef.subsection].(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("OpenAPI spec file %s has no such
subsection: %s", c.filename, ref)
+ }
+ object, ok := subsection[parsedRef.object].(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("OpenAPI spec file %s has no such
object: %s", c.filename, ref)
+ }
+
+ return object, nil
+}
+
+func (c *collector) findByOperationId(operationId string, m
map[string]interface{}) (any, error) {
+ paths, ok := m["paths"].(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("OpenAPI spec file %s has no paths",
c.filename)
+ }
+ for _, pathItem := range paths {
+ operations, ok := pathItem.(map[string]interface{})
+ if !ok {
+ continue
+ }
+ for _, operationDetails := range operations {
+ operation, ok :=
operationDetails.(map[string]interface{})
+ if !ok {
+ continue
+ }
+ if operation["operationId"] == operationId {
+ return operation, nil
+ }
+ }
+ }
+ return nil, fmt.Errorf("operationId %s not found at OpenAPI spec file
%s", operationId, c.filename)
+}
+
+func entry(e any, refs sets.Set[string]) {
+ switch v := e.(type) {
+ case map[string]interface{}:
+ mapEntry(v, refs)
+ case []interface{}:
+ sliceEntry(v, refs)
+ default:
+ return
+ }
+}
+
+func sliceEntry(s []interface{}, refs sets.Set[string]) {
+ for _, v := range s {
+ entry(v, refs)
+ }
+}
+
+func mapEntry(m map[string]interface{}, refs sets.Set[string]) {
+ for k, v := range m {
+ if k == "$ref" {
+ refs.Insert(v.(string))
+ continue
+ }
+ entry(v, refs)
+ }
+}
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/emptyworkflow.sw.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/emptyworkflow.sw.yaml
new file mode 100644
index 00000000000..bd4c84d6c89
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/emptyworkflow.sw.yaml
@@ -0,0 +1,29 @@
+id: flink-workflow
+version: "1.0"
+specVersion: "0.8"
+name: flink workflow
+description: Create a starter flink job management
+start: Get Flink Jars
+states:
+ - name: Get Flink Jars
+ type: operation
+ actionMode: sequential
+ actions:
+ - name: Get Flink Jars
+ functionRef:
+ refName: getJars
+ transition: Run Flink Job
+ - name: Run Flink Job
+ type: operation
+ actionMode: sequential
+ actions:
+ - actionDataFilter:
+ useResults: true
+ name: Run Flink Job
+ functionRef:
+ refName: runFlinkJob
+ arguments:
+ jarid: 72ecfc25-43ca-4f53-a4ee-1aaf93ac709a_flink-streaming-1.0.jar
+ entry-class: com.demo.flink.streaming.StreamingJob
+ end:
+ terminate: true
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi-subflow34.expected.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi-subflow34.expected.yaml
new file mode 100644
index 00000000000..2e9cf85ce25
--- /dev/null
+++
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi-subflow34.expected.yaml
@@ -0,0 +1,115 @@
+info:
+ title: Example API
+ version: 1.0.0
+openapi: 3.0.3
+paths:
+ /items:
+ get:
+ operationId: getItems
+ parameters:
+ - in: query
+ name: filter
+ schema:
+ $ref: "#/components/schemas/FilterSchema"
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ items:
+ $ref: "#/components/schemas/Item"
+ type: array
+ description: Successful response
+ "201":
+ description: Created
+ links:
+ GetItemById:
+ $ref: "#/components/links/GetItemById"
+ summary: Get a list of items
+ tags:
+ - items
+ /orders:
+ get:
+ operationId: getOrders
+ parameters:
+ - $ref: "#/components/parameters/OrderId"
+ - $ref: "#/components/parameters/Limit"
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OrdersList"
+ description: List of orders
+ headers:
+ X-Rate-Limit-Remaining:
+ $ref: "#/components/headers/RateLimitHeader"
+ summary: Get a list of orders
+components:
+ parameters:
+ Limit:
+ description: Record limit
+ in: query
+ name: limit
+ schema:
+ type: integer
+ OrderId:
+ description: Order identifier
+ in: query
+ name: orderId
+ required: true
+ schema:
+ type: string
+ schemas:
+ FilterSchema:
+ properties:
+ category:
+ type: string
+ status:
+ type: string
+ type: object
+ Item:
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ type: object
+ Order:
+ type: object
+ properties:
+ id:
+ type: string
+ status:
+ type: string
+ OrdersList:
+ type: array
+ items:
+ $ref: "#/components/schemas/Order"
+ links:
+ GetItemById:
+ operationId: getItem
+ parameters:
+ itemId: $response.body#/id
+ headers:
+ RateLimitHeader:
+ description: Remaining request limit
+ schema:
+ type: integer
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+ OAuth2Security:
+ type: oauth2
+ flows:
+ authorizationCode:
+ authorizationUrl: https://example.com/oauth/authorize
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ read: Read access
+ write: Write access
+tags:
+ - description: Item operations
+ name: items
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi.expected.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi.expected.yaml
new file mode 100644
index 00000000000..83ff5bd3835
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi.expected.yaml
@@ -0,0 +1,186 @@
+info:
+ title: Example API
+ version: 1.0.0
+openapi: 3.0.3
+paths:
+ /items:
+ get:
+ operationId: getItems
+ parameters:
+ - in: query
+ name: filter
+ schema:
+ $ref: "#/components/schemas/FilterSchema"
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ items:
+ $ref: "#/components/schemas/Item"
+ type: array
+ description: Successful response
+ "201":
+ description: Created
+ links:
+ GetItemById:
+ $ref: "#/components/links/GetItemById"
+ summary: Get a list of items
+ tags:
+ - items
+ /orders:
+ get:
+ operationId: getOrders
+ parameters:
+ - $ref: "#/components/parameters/OrderId"
+ - $ref: "#/components/parameters/Limit"
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OrdersList"
+ description: List of orders
+ headers:
+ X-Rate-Limit-Remaining:
+ $ref: "#/components/headers/RateLimitHeader"
+ summary: Get a list of orders
+ post:
+ operationId: createOrder
+ requestBody:
+ $ref: "#/components/requestBodies/CreateOrderRequest"
+ responses:
+ "201":
+ $ref: "#/components/responses/OrderCreatedResponse"
+ summary: Create a new order
+ /orders/{orderId}:
+ get:
+ operationId: getOrder
+ parameters:
+ - in: path
+ name: orderId
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ $ref: "#/components/responses/OrderResponse"
+ "404":
+ $ref: "#/components/responses/NotFoundResponse"
+ summary: Get order information
+components:
+ examples:
+ OrderExample:
+ summary: Order example
+ value:
+ id: "12345"
+ status: processing
+ headers:
+ RateLimitHeader:
+ description: Remaining request limit
+ schema:
+ type: integer
+ links:
+ GetItemById:
+ operationId: getItem
+ parameters:
+ itemId: $response.body#/id
+ parameters:
+ Limit:
+ description: Record limit
+ in: query
+ name: limit
+ schema:
+ type: integer
+ OrderId:
+ description: Order identifier
+ in: query
+ name: orderId
+ required: true
+ schema:
+ type: string
+ requestBodies:
+ CreateOrderRequest:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/CreateOrderSchema"
+ description: Data to create a new order
+ responses:
+ NotFoundResponse:
+ content:
+ application/json:
+ schema:
+ properties:
+ error:
+ type: string
+ type: object
+ description: Resource not found
+ OrderCreatedResponse:
+ content:
+ application/json:
+ schema:
+ properties:
+ id:
+ type: string
+ type: object
+ description: Order created
+ OrderResponse:
+ content:
+ application/json:
+ examples:
+ orderExample:
+ $ref: "#/components/examples/OrderExample"
+ schema:
+ $ref: "#/components/schemas/Order"
+ description: Order information
+ schemas:
+ CreateOrderSchema:
+ properties:
+ productId:
+ type: string
+ quantity:
+ type: integer
+ type: object
+ FilterSchema:
+ properties:
+ category:
+ type: string
+ status:
+ type: string
+ type: object
+ Item:
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ type: object
+ Order:
+ properties:
+ id:
+ type: string
+ status:
+ type: string
+ type: object
+ OrdersList:
+ items:
+ $ref: "#/components/schemas/Order"
+ type: array
+ securitySchemes:
+ ApiKeyAuth:
+ in: header
+ name: X-API-Key
+ type: apiKey
+ OAuth2Security:
+ flows:
+ authorizationCode:
+ authorizationUrl: https://example.com/oauth/authorize
+ scopes:
+ read: Read access
+ write: Write access
+ tokenUrl: https://example.com/oauth/token
+ type: oauth2
+tags:
+ - description: Item operations
+ name: items
diff --git a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi.yaml
new file mode 100644
index 00000000000..a8370ccf447
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi.yaml
@@ -0,0 +1,320 @@
+openapi: 3.0.3
+info:
+ title: Example API
+ version: 1.0.0
+
+paths:
+ # 1. $ref within a parameter using a schema
+ /items:
+ get:
+ summary: Get a list of items
+ operationId: getItems
+ tags:
+ - items
+ parameters:
+ - name: filter
+ in: query
+ schema:
+ $ref: "#/components/schemas/FilterSchema"
+ responses:
+ "200":
+ description: Successful response
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Item"
+ "201":
+ description: Created
+ links:
+ GetItemById:
+ $ref: "#/components/links/GetItemById"
+
+ # 2. $ref within response headers
+ /orders:
+ get:
+ summary: Get a list of orders
+ operationId: getOrders
+ parameters:
+ - $ref: "#/components/parameters/OrderId"
+ - $ref: "#/components/parameters/Limit"
+ responses:
+ "200":
+ description: List of orders
+ headers:
+ X-Rate-Limit-Remaining:
+ $ref: "#/components/headers/RateLimitHeader"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OrdersList"
+ post:
+ summary: Create a new order
+ operationId: createOrder
+ requestBody:
+ $ref: "#/components/requestBodies/CreateOrderRequest"
+ responses:
+ "201":
+ $ref: "#/components/responses/OrderCreatedResponse"
+
+ # 3. $ref within the request body schema
+ /orders/{orderId}:
+ get:
+ summary: Get order information
+ operationId: getOrder
+ parameters:
+ - name: orderId
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ $ref: "#/components/responses/OrderResponse"
+ "404":
+ $ref: "#/components/responses/NotFoundResponse"
+
+ # 4. $ref for examples in responses
+ put:
+ summary: Update an order
+ operationId: updateOrder
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UpdateOrder"
+ examples:
+ updateExample:
+ $ref: "#/components/examples/UpdateOrderExample"
+ responses:
+ "200":
+ $ref: "#/components/responses/OrderResponse"
+
+ # 5. $ref within callbacks
+ /webhooks:
+ post:
+ summary: Set a webhook
+ operationId: setWebhook
+ requestBody:
+ $ref: "#/components/requestBodies/WebhookRequest"
+ responses:
+ "201":
+ description: Webhook set
+ callbacks:
+ onEvent:
+ $ref: "#/components/callbacks/EventCallback"
+
+ # 7. Using security schemes
+ /secure-data:
+ get:
+ summary: Get secure data
+ operationId: getSecureData
+ security:
+ - ApiKeyAuth: []
+ - OAuth2Security: ["read", "write"]
+ responses:
+ "200":
+ description: Successful response
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: string
+
+components:
+ # 1. Schemas used in parameters and request bodies
+ schemas:
+ FilterSchema:
+ type: object
+ properties:
+ status:
+ type: string
+ category:
+ type: string
+ Item:
+ type: object
+ properties:
+ id:
+ type: string
+ name:
+ type: string
+ CreateOrderSchema:
+ type: object
+ properties:
+ productId:
+ type: string
+ quantity:
+ type: integer
+ UpdateOrder:
+ type: object
+ properties:
+ status:
+ type: string
+ Order:
+ type: object
+ properties:
+ id:
+ type: string
+ status:
+ type: string
+ OrdersList:
+ type: array
+ items:
+ $ref: "#/components/schemas/Order"
+ Event:
+ type: object
+ properties:
+ event:
+ type: string
+
+ # 2. Parameters
+ parameters:
+ OrderId:
+ name: orderId
+ in: query
+ description: Order identifier
+ required: true
+ schema:
+ type: string
+ Limit:
+ name: limit
+ in: query
+ description: Record limit
+ required: false
+ schema:
+ type: integer
+
+ # 3. Headers
+ headers:
+ RateLimitHeader:
+ description: Remaining request limit
+ schema:
+ type: integer
+
+ # 4. Request bodies
+ requestBodies:
+ CreateOrderRequest:
+ description: Data to create a new order
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/CreateOrderSchema"
+ WebhookRequest:
+ description: Data to set a webhook
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ url:
+ type: string
+
+ # 6. Responses
+ responses:
+ OrderCreatedResponse:
+ description: Order created
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ OrderCreatedResponseWithLink:
+ description: Order created
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ links:
+ GetOrderById:
+ $ref: "#/components/links/GetOrderById"
+ OrdersResponse:
+ description: List of orders
+ headers:
+ X-Rate-Limit-Remaining:
+ $ref: "#/components/headers/RateLimitHeader"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OrdersList"
+ OrderResponse:
+ description: Order information
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Order"
+ examples:
+ orderExample:
+ $ref: "#/components/examples/OrderExample"
+ NotFoundResponse:
+ description: Resource not found
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+
+ # 7. Examples for responses
+ examples:
+ OrderExample:
+ summary: Order example
+ value:
+ id: "12345"
+ status: "processing"
+ UpdateOrderExample:
+ summary: Order update example
+ value:
+ status: "shipped"
+
+ # 8. Callbacks
+ callbacks:
+ EventCallback:
+ "{$request.body#/callbackUrl}":
+ post:
+ summary: Event received
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Event"
+ responses:
+ "200":
+ description: Event processed
+
+ # 9. Links
+ links:
+ GetOrderById:
+ operationId: getOrder
+ parameters:
+ orderId: "$response.body#/id"
+ GetItemById:
+ operationId: getItem
+ parameters:
+ itemId: "$response.body#/id"
+ # 10. Security schemes
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+ OAuth2Security:
+ type: oauth2
+ flows:
+ authorizationCode:
+ authorizationUrl: https://example.com/oauth/authorize
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ read: Read access
+ write: Write access
+tags:
+ - name: items
+ description: Item operations
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi1.expected.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi1.expected.yaml
new file mode 100644
index 00000000000..7d5905f2cc9
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi1.expected.yaml
@@ -0,0 +1,42 @@
+openapi: 3.0.3
+info:
+ title: Example API
+ version: 1.0.0
+
+paths:
+ /items:
+ get:
+ summary: Get a list of items
+ operationId: getItems
+ tags:
+ - items
+ parameters:
+ - name: filter
+ in: query
+ schema:
+ $ref: "#/components/schemas/FilterSchema"
+ responses:
+ "200":
+ description: Successful response
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Item"
+components:
+ schemas:
+ Item:
+ type: object
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ FilterSchema:
+ type: object
+ properties:
+ type:
+ type: string
+ description:
+ type: string
diff --git a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi1.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi1.yaml
new file mode 100644
index 00000000000..3f87553a10c
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi1.yaml
@@ -0,0 +1,75 @@
+openapi: 3.0.3
+info:
+ title: Example API
+ version: 1.0.0
+
+paths:
+ /items:
+ get:
+ summary: Get a list of items
+ operationId: getItems
+ tags:
+ - items
+ parameters:
+ - name: filter
+ in: query
+ schema:
+ $ref: "#/components/schemas/FilterSchema"
+ responses:
+ "200":
+ description: Successful response
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: "#/components/schemas/Item"
+components:
+ schemas:
+ Item:
+ type: object
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ FilterSchema:
+ type: object
+ properties:
+ type:
+ type: string
+ description:
+ type: string
+ OrdersList:
+ type: object
+ properties:
+ orders:
+ type: array
+ items:
+ $ref: "#/components/schemas/Order"
+ Order:
+ type: object
+ properties:
+ id:
+ type: integer
+
+ Jars:
+ type: object
+ properties:
+ jarid:
+ type: string
+ jarname:
+ type: string
+ jarversion:
+ type: string
+ Message:
+ type: object
+ properties:
+ message:
+ type: string
+ requestBodies:
+ HelloWorld:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Message"
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi2.expected.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi2.expected.yaml
new file mode 100644
index 00000000000..2d2ba09440b
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi2.expected.yaml
@@ -0,0 +1,36 @@
+openapi: 3.0.3
+info:
+ title: Example API
+ version: 1.0.0
+
+paths:
+ /orders:
+ get:
+ summary: Get a list of items
+ operationId: getOrders
+ responses:
+ "200":
+ description: List of orders
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OrdersList"
+components:
+ schemas:
+ OrdersList:
+ type: object
+ properties:
+ orders:
+ type: array
+ items:
+ $ref: "#/components/schemas/Order"
+ Order:
+ type: object
+ properties:
+ id:
+ type: integer
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
diff --git a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi2.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi2.yaml
new file mode 100644
index 00000000000..9365ac5ad22
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/openapi2.yaml
@@ -0,0 +1,64 @@
+openapi: 3.0.3
+info:
+ title: Example API
+ version: 1.0.0
+
+paths:
+ /orders:
+ get:
+ summary: Get a list of items
+ operationId: getOrders
+ responses:
+ "200":
+ description: List of orders
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/OrdersList"
+
+components:
+ schemas:
+ OrdersList:
+ type: object
+ properties:
+ orders:
+ type: array
+ items:
+ $ref: "#/components/schemas/Order"
+ Order:
+ type: object
+ properties:
+ id:
+ type: integer
+ parameters:
+ OrderId:
+ name: orderId
+ in: path
+ required: true
+ schema:
+ type: integer
+ Limit:
+ name: limit
+ in: query
+ required: false
+ schema:
+ type: integer
+ links:
+ GetItemById:
+ operationId: getItemById
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: X-API-Key
+ requestBodies:
+ CreateOrderRequest:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Order"
+ headers:
+ RateLimitHeader:
+ description: Rate limit remaining
+ schema:
+ type: integer
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow1.sw.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow1.sw.yaml
new file mode 100644
index 00000000000..3a034500437
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow1.sw.yaml
@@ -0,0 +1,21 @@
+---
+id: "helloworldyaml1"
+version: "1.0"
+specVersion: "0.8"
+name: "Hello World Workflow"
+description: "JSON based hello world workflow"
+start: "Inject Hello World SubFlow"
+functions:
+ - name: getItems
+ operation: custom_specs/openapi1.yaml#getItems
+states:
+ - name: "Inject Hello World SubFlow"
+ type: "inject"
+ data:
+ greeting-subflow: "Hello World SubFlow"
+ transition: "Inject Mantra SubFlow"
+ - name: "Inject Mantra SubFlow"
+ type: "inject"
+ data:
+ mantra-subflow: "SubFlow Serverless Workflow is awesome!"
+ end: true
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow2.sw.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow2.sw.yaml
new file mode 100644
index 00000000000..4131ae2ea1f
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow2.sw.yaml
@@ -0,0 +1,21 @@
+---
+id: "helloworldyaml1"
+version: "1.0"
+specVersion: "0.8"
+name: "Hello World Workflow"
+description: "JSON based hello world workflow"
+start: "Inject Hello World SubFlow"
+functions:
+ - name: getOrders
+ operation: custom_specs/openapi2.yaml#getOrders
+states:
+ - name: "Inject Hello World SubFlow"
+ type: "inject"
+ data:
+ greeting-subflow: "Hello World SubFlow"
+ transition: "Inject Mantra SubFlow"
+ - name: "Inject Mantra SubFlow"
+ type: "inject"
+ data:
+ mantra-subflow: "SubFlow Serverless Workflow is awesome!"
+ end: true
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow3.sw.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow3.sw.yaml
new file mode 100644
index 00000000000..107c96a3951
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow3.sw.yaml
@@ -0,0 +1,21 @@
+---
+id: "helloworldyaml1"
+version: "1.0"
+specVersion: "0.8"
+name: "Hello World Workflow"
+description: "JSON based hello world workflow"
+start: "Inject Hello World SubFlow"
+functions:
+ - name: getItems
+ operation: specs/openapi.yaml#getItems
+states:
+ - name: "Inject Hello World SubFlow"
+ type: "inject"
+ data:
+ greeting-subflow: "Hello World SubFlow"
+ transition: "Inject Mantra SubFlow"
+ - name: "Inject Mantra SubFlow"
+ type: "inject"
+ data:
+ mantra-subflow: "SubFlow Serverless Workflow is awesome!"
+ end: true
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow4.sw.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow4.sw.yaml
new file mode 100644
index 00000000000..7c854e30afe
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/subflow4.sw.yaml
@@ -0,0 +1,21 @@
+---
+id: "helloworldyaml1"
+version: "1.0"
+specVersion: "0.8"
+name: "Hello World Workflow"
+description: "JSON based hello world workflow"
+start: "Inject Hello World SubFlow"
+functions:
+ - name: getOrders
+ operation: specs/openapi.yaml#getOrders
+states:
+ - name: "Inject Hello World SubFlow"
+ type: "inject"
+ data:
+ greeting-subflow: "Hello World SubFlow"
+ transition: "Inject Mantra SubFlow"
+ - name: "Inject Mantra SubFlow"
+ type: "inject"
+ data:
+ mantra-subflow: "SubFlow Serverless Workflow is awesome!"
+ end: true
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/workflow.sw.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/workflow.sw.yaml
new file mode 100644
index 00000000000..566d18e4fcd
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/workflow.sw.yaml
@@ -0,0 +1,38 @@
+id: flink-workflow
+version: "1.0"
+specVersion: "0.8"
+name: flink workflow
+description: Create a starter flink job management
+functions:
+ - name: getItems
+ operation: specs/openapi.yaml#getItems
+ - name: getOrders
+ operation: specs/openapi.yaml#getOrders
+ - name: createOrder
+ operation: specs/openapi.yaml#createOrder
+ - name: getOrder
+ operation: specs/openapi.yaml#getOrder
+start: Get Flink Jars
+states:
+ - name: Get Flink Jars
+ type: operation
+ actionMode: sequential
+ actions:
+ - name: Get Flink Jars
+ functionRef:
+ refName: getJars
+ transition: Run Flink Job
+ - name: Run Flink Job
+ type: operation
+ actionMode: sequential
+ actions:
+ - actionDataFilter:
+ useResults: true
+ name: Run Flink Job
+ functionRef:
+ refName: runFlinkJob
+ arguments:
+ jarid: 72ecfc25-43ca-4f53-a4ee-1aaf93ac709a_flink-streaming-1.0.jar
+ entry-class: com.demo.flink.streaming.StreamingJob
+ end:
+ terminate: true
diff --git
a/packages/kn-plugin-workflow/pkg/specs/testdata/refs/workflow1.sw.yaml
b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/workflow1.sw.yaml
new file mode 100644
index 00000000000..6e7f3233236
--- /dev/null
+++ b/packages/kn-plugin-workflow/pkg/specs/testdata/refs/workflow1.sw.yaml
@@ -0,0 +1,34 @@
+id: flink-workflow
+version: "1.0"
+specVersion: "0.8"
+name: flink workflow
+description: Create a starter flink job management
+functions:
+ - name: getItems
+ operation: specs/openapi1.yaml#getItems
+ - name: getOrders
+ operation: specs/openapi2.yaml#getOrders
+start: Get Flink Jars
+states:
+ - name: Get Flink Jars
+ type: operation
+ actionMode: sequential
+ actions:
+ - name: Get Flink Jars
+ functionRef:
+ refName: getJars
+ transition: Run Flink Job
+ - name: Run Flink Job
+ type: operation
+ actionMode: sequential
+ actions:
+ - actionDataFilter:
+ useResults: true
+ name: Run Flink Job
+ functionRef:
+ refName: runFlinkJob
+ arguments:
+ jarid: 72ecfc25-43ca-4f53-a4ee-1aaf93ac709a_flink-streaming-1.0.jar
+ entry-class: com.demo.flink.streaming.StreamingJob
+ end:
+ terminate: true
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]