Skip to content

Commit

Permalink
feat: add oas-overlay command
Browse files Browse the repository at this point in the history
This command implements v1.0.0 of the OpenAPI Overlay specification
It allows you to apply an Overlay to an existing OAS.
micovery committed Oct 23, 2024
1 parent 4e04338 commit 360e538
Showing 22 changed files with 6,237 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/apigee-go-gen/transform/cmd.go
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ package transform
import (
apiproxy_to_yaml "github.com/apigee/apigee-go-gen/cmd/apigee-go-gen/transform/apiproxy-to-yaml"
json_to_yaml "github.com/apigee/apigee-go-gen/cmd/apigee-go-gen/transform/json-to-yaml"
oas_overlay "github.com/apigee/apigee-go-gen/cmd/apigee-go-gen/transform/oas-overlay"
oas2_to_oas3 "github.com/apigee/apigee-go-gen/cmd/apigee-go-gen/transform/oas2-to-oas3"
resolve_refs "github.com/apigee/apigee-go-gen/cmd/apigee-go-gen/transform/resolve-refs"
sharedflow_to_yaml "github.com/apigee/apigee-go-gen/cmd/apigee-go-gen/transform/sharedflow-to-yaml"
@@ -45,4 +46,5 @@ func init() {
Cmd.AddCommand(resolve_refs.Cmd)
Cmd.AddCommand(json_to_yaml.Cmd)
Cmd.AddCommand(yaml_to_json.Cmd)
Cmd.AddCommand(oas_overlay.Cmd)
}
44 changes: 44 additions & 0 deletions cmd/apigee-go-gen/transform/oas-overlay/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 Google LLC
//
// Licensed 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 oas_overlay

import (
"github.com/apigee/apigee-go-gen/pkg/flags"
"github.com/apigee/apigee-go-gen/pkg/utils"
"github.com/spf13/cobra"
)

var spec flags.String
var overlay flags.String
var output flags.String

var Cmd = &cobra.Command{
Use: "oas-overlay",
Short: "Transforms Open API spec by applying overlay file",
RunE: func(cmd *cobra.Command, args []string) error {
return utils.OASOverlay(string(overlay), string(spec), string(output))
},
}

func init() {

Cmd.Flags().SortFlags = false
Cmd.Flags().VarP(&spec, "spec", "s", "path to OpenAPI spec file (optional)")
Cmd.Flags().VarP(&overlay, "overlay", "", "path to overlay file")
Cmd.Flags().VarP(&output, "output", "o", "path to output file, or omit to use stdout")

_ = Cmd.MarkFlagRequired("overlay")

}
66 changes: 66 additions & 0 deletions docs/transform/commands/oas-overlay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# OAS Overlay
<!--
Copyright 2024 Google LLC
Licensed 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.
-->

This command applies an OpenAPI Overlay to an OpenAPI spec.

## Usage

The `oas-overlay` command takes the following parameters:

* `--overlay` is the path to the OpenAPI Overlay (either as JSON or YAML)

* `--spec` (*optional*) is the path to the OpenAPI Spec to transform (either as JSON or YAML)

> The `--spec` parameter is optional. If omitted, the OAS path is read form the `extends` property of the overlay.
> In this case, the path is relative to the location of the overlay file itself.
* `--output` is the document to be created (either as JSON or YAML)

> Full path is created if it does not exist (like `mkdir -p`)

> You may omit the `--output` flags to write to stdout


### Examples

Below are a few examples for using the `oas-overlay` command.

#### Write to file
Writing the output to a new file
```shell
apigee-go-gen transform oas-overlay \
--spec ./examples/specs/oas3/petstore.yaml \
--overlay ./examples/overlays/petstore.yaml \
--output ./out/specs/oas3/petstore-overlaid.yaml
```

#### Write To stdout
Writing the output to stdout
```shell
apigee-go-gen transform oas-overlay \
--spec ./examples/specs/oas3/petstore.yaml \
--overlay ./examples/overlays/petstore.yaml
```

#### Using the `extends` property
Instead of passing `--spec`, use the value of the `extends` property in the overlay
```shell
apigee-go-gen transform oas-overlay \
--overlay ./examples/overlays/petstore.yaml
```
23 changes: 23 additions & 0 deletions examples/overlays/petstore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
overlay: 1.0.0
info:
title: Targeted Overlay
version: 1.0.0
extends: ../specs/oas3/petstore.yaml
actions:
- target: $.info.contact
remove: true
- target: $.info
update:
description: This is a new description
- target: $.tags[?(@.name=='pet')]
update:
externalDocs:
url: https://example.com/pet
- target: $.paths['/pet/{petId}/uploadImage'].post
update:
description: This is the new description for uploadImage operation
- target: $.paths['/pet/{petId}']
update:
get:
description: This is an updated description of a child object
x-safe: false
10 changes: 10 additions & 0 deletions pkg/utils/io.go
Original file line number Diff line number Diff line change
@@ -103,3 +103,13 @@ func ReadInputText(input string) ([]byte, error) {
}
return text, nil
}

func ReadInputTextFile(input string) ([]byte, error) {
var text []byte
var err error
text, err = os.ReadFile(input)
if err != nil {
return nil, errors.New(err)
}
return text, nil
}
397 changes: 397 additions & 0 deletions pkg/utils/overlay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,397 @@
// Copyright 2024 Google LLC
//
// Licensed 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 utils

import (
"github.com/go-errors/errors"
libopenapijson "github.com/pb33f/libopenapi/json"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
"path"
"path/filepath"
"slices"
)

func OASOverlay(overlayFile string, specFile string, outputFile string) error {
overlayText, err := ReadInputTextFile(overlayFile)
if err != nil {
return err
}

var overlayNode *yaml.Node
overlayNode = &yaml.Node{}
err = yaml.Unmarshal(overlayText, overlayNode)
if err != nil {
return errors.New(err)
}

extendsField, err := getExtends(overlayNode, overlayFile)
if err != nil {
return err
}

//if extends field is set, and there is no spec file specified, use the extends field
if extendsField != nil && specFile == "" {
if path.IsAbs(*extendsField) {
specFile = *extendsField
} else {
//when using extends, if path is relative, then it's relative to the overlay itself
dir := path.Dir(overlayFile)
specFile = path.Join(dir, *extendsField)
}
}

if specFile == "" {
return errors.Errorf("neither --spec parameter nor Overlay 'extends' field was specified")
}

specText, err := ReadInputTextFile(specFile)
if err != nil {
return err
}

var specNode *yaml.Node
specNode = &yaml.Node{}
err = yaml.Unmarshal(specText, specNode)
if err != nil {
return errors.New(err)
}

//verify we are actually working with OAS3
if slices.IndexFunc(specNode.Content[0].Content, func(n *yaml.Node) bool {
return n.Value == "openapi"
}) < 0 {
return errors.Errorf("%s is not an OpenAPI 3.X spec file", specFile)
}

resultNode, err := ApplyOASOverlay(overlayNode, specNode, overlayFile, specFile)
if err != nil {
return err
}

ext := filepath.Ext(outputFile)
if ext == "" {
ext = filepath.Ext(specFile)
}

//depending on the file extension write the outputFile as either JSON or YAML
var outputText []byte
if ext == ".json" {
outputText, err = libopenapijson.YAMLNodeToJSON(resultNode, " ")
if err != nil {
return errors.New(err)
}
} else {
outputText, err = YAML2Text(UnFlowYAMLNode(resultNode), 2)
if err != nil {
return err
}
}

return WriteOutputText(outputFile, outputText)
}

func ApplyOASOverlay(overlayNode *yaml.Node, specNode *yaml.Node, overlayFile string, specFile string) (*yaml.Node, error) {
actions, err := getActions(overlayNode, overlayFile)
if err != nil {
return nil, err
}

var newSpec *yaml.Node = specNode
for _, action := range actions {
newSpec, err = ApplyOASOverlayAction(action, newSpec, overlayFile, specFile)
if err != nil {
return nil, err
}
}

return specNode, nil
}

func ApplyOASOverlayAction(action *yaml.Node, specNode *yaml.Node, overlayFile string, specFile string) (*yaml.Node, error) {

targetNode, err := getActionTarget(action, overlayFile)
if err != nil {
return nil, err
}

updateNode, err := getActionUpdate(action, overlayFile)
if err != nil {
return nil, err
}

remove, err := getActionRemove(action, overlayFile)
if err != nil {
return nil, err
}

if targetNode == nil {
return nil, errors.Errorf("'target' field is required for action")
}

if remove == nil && updateNode == nil {
return nil, errors.Errorf("action does not contain neither 'remove' nor 'update' field at %s:%d", overlayFile, action.Line)
} else if remove != nil && *remove == true {
//handle remove action
specNode, err = removeYAMLNode(specNode, targetNode, overlayFile, specFile)
if err != nil {
return nil, err
}
} else if updateNode != nil {
//handle updateNode action
specNode, err = updateYAMLNode(specNode, targetNode, updateNode, overlayFile, specFile)
if err != nil {
return nil, err
}
} else {
//no-op action
return specNode, nil
}

return specNode, nil
}

func getActionTarget(actionNode *yaml.Node, overlayFile string) (*yaml.Node, error) {
targetPath, err := yamlpath.NewPath("$.target")
if err != nil {
return nil, errors.New(err)
}

results, err := targetPath.Find(actionNode)
if err != nil {
return nil, errors.New(err)
}

if len(results) == 0 {
return nil, nil
}

targetNode := results[0]

if targetNode.Kind != yaml.ScalarNode {
return nil, errors.Errorf("'target' field within overlay action is not a string at %s:%d", overlayFile, targetNode.Line)
}

return targetNode, nil
}

func getActionUpdate(actionNode *yaml.Node, overlayFile string) (*yaml.Node, error) {
updatePath, err := yamlpath.NewPath("$.update")
if err != nil {
return nil, errors.New(err)
}

results, err := updatePath.Find(actionNode)
if err != nil {
return nil, errors.New(err)
}

if len(results) == 0 {
return nil, nil
}

return results[0], nil
}

func getActionRemove(actionNode *yaml.Node, overlayFile string) (*bool, error) {
var remove bool
removePath, err := yamlpath.NewPath("$.remove")
if err != nil {
return &remove, errors.New(err)
}

results, err := removePath.Find(actionNode)
if err != nil {
return nil, errors.New(err)
}

if len(results) == 0 {
return nil, nil
}

removeNode := results[0]

if removeNode.Kind != yaml.ScalarNode ||
!(removeNode.Value == "true" || removeNode.Value == "false") {
return nil, errors.Errorf("'remove' field within overlay action is not boolean at %s:%d", overlayFile, removeNode.Line)
}

remove = results[0].Value == "true"
return &remove, nil
}

func getActions(overlayNode *yaml.Node, overlayFile string) ([]*yaml.Node, error) {
actionsPath, err := yamlpath.NewPath("$.actions")
if err != nil {
return nil, errors.New(err)
}

actionsNodes, err := actionsPath.Find(overlayNode)
if err != nil {
return nil, errors.New(err)
}

if len(actionsNodes) == 0 {
return nil, nil
}

actionsNode := actionsNodes[0]

if actionsNode.Kind != yaml.SequenceNode {
return nil, errors.Errorf("'actions' field must be an array at %s:%d", overlayFile, actionsNode.Line)
}

return actionsNode.Content, nil
}

func getExtends(overlayNode *yaml.Node, overlayFile string) (*string, error) {
var extends *string
extendsPath, err := yamlpath.NewPath("$.extends")
if err != nil {
return extends, errors.New(err)
}

results, err := extendsPath.Find(overlayNode)
if err != nil {
return extends, errors.New(err)
}

if len(results) == 0 {
return extends, nil
}

extendsNode := *results[0]
if extendsNode.Kind != yaml.ScalarNode {
return nil, errors.Errorf("extends field is not a string at %s:%d", overlayFile, extendsNode.Line)
}

return &extendsNode.Value, nil
}

func updateYAMLNode(root *yaml.Node, targetNode *yaml.Node, updateNode *yaml.Node, overlayFile string, specFile string) (*yaml.Node, error) {
pathToUpdate, err := yamlpath.NewPath(targetNode.Value)
if err != nil {
return nil, errors.Errorf("%s at %s:%d", err.Error(), overlayFile, targetNode.Line)
}

nodesToUpdate, err := pathToUpdate.Find(root)
if err != nil {
return nil, errors.New(err)
}

if len(nodesToUpdate) == 0 {
return root, nil
}

for _, nodeToUpdate := range nodesToUpdate {
updateYAMLNodeRecursiveInPlace(nodeToUpdate, updateNode)
}

return root, nil

}

func removeYAMLNode(root *yaml.Node, targetNode *yaml.Node, overlayFile string, specFile string) (*yaml.Node, error) {
pathToRemove, err := yamlpath.NewPath(targetNode.Value)
if err != nil {
return nil, errors.Errorf("%s at %s:%d", err.Error(), overlayFile, targetNode.Line)
}

nodesToRemove, err := pathToRemove.Find(root)
if err != nil {
return nil, errors.New(err)
}

if len(nodesToRemove) == 0 {
return root, nil
}

for _, nodeToRemove := range nodesToRemove {
removeYAMLNodeRecursiveInPlace(nodeToRemove, root)
}

return root, nil
}

func removeYAMLNodeRecursiveInPlace(needle *yaml.Node, haystack *yaml.Node) {
if needle == nil {
return
}

if haystack.Kind == yaml.MappingNode {
for i := 0; i+1 < len(haystack.Content); i += 2 {
value := haystack.Content[i+1]
if value == needle {
haystack.Content = slices.Delete(haystack.Content, i, i+2)
return
} else {
removeYAMLNodeRecursiveInPlace(needle, value)
}
}
} else if haystack.Kind == yaml.DocumentNode {
removeYAMLNodeRecursiveInPlace(needle, haystack.Content[0])
} else if haystack.Kind == yaml.SequenceNode {
for i := 0; i < len(haystack.Content); i += 1 {
value := haystack.Content[i]
if value == needle {
haystack.Content = slices.Delete(haystack.Content, i, i+1)
return
} else {
removeYAMLNodeRecursiveInPlace(needle, value)
}
}
}

return
}

func updateYAMLNodeRecursiveInPlace(target *yaml.Node, source *yaml.Node) {
if target.Kind == yaml.MappingNode && source.Kind == yaml.MappingNode {
//create a lookup map for the target
lookupMap := map[string]*yaml.Node{}
for i := 0; i+1 < len(target.Content); i += 2 {
key := target.Content[i].Value
value := target.Content[i+1]
lookupMap[key] = value
}

//merge keys that match
for i := 0; i+1 < len(source.Content); i += 2 {
key := source.Content[i].Value
if subTarget, found := lookupMap[key]; found {
updateYAMLNodeRecursiveInPlace(subTarget, source.Content[i+1])
} else {
target.Content = append(target.Content, source.Content[i], source.Content[i+1])
}
}
return
}

if target.Kind == yaml.SequenceNode && (source.Kind == yaml.MappingNode ||
source.Kind == yaml.ScalarNode) {
target.Content = append(target.Content, source)
}

if target.Kind == yaml.ScalarNode && source.Kind == yaml.ScalarNode {
target.Value = source.Value
return
}

if target.Kind == yaml.SequenceNode && source.Kind == yaml.SequenceNode {
target.Content = append(target.Content, source.Content...)
return
}

}
110 changes: 110 additions & 0 deletions pkg/utils/overlay_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2024 Google LLC
//
// Licensed 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 utils

import (
"github.com/go-errors/errors"
"github.com/stretchr/testify/require"
"os"
"path"
"path/filepath"
"testing"
)

func TestOASOverlay(t *testing.T) {
type args struct {
}

tests := []struct {
dir string
specFile string
wantErr error
}{
{
"structured",
"petstore/oas3.yaml",
nil,
},
{
"targeted",
"petstore/oas3.yaml",
nil,
},
{
"wildcard",
"petstore/oas3.yaml",
nil,
},
{
"array",
"petstore/oas3.yaml",
nil,
},
{
"bad-update-target",
"petstore/oas3.yaml",
errors.New("invalid array index [?@.name=='status' && @.in=='query'] before position 58: non-integer array index at testdata/oas-overlay/bad-update-target/overlay.yaml:19"),
},
{
"bad-remove-value",
"petstore/oas3.yaml",
errors.New("'remove' field within overlay action is not boolean at testdata/oas-overlay/bad-remove-value/overlay.yaml:20"),
},
{
"bad-action-op",
"petstore/oas3.yaml",
errors.New("action does not contain neither 'remove' nor 'update' field at testdata/oas-overlay/bad-action-op/overlay.yaml:19"),
},
{
"bad-actions-value",
"petstore/oas3.yaml",
errors.New("'actions' field must be an array at testdata/oas-overlay/bad-actions-value/overlay.yaml:18"),
},
}
for _, tt := range tests {
t.Run(tt.dir, func(t *testing.T) {
testDir := path.Join("testdata", "oas-overlay", tt.dir)
specFile := path.Join("testdata", "specs", "oas3", tt.specFile)
overlayFile := path.Join(testDir, "overlay.yaml")
outputFile := path.Join(testDir, "out-oas3.yaml")
expectedFile := path.Join(testDir, "exp-oas3.yaml")

var err error
err = os.RemoveAll(outputFile)
require.NoError(t, err)

err = OASOverlay(overlayFile, specFile, outputFile)

if tt.wantErr != nil {
require.EqualError(t, err, tt.wantErr.Error())
return
}
require.NoError(t, err)

outputBytes := MustReadFileBytes(outputFile)
expectedBytes := MustReadFileBytes(expectedFile)

if filepath.Ext(expectedFile) == ".json" {
require.JSONEq(t, string(expectedBytes), string(outputBytes))
} else if filepath.Ext(expectedFile) == ".yaml" {
outputBytes = RemoveYAMLComments(outputBytes)
expectedBytes = RemoveYAMLComments(expectedBytes)
require.YAMLEq(t, string(expectedBytes), string(outputBytes))
} else {
t.Error("unknown output format in testcase")
}
})
}
}
16 changes: 16 additions & 0 deletions pkg/utils/testdata/oas-overlay/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2024 Google LLC
#
# Licensed 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.

**/out-*.yaml
**/out-*.json
847 changes: 847 additions & 0 deletions pkg/utils/testdata/oas-overlay/array/exp-oas3.yaml

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions pkg/utils/testdata/oas-overlay/array/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
overlay: 1.0.0
info:
title: Update many objects at once
version: 1.0.0
actions:
- target: $.paths.*.get.parameters
update:
name: newParam
in: query
description: New parameter
schema:
type: string
- target: $.paths.*.*.parameters[?(@.name=='additionalMetadata')]
remove: true
19 changes: 19 additions & 0 deletions pkg/utils/testdata/oas-overlay/bad-action-op/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2024 Google LLC
#
# Licensed 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.
overlay: 1.0.0
info:
title: Missing remove / update field
version: 1.0.0
actions:
- target: $.info.contact
18 changes: 18 additions & 0 deletions pkg/utils/testdata/oas-overlay/bad-actions-value/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2024 Google LLC
#
# Licensed 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.
overlay: 1.0.0
info:
title: The actions value must be an array
version: 1.0.0
actions: "Bad"
20 changes: 20 additions & 0 deletions pkg/utils/testdata/oas-overlay/bad-remove-value/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2024 Google LLC
#
# Licensed 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.
overlay: 1.0.0
info:
title: The remove field only accepts booleans
version: 1.0.0
actions:
- target: $.info.contact
remove: yes
22 changes: 22 additions & 0 deletions pkg/utils/testdata/oas-overlay/bad-update-target/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Google LLC
#
# Licensed 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.
overlay: 1.0.0
info:
title: Target JSONPath is not valid
version: 1.0.0
actions:
- target: $.paths.*.*.parameters[?@.name=='status' && @.in=='query']
update:
schema:
$ref: '#/components/schemas/filterSchema'
833 changes: 833 additions & 0 deletions pkg/utils/testdata/oas-overlay/structured/exp-oas3.yaml

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions pkg/utils/testdata/oas-overlay/structured/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2024 Google LLC
#
# Licensed 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.
overlay: 1.0.0
info:
title: Structured Overlay
version: 1.0.0
actions:
- target: '$' # Root of document
update:
info:
x-overlay-applied: structured-overlay
paths:
'/':
summary: 'The root resource'
get:
summary: 'Retrieve the root resource'
responses:
200:
description: OK
x-rate-limit: 100
'/pet':
get:
summary: 'Retrieve a list of pets'
responses:
200:
description: OK
x-rate-limit: 100

809 changes: 809 additions & 0 deletions pkg/utils/testdata/oas-overlay/targeted/exp-oas3.yaml

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions pkg/utils/testdata/oas-overlay/targeted/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2024 Google LLC
#
# Licensed 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.
overlay: 1.0.0
info:
title: Targeted Overlay
version: 1.0.0
actions:
- target: $.info.contact
remove: true
- target: $.info
update:
description: This is a new description
- target: $.tags[?(@.name=='pet')]
update:
externalDocs:
url: https://example.com/pet
- target: $.paths['/pet/{petId}/uploadImage'].post
update:
description: This is the new description for uploadImage operation
- target: $.paths['/pet/{petId}']
update:
get:
description: This is an updated description of a child object
x-safe: false
828 changes: 828 additions & 0 deletions pkg/utils/testdata/oas-overlay/wildcard/exp-oas3.yaml

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions pkg/utils/testdata/oas-overlay/wildcard/overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2024 Google LLC
#
# Licensed 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.
overlay: 1.0.0
info:
title: Update many objects at once
version: 1.0.0
actions:
- target: $.paths.*.get
update:
x-safe: true
- target: $.components.schemas
update:
filterSchema:
type: string
default: available
enum:
- available
- pending
- sold
- target: $.paths.*.*.parameters[?(@.name=='status' && @.in=='query')].schema
remove: true
- target: $.paths.*.*.parameters[?(@.name=='status' && @.in=='query')]
update:
schema:
$ref: '#/components/schemas/filterSchema'
1,231 changes: 1,231 additions & 0 deletions pkg/utils/testdata/specs/oas3/petstore/oas3.json

Large diffs are not rendered by default.

818 changes: 818 additions & 0 deletions pkg/utils/testdata/specs/oas3/petstore/oas3.yaml

Large diffs are not rendered by default.

0 comments on commit 360e538

Please sign in to comment.