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 <trebler...@users.noreply.github.com> 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 <treblereel@Dmitriis-MacBook-Pro.local> --- .../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: commits-unsubscr...@kie.apache.org For additional commands, e-mail: commits-h...@kie.apache.org