Skip to content

Commit

Permalink
Merge pull request #47 from melvinitcr/multi_media_types
Browse files Browse the repository at this point in the history
Add support for multiple media types inside content component
  • Loading branch information
Reuven Harrison authored Oct 2, 2021
2 parents fde406a + 819a5f3 commit cc53cdd
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 152 deletions.
146 changes: 99 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,46 +63,58 @@ Add the `-format` flag to generate other formats (text or html).
docker run --rm -t tufin/oasdiff -help
```

## Output example - Text
## Output example - Text/Markdown
```./oasdiff -base data/openapi-test1.yaml -revision data/openapi-test5.yaml -format text```

### New Endpoints
-----------------
### New Endpoints: None
-----------------------

### Deleted Endpoints
---------------------
POST /subscribe
POST /register
### Deleted Endpoints: 2
------------------------
POST /register
POST /subscribe

### Modified Endpoints
----------------------
GET /api/{domain}/{project}/badges/security-score
* Modified query param: filter
### Modified Endpoints: 2
-------------------------
GET /api/{domain}/{project}/badges/security-score
- Modified query param: filter
- Content changed
- Schema changed
* Modified query param: image
* Modified header param: user
- Modified media type: application/json
- Schema changed
- Required changed
- Modified query param: image
- Modified query param: token
- Schema changed
- MaxLength changed from 29 to <nil>
- Modified header param: user
- Schema changed
- Schema added
- Content changed
* Modified cookie param: test
- Deleted media type: application/json
- Modified cookie param: test
- Content changed
* Response changed
- Modified media type: application/json
- Schema changed
- Type changed from 'object' to 'string'
- Responses changed
- New response: default
- Deleted response: 200
- Modified response: 201
- Content changed
- Schema changed
- Type changed from 'string' to 'object'
- Modified media type: application/xml
- Schema changed
- Type changed from 'string' to 'object'

GET /api/{domain}/{project}/install-command
* Deleted header param: network-policies
* Response changed
- Deleted header param: network-policies
- Responses changed
- Modified response: default
- Description changed from 'Tufin1' to 'Tufin'
- Headers changed
- Deleted header: X-RateLimit-Limit

## Output example - YAML
```./oasdiff -base data/openapi-test1.yaml -revision data/openapi-test5.yaml -format yaml```

```yaml
info:
Expand Down Expand Up @@ -133,20 +145,37 @@ paths:
cookie:
test:
content:
mediaType: true
mediaTypeModified:
application/json:
schema:
type:
from: object
to: string
header:
user:
schema:
schemaAdded: true
content:
mediaTypeDeleted: true
mediaTypeDeleted:
- application/json
query:
filter:
content:
schema:
required:
added:
- type
mediaTypeModified:
application/json:
schema:
required:
added:
- type
image:
examples:
deleted:
- "0"
token:
schema:
maxLength:
from: 29
to: null
responses:
added:
- default
Expand All @@ -155,10 +184,12 @@ paths:
modified:
"201":
content:
schema:
type:
from: string
to: object
mediaTypeModified:
application/xml:
schema:
type:
from: string
to: object
parameters:
deleted:
path:
Expand All @@ -185,10 +216,10 @@ paths:
- https://www.tufin.io/securecloud
endpoints:
deleted:
- method: POST
path: /subscribe
- method: POST
path: /register
- method: POST
path: /subscribe
modified:
? method: GET
path: /api/{domain}/{project}/badges/security-score
Expand All @@ -200,20 +231,37 @@ endpoints:
cookie:
test:
content:
mediaType: true
mediaTypeModified:
application/json:
schema:
type:
from: object
to: string
header:
user:
schema:
schemaAdded: true
content:
mediaTypeDeleted: true
mediaTypeDeleted:
- application/json
query:
filter:
content:
schema:
required:
added:
- type
mediaTypeModified:
application/json:
schema:
required:
added:
- type
image:
examples:
deleted:
- "0"
token:
schema:
maxLength:
from: 29
to: null
responses:
added:
- default
Expand All @@ -222,10 +270,12 @@ endpoints:
modified:
"201":
content:
schema:
type:
from: string
to: object
mediaTypeModified:
application/xml:
schema:
type:
from: string
to: object
? method: GET
path: /api/{domain}/{project}/install-command
: parameters:
Expand Down Expand Up @@ -284,10 +334,12 @@ components:
to: false
testc:
content:
schema:
type:
from: object
to: string
mediaTypeModified:
application/json:
schema:
type:
from: object
to: string
requestBodies:
deleted:
- reuven
Expand Down
4 changes: 2 additions & 2 deletions data/openapi-test5.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ paths:
- in: cookie
name: test
content:
application/xml:
application/json:
schema:
type: object
type: string
- in: query
name: image
schema:
Expand Down
116 changes: 37 additions & 79 deletions diff/content_diff.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
package diff

import (
"fmt"

"github.com/getkin/kin-openapi/openapi3"
)

// ContentDiff describes the changes between a pair of content objects each containing a media type object: https://swagger.io/specification/#media-type-object
// ContentDiff describes the changes between content properties each containing media type objects: https://swagger.io/specification/#media-type-object
type ContentDiff struct {
MediaTypeAdded bool `json:"mediaTypeAdded,omitempty" yaml:"mediaTypeAdded,omitempty"`
MediaTypeDeleted bool `json:"mediaTypeDeleted,omitempty" yaml:"mediaTypeDeleted,omitempty"`
MediaTypeDiff bool `json:"mediaType,omitempty" yaml:"mediaType,omitempty"`
ExtensionsDiff *ExtensionsDiff `json:"extensions,omitempty" yaml:"extensions,omitempty"`
SchemaDiff *SchemaDiff `json:"schema,omitempty" yaml:"schema,omitempty"`
ExampleDiff *ValueDiff `json:"example,omitempty" yaml:"example,omitempty"`
ExamplesDiff *ExamplesDiff `json:"examples,omitempty" yaml:"examples,omitempty"`
EncodingsDiff *EncodingsDiff `json:"encoding,omitempty" yaml:"encoding,omitempty"`
MediaTypeAdded StringList `json:"mediaTypeAdded,omitempty" yaml:"mediaTypeAdded,omitempty"`
MediaTypeDeleted StringList `json:"mediaTypeDeleted,omitempty" yaml:"mediaTypeDeleted,omitempty"`
MediaTypeModified ModifiedMediaTypes `json:"mediaTypeModified,omitempty" yaml:"mediaTypeModified,omitempty"`
}

// ModifiedMediaTypes is map of media type names to their respective diffs
type ModifiedMediaTypes map[string]*MediaTypeDiff

func newContentDiff() *ContentDiff {
return &ContentDiff{}
return &ContentDiff{
MediaTypeAdded: StringList{},
MediaTypeDeleted: StringList{},
MediaTypeModified: ModifiedMediaTypes{},
}
}

// Empty indicates whether a change was found in this element
func (contentDiff *ContentDiff) Empty() bool {
return contentDiff == nil || *contentDiff == ContentDiff{}
func (diff *ContentDiff) Empty() bool {
if diff == nil {
return true
}

return len(diff.MediaTypeAdded) == 0 &&
len(diff.MediaTypeDeleted) == 0 &&
len(diff.MediaTypeModified) == 0
}

func getContentDiff(config *Config, content1, content2 openapi3.Content) (*ContentDiff, error) {
Expand All @@ -41,76 +47,28 @@ func getContentDiff(config *Config, content1, content2 openapi3.Content) (*Conte

func getContentDiffInternal(config *Config, content1, content2 openapi3.Content) (*ContentDiff, error) {

if len(content1) == 0 && len(content2) == 0 {
return nil, nil
}

result := newContentDiff()

if len(content1) == 0 && len(content2) != 0 {
result.MediaTypeAdded = true
return result, nil
for name1, media1 := range content1 {
if media2, ok := content2[name1]; ok {
diff, err := getMediaTypeDiff(config, media1, media2)
if err != nil {
return nil, err
}

if !diff.Empty() {
result.MediaTypeModified[name1] = diff
}
} else {
result.MediaTypeDeleted = append(result.MediaTypeDeleted, name1)
}
}

if len(content1) != 0 && len(content2) == 0 {
result.MediaTypeDeleted = true
return result, nil
}

mediaType1, mediaTypeValue1, err := getMediaType(content1)
if err != nil {
return nil, err
}

mediaType2, mediaTypeValue2, err := getMediaType(content2)
if err != nil {
return nil, err
}

if mediaTypeValue1 == nil || mediaTypeValue2 == nil {
return nil, fmt.Errorf("media type is nil")
}

if mediaType1 != mediaType2 {
result.MediaTypeDiff = true
return result, nil
}

result.ExtensionsDiff = getExtensionsDiff(config, mediaTypeValue1.ExtensionProps, mediaTypeValue2.ExtensionProps)
result.SchemaDiff, err = getSchemaDiff(config, mediaTypeValue1.Schema, mediaTypeValue2.Schema)
if err != nil {
return nil, err
}

result.ExampleDiff = getValueDiffConditional(config.ExcludeExamples, mediaTypeValue1.Example, mediaTypeValue1.Example)

result.EncodingsDiff, err = getEncodingsDiff(config, mediaTypeValue1.Encoding, mediaTypeValue2.Encoding)
if err != nil {
return nil, err
}

result.ExamplesDiff, err = getExamplesDiff(config, mediaTypeValue1.Examples, mediaTypeValue2.Examples)
if err != nil {
return nil, err
for name2 := range content2 {
if _, ok := content1[name2]; !ok {
result.MediaTypeAdded = append(result.MediaTypeDeleted, name2)
}
}

return result, nil
}

// getMediaType returns the single MediaType entry in the content map
func getMediaType(content openapi3.Content) (string, *openapi3.MediaType, error) {

var mediaType string
var mediaTypeValue *openapi3.MediaType

if len(content) != 1 {
return "", nil, fmt.Errorf("content map has more than one value")
}

for k, v := range content {
mediaType = k
mediaTypeValue = v
}

return mediaType, mediaTypeValue, nil
}
Loading

0 comments on commit cc53cdd

Please sign in to comment.