├── .gitignore ├── fixtures ├── specs │ ├── deeper │ │ ├── stringProp.json │ │ └── arrayProp.json │ ├── resolution2.json │ ├── resolution.json │ └── refed.json ├── local_expansion │ ├── item.json │ └── spec.json └── expansion │ ├── params.json │ ├── missingItemRef.json │ ├── circularSpec.json │ ├── circularRefs.json │ ├── circularSpec.yaml │ ├── invalid-refs.json │ ├── schemas1.json │ ├── schemas2.json │ ├── overflow.json │ ├── missingRef.json │ └── all-the-things.json ├── license.go ├── contact.go ├── reference.go ├── external_documentation.go ├── xml.go ├── tag.go ├── go.mod ├── doc.go ├── info.go ├── discriminator.go ├── .editorconfig ├── open_api.go ├── oauth_flows.go ├── path_item.go ├── .travis.yml ├── operation.go ├── components.go ├── header.go ├── schema.go ├── example.go ├── request_body.go ├── media_type.go ├── encoding.go ├── paths.go ├── server.go ├── link.go ├── parameter.go ├── security_requirement.go ├── security_scheme.go ├── license_easyjson.go ├── external_documentation_easyjson.go ├── contact_easyjson.go ├── CODE_OF_CONDUCT.md ├── tag_easyjson.go ├── callback.go ├── responses.go ├── go.sum ├── info_easyjson.go ├── extensions.go ├── README.md ├── .github └── CONTRIBUTING.md ├── link_test.go ├── example_test.go ├── header_test.go ├── schema_test.go ├── media_type_test.go ├── parameter_test.go ├── encoding_test.go ├── server_test.go ├── request_body_test.go ├── security_scheme_test.go ├── orderedmap.go ├── server_easyjson.go └── responses_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | secrets.yml 2 | coverage.out 3 | .idea 4 | .vscode 5 | -------------------------------------------------------------------------------- /fixtures/specs/deeper/stringProp.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "string" 3 | } -------------------------------------------------------------------------------- /fixtures/specs/deeper/arrayProp.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"array", 3 | "items": { 4 | "type": "string" 5 | } 6 | } -------------------------------------------------------------------------------- /fixtures/specs/resolution2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://localhost:1234", 3 | "items": { 4 | "id": "deeper/", 5 | "items": { 6 | "$ref": "arrayProp.json#/items" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /license.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // License contains information for the exposed API. 4 | // easyjson:json 5 | type License struct { 6 | VendorExtensible 7 | 8 | Name string 9 | URL string 10 | } 11 | -------------------------------------------------------------------------------- /contact.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Contact contains information for the exposed API. 4 | //easyjson:json 5 | type Contact struct { 6 | VendorExtensible 7 | 8 | Name string 9 | URL string 10 | Email string 11 | } 12 | -------------------------------------------------------------------------------- /reference.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import "github.com/go-openapi/jsonreference" 4 | 5 | // Reference is a simple object to allow referencing other components in the specification, internally and externally. 6 | type Reference struct { 7 | Ref jsonreference.Ref 8 | } 9 | -------------------------------------------------------------------------------- /external_documentation.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // ExternalDocumentation allows referencing an external resource for extended documentation. 4 | // easyjson:json 5 | type ExternalDocumentation struct { 6 | VendorExtensible 7 | 8 | Description string 9 | URL string 10 | } 11 | -------------------------------------------------------------------------------- /fixtures/specs/resolution.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "http://localhost:1234", 3 | "items": { 4 | "id": "deeper/", 5 | "items": { 6 | "$ref": "stringProp.json" 7 | } 8 | }, 9 | "definitions": { 10 | "bool": { 11 | "$ref": "boolProp.json" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /xml.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // XML a metadata object that allows for more fine-tuned XML model definitions. 4 | type XML struct { 5 | VendorExtensible 6 | 7 | Name string `json:"name"` 8 | Namespace string `json:"namespace"` 9 | Prefix string `json:"prefix"` 10 | Attribute bool `json:"attribute"` 11 | Wrapped bool `json:"wrapped"` 12 | } 13 | -------------------------------------------------------------------------------- /fixtures/local_expansion/item.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "id": { 4 | "format": "int64", 5 | "readOnly": true, 6 | "type": "integer" 7 | }, 8 | "title": { 9 | "maxLength": 80, 10 | "minLength": 2, 11 | "type": "string" 12 | } 13 | }, 14 | "required": [ 15 | "title" 16 | ], 17 | "type": "object" 18 | } 19 | -------------------------------------------------------------------------------- /tag.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Tag adds metadata to a single tag that is used by the Operation Object. 4 | // It is not mandatory to have a Tag Object per tag defined in the Operation Object instances. 5 | // easyjson:json 6 | type Tag struct { 7 | VendorExtensible 8 | 9 | Name string 10 | Description string 11 | ExternalDocs *ExternalDocumentation 12 | } 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-openapi/spec3 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/brianvoe/gofakeit v3.18.0+incompatible 7 | github.com/go-openapi/jsonreference v0.19.3 8 | github.com/go-openapi/swag v0.19.7 // indirect 9 | github.com/mailru/easyjson v0.7.1 10 | github.com/stretchr/testify v1.5.0 11 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect 12 | gopkg.in/yaml.v2 v2.2.8 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | //go:generate easyjson -all -omit_empty -lower_camel_case contact.go 4 | //go:generate easyjson -all -omit_empty -lower_camel_case license.go 5 | //go:generate easyjson -all -omit_empty -lower_camel_case info.go 6 | //go:generate easyjson -all -omit_empty -lower_camel_case server.go 7 | //go:generate easyjson -all -omit_empty -lower_camel_case external_documentation.go 8 | //go:generate easyjson -all -omit_empty -lower_camel_case tag.go 9 | -------------------------------------------------------------------------------- /info.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Info provides metadata about the API. 4 | // The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience. 5 | // easyjson:json 6 | type Info struct { 7 | VendorExtensible 8 | 9 | Title string 10 | Description string 11 | TermsOfService string 12 | Contact *Contact 13 | License *License 14 | Version string 15 | } 16 | -------------------------------------------------------------------------------- /fixtures/expansion/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "parameters": { 3 | "id": { 4 | "type": "integer", 5 | "format": "int64", 6 | "in": "path", 7 | "required": true 8 | }, 9 | "tag": { 10 | "type": "string", 11 | "in": "query", 12 | "required": false 13 | }, 14 | "query": { 15 | "$ref": "#/parameters/tag" 16 | } 17 | }, 18 | "paths": { 19 | "/cars/{id}": { 20 | "parameters": [ 21 | { "$ref": "#/parameters/id"} 22 | ] 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /discriminator.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Discriminator When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be used to aid in serialization, deserialization, and validation. 4 | // The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. 5 | type Discriminator struct { 6 | PropertyName string `json:"propertyName"` 7 | Mapping OrderedStrings `json:"mapping"` 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | trim_trailing_whitespace = true 11 | 12 | # Set default charset 13 | [*.{js,py,go,scala,rb,java,html,css,less,sass,md}] 14 | charset = utf-8 15 | 16 | # Tab indentation (no size specified) 17 | [*.go] 18 | indent_style = tab 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | # Matches the exact files either package.json or .travis.yml 24 | [{package.json,.travis.yml}] 25 | indent_style = space 26 | indent_size = 2 27 | -------------------------------------------------------------------------------- /open_api.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // OpenAPI is the root document object of the OpenAPI document 4 | type OpenAPI struct { 5 | VendorExtensible 6 | 7 | Components *Components `json:"components,omitempty"` 8 | ExternalDocs []ExternalDocumentation `json:"externalDocs,omitempty"` 9 | Info *Info `json:"info,omitempty"` 10 | OpenAPI string `json:"openapi"` 11 | Paths *PathItem `json:"paths"` 12 | Security []SecurityRequirement `json:"security,omitempty"` 13 | Servers []Server `json:"servers,omitempty"` 14 | Tags []Tag `json:"tags,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /fixtures/expansion/missingItemRef.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "2.1.0", 5 | "title": "Missing Item API" 6 | }, 7 | "host": "item.com", 8 | "basePath": "/missing/ref", 9 | "schemes": [ 10 | "http" 11 | ], 12 | "paths": { 13 | "/employees": { 14 | "get": { 15 | "operationId": "LIST-Employees", 16 | "summary": "List Employee Types", 17 | "responses": { 18 | "200": { 19 | "description": "", 20 | "schema": { 21 | "type": "array", 22 | "items": { 23 | "$ref": "#/definitions/employees-output" 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /oauth_flows.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // OAuthFlows allows configuration of the supported OAuth Flows. 4 | type OAuthFlows struct { 5 | VendorExtensible 6 | 7 | Implicit OAuthFlow `json:"implicit,omitempty"` 8 | Password OAuthFlow `json:"password,omitempty"` 9 | ClientCredentials OAuthFlow `json:"clientCredentials,omitempty"` 10 | AuthorizationCode OAuthFlow `json:"authorizationCode,omitempty"` 11 | } 12 | 13 | // OAuthFlow configuration details for a supported OAuth Flow 14 | type OAuthFlow struct { 15 | VendorExtensible 16 | 17 | AuthorizationURL string `json:"authorizationUrl,omitempty"` 18 | TokenURL string `json:"tokenUrl,omitempty"` 19 | RefreshURL string `json:"refreshUrl,omitempty"` 20 | Scopes OrderedStrings `json:"scopes,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /path_item.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // PathItem describes the operations available on a single path. 4 | // A Path Item MAY be empty, due to ACL constraints. 5 | // The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available. 6 | type PathItem struct { 7 | Reference 8 | 9 | Summary string `json:"summary"` 10 | Description string `json:"description"` 11 | Get Operation `json:"get"` 12 | Put Operation `json:"put"` 13 | Post Operation `json:"post"` 14 | Delete Operation `json:"delete"` 15 | Options Operation `json:"options"` 16 | Head Operation `json:"head"` 17 | Patch Operation `json:"patch"` 18 | Trace Operation `json:"trace"` 19 | Servers []Server `json:"servers"` 20 | Parameters []Parameter `json:"parameters"` 21 | } 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.13.x 4 | - 1.14.x 5 | script: 6 | - go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./... 7 | after_success: 8 | - bash <(curl -s https://codecov.io/bash) 9 | notifications: 10 | slack: 11 | secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E= 12 | -------------------------------------------------------------------------------- /operation.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Operation describes a single API operation on a path. 4 | type Operation struct { 5 | VendorExtensible 6 | 7 | Tags []string `json:"tags,omitempty"` 8 | Summary string `json:"summary,omitempty"` 9 | Description string `json:"description,omitempty"` 10 | ExternalDocs ExternalDocumentation `json:"externalDocs,omitempty"` 11 | OperationID string `json:"operationId,omitempty"` 12 | Parameters []Parameter `json:"parameters,omitempty"` 13 | RequestBody RequestBody `json:"requestBody,omitempty"` 14 | Responses OrderedResponses `json:"responses,omitempty"` 15 | Callbacks map[string]Callback `json:"callbacks,omitempty"` 16 | Deprecated bool `json:"deprecated,omitempty"` 17 | Security []SecurityRequirement `json:"security,omitempty"` 18 | Servers []Server `json:"servers,omitempty"` 19 | } 20 | -------------------------------------------------------------------------------- /fixtures/local_expansion/spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "basePath": "/v1", 3 | "consumes": [ 4 | "application/json" 5 | ], 6 | "host": "item.api.local", 7 | "info": { 8 | "description": "Item API", 9 | "title": "Item API", 10 | "version": "1.0.0" 11 | }, 12 | "paths": { 13 | "/item": { 14 | "get": { 15 | "operationId": "GetItem", 16 | "responses": { 17 | "200": { 18 | "description": "item detail response", 19 | "schema": { 20 | "$ref": "item.json" 21 | } 22 | } 23 | } 24 | } 25 | } 26 | }, 27 | "produces": [ 28 | "application/json" 29 | ], 30 | "schemes": [ 31 | "http" 32 | ], 33 | "security": [ 34 | { 35 | "key": [] 36 | } 37 | ], 38 | "securityDefinitions": { 39 | "key": { 40 | "in": "header", 41 | "name": "x-item-token", 42 | "type": "apiKey" 43 | } 44 | }, 45 | "swagger": "2.0" 46 | } 47 | -------------------------------------------------------------------------------- /components.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Components holds a set of reusable objects for different aspects of the OAS. 4 | // All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object. 5 | type Components struct { 6 | VendorExtensible 7 | 8 | Schemas OrderedSchemas `json:"schemas,omitempty"` 9 | Responses OrderedResponses `json:"responses,omitempty"` 10 | Parameters OrderedParameters `json:"parameters,omitempty"` 11 | Examples OrderedExamples `json:"examples,omitempty"` 12 | RequestBodies OrderedRequestBodies `json:"requestBodies,omitempty"` 13 | Headers OrderedHeaders `json:"headers,omitempty"` 14 | SecuritySchemes OrderedSecuritySchemes `json:"securitySchemes,omitempty"` 15 | Links OrderedLinks `json:"links,omitempty"` 16 | Callbacks OrderedCallbacks `json:"callbacks,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /fixtures/expansion/circularSpec.json: -------------------------------------------------------------------------------- 1 | {"swagger":"2.0","info":{"title":"Swagger Sample","description":"Sample API Playground.","version":"1.0.0"},"basePath":"/v1","schemes":["http"],"consumes":["application/vdn.sample.v1+json"],"produces":["application/vdn.sample.v1+json"],"paths":{"/books":{"get":{"summary":"List all books","operationId":"listBooks","tags":["books"],"responses":{"200":{"headers":{"Link":{"type":"string"}},"description":"An array of books","schema":{"type":"array","items":{"$ref":"#/definitions/Book"}}},"default":{"description":"generic error response","schema":{"$ref":"#/definitions/Error"}}}}}},"definitions":{"Book":{"type":"object","required":["title","summary"],"properties":{"title":{"type":"string","example":"Winnie the Pooh"},"summary":{"type":"string","example":"Famous children's book"},"related_books":{"type":"array","items":{"$ref":"#/definitions/Book"}}}},"Error":{"type":"object","readOnly":true,"properties":{"code":{"type":"integer","format":"int64","example":400},"message":{"type":"string","example":"Unexpected error"}},"required":["message"]}}} 2 | -------------------------------------------------------------------------------- /fixtures/expansion/circularRefs.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "brand": { 4 | "type": "object", 5 | "properties": { 6 | "name": { 7 | "type": "string" 8 | } 9 | } 10 | }, 11 | "category": { 12 | "type": "object", 13 | "properties": { 14 | "children": { 15 | "type": "array", 16 | "items": { 17 | "$ref": "#/definitions/category" 18 | } 19 | } 20 | } 21 | }, 22 | "car": { 23 | "type": "object", 24 | "properties": { 25 | "id": { 26 | "type": "integer", 27 | "format": "int64" 28 | }, 29 | "make": { 30 | "type": "string" 31 | }, 32 | "similar": { 33 | "items": { 34 | "$ref": "#/definitions/car" 35 | } 36 | }, 37 | "notSimilar": { 38 | "additionalProperties": { 39 | "$ref": "#/definitions/car" 40 | } 41 | }, 42 | "oneCar": { 43 | "$ref": "#/definitions/car" 44 | }, 45 | "category": { 46 | "$ref": "#/definitions/category" 47 | }, 48 | "brand": { 49 | "$ref": "#/definitions/brand" 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /fixtures/expansion/circularSpec.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | swagger: "2.0" 3 | info: 4 | title: Swagger Sample 5 | description: Sample API Playground. 6 | version: 1.0.0 7 | basePath: /v1 8 | schemes: 9 | - http 10 | consumes: 11 | - application/vdn.sample.v1+json 12 | produces: 13 | - application/vdn.sample.v1+json 14 | 15 | paths: 16 | /books: 17 | get: 18 | summary: List all books 19 | operationId: listBooks 20 | tags: 21 | - books 22 | responses: 23 | 200: 24 | headers: 25 | Link: 26 | type: string 27 | description: An array of books 28 | schema: 29 | type: array 30 | items: 31 | $ref: "#/definitions/Book" 32 | default: 33 | description: generic error response 34 | schema: 35 | $ref: "#/definitions/Error" 36 | 37 | definitions: 38 | Book: 39 | type: object 40 | required: 41 | - title 42 | - summary 43 | properties: 44 | title: 45 | type: string 46 | example: Winnie the Pooh 47 | summary: 48 | type: string 49 | example: Famous children's book 50 | related_books: 51 | type: array 52 | items: 53 | $ref: "#/definitions/Book" 54 | 55 | Error: 56 | type: object 57 | readOnly: true 58 | properties: 59 | code: 60 | type: integer 61 | format: int64 62 | example: 400 63 | message: 64 | type: string 65 | example: Unexpected error 66 | required: 67 | - message 68 | -------------------------------------------------------------------------------- /header.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Header follows the structure of the Parameter Object 4 | type Header struct { 5 | Parameter 6 | } 7 | 8 | // OrderedHeaders is a map between a variable name and its value. The value is used for substitution in the server's URL template. 9 | type OrderedHeaders struct { 10 | data OrderedMap 11 | } 12 | 13 | // NewOrderedHeaders creates a new instance of OrderedHeaders with correct filter 14 | func NewOrderedHeaders() OrderedHeaders { 15 | return OrderedHeaders{ 16 | data: OrderedMap{ 17 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 18 | }, 19 | } 20 | } 21 | 22 | // Get gets the security requirement by key 23 | func (s *OrderedHeaders) Get(key string) *Header { 24 | v := s.data.Get(key) 25 | if v == nil { 26 | return nil 27 | } 28 | return v.(*Header) 29 | } 30 | 31 | // GetOK checks if the key exists in the security requirement 32 | func (s *OrderedHeaders) GetOK(key string) (*Header, bool) { 33 | v, ok := s.data.GetOK(key) 34 | if !ok { 35 | return nil, ok 36 | } 37 | 38 | sr, ok := v.(*Header) 39 | return sr, ok 40 | } 41 | 42 | // Set sets the value to the security requirement 43 | func (s *OrderedHeaders) Set(key string, val *Header) bool { 44 | return s.data.Set(key, val) 45 | } 46 | 47 | // ForEach executes the function for each security requirement 48 | func (s *OrderedHeaders) ForEach(fn func(string, *Header) error) error { 49 | s.data.ForEach(func(key string, val interface{}) error { 50 | response, _ := val.(*Header) 51 | if err := fn(key, response); err != nil { 52 | return err 53 | } 54 | return nil 55 | }) 56 | return nil 57 | } 58 | 59 | // Keys gets the list of keys 60 | func (s *OrderedHeaders) Keys() []string { 61 | return s.data.Keys() 62 | } 63 | 64 | // TODO: (s *OrderedHeaders) Implement Marshal & Unmarshal -> JSON, YAML 65 | -------------------------------------------------------------------------------- /schema.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Schema object allows the definition of input and output data types. These types can be objects, but also primitives and arrays. This object is an extended subset of the JSON Schema Specification Wright Draft 00. 4 | type Schema struct { 5 | VendorExtensible 6 | Reference 7 | } 8 | 9 | // OrderedSchemas is a map between a variable name and its value. The value is used for substitution in the server's URL template. 10 | type OrderedSchemas struct { 11 | data OrderedMap 12 | } 13 | 14 | // NewOrderedSchemas creates a new instance of OrderedSchemas with correct filter 15 | func NewOrderedSchemas() OrderedSchemas { 16 | return OrderedSchemas{ 17 | data: OrderedMap{ 18 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 19 | }, 20 | } 21 | } 22 | 23 | // Get gets the security requirement by key 24 | func (s *OrderedSchemas) Get(key string) *Schema { 25 | v := s.data.Get(key) 26 | if v == nil { 27 | return nil 28 | } 29 | return v.(*Schema) 30 | } 31 | 32 | // GetOK checks if the key exists in the security requirement 33 | func (s *OrderedSchemas) GetOK(key string) (*Schema, bool) { 34 | v, ok := s.data.GetOK(key) 35 | if !ok { 36 | return nil, ok 37 | } 38 | 39 | sr, ok := v.(*Schema) 40 | return sr, ok 41 | } 42 | 43 | // Set sets the value to the security requirement 44 | func (s *OrderedSchemas) Set(key string, val *Schema) bool { 45 | return s.data.Set(key, val) 46 | } 47 | 48 | // ForEach executes the function for each security requirement 49 | func (s *OrderedSchemas) ForEach(fn func(string, *Schema) error) error { 50 | s.data.ForEach(func(key string, val interface{}) error { 51 | response, _ := val.(*Schema) 52 | if err := fn(key, response); err != nil { 53 | return err 54 | } 55 | return nil 56 | }) 57 | return nil 58 | } 59 | 60 | // Keys gets the list of keys 61 | func (s *OrderedSchemas) Keys() []string { 62 | return s.data.Keys() 63 | } 64 | 65 | // TODO: (s *OrderedSchemas) Implement Marshal & Unmarshal -> JSON, YAML 66 | -------------------------------------------------------------------------------- /example.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Example 4 | type Example struct { 5 | VendorExtensible 6 | Reference 7 | 8 | Summary string `json:"summary"` 9 | Description string `json:"description"` 10 | Value interface{} `json:"value"` 11 | ExternalValue string `json:"externalValue"` 12 | } 13 | 14 | // OrderedExamples is a map between a variable name and its value. The value is used for substitution in the server's URL template. 15 | type OrderedExamples struct { 16 | data OrderedMap 17 | } 18 | 19 | // NewOrderedExamples creates a new instance of OrderedExamples with correct filter 20 | func NewOrderedExamples() OrderedExamples { 21 | return OrderedExamples{ 22 | data: OrderedMap{ 23 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 24 | }, 25 | } 26 | } 27 | 28 | // Get gets the security requirement by key 29 | func (s *OrderedExamples) Get(key string) *Example { 30 | v := s.data.Get(key) 31 | if v == nil { 32 | return nil 33 | } 34 | return v.(*Example) 35 | } 36 | 37 | // GetOK checks if the key exists in the security requirement 38 | func (s *OrderedExamples) GetOK(key string) (*Example, bool) { 39 | v, ok := s.data.GetOK(key) 40 | if !ok { 41 | return nil, ok 42 | } 43 | 44 | sr, ok := v.(*Example) 45 | return sr, ok 46 | } 47 | 48 | // Set sets the value to the security requirement 49 | func (s *OrderedExamples) Set(key string, val *Example) bool { 50 | return s.data.Set(key, val) 51 | } 52 | 53 | // ForEach executes the function for each security requirement 54 | func (s *OrderedExamples) ForEach(fn func(string, *Example) error) error { 55 | s.data.ForEach(func(key string, val interface{}) error { 56 | response, _ := val.(*Example) 57 | if err := fn(key, response); err != nil { 58 | return err 59 | } 60 | return nil 61 | }) 62 | return nil 63 | } 64 | 65 | // Keys gets the list of keys 66 | func (s *OrderedExamples) Keys() []string { 67 | return s.data.Keys() 68 | } 69 | 70 | // TODO: (s *OrderedExamples) Implement Marshal & Unmarshal -> JSON, YAML 71 | -------------------------------------------------------------------------------- /fixtures/expansion/invalid-refs.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "contact": { 7 | "name": "wordnik api team", 8 | "url": "http://developer.wordnik.com" 9 | }, 10 | "license": { 11 | "name": "Creative Commons 4.0 International", 12 | "url": "http://creativecommons.org/licenses/by/4.0/" 13 | } 14 | }, 15 | "host": "petstore.swagger.wordnik.com", 16 | "basePath": "/api", 17 | "schemes": [ 18 | "http" 19 | ], 20 | "paths": { 21 | "/pets": { 22 | "get": { 23 | "tags": [ "Pet Operations" ], 24 | "summary": "finds pets in the system", 25 | "responses": { 26 | "200": { 27 | "description": "pet response", 28 | "schema": { 29 | "type": "array", 30 | "items": { 31 | "$ref": "NotCorrectRef" 32 | } 33 | }, 34 | "headers": { 35 | "x-expires": { 36 | "type": "string" 37 | } 38 | } 39 | }, 40 | "default": { 41 | "description": "unexpected error", 42 | "schema": { 43 | "$ref": "NotCorrectRef" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | }, 50 | "definitions": { 51 | "Pet": { 52 | "required": [ 53 | "id", 54 | "name" 55 | ], 56 | "properties": { 57 | "id": { 58 | "type": "integer", 59 | "format": "int64" 60 | }, 61 | "name": { 62 | "type": "string" 63 | }, 64 | "tag": { 65 | "type": "string" 66 | } 67 | } 68 | }, 69 | "Error": { 70 | "required": [ 71 | "code", 72 | "message" 73 | ], 74 | "properties": { 75 | "code": { 76 | "type": "integer", 77 | "format": "int32" 78 | }, 79 | "message": { 80 | "type": "string" 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /request_body.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // RequestBody describes a single request body. 4 | type RequestBody struct { 5 | VendorExtensible 6 | Reference 7 | 8 | Description string `json:"description,omitempty"` 9 | Content string `json:"content,omitempty"` 10 | Required string `json:"required,omitempty"` 11 | } 12 | 13 | // OrderedRequestBodies is a map between a variable name and its value. The value is used for substitution in the server's URL template. 14 | type OrderedRequestBodies struct { 15 | data OrderedMap 16 | } 17 | 18 | // NewOrderedRequestBodies creates a new instance of OrderedRequestBodies with correct filter 19 | func NewOrderedRequestBodies() OrderedRequestBodies { 20 | return OrderedRequestBodies{ 21 | data: OrderedMap{ 22 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 23 | }, 24 | } 25 | } 26 | 27 | // Get gets the security requirement by key 28 | func (s *OrderedRequestBodies) Get(key string) *RequestBody { 29 | v := s.data.Get(key) 30 | if v == nil { 31 | return nil 32 | } 33 | return v.(*RequestBody) 34 | } 35 | 36 | // GetOK checks if the key exists in the security requirement 37 | func (s *OrderedRequestBodies) GetOK(key string) (*RequestBody, bool) { 38 | v, ok := s.data.GetOK(key) 39 | if !ok { 40 | return nil, ok 41 | } 42 | 43 | sr, ok := v.(*RequestBody) 44 | return sr, ok 45 | } 46 | 47 | // Set sets the value to the security requirement 48 | func (s *OrderedRequestBodies) Set(key string, val *RequestBody) bool { 49 | return s.data.Set(key, val) 50 | } 51 | 52 | // ForEach executes the function for each security requirement 53 | func (s *OrderedRequestBodies) ForEach(fn func(string, *RequestBody) error) error { 54 | s.data.ForEach(func(key string, val interface{}) error { 55 | response, _ := val.(*RequestBody) 56 | if err := fn(key, response); err != nil { 57 | return err 58 | } 59 | return nil 60 | }) 61 | return nil 62 | } 63 | 64 | // Keys gets the list of keys 65 | func (s *OrderedRequestBodies) Keys() []string { 66 | return s.data.Keys() 67 | } 68 | 69 | // TODO: (s *OrderedRequestBodies) Implement Marshal & Unmarshal -> JSON, YAML 70 | -------------------------------------------------------------------------------- /media_type.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // MediaType provides schema and examples for the media type identified by its key. 4 | type MediaType struct { 5 | VendorExtensible 6 | 7 | Schema Schema `json:"schema,omitempty"` 8 | Example interface{} `json:"example,omitempty"` 9 | Examples OrderedExamples `json:"examples,omitempty"` 10 | Encoding OrderedEncodings `json:"encoding,omitempty"` 11 | } 12 | 13 | // OrderedMediaTypes is a map between a variable name and its value. The value is used for substitution in the server's URL template. 14 | type OrderedMediaTypes struct { 15 | data OrderedMap 16 | } 17 | 18 | // NewOrderedMediaTypes creates a new instance of OrderedMediaTypes with correct filter 19 | func NewOrderedMediaTypes() OrderedMediaTypes { 20 | return OrderedMediaTypes{ 21 | data: OrderedMap{ 22 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 23 | }, 24 | } 25 | } 26 | 27 | // Get gets the security requirement by key 28 | func (s *OrderedMediaTypes) Get(key string) *MediaType { 29 | v := s.data.Get(key) 30 | if v == nil { 31 | return nil 32 | } 33 | return v.(*MediaType) 34 | } 35 | 36 | // GetOK checks if the key exists in the security requirement 37 | func (s *OrderedMediaTypes) GetOK(key string) (*MediaType, bool) { 38 | v, ok := s.data.GetOK(key) 39 | if !ok { 40 | return nil, ok 41 | } 42 | 43 | sr, ok := v.(*MediaType) 44 | return sr, ok 45 | } 46 | 47 | // Set sets the value to the security requirement 48 | func (s *OrderedMediaTypes) Set(key string, val *MediaType) bool { 49 | return s.data.Set(key, val) 50 | } 51 | 52 | // ForEach executes the function for each security requirement 53 | func (s *OrderedMediaTypes) ForEach(fn func(string, *MediaType) error) error { 54 | s.data.ForEach(func(key string, val interface{}) error { 55 | response, _ := val.(*MediaType) 56 | if err := fn(key, response); err != nil { 57 | return err 58 | } 59 | return nil 60 | }) 61 | return nil 62 | } 63 | 64 | // Keys gets the list of keys 65 | func (s *OrderedMediaTypes) Keys() []string { 66 | return s.data.Keys() 67 | } 68 | 69 | // TODO: (s *OrderedMediaTypes) Implement Marshal & Unmarshal -> JSON, YAML 70 | -------------------------------------------------------------------------------- /encoding.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Encoding definition applied to a single schema property. 4 | type Encoding struct { 5 | VendorExtensible 6 | 7 | ContentType string `json:"contentType,omitempty"` 8 | Headers OrderedHeaders `json:"headers,omitempty"` 9 | Style string `json:"style,omitempty"` 10 | Explode bool `json:"explode,omitempty"` 11 | AllowReserved bool `json:"allowReserved,omitempty"` 12 | } 13 | 14 | // OrderedEncodings is a map between a variable name and its value. The value is used for substitution in the server's URL template. 15 | type OrderedEncodings struct { 16 | data OrderedMap 17 | } 18 | 19 | // NewOrderedEncodings creates a new instance of OrderedEncodings with correct filter 20 | func NewOrderedEncodings() OrderedEncodings { 21 | return OrderedEncodings{ 22 | data: OrderedMap{ 23 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 24 | }, 25 | } 26 | } 27 | 28 | // Get gets the security requirement by key 29 | func (s *OrderedEncodings) Get(key string) *Encoding { 30 | v := s.data.Get(key) 31 | if v == nil { 32 | return nil 33 | } 34 | return v.(*Encoding) 35 | } 36 | 37 | // GetOK checks if the key exists in the security requirement 38 | func (s *OrderedEncodings) GetOK(key string) (*Encoding, bool) { 39 | v, ok := s.data.GetOK(key) 40 | if !ok { 41 | return nil, ok 42 | } 43 | 44 | sr, ok := v.(*Encoding) 45 | return sr, ok 46 | } 47 | 48 | // Set sets the value to the security requirement 49 | func (s *OrderedEncodings) Set(key string, val *Encoding) bool { 50 | return s.data.Set(key, val) 51 | } 52 | 53 | // ForEach executes the function for each security requirement 54 | func (s *OrderedEncodings) ForEach(fn func(string, *Encoding) error) error { 55 | s.data.ForEach(func(key string, val interface{}) error { 56 | response, _ := val.(*Encoding) 57 | if err := fn(key, response); err != nil { 58 | return err 59 | } 60 | return nil 61 | }) 62 | return nil 63 | } 64 | 65 | // Keys gets the list of keys 66 | func (s *OrderedEncodings) Keys() []string { 67 | return s.data.Keys() 68 | } 69 | 70 | // TODO: (s *OrderedEncodings) Implement Marshal & Unmarshal -> JSON, YAML 71 | -------------------------------------------------------------------------------- /paths.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "fmt" 5 | 6 | jlexer "github.com/mailru/easyjson/jlexer" 7 | jwriter "github.com/mailru/easyjson/jwriter" 8 | ) 9 | 10 | // Paths holds the relative paths to the individual endpoints and their operations. 11 | // The path is appended to the URL from the Server Object in order to construct the full URL. 12 | // The Paths MAY be empty, due to ACL constraints. 13 | type Paths struct { 14 | data OrderedMap 15 | } 16 | 17 | func (p *Paths) ForEach(fn func(string, []string) error) error { 18 | for _, k := range p.data.Keys() { 19 | scopes, ok := p.data.Get(k).([]string) 20 | if !ok { 21 | return fmt.Errorf("security requirement scopes not a []string but %T", p.data.Get(k)) 22 | } 23 | if err := fn(k, scopes); err != nil { 24 | return err 25 | } 26 | } 27 | return nil 28 | } 29 | 30 | // Keys gets list of all the keys 31 | func (p *Paths) Keys() []string { 32 | return p.data.Keys() 33 | } 34 | 35 | // MarshalJSON supports json.Marshaler interface 36 | func (p Paths) MarshalJSON() ([]byte, error) { 37 | w := jwriter.Writer{} 38 | encodeSortedMap(&w, p.data) 39 | return w.Buffer.BuildBytes(), w.Error 40 | } 41 | 42 | // MarshalEasyJSON supports easyjson.Marshaler interface 43 | func (p Paths) MarshalEasyJSON(w *jwriter.Writer) { 44 | encodeSortedMap(w, p.data) 45 | } 46 | 47 | // UnmarshalJSON supports json.Unmarshaler interface 48 | func (p *Paths) UnmarshalJSON(data []byte) error { 49 | r := jlexer.Lexer{Data: data} 50 | decodeSortedMap(&r, &p.data) 51 | return r.Error() 52 | } 53 | 54 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 55 | func (p *Paths) UnmarshalEasyJSON(l *jlexer.Lexer) { 56 | decodeSortedMap(l, &p.data) 57 | } 58 | 59 | func (p *Paths) Get(path string) *PathItem { 60 | v, ok := p.data.GetOK(path) 61 | if !ok { 62 | return nil 63 | } 64 | 65 | pi, ok := v.(*PathItem) 66 | if !ok { 67 | return nil 68 | } 69 | return pi 70 | } 71 | 72 | func (p *Paths) GetOK(path string) (*PathItem, bool) { 73 | v, ok := p.data.GetOK(path) 74 | if !ok { 75 | return nil, false 76 | } 77 | 78 | pi, ok := v.(*PathItem) 79 | return pi, ok 80 | } 81 | 82 | func (p *Paths) Set(path string, item *PathItem) bool { 83 | return p.data.Set(path, item) 84 | } 85 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Server object representing a Server. 4 | // easyjson:json 5 | type Server struct { 6 | VendorExtensible 7 | 8 | URL string 9 | Description string 10 | Variables ServerVariables 11 | } 12 | 13 | // ServerVariable object representing a Server Variable for server URL template substitution. 14 | type ServerVariable struct { 15 | VendorExtensible 16 | 17 | Enum []string 18 | Default string 19 | Description string 20 | } 21 | 22 | // ServerVariables is a map between a variable name and its value. The value is used for substitution in the server's URL template. 23 | type ServerVariables struct { 24 | data OrderedMap 25 | } 26 | 27 | // NewServerVariables creates a new instance of ServerVariables with correct filter 28 | func NewServerVariables() ServerVariables { 29 | return ServerVariables{ 30 | data: OrderedMap{ 31 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 32 | }, 33 | } 34 | } 35 | 36 | // Get gets the security requirement by key 37 | func (s *ServerVariables) Get(key string) *ServerVariable { 38 | v := s.data.Get(key) 39 | if v == nil { 40 | return nil 41 | } 42 | return v.(*ServerVariable) 43 | } 44 | 45 | // GetOK checks if the key exists in the security requirement 46 | func (s *ServerVariables) GetOK(key string) (*ServerVariable, bool) { 47 | v, ok := s.data.GetOK(key) 48 | if !ok { 49 | return nil, ok 50 | } 51 | 52 | sr, ok := v.(*ServerVariable) 53 | return sr, ok 54 | } 55 | 56 | // Set sets the value to the security requirement 57 | func (s *ServerVariables) Set(key string, val *ServerVariable) bool { 58 | return s.data.Set(key, val) 59 | } 60 | 61 | // ForEach executes the function for each security requirement 62 | func (s *ServerVariables) ForEach(fn func(string, *ServerVariable) error) error { 63 | s.data.ForEach(func(key string, val interface{}) error { 64 | response, _ := val.(*ServerVariable) 65 | if err := fn(key, response); err != nil { 66 | return err 67 | } 68 | return nil 69 | }) 70 | return nil 71 | } 72 | 73 | // Keys gets the list of keys 74 | func (s *ServerVariables) Keys() []string { 75 | return s.data.Keys() 76 | } 77 | 78 | // TODO: (s *ServerVariables) Implement Marshal & Unmarshal -> JSON, YAML 79 | -------------------------------------------------------------------------------- /link.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Link represents a possible design-time link for a response. 4 | // The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. 5 | type Link struct { 6 | VendorExtensible 7 | Reference 8 | 9 | OperationRef string `json:"operationRef,omitempty"` 10 | OperationID string `json:"operationId,omitempty"` 11 | Parameters map[string]interface{} `json:"parameters,omitempty"` 12 | RequestBody interface{} `json:"requestBody,omitempty"` 13 | Description string `json:"description,omitempty"` 14 | Server Server `json:"server,omitempty"` 15 | } 16 | 17 | // OrderedLinks is a map between a variable name and its value. The value is used for substitution in the server's URL template. 18 | type OrderedLinks struct { 19 | data OrderedMap 20 | } 21 | 22 | // NewOrderedLinks creates a new instance of OrderedLinks with correct filter 23 | func NewOrderedLinks() OrderedLinks { 24 | return OrderedLinks{ 25 | data: OrderedMap{ 26 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 27 | }, 28 | } 29 | } 30 | 31 | // Get gets the security requirement by key 32 | func (s *OrderedLinks) Get(key string) *Link { 33 | v := s.data.Get(key) 34 | if v == nil { 35 | return nil 36 | } 37 | return v.(*Link) 38 | } 39 | 40 | // GetOK checks if the key exists in the security requirement 41 | func (s *OrderedLinks) GetOK(key string) (*Link, bool) { 42 | v, ok := s.data.GetOK(key) 43 | if !ok { 44 | return nil, ok 45 | } 46 | 47 | sr, ok := v.(*Link) 48 | return sr, ok 49 | } 50 | 51 | // Set sets the value to the security requirement 52 | func (s *OrderedLinks) Set(key string, val *Link) bool { 53 | return s.data.Set(key, val) 54 | } 55 | 56 | // ForEach executes the function for each security requirement 57 | func (s *OrderedLinks) ForEach(fn func(string, *Link) error) error { 58 | s.data.ForEach(func(key string, val interface{}) error { 59 | response, _ := val.(*Link) 60 | if err := fn(key, response); err != nil { 61 | return err 62 | } 63 | return nil 64 | }) 65 | return nil 66 | } 67 | 68 | // Keys gets the list of keys 69 | func (s *OrderedLinks) Keys() []string { 70 | return s.data.Keys() 71 | } 72 | 73 | // TODO: (s *OrderedLinks) Implement Marshal & Unmarshal -> JSON, YAML 74 | -------------------------------------------------------------------------------- /parameter.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Parameter describes a single operation parameter. 4 | // A unique parameter is defined by a combination of a name and location. 5 | type Parameter struct { 6 | VendorExtensible 7 | Reference 8 | 9 | Name string `json:"name"` 10 | In string `json:"in"` 11 | Description string `json:"description"` 12 | Required bool `json:"required"` 13 | Deprecated bool `json:"deprecated"` 14 | AllowEmptyValue bool `json:"allowEmptyValue"` 15 | Style string `json:"style"` 16 | Explode bool `json:"explode"` 17 | AllowReserved bool `json:"allowReserved"` 18 | Schema Schema `json:"schema"` 19 | Example interface{} `json:"example"` 20 | Examples OrderedExamples `json:"examples"` 21 | Contents OrderedMediaTypes `json:"contents"` 22 | } 23 | 24 | type OrderedParameters struct { 25 | data OrderedMap 26 | } 27 | 28 | func NewOrderedParameters() OrderedParameters { 29 | return OrderedParameters{ 30 | data: OrderedMap{ 31 | filter: MatchNonEmptyKeys, 32 | }, 33 | } 34 | } 35 | 36 | // Get gets the security requirement by key 37 | func (s *OrderedParameters) Get(key string) *Parameter { 38 | v := s.data.Get(key) 39 | if v == nil { 40 | return nil 41 | } 42 | return v.(*Parameter) 43 | } 44 | 45 | // GetOK checks if the key exists in the security requirement 46 | func (s *OrderedParameters) GetOK(key string) (*Parameter, bool) { 47 | v, ok := s.data.GetOK(key) 48 | if !ok { 49 | return nil, ok 50 | } 51 | 52 | sr, ok := v.(*Parameter) 53 | return sr, ok 54 | } 55 | 56 | // Set sets the value to the security requirement 57 | func (s *OrderedParameters) Set(key string, val *Parameter) bool { 58 | return s.data.Set(key, val) 59 | } 60 | 61 | // ForEach executes the function for each security requirement 62 | func (s *OrderedParameters) ForEach(fn func(string, *Parameter) error) error { 63 | s.data.ForEach(func(key string, val interface{}) error { 64 | response, _ := val.(*Parameter) 65 | if err := fn(key, response); err != nil { 66 | return err 67 | } 68 | return nil 69 | }) 70 | return nil 71 | } 72 | 73 | // Keys gets the list of keys 74 | func (s *OrderedParameters) Keys() []string { 75 | return s.data.Keys() 76 | } 77 | 78 | // TODO: (s *OrderedParameters) Implement Marshal & Unmarshal -> JSON, YAML 79 | -------------------------------------------------------------------------------- /security_requirement.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "fmt" 5 | 6 | jlexer "github.com/mailru/easyjson/jlexer" 7 | jwriter "github.com/mailru/easyjson/jwriter" 8 | ) 9 | 10 | // SecurityRequirement lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. 11 | type SecurityRequirement struct { 12 | data OrderedMap 13 | } 14 | 15 | // Get gets the security requirement by key 16 | func (s *SecurityRequirement) Get(key string) []string { 17 | v, ok := s.data.GetOK(key) 18 | if !ok { 19 | return nil 20 | } 21 | sr, ok := v.([]string) 22 | if !ok { 23 | return nil 24 | } 25 | return sr 26 | } 27 | 28 | // GetOK checks if the key exists in the security requirement 29 | func (s *SecurityRequirement) GetOK(key string) ([]string, bool) { 30 | v, ok := s.data.GetOK(key) 31 | if !ok { 32 | return nil, ok 33 | } 34 | 35 | sr, ok := v.([]string) 36 | return sr, ok 37 | } 38 | 39 | // Set sets the value to the security requirement 40 | func (s *SecurityRequirement) Set(key string, scopes ...string) bool { 41 | return s.data.Set(key, scopes) 42 | } 43 | 44 | // ForEach executes the function for each security requirement 45 | func (s *SecurityRequirement) ForEach(fn func(string, []string) error) error { 46 | for _, k := range s.data.Keys() { 47 | scopes, ok := s.data.Get(k).([]string) 48 | if !ok { 49 | return fmt.Errorf("security requirement scopes not a []string but %T", s.data.Get(k)) 50 | } 51 | if err := fn(k, scopes); err != nil { 52 | return err 53 | } 54 | } 55 | return nil 56 | } 57 | 58 | // Keys gets the list of keys 59 | func (s *SecurityRequirement) Keys() []string { 60 | return s.data.Keys() 61 | } 62 | 63 | // MarshalJSON supports json.Marshaler interface 64 | func (s SecurityRequirement) MarshalJSON() ([]byte, error) { 65 | w := jwriter.Writer{} 66 | encodeSortedMap(&w, s.data) 67 | return w.Buffer.BuildBytes(), w.Error 68 | } 69 | 70 | // MarshalEasyJSON supports easyjson.Marshaler interface 71 | func (s SecurityRequirement) MarshalEasyJSON(w *jwriter.Writer) { 72 | encodeSortedMap(w, s.data) 73 | } 74 | 75 | // UnmarshalJSON supports json.Unmarshaler interface 76 | func (s *SecurityRequirement) UnmarshalJSON(data []byte) error { 77 | r := jlexer.Lexer{Data: data} 78 | decodeSortedMap(&r, &s.data) 79 | return r.Error() 80 | } 81 | 82 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 83 | func (s *SecurityRequirement) UnmarshalEasyJSON(l *jlexer.Lexer) { 84 | decodeSortedMap(l, &s.data) 85 | } 86 | -------------------------------------------------------------------------------- /security_scheme.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // SecurityScheme defines a security scheme that can be used by the operations. 4 | // Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect Discovery. 5 | type SecurityScheme struct { 6 | VendorExtensible 7 | Reference 8 | 9 | Type string `json:"type,omitempty"` 10 | Description string `json:"description,omitempty"` 11 | Name string `json:"name,omitempty"` 12 | In string `json:"in,omitempty"` 13 | Scheme string `json:"scheme,omitempty"` 14 | BearerFormat string `json:"bearerFormat,omitempty"` 15 | Flows OAuthFlow `json:"flows,omitempty"` 16 | OpenIDConnectURL string `json:"openIdConnectUrl,omitempty"` 17 | } 18 | 19 | // OrderedSecuritySchemes is a map between a variable name and its value. The value is used for substitution in the server's URL template. 20 | type OrderedSecuritySchemes struct { 21 | data OrderedMap 22 | } 23 | 24 | // NewOrderedSecuritySchemes creates a new instance of OrderedSecuritySchemes with correct filter 25 | func NewOrderedSecuritySchemes() OrderedSecuritySchemes { 26 | return OrderedSecuritySchemes{ 27 | data: OrderedMap{ 28 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 29 | }, 30 | } 31 | } 32 | 33 | // Get gets the security requirement by key 34 | func (s *OrderedSecuritySchemes) Get(key string) *SecurityScheme { 35 | v := s.data.Get(key) 36 | if v == nil { 37 | return nil 38 | } 39 | return v.(*SecurityScheme) 40 | } 41 | 42 | // GetOK checks if the key exists in the security requirement 43 | func (s *OrderedSecuritySchemes) GetOK(key string) (*SecurityScheme, bool) { 44 | v, ok := s.data.GetOK(key) 45 | if !ok { 46 | return nil, ok 47 | } 48 | 49 | sr, ok := v.(*SecurityScheme) 50 | return sr, ok 51 | } 52 | 53 | // Set sets the value to the security requirement 54 | func (s *OrderedSecuritySchemes) Set(key string, val *SecurityScheme) bool { 55 | return s.data.Set(key, val) 56 | } 57 | 58 | // ForEach executes the function for each security requirement 59 | func (s *OrderedSecuritySchemes) ForEach(fn func(string, *SecurityScheme) error) error { 60 | s.data.ForEach(func(key string, val interface{}) error { 61 | response, _ := val.(*SecurityScheme) 62 | if err := fn(key, response); err != nil { 63 | return err 64 | } 65 | return nil 66 | }) 67 | return nil 68 | } 69 | 70 | // Keys gets the list of keys 71 | func (s *OrderedSecuritySchemes) Keys() []string { 72 | return s.data.Keys() 73 | } 74 | 75 | // TODO: (s *OrderedSecuritySchemes) Implement Marshal & Unmarshal -> JSON, YAML 76 | -------------------------------------------------------------------------------- /fixtures/expansion/schemas1.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "car": { 4 | "type": "object", 5 | "properties": { 6 | "id": { 7 | "type": "integer", 8 | "format": "int64" 9 | }, 10 | "make": { 11 | "type": "string" 12 | }, 13 | "brand": { 14 | "$ref": "#/definitions/brand" 15 | } 16 | } 17 | }, 18 | "tag": { 19 | "type": "object", 20 | "properties": { 21 | "id": { 22 | "type": "integer", 23 | "format": "int64" 24 | }, 25 | "value": { 26 | "type": "string" 27 | } 28 | } 29 | }, 30 | "brand": { 31 | "type": "object", 32 | "properties": { 33 | "id": { 34 | "type": "integer", 35 | "format": "int64" 36 | }, 37 | "name": { 38 | "type": "string" 39 | } 40 | } 41 | }, 42 | "truck": { 43 | "$ref": "#/definitions/car" 44 | }, 45 | "batch": { 46 | "items": { 47 | "$ref": "#/definitions/brand" 48 | } 49 | }, 50 | "batch2": { 51 | "items": [ 52 | { 53 | "$ref": "#/definitions/brand" 54 | }, 55 | { 56 | "$ref": "#/definitions/tag" 57 | } 58 | ] 59 | }, 60 | "allofBoth": { 61 | "allOf": [ 62 | { 63 | "$ref": "#/definitions/brand" 64 | }, 65 | { 66 | "$ref": "#/definitions/tag" 67 | } 68 | ] 69 | }, 70 | "anyofBoth": { 71 | "anyOf": [ 72 | { 73 | "$ref": "#/definitions/brand" 74 | }, 75 | { 76 | "$ref": "#/definitions/tag" 77 | } 78 | ] 79 | }, 80 | "oneofBoth": { 81 | "oneOf": [ 82 | { 83 | "$ref": "#/definitions/brand" 84 | }, 85 | { 86 | "$ref": "#/definitions/tag" 87 | } 88 | ] 89 | }, 90 | "notSomething": { 91 | "not": { 92 | "$ref": "#/definitions/tag" 93 | } 94 | }, 95 | "withAdditional": { 96 | "additionalProperties": { 97 | "$ref": "#/definitions/tag" 98 | } 99 | }, 100 | "withPattern": { 101 | "patternProperties": { 102 | "^x-ab": { 103 | "$ref": "#/definitions/tag" 104 | } 105 | } 106 | }, 107 | "withAdditionalItems": { 108 | "additionalItems": { 109 | "$ref": "#/definitions/tag" 110 | } 111 | }, 112 | "deps": { 113 | "dependencies": { 114 | "something": { 115 | "$ref": "#/definitions/tag" 116 | } 117 | } 118 | }, 119 | "defined": { 120 | "definitions": { 121 | "something": { 122 | "$ref": "#/definitions/tag" 123 | } 124 | } 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /license_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package spec3 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson967143c7DecodeGithubComGoOpenapiSpec3(in *jlexer.Lexer, out *License) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "name": 40 | out.Name = string(in.String()) 41 | case "url": 42 | out.URL = string(in.String()) 43 | case "extensions": 44 | (out.Extensions).UnmarshalEasyJSON(in) 45 | default: 46 | in.SkipRecursive() 47 | } 48 | in.WantComma() 49 | } 50 | in.Delim('}') 51 | if isTopLevel { 52 | in.Consumed() 53 | } 54 | } 55 | func easyjson967143c7EncodeGithubComGoOpenapiSpec3(out *jwriter.Writer, in License) { 56 | out.RawByte('{') 57 | first := true 58 | _ = first 59 | if in.Name != "" { 60 | const prefix string = ",\"name\":" 61 | if first { 62 | first = false 63 | out.RawString(prefix[1:]) 64 | } else { 65 | out.RawString(prefix) 66 | } 67 | out.String(string(in.Name)) 68 | } 69 | if in.URL != "" { 70 | const prefix string = ",\"url\":" 71 | if first { 72 | first = false 73 | out.RawString(prefix[1:]) 74 | } else { 75 | out.RawString(prefix) 76 | } 77 | out.String(string(in.URL)) 78 | } 79 | if true { 80 | const prefix string = ",\"extensions\":" 81 | if first { 82 | first = false 83 | out.RawString(prefix[1:]) 84 | } else { 85 | out.RawString(prefix) 86 | } 87 | (in.Extensions).MarshalEasyJSON(out) 88 | } 89 | out.RawByte('}') 90 | } 91 | 92 | // MarshalJSON supports json.Marshaler interface 93 | func (v License) MarshalJSON() ([]byte, error) { 94 | w := jwriter.Writer{} 95 | easyjson967143c7EncodeGithubComGoOpenapiSpec3(&w, v) 96 | return w.Buffer.BuildBytes(), w.Error 97 | } 98 | 99 | // MarshalEasyJSON supports easyjson.Marshaler interface 100 | func (v License) MarshalEasyJSON(w *jwriter.Writer) { 101 | easyjson967143c7EncodeGithubComGoOpenapiSpec3(w, v) 102 | } 103 | 104 | // UnmarshalJSON supports json.Unmarshaler interface 105 | func (v *License) UnmarshalJSON(data []byte) error { 106 | r := jlexer.Lexer{Data: data} 107 | easyjson967143c7DecodeGithubComGoOpenapiSpec3(&r, v) 108 | return r.Error() 109 | } 110 | 111 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 112 | func (v *License) UnmarshalEasyJSON(l *jlexer.Lexer) { 113 | easyjson967143c7DecodeGithubComGoOpenapiSpec3(l, v) 114 | } 115 | -------------------------------------------------------------------------------- /external_documentation_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package spec3 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson1a518086DecodeGithubComGoOpenapiSpec3(in *jlexer.Lexer, out *ExternalDocumentation) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "description": 40 | out.Description = string(in.String()) 41 | case "url": 42 | out.URL = string(in.String()) 43 | case "extensions": 44 | (out.Extensions).UnmarshalEasyJSON(in) 45 | default: 46 | in.SkipRecursive() 47 | } 48 | in.WantComma() 49 | } 50 | in.Delim('}') 51 | if isTopLevel { 52 | in.Consumed() 53 | } 54 | } 55 | func easyjson1a518086EncodeGithubComGoOpenapiSpec3(out *jwriter.Writer, in ExternalDocumentation) { 56 | out.RawByte('{') 57 | first := true 58 | _ = first 59 | if in.Description != "" { 60 | const prefix string = ",\"description\":" 61 | if first { 62 | first = false 63 | out.RawString(prefix[1:]) 64 | } else { 65 | out.RawString(prefix) 66 | } 67 | out.String(string(in.Description)) 68 | } 69 | if in.URL != "" { 70 | const prefix string = ",\"url\":" 71 | if first { 72 | first = false 73 | out.RawString(prefix[1:]) 74 | } else { 75 | out.RawString(prefix) 76 | } 77 | out.String(string(in.URL)) 78 | } 79 | if true { 80 | const prefix string = ",\"extensions\":" 81 | if first { 82 | first = false 83 | out.RawString(prefix[1:]) 84 | } else { 85 | out.RawString(prefix) 86 | } 87 | (in.Extensions).MarshalEasyJSON(out) 88 | } 89 | out.RawByte('}') 90 | } 91 | 92 | // MarshalJSON supports json.Marshaler interface 93 | func (v ExternalDocumentation) MarshalJSON() ([]byte, error) { 94 | w := jwriter.Writer{} 95 | easyjson1a518086EncodeGithubComGoOpenapiSpec3(&w, v) 96 | return w.Buffer.BuildBytes(), w.Error 97 | } 98 | 99 | // MarshalEasyJSON supports easyjson.Marshaler interface 100 | func (v ExternalDocumentation) MarshalEasyJSON(w *jwriter.Writer) { 101 | easyjson1a518086EncodeGithubComGoOpenapiSpec3(w, v) 102 | } 103 | 104 | // UnmarshalJSON supports json.Unmarshaler interface 105 | func (v *ExternalDocumentation) UnmarshalJSON(data []byte) error { 106 | r := jlexer.Lexer{Data: data} 107 | easyjson1a518086DecodeGithubComGoOpenapiSpec3(&r, v) 108 | return r.Error() 109 | } 110 | 111 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 112 | func (v *ExternalDocumentation) UnmarshalEasyJSON(l *jlexer.Lexer) { 113 | easyjson1a518086DecodeGithubComGoOpenapiSpec3(l, v) 114 | } 115 | -------------------------------------------------------------------------------- /contact_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package spec3 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson693b863aDecodeGithubComGoOpenapiSpec3(in *jlexer.Lexer, out *Contact) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "name": 40 | out.Name = string(in.String()) 41 | case "url": 42 | out.URL = string(in.String()) 43 | case "email": 44 | out.Email = string(in.String()) 45 | case "extensions": 46 | (out.Extensions).UnmarshalEasyJSON(in) 47 | default: 48 | in.SkipRecursive() 49 | } 50 | in.WantComma() 51 | } 52 | in.Delim('}') 53 | if isTopLevel { 54 | in.Consumed() 55 | } 56 | } 57 | func easyjson693b863aEncodeGithubComGoOpenapiSpec3(out *jwriter.Writer, in Contact) { 58 | out.RawByte('{') 59 | first := true 60 | _ = first 61 | if in.Name != "" { 62 | const prefix string = ",\"name\":" 63 | first = false 64 | out.RawString(prefix[1:]) 65 | out.String(string(in.Name)) 66 | } 67 | if in.URL != "" { 68 | const prefix string = ",\"url\":" 69 | if first { 70 | first = false 71 | out.RawString(prefix[1:]) 72 | } else { 73 | out.RawString(prefix) 74 | } 75 | out.String(string(in.URL)) 76 | } 77 | if in.Email != "" { 78 | const prefix string = ",\"email\":" 79 | if first { 80 | first = false 81 | out.RawString(prefix[1:]) 82 | } else { 83 | out.RawString(prefix) 84 | } 85 | out.String(string(in.Email)) 86 | } 87 | if true { 88 | const prefix string = ",\"extensions\":" 89 | if first { 90 | first = false 91 | out.RawString(prefix[1:]) 92 | } else { 93 | out.RawString(prefix) 94 | } 95 | (in.Extensions).MarshalEasyJSON(out) 96 | } 97 | out.RawByte('}') 98 | } 99 | 100 | // MarshalJSON supports json.Marshaler interface 101 | func (v Contact) MarshalJSON() ([]byte, error) { 102 | w := jwriter.Writer{} 103 | easyjson693b863aEncodeGithubComGoOpenapiSpec3(&w, v) 104 | return w.Buffer.BuildBytes(), w.Error 105 | } 106 | 107 | // MarshalEasyJSON supports easyjson.Marshaler interface 108 | func (v Contact) MarshalEasyJSON(w *jwriter.Writer) { 109 | easyjson693b863aEncodeGithubComGoOpenapiSpec3(w, v) 110 | } 111 | 112 | // UnmarshalJSON supports json.Unmarshaler interface 113 | func (v *Contact) UnmarshalJSON(data []byte) error { 114 | r := jlexer.Lexer{Data: data} 115 | easyjson693b863aDecodeGithubComGoOpenapiSpec3(&r, v) 116 | return r.Error() 117 | } 118 | 119 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 120 | func (v *Contact) UnmarshalEasyJSON(l *jlexer.Lexer) { 121 | easyjson693b863aDecodeGithubComGoOpenapiSpec3(l, v) 122 | } 123 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ivan+abuse@flanders.co.nz. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /tag_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package spec3 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson13673cd6DecodeGithubComGoOpenapiSpec3(in *jlexer.Lexer, out *Tag) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "name": 40 | out.Name = string(in.String()) 41 | case "description": 42 | out.Description = string(in.String()) 43 | case "externalDocs": 44 | if in.IsNull() { 45 | in.Skip() 46 | out.ExternalDocs = nil 47 | } else { 48 | if out.ExternalDocs == nil { 49 | out.ExternalDocs = new(ExternalDocumentation) 50 | } 51 | (*out.ExternalDocs).UnmarshalEasyJSON(in) 52 | } 53 | case "extensions": 54 | (out.Extensions).UnmarshalEasyJSON(in) 55 | default: 56 | in.SkipRecursive() 57 | } 58 | in.WantComma() 59 | } 60 | in.Delim('}') 61 | if isTopLevel { 62 | in.Consumed() 63 | } 64 | } 65 | func easyjson13673cd6EncodeGithubComGoOpenapiSpec3(out *jwriter.Writer, in Tag) { 66 | out.RawByte('{') 67 | first := true 68 | _ = first 69 | if in.Name != "" { 70 | const prefix string = ",\"name\":" 71 | if first { 72 | first = false 73 | out.RawString(prefix[1:]) 74 | } else { 75 | out.RawString(prefix) 76 | } 77 | out.String(string(in.Name)) 78 | } 79 | if in.Description != "" { 80 | const prefix string = ",\"description\":" 81 | if first { 82 | first = false 83 | out.RawString(prefix[1:]) 84 | } else { 85 | out.RawString(prefix) 86 | } 87 | out.String(string(in.Description)) 88 | } 89 | if in.ExternalDocs != nil { 90 | const prefix string = ",\"externalDocs\":" 91 | if first { 92 | first = false 93 | out.RawString(prefix[1:]) 94 | } else { 95 | out.RawString(prefix) 96 | } 97 | (*in.ExternalDocs).MarshalEasyJSON(out) 98 | } 99 | if true { 100 | const prefix string = ",\"extensions\":" 101 | if first { 102 | first = false 103 | out.RawString(prefix[1:]) 104 | } else { 105 | out.RawString(prefix) 106 | } 107 | (in.Extensions).MarshalEasyJSON(out) 108 | } 109 | out.RawByte('}') 110 | } 111 | 112 | // MarshalJSON supports json.Marshaler interface 113 | func (v Tag) MarshalJSON() ([]byte, error) { 114 | w := jwriter.Writer{} 115 | easyjson13673cd6EncodeGithubComGoOpenapiSpec3(&w, v) 116 | return w.Buffer.BuildBytes(), w.Error 117 | } 118 | 119 | // MarshalEasyJSON supports easyjson.Marshaler interface 120 | func (v Tag) MarshalEasyJSON(w *jwriter.Writer) { 121 | easyjson13673cd6EncodeGithubComGoOpenapiSpec3(w, v) 122 | } 123 | 124 | // UnmarshalJSON supports json.Unmarshaler interface 125 | func (v *Tag) UnmarshalJSON(data []byte) error { 126 | r := jlexer.Lexer{Data: data} 127 | easyjson13673cd6DecodeGithubComGoOpenapiSpec3(&r, v) 128 | return r.Error() 129 | } 130 | 131 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 132 | func (v *Tag) UnmarshalEasyJSON(l *jlexer.Lexer) { 133 | easyjson13673cd6DecodeGithubComGoOpenapiSpec3(l, v) 134 | } 135 | -------------------------------------------------------------------------------- /fixtures/expansion/schemas2.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "car": { 4 | "type": "object", 5 | "properties": { 6 | "id": { 7 | "type": "integer", 8 | "format": "int64" 9 | }, 10 | "make": { 11 | "type": "string" 12 | }, 13 | "brand": { 14 | "items": { 15 | "$ref": "#/definitions/brand" 16 | } 17 | } 18 | } 19 | }, 20 | "tag": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "integer", 25 | "format": "int64" 26 | }, 27 | "value": { 28 | "type": "string" 29 | } 30 | } 31 | }, 32 | "brand": { 33 | "type": "object", 34 | "properties": { 35 | "id": { 36 | "type": "integer", 37 | "format": "int64" 38 | }, 39 | "name": { 40 | "type": "string" 41 | } 42 | } 43 | }, 44 | "truck": { 45 | "items": { 46 | "$ref": "#/definitions/car" 47 | } 48 | }, 49 | "batch": { 50 | "items": { 51 | "items": { 52 | "$ref": "#/definitions/brand" 53 | } 54 | } 55 | }, 56 | "batch2": { 57 | "items": [ 58 | { 59 | "items": { 60 | "$ref": "#/definitions/brand" 61 | } 62 | }, 63 | { 64 | "items": { 65 | "$ref": "#/definitions/tag" 66 | } 67 | } 68 | ] 69 | }, 70 | "allofBoth": { 71 | "allOf": [ 72 | { 73 | "items": { 74 | "$ref": "#/definitions/brand" 75 | } 76 | }, 77 | { 78 | "items": { 79 | "$ref": "#/definitions/tag" 80 | } 81 | } 82 | ] 83 | }, 84 | "anyofBoth": { 85 | "anyOf": [ 86 | { 87 | "items": { 88 | "$ref": "#/definitions/brand" 89 | } 90 | }, 91 | { 92 | "items": { 93 | "$ref": "#/definitions/tag" 94 | } 95 | } 96 | ] 97 | }, 98 | "oneofBoth": { 99 | "oneOf": [ 100 | { 101 | "items": { 102 | "$ref": "#/definitions/brand" 103 | } 104 | }, 105 | { 106 | "items": { 107 | "$ref": "#/definitions/tag" 108 | } 109 | } 110 | ] 111 | }, 112 | "notSomething": { 113 | "not": { 114 | "items": { 115 | "$ref": "#/definitions/tag" 116 | } 117 | } 118 | }, 119 | "withAdditional": { 120 | "additionalProperties": { 121 | "items": { 122 | "$ref": "#/definitions/tag" 123 | } 124 | } 125 | }, 126 | "withPattern": { 127 | "patternProperties": { 128 | "^x-ab": { 129 | "items": { 130 | "$ref": "#/definitions/tag" 131 | } 132 | } 133 | } 134 | }, 135 | "withAdditionalItems": { 136 | "additionalItems": { 137 | "items": { 138 | "$ref": "#/definitions/tag" 139 | } 140 | } 141 | }, 142 | "deps": { 143 | "dependencies": { 144 | "something": { 145 | "items": { 146 | "$ref": "#/definitions/tag" 147 | } 148 | } 149 | } 150 | }, 151 | "defined": { 152 | "definitions": { 153 | "something": { 154 | "items": { 155 | "$ref": "#/definitions/tag" 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /callback.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | // Callback A map of possible out-of band callbacks related to the parent operation. 4 | // Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. 5 | // The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation. 6 | type Callback struct { 7 | data OrderedMap 8 | } 9 | 10 | // NewCallback creates a new instance of Callback with correct filter 11 | func NewCallback() Callback { 12 | return Callback{ 13 | data: OrderedMap{ 14 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 15 | }, 16 | } 17 | } 18 | 19 | // Get gets the security requirement by key 20 | func (s *Callback) Get(key string) *PathItem { 21 | v := s.data.Get(key) 22 | if v == nil { 23 | return nil 24 | } 25 | return v.(*PathItem) 26 | } 27 | 28 | // GetOK checks if the key exists in the security requirement 29 | func (s *Callback) GetOK(key string) (*PathItem, bool) { 30 | v, ok := s.data.GetOK(key) 31 | if !ok { 32 | return nil, ok 33 | } 34 | 35 | sr, ok := v.(*PathItem) 36 | return sr, ok 37 | } 38 | 39 | // Set sets the value to the security requirement 40 | func (s *Callback) Set(key string, val *PathItem) bool { 41 | return s.data.Set(key, val) 42 | } 43 | 44 | // ForEach executes the function for each security requirement 45 | func (s *Callback) ForEach(fn func(string, *PathItem) error) error { 46 | s.data.ForEach(func(key string, val interface{}) error { 47 | response, _ := val.(*PathItem) 48 | if err := fn(key, response); err != nil { 49 | return err 50 | } 51 | return nil 52 | }) 53 | return nil 54 | } 55 | 56 | // Keys gets the list of keys 57 | func (s *Callback) Keys() []string { 58 | return s.data.Keys() 59 | } 60 | 61 | // TODO: (s *Callback) Implement Marshal & Unmarshal -> JSON, YAML 62 | 63 | // OrderedCallbacks is a map between a variable name and its value. The value is used for substitution in the server's URL template. 64 | type OrderedCallbacks struct { 65 | data OrderedMap 66 | } 67 | 68 | // NewOrderedCallbacks creates a new instance of OrderedCallbacks with correct filter 69 | func NewOrderedCallbacks() OrderedCallbacks { 70 | return OrderedCallbacks{ 71 | data: OrderedMap{ 72 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 73 | }, 74 | } 75 | } 76 | 77 | // Get gets the security requirement by key 78 | func (s *OrderedCallbacks) Get(key string) *Callback { 79 | v := s.data.Get(key) 80 | if v == nil { 81 | return nil 82 | } 83 | return v.(*Callback) 84 | } 85 | 86 | // GetOK checks if the key exists in the security requirement 87 | func (s *OrderedCallbacks) GetOK(key string) (*Callback, bool) { 88 | v, ok := s.data.GetOK(key) 89 | if !ok { 90 | return nil, ok 91 | } 92 | 93 | sr, ok := v.(*Callback) 94 | return sr, ok 95 | } 96 | 97 | // Set sets the value to the security requirement 98 | func (s *OrderedCallbacks) Set(key string, val *Callback) bool { 99 | return s.data.Set(key, val) 100 | } 101 | 102 | // ForEach executes the function for each security requirement 103 | func (s *OrderedCallbacks) ForEach(fn func(string, *Callback) error) error { 104 | s.data.ForEach(func(key string, val interface{}) error { 105 | response, _ := val.(*Callback) 106 | if err := fn(key, response); err != nil { 107 | return err 108 | } 109 | return nil 110 | }) 111 | return nil 112 | } 113 | 114 | // Keys gets the list of keys 115 | func (s *OrderedCallbacks) Keys() []string { 116 | return s.data.Keys() 117 | } 118 | 119 | // TODO: (s *OrderedCallbacks) Implement Marshal & Unmarshal -> JSON, YAML 120 | -------------------------------------------------------------------------------- /fixtures/expansion/overflow.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "Swagger Sample", 5 | "description": "Sample API Playground.", 6 | "version": "1.0.0" 7 | }, 8 | "basePath": "/v1", 9 | "schemes": [ 10 | "http" 11 | ], 12 | "consumes": [ 13 | "application/vdn.sample.v1+json" 14 | ], 15 | "produces": [ 16 | "application/vdn.sample.v1+json" 17 | ], 18 | "paths": { 19 | "/books": { 20 | "get": { 21 | "summary": "List all books", 22 | "operationId": "listBooks", 23 | "tags": [ 24 | "books" 25 | ], 26 | "responses": { 27 | "200": { 28 | "headers": { 29 | "Link": { 30 | "type": "string" 31 | } 32 | }, 33 | "description": "An array of books", 34 | "schema": { 35 | "type": "array", 36 | "items": { 37 | "$ref": "#/definitions/Book" 38 | } 39 | } 40 | }, 41 | "default": { 42 | "description": "generic error response", 43 | "schema": { 44 | "$ref": "#/definitions/Error" 45 | } 46 | } 47 | } 48 | } 49 | } 50 | }, 51 | "definitions": { 52 | "Store": { 53 | "type": "object", 54 | "properties": { 55 | "title": { 56 | "type": "string", 57 | "example": "Book Shop" 58 | }, 59 | "categories": { 60 | "type": "array", 61 | "items": { 62 | "$ref": "#/definitions/Category" 63 | } 64 | } 65 | } 66 | }, 67 | "Category": { 68 | "type": "object", 69 | "properties": { 70 | "title": { 71 | "type": "string", 72 | "example": "Drama" 73 | }, 74 | "books": { 75 | "type": "array", 76 | "items": { 77 | "$ref": "#/definitions/Book" 78 | } 79 | } 80 | } 81 | }, 82 | "Book": { 83 | "type": "object", 84 | "required": [ 85 | "title", 86 | "summary" 87 | ], 88 | "properties": { 89 | "title": { 90 | "type": "string", 91 | "example": "Winnie the Pooh" 92 | }, 93 | "summary": { 94 | "type": "string", 95 | "example": "Famous children's book" 96 | }, 97 | "related_books": { 98 | "type": "array", 99 | "items": { 100 | "$ref": "#/definitions/Book" 101 | } 102 | } 103 | } 104 | }, 105 | "Error": { 106 | "type": "object", 107 | "readOnly": true, 108 | "properties": { 109 | "code": { 110 | "type": "integer", 111 | "format": "int64", 112 | "example": 400 113 | }, 114 | "message": { 115 | "type": "string", 116 | "example": "Unexpected error" 117 | } 118 | }, 119 | "required": [ 120 | "message" 121 | ] 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /responses.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | jlexer "github.com/mailru/easyjson/jlexer" 5 | jwriter "github.com/mailru/easyjson/jwriter" 6 | ) 7 | 8 | // Response describes a single response from an API Operation, including design-time, static links to operations based on the response. 9 | type Response struct { 10 | VendorExtensible 11 | Reference 12 | 13 | Description string `json:"description"` 14 | Headers OrderedHeaders `json:"headers"` 15 | Content OrderedMediaTypes `json:"content"` 16 | Links OrderedLinks `json:"links"` 17 | } 18 | 19 | // OrderedResponses is a container for the expected responses of an operation. The container maps a HTTP response code to the expected response. 20 | type OrderedResponses struct { 21 | data OrderedMap 22 | } 23 | 24 | // NewOrderedResponses creates the new instance of the OrderedResponses with correct key-filter 25 | func NewOrderedResponses() OrderedResponses { 26 | return OrderedResponses{ 27 | data: OrderedMap{ 28 | filter: matchResponseCode, 29 | }, 30 | } 31 | } 32 | 33 | // Get gets the security requirement by key 34 | func (s *OrderedResponses) Get(key string) *Response { 35 | v := s.data.Get(key) 36 | if v == nil { 37 | return nil 38 | } 39 | return v.(*Response) 40 | } 41 | 42 | // GetOK checks if the key exists in the security requirement 43 | func (s *OrderedResponses) GetOK(key string) (*Response, bool) { 44 | v, ok := s.data.GetOK(key) 45 | if !ok { 46 | return nil, ok 47 | } 48 | 49 | sr, ok := v.(*Response) 50 | return sr, ok 51 | } 52 | 53 | // Set sets the value to the security requirement 54 | func (s *OrderedResponses) Set(key string, val *Response) bool { 55 | return s.data.Set(key, val) 56 | } 57 | 58 | // ForEach executes the function for each security requirement 59 | func (s *OrderedResponses) ForEach(fn func(string, *Response) error) error { 60 | s.data.ForEach(func(key string, val interface{}) error { 61 | response, _ := val.(*Response) 62 | if err := fn(key, response); err != nil { 63 | return err 64 | } 65 | return nil 66 | }) 67 | return nil 68 | } 69 | 70 | // Keys gets the list of keys 71 | func (s *OrderedResponses) Keys() []string { 72 | return s.data.Keys() 73 | } 74 | 75 | // MarshalJSON supports json.Marshaler interface 76 | func (s OrderedResponses) MarshalJSON() ([]byte, error) { 77 | w := jwriter.Writer{} 78 | encodeSortedMap(&w, s.data) 79 | return w.Buffer.BuildBytes(), w.Error 80 | } 81 | 82 | // MarshalEasyJSON supports easyjson.Marshaler interface 83 | func (s OrderedResponses) MarshalEasyJSON(w *jwriter.Writer) { 84 | encodeSortedMap(w, s.data) 85 | } 86 | 87 | // UnmarshalJSON supports json.Unmarshaler interface 88 | func (s *OrderedResponses) UnmarshalJSON(data []byte) error { 89 | r := jlexer.Lexer{Data: data} 90 | decodeSortedMap(&r, &s.data) 91 | return r.Error() 92 | } 93 | 94 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 95 | func (s *OrderedResponses) UnmarshalEasyJSON(l *jlexer.Lexer) { 96 | decodeSortedMap(l, &s.data) 97 | } 98 | 99 | // matchResponseCode is used as filter for response codes 100 | func matchResponseCode(key string) bool { 101 | if key == "default" || 102 | key == "100" || 103 | key == "101" || 104 | key == "200" || 105 | key == "201" || 106 | key == "202" || 107 | key == "203" || 108 | key == "204" || 109 | key == "205" || 110 | key == "206" || 111 | key == "300" || 112 | key == "301" || 113 | key == "302" || 114 | key == "303" || 115 | key == "304" || 116 | key == "305" || 117 | key == "307" || 118 | key == "400" || 119 | key == "401" || 120 | key == "402" || 121 | key == "403" || 122 | key == "404" || 123 | key == "405" || 124 | key == "406" || 125 | key == "407" || 126 | key == "408" || 127 | key == "409" || 128 | key == "410" || 129 | key == "411" || 130 | key == "412" || 131 | key == "413" || 132 | key == "414" || 133 | key == "415" || 134 | key == "416" || 135 | key == "417" || 136 | key == "426" || 137 | key == "500" || 138 | key == "501" || 139 | key == "502" || 140 | key == "503" || 141 | key == "504" || 142 | key == "505" { 143 | return true 144 | } 145 | return false 146 | } 147 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= 2 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 3 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= 4 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 5 | github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= 6 | github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= 11 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 12 | github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= 13 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= 14 | github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= 15 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 16 | github.com/go-openapi/swag v0.19.7 h1:VRuXN2EnMSsZdauzdss6JBC29YotDqG59BZ+tdlIL1s= 17 | github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= 18 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 19 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 20 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 21 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 22 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 23 | github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= 24 | github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= 25 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 26 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 27 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 28 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 29 | github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw= 30 | github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 31 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 32 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= 33 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 34 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= 35 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 36 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 37 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 38 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 39 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 40 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 41 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 42 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 43 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 44 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 45 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 46 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 47 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 48 | -------------------------------------------------------------------------------- /fixtures/expansion/missingRef.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "swagger": "2.0", 4 | "info": { 5 | "version": "1.0", 6 | "title": "Continue On Error" 7 | }, 8 | "paths": { 9 | "/todos": { 10 | "get": { 11 | "responses": { 12 | "200": { 13 | "description": "List Todos", 14 | "schema": { 15 | "type": "array", 16 | "items": { 17 | "$ref": "#/definitions/todo-full" 18 | } 19 | } 20 | }, 21 | "404": { 22 | "$ref": "#/responses/404" 23 | } 24 | } 25 | } 26 | } 27 | }, 28 | "definitions": { 29 | "todo-partial": { 30 | "type": "object", 31 | "properties": { 32 | "name": { 33 | "type": "string" 34 | }, 35 | "completed": { 36 | "type": "boolean" 37 | } 38 | } 39 | }, 40 | "todo-full": { 41 | "allOf": [ 42 | { 43 | "$ref": "#/definitions/todo-partial" 44 | }, 45 | { 46 | "type": "object", 47 | "properties": { 48 | "id": { 49 | "type": "integer" 50 | }, 51 | "completed_at": { 52 | "type": "string" 53 | }, 54 | "created_at": { 55 | "type": "string" 56 | }, 57 | "updated_at": { 58 | "type": "string" 59 | } 60 | } 61 | } 62 | ] 63 | } 64 | } 65 | }, 66 | "expected": { 67 | "swagger": "2.0", 68 | "info": { 69 | "title": "Continue On Error", 70 | "version": "1.0" 71 | }, 72 | "paths": { 73 | "/todos": { 74 | "get": { 75 | "responses": { 76 | "200": { 77 | "description": "List Todos", 78 | "schema": { 79 | "type": "array", 80 | "items": { 81 | "allOf": [ 82 | { 83 | "type": "object", 84 | "properties": { 85 | "completed": { 86 | "type": "boolean" 87 | }, 88 | "name": { 89 | "type": "string" 90 | } 91 | } 92 | }, 93 | { 94 | "type": "object", 95 | "properties": { 96 | "completed_at": { 97 | "type": "string" 98 | }, 99 | "created_at": { 100 | "type": "string" 101 | }, 102 | "id": { 103 | "type": "integer" 104 | }, 105 | "updated_at": { 106 | "type": "string" 107 | } 108 | } 109 | } 110 | ] 111 | } 112 | } 113 | }, 114 | "404": {} 115 | } 116 | } 117 | } 118 | }, 119 | "definitions": { 120 | "todo-full": { 121 | "allOf": [ 122 | { 123 | "type": "object", 124 | "properties": { 125 | "completed": { 126 | "type": "boolean" 127 | }, 128 | "name": { 129 | "type": "string" 130 | } 131 | } 132 | }, 133 | { 134 | "type": "object", 135 | "properties": { 136 | "completed_at": { 137 | "type": "string" 138 | }, 139 | "created_at": { 140 | "type": "string" 141 | }, 142 | "id": { 143 | "type": "integer" 144 | }, 145 | "updated_at": { 146 | "type": "string" 147 | } 148 | } 149 | } 150 | ] 151 | }, 152 | "todo-partial": { 153 | "type": "object", 154 | "properties": { 155 | "completed": { 156 | "type": "boolean" 157 | }, 158 | "name": { 159 | "type": "string" 160 | } 161 | } 162 | } 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /info_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package spec3 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjsonDdc53814DecodeGithubComGoOpenapiSpec3(in *jlexer.Lexer, out *Info) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "title": 40 | out.Title = string(in.String()) 41 | case "description": 42 | out.Description = string(in.String()) 43 | case "termsOfService": 44 | out.TermsOfService = string(in.String()) 45 | case "contact": 46 | if in.IsNull() { 47 | in.Skip() 48 | out.Contact = nil 49 | } else { 50 | if out.Contact == nil { 51 | out.Contact = new(Contact) 52 | } 53 | (*out.Contact).UnmarshalEasyJSON(in) 54 | } 55 | case "license": 56 | if in.IsNull() { 57 | in.Skip() 58 | out.License = nil 59 | } else { 60 | if out.License == nil { 61 | out.License = new(License) 62 | } 63 | (*out.License).UnmarshalEasyJSON(in) 64 | } 65 | case "version": 66 | out.Version = string(in.String()) 67 | case "extensions": 68 | (out.Extensions).UnmarshalEasyJSON(in) 69 | default: 70 | in.SkipRecursive() 71 | } 72 | in.WantComma() 73 | } 74 | in.Delim('}') 75 | if isTopLevel { 76 | in.Consumed() 77 | } 78 | } 79 | func easyjsonDdc53814EncodeGithubComGoOpenapiSpec3(out *jwriter.Writer, in Info) { 80 | out.RawByte('{') 81 | first := true 82 | _ = first 83 | if in.Title != "" { 84 | const prefix string = ",\"title\":" 85 | if first { 86 | first = false 87 | out.RawString(prefix[1:]) 88 | } else { 89 | out.RawString(prefix) 90 | } 91 | out.String(string(in.Title)) 92 | } 93 | if in.Description != "" { 94 | const prefix string = ",\"description\":" 95 | if first { 96 | first = false 97 | out.RawString(prefix[1:]) 98 | } else { 99 | out.RawString(prefix) 100 | } 101 | out.String(string(in.Description)) 102 | } 103 | if in.TermsOfService != "" { 104 | const prefix string = ",\"termsOfService\":" 105 | if first { 106 | first = false 107 | out.RawString(prefix[1:]) 108 | } else { 109 | out.RawString(prefix) 110 | } 111 | out.String(string(in.TermsOfService)) 112 | } 113 | if in.Contact != nil { 114 | const prefix string = ",\"contact\":" 115 | if first { 116 | first = false 117 | out.RawString(prefix[1:]) 118 | } else { 119 | out.RawString(prefix) 120 | } 121 | (*in.Contact).MarshalEasyJSON(out) 122 | } 123 | if in.License != nil { 124 | const prefix string = ",\"license\":" 125 | if first { 126 | first = false 127 | out.RawString(prefix[1:]) 128 | } else { 129 | out.RawString(prefix) 130 | } 131 | (*in.License).MarshalEasyJSON(out) 132 | } 133 | if in.Version != "" { 134 | const prefix string = ",\"version\":" 135 | if first { 136 | first = false 137 | out.RawString(prefix[1:]) 138 | } else { 139 | out.RawString(prefix) 140 | } 141 | out.String(string(in.Version)) 142 | } 143 | if true { 144 | const prefix string = ",\"extensions\":" 145 | if first { 146 | first = false 147 | out.RawString(prefix[1:]) 148 | } else { 149 | out.RawString(prefix) 150 | } 151 | (in.Extensions).MarshalEasyJSON(out) 152 | } 153 | out.RawByte('}') 154 | } 155 | 156 | // MarshalJSON supports json.Marshaler interface 157 | func (v Info) MarshalJSON() ([]byte, error) { 158 | w := jwriter.Writer{} 159 | easyjsonDdc53814EncodeGithubComGoOpenapiSpec3(&w, v) 160 | return w.Buffer.BuildBytes(), w.Error 161 | } 162 | 163 | // MarshalEasyJSON supports easyjson.Marshaler interface 164 | func (v Info) MarshalEasyJSON(w *jwriter.Writer) { 165 | easyjsonDdc53814EncodeGithubComGoOpenapiSpec3(w, v) 166 | } 167 | 168 | // UnmarshalJSON supports json.Unmarshaler interface 169 | func (v *Info) UnmarshalJSON(data []byte) error { 170 | r := jlexer.Lexer{Data: data} 171 | easyjsonDdc53814DecodeGithubComGoOpenapiSpec3(&r, v) 172 | return r.Error() 173 | } 174 | 175 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 176 | func (v *Info) UnmarshalEasyJSON(l *jlexer.Lexer) { 177 | easyjsonDdc53814DecodeGithubComGoOpenapiSpec3(l, v) 178 | } 179 | -------------------------------------------------------------------------------- /extensions.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/mailru/easyjson/jlexer" 7 | "github.com/mailru/easyjson/jwriter" 8 | ) 9 | 10 | // Extensions vendor specific extensions 11 | type Extensions struct { 12 | data OrderedMap 13 | } 14 | 15 | func (e Extensions) Set(key string, value interface{}) bool { 16 | e.data.filter = MatchExtension 17 | e.data.normalize = LowerCaseKeys 18 | return e.data.Set(key, value) 19 | } 20 | 21 | // Add adds a value to these extensions 22 | func (e Extensions) Add(key string, value interface{}) { 23 | e.Set(key, value) 24 | } 25 | 26 | func (e Extensions) GetOK(key string) (interface{}, bool) { 27 | e.data.filter = MatchExtension 28 | e.data.normalize = LowerCaseKeys 29 | return e.data.GetOK(key) 30 | } 31 | 32 | func (e Extensions) Get(key string) interface{} { 33 | e.data.filter = MatchExtension 34 | e.data.normalize = LowerCaseKeys 35 | return e.data.Get(key) 36 | } 37 | 38 | // GetString gets a string value from the extensions 39 | func (e Extensions) GetString(key string) (string, bool) { 40 | if v, ok := e.GetOK(key); ok { 41 | str, ok := v.(string) 42 | return str, ok 43 | } 44 | return "", false 45 | } 46 | 47 | // GetBool gets a boolean value from the extensions 48 | func (e Extensions) GetBool(key string) (bool, bool) { 49 | if v, ok := e.GetOK(key); ok { 50 | str, ok := v.(bool) 51 | return str, ok 52 | } 53 | return false, false 54 | } 55 | 56 | // GetInt gets an int value from the extensions 57 | func (e Extensions) GetInt(key string) (int, bool) { 58 | if v, ok := e.GetOK(key); ok { 59 | switch res := v.(type) { 60 | case int: 61 | return res, ok 62 | case int8: 63 | return int(res), ok 64 | case int16: 65 | return int(res), ok 66 | case int32: 67 | return int(res), ok 68 | case int64: 69 | return int(res), ok 70 | default: 71 | return 0, ok 72 | } 73 | } 74 | return 0, false 75 | } 76 | 77 | // GetInt32 gets an int32 value from the extensions 78 | func (e Extensions) GetInt32(key string) (int32, bool) { 79 | if v, ok := e.GetOK(key); ok { 80 | str, ok := v.(int32) 81 | return str, ok 82 | } 83 | return 0, false 84 | } 85 | 86 | // GetInt64 gets an int64 value from the extensions 87 | func (e Extensions) GetInt64(key string) (int64, bool) { 88 | if v, ok := e.GetOK(key); ok { 89 | str, ok := v.(int64) 90 | return str, ok 91 | } 92 | return 0, false 93 | } 94 | 95 | // GetStringSlice gets a string value from the extensions 96 | func (e Extensions) GetStringSlice(key string) ([]string, bool) { 97 | if v, ok := e.GetOK(key); ok { 98 | strv, ok := v.([]string) // get out quick 99 | if ok { 100 | return strv, ok 101 | } 102 | 103 | // do the thing 104 | arr, ok := v.([]interface{}) 105 | if !ok { 106 | return nil, false 107 | } 108 | var strs []string 109 | for _, iface := range arr { 110 | str, ok := iface.(string) 111 | if !ok { 112 | return nil, false 113 | } 114 | strs = append(strs, str) 115 | } 116 | return strs, ok 117 | } 118 | return nil, false 119 | } 120 | 121 | // MarshalJSON supports json.Marshaler interface 122 | func (e Extensions) MarshalJSON() ([]byte, error) { 123 | w := jwriter.Writer{} 124 | e.data.filter = MatchExtension 125 | e.data.normalize = LowerCaseKeys 126 | encodeSortedMap(&w, e.data) 127 | return w.Buffer.BuildBytes(), w.Error 128 | } 129 | 130 | // MarshalEasyJSON supports easyjson.Marshaler interface 131 | func (e Extensions) MarshalEasyJSON(w *jwriter.Writer) { 132 | e.data.filter = MatchExtension 133 | e.data.normalize = LowerCaseKeys 134 | encodeSortedMap(w, e.data) 135 | } 136 | 137 | // UnmarshalJSON supports json.Unmarshaler interface 138 | func (e *Extensions) UnmarshalJSON(data []byte) error { 139 | r := jlexer.Lexer{Data: data} 140 | e.data.filter = MatchExtension 141 | e.data.normalize = LowerCaseKeys 142 | decodeSortedMap(&r, &e.data) 143 | return r.Error() 144 | } 145 | 146 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 147 | func (e *Extensions) UnmarshalEasyJSON(l *jlexer.Lexer) { 148 | e.data.filter = MatchExtension 149 | e.data.normalize = LowerCaseKeys 150 | decodeSortedMap(l, &e.data) 151 | } 152 | 153 | // VendorExtensible composition block. 154 | type VendorExtensible struct { 155 | Extensions Extensions 156 | } 157 | 158 | // AddExtension adds an extension to this extensible object 159 | func (v *VendorExtensible) AddExtension(key string, value interface{}) { 160 | if value == nil { 161 | return 162 | } 163 | v.Extensions.Add(key, value) 164 | } 165 | 166 | // MarshalJSON marshals the extensions to json 167 | func (v VendorExtensible) MarshalJSON() ([]byte, error) { 168 | return json.Marshal(v.Extensions) 169 | } 170 | 171 | // UnmarshalJSON for this extensible object 172 | func (v *VendorExtensible) UnmarshalJSON(data []byte) error { 173 | if err := json.Unmarshal(data, &v.Extensions); err != nil { 174 | return err 175 | } 176 | return nil 177 | } 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OAI object model [![Build Status](https://travis-ci.org/go-openapi/spec3.svg?branch=master)](https://travis-ci.org/go-openapi/spec3) [![codecov](https://codecov.io/gh/go-openapi/spec3/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec3) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) 2 | 3 | [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec3/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/spec3?status.svg)](http://godoc.org/github.com/go-openapi/spec3) 4 | 5 | **This project has been archived on 2025/11/09** 6 | 7 | ***This repository is not usable at this moment, the implementation is incomplete*** 8 | 9 | The object model for OpenAPI specification v3 documents. 10 | 11 | It aims to fix some mistakes that were made in the spec for v2. Top-level maps are now sorted by default so you can rely on their ordering. 12 | 13 | ## Schemas 14 | 15 | | Schema | `struct{}` + `map` | Unit tests (`struct{}` + `map`) | 16 | | ---------------------- | ----------------------------------------------- | --------------------------------------------- | 17 | | OpenAPI | :ballot_box_with_check: | :black_square_button: | 18 | | Info | :ballot_box_with_check: | :black_square_button: | 19 | | Contact | :ballot_box_with_check: | :black_square_button: | 20 | | License | :ballot_box_with_check: | :black_square_button: | 21 | | Server | :ballot_box_with_check: | :black_square_button: | 22 | | Server Variable | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 23 | | Components | :ballot_box_with_check: | :black_square_button: | 24 | | Paths | :ballot_box_with_check: | :black_square_button: | 25 | | Path Item | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 26 | | Operation | :ballot_box_with_check: | :black_square_button: | 27 | | External Documentation | :ballot_box_with_check: | :black_square_button: | 28 | | Parameter | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 29 | | Request Body | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 30 | | Media Type | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 31 | | Encoding | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 32 | | Responses | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 33 | | Response | :ballot_box_with_check: | :black_square_button: | 34 | | Callback | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 35 | | Example | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 36 | | Link | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 37 | | Header | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 38 | | Tag | :ballot_box_with_check: | :black_square_button: | 39 | | Reference | :ballot_box_with_check: | :black_square_button: | 40 | | Schema | :black_square_button: + :black_square_button: | :black_square_button: + :black_square_button: | 41 | | Discriminator | :ballot_box_with_check: | :black_square_button: | 42 | | XML | :ballot_box_with_check: | :black_square_button: | 43 | | Security Scheme | :ballot_box_with_check: + :black_square_button: | :black_square_button: + :black_square_button: | 44 | | OAuth Flows | :ballot_box_with_check: | :black_square_button: | 45 | | OAuth Flow | :ballot_box_with_check: | :black_square_button: | 46 | | Security Requirement | :ballot_box_with_check: | :black_square_button: | 47 | 48 | ## TODO 49 | 50 | - [ ] Update OrderedMap to use ART under the hood instead of the golang's map 51 | - [ ] Use GoJay for decoding/encoding the JSON 52 | - [ ] Retire EasyJson dependency from the project 53 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contribution Guidelines 2 | 3 | ### Pull requests are always welcome 4 | 5 | We are always thrilled to receive pull requests, and do our best to 6 | process them as fast as possible. Not sure if that typo is worth a pull 7 | request? Do it! We will appreciate it. 8 | 9 | If your pull request is not accepted on the first try, don't be 10 | discouraged! If there's a problem with the implementation, hopefully you 11 | received feedback on what to improve. 12 | 13 | We're trying very hard to keep go-swagger lean and focused. We don't want it 14 | to do everything for everybody. This means that we might decide against 15 | incorporating a new feature. However, there might be a way to implement 16 | that feature *on top of* go-swagger. 17 | 18 | 19 | ### Conventions 20 | 21 | Fork the repo and make changes on your fork in a feature branch: 22 | 23 | - If it's a bugfix branch, name it XXX-something where XXX is the number of the 24 | issue 25 | - If it's a feature branch, create an enhancement issue to announce your 26 | intentions, and name it XXX-something where XXX is the number of the issue. 27 | 28 | Submit unit tests for your changes. Go has a great test framework built in; use 29 | it! Take a look at existing tests for inspiration. Run the full test suite on 30 | your branch before submitting a pull request. 31 | 32 | Update the documentation when creating or modifying features. Test 33 | your documentation changes for clarity, concision, and correctness, as 34 | well as a clean documentation build. See ``docs/README.md`` for more 35 | information on building the docs and how docs get released. 36 | 37 | Write clean code. Universally formatted code promotes ease of writing, reading, 38 | and maintenance. Always run `gofmt -s -w file.go` on each changed file before 39 | committing your changes. Most editors have plugins that do this automatically. 40 | 41 | Pull requests descriptions should be as clear as possible and include a 42 | reference to all the issues that they address. 43 | 44 | Pull requests must not contain commits from other users or branches. 45 | 46 | Commit messages must start with a capitalized and short summary (max. 50 47 | chars) written in the imperative, followed by an optional, more detailed 48 | explanatory text which is separated from the summary by an empty line. 49 | 50 | Code review comments may be added to your pull request. Discuss, then make the 51 | suggested modifications and push additional commits to your feature branch. Be 52 | sure to post a comment after pushing. The new commits will show up in the pull 53 | request automatically, but the reviewers will not be notified unless you 54 | comment. 55 | 56 | Before the pull request is merged, make sure that you squash your commits into 57 | logical units of work using `git rebase -i` and `git push -f`. After every 58 | commit the test suite should be passing. Include documentation changes in the 59 | same commit so that a revert would remove all traces of the feature or fix. 60 | 61 | Commits that fix or close an issue should include a reference like `Closes #XXX` 62 | or `Fixes #XXX`, which will automatically close the issue when merged. 63 | 64 | ### Sign your work 65 | 66 | The sign-off is a simple line at the end of the explanation for the 67 | patch, which certifies that you wrote it or otherwise have the right to 68 | pass it on as an open-source patch. The rules are pretty simple: if you 69 | can certify the below (from 70 | [developercertificate.org](http://developercertificate.org/)): 71 | 72 | ``` 73 | Developer Certificate of Origin 74 | Version 1.1 75 | 76 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 77 | 660 York Street, Suite 102, 78 | San Francisco, CA 94110 USA 79 | 80 | Everyone is permitted to copy and distribute verbatim copies of this 81 | license document, but changing it is not allowed. 82 | 83 | 84 | Developer's Certificate of Origin 1.1 85 | 86 | By making a contribution to this project, I certify that: 87 | 88 | (a) The contribution was created in whole or in part by me and I 89 | have the right to submit it under the open source license 90 | indicated in the file; or 91 | 92 | (b) The contribution is based upon previous work that, to the best 93 | of my knowledge, is covered under an appropriate open source 94 | license and I have the right under that license to submit that 95 | work with modifications, whether created in whole or in part 96 | by me, under the same open source license (unless I am 97 | permitted to submit under a different license), as indicated 98 | in the file; or 99 | 100 | (c) The contribution was provided directly to me by some other 101 | person who certified (a), (b) or (c) and I have not modified 102 | it. 103 | 104 | (d) I understand and agree that this project and the contribution 105 | are public and that a record of the contribution (including all 106 | personal information I submit with it, including my sign-off) is 107 | maintained indefinitely and may be redistributed consistent with 108 | this project or the open source license(s) involved. 109 | ``` 110 | 111 | then you just add a line to every git commit message: 112 | 113 | Signed-off-by: Joe Smith 114 | 115 | using your real name (sorry, no pseudonyms or anonymous contributions.) 116 | 117 | You can add the sign off when creating the git commit via `git commit -s`. 118 | -------------------------------------------------------------------------------- /fixtures/specs/refed.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", 7 | "termsOfService": "http://helloreverb.com/terms/", 8 | "contact": { 9 | "name": "Wordnik API Team" 10 | }, 11 | "license": { 12 | "name": "MIT" 13 | } 14 | }, 15 | "host": "petstore.swagger.wordnik.com", 16 | "basePath": "/api", 17 | "schemes": [ 18 | "http" 19 | ], 20 | "consumes": [ 21 | "application/json" 22 | ], 23 | "produces": [ 24 | "application/json" 25 | ], 26 | "parameters": { 27 | "idParam": { 28 | "name": "id", 29 | "in": "path", 30 | "description": "ID of pet to fetch", 31 | "required": true, 32 | "type": "integer", 33 | "format": "int64" 34 | } 35 | }, 36 | "responses": { 37 | "petResponse": { 38 | "description": "pet response", 39 | "schema": { 40 | "$ref": "#/definitions/pet" 41 | } 42 | } 43 | }, 44 | "paths": { 45 | "/pets": { 46 | "get": { 47 | "description": "Returns all pets from the system that the user has access to", 48 | "operationId": "findPets", 49 | "produces": [ 50 | "application/json", 51 | "application/xml", 52 | "text/xml", 53 | "text/html" 54 | ], 55 | "parameters": [ 56 | { 57 | "name": "tags", 58 | "in": "query", 59 | "description": "tags to filter by", 60 | "required": false, 61 | "type": "array", 62 | "items": { 63 | "type": "string" 64 | }, 65 | "collectionFormat": "csv" 66 | }, 67 | { 68 | "name": "limit", 69 | "in": "query", 70 | "description": "maximum number of results to return", 71 | "required": false, 72 | "type": "integer", 73 | "format": "int32" 74 | } 75 | ], 76 | "responses": { 77 | "200": { 78 | "description": "pet response", 79 | "schema": { 80 | "type": "array", 81 | "items": { 82 | "$ref": "#/definitions/pet" 83 | } 84 | } 85 | }, 86 | "default": { 87 | "description": "unexpected error", 88 | "schema": { 89 | "$ref": "#/definitions/errorModel" 90 | } 91 | } 92 | } 93 | }, 94 | "post": { 95 | "description": "Creates a new pet in the store. Duplicates are allowed", 96 | "operationId": "addPet", 97 | "produces": [ 98 | "application/json" 99 | ], 100 | "parameters": [ 101 | { 102 | "name": "pet", 103 | "in": "body", 104 | "description": "Pet to add to the store", 105 | "required": true, 106 | "schema": { 107 | "$ref": "#/definitions/petInput" 108 | } 109 | } 110 | ], 111 | "responses": { 112 | "200": { "$ref": "#/responses/petResponse" }, 113 | "default": { 114 | "description": "unexpected error", 115 | "schema": { 116 | "$ref": "#/definitions/errorModel" 117 | } 118 | } 119 | } 120 | } 121 | }, 122 | "/pets/{id}": { 123 | "get": { 124 | "description": "Returns a user based on a single ID, if the user does not have access to the pet", 125 | "operationId": "findPetById", 126 | "produces": [ 127 | "application/json", 128 | "application/xml", 129 | "text/xml", 130 | "text/html" 131 | ], 132 | "parameters": [ 133 | { 134 | "$ref": "#/parameters/idParam" 135 | } 136 | ], 137 | "responses": { 138 | "200": { 139 | "$ref": "#/responses/petResponse" 140 | }, 141 | "default": { 142 | "description": "unexpected error", 143 | "schema": { 144 | "$ref": "#/definitions/errorModel" 145 | } 146 | } 147 | } 148 | }, 149 | "delete": { 150 | "description": "deletes a single pet based on the ID supplied", 151 | "operationId": "deletePet", 152 | "parameters": [ 153 | { 154 | "$ref": "#/parameters/idParam" 155 | } 156 | ], 157 | "responses": { 158 | "204": { 159 | "description": "pet deleted" 160 | }, 161 | "default": { 162 | "description": "unexpected error", 163 | "schema": { 164 | "$ref": "#/definitions/errorModel" 165 | } 166 | } 167 | } 168 | } 169 | } 170 | }, 171 | "definitions": { 172 | "pet": { 173 | "required": [ 174 | "id", 175 | "name" 176 | ], 177 | "properties": { 178 | "id": { 179 | "type": "integer", 180 | "format": "int64" 181 | }, 182 | "name": { 183 | "type": "string" 184 | }, 185 | "tag": { 186 | "type": "string" 187 | } 188 | } 189 | }, 190 | "petInput": { 191 | "allOf": [ 192 | { 193 | "$ref": "pet" 194 | }, 195 | { 196 | "required": [ 197 | "name" 198 | ], 199 | "properties": { 200 | "id": { 201 | "type": "integer", 202 | "format": "int64" 203 | } 204 | } 205 | } 206 | ] 207 | }, 208 | "errorModel": { 209 | "required": [ 210 | "code", 211 | "message" 212 | ], 213 | "properties": { 214 | "code": { 215 | "type": "integer", 216 | "format": "int32" 217 | }, 218 | "message": { 219 | "type": "string" 220 | } 221 | } 222 | } 223 | } 224 | } -------------------------------------------------------------------------------- /fixtures/expansion/all-the-things.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", 7 | "termsOfService": "http://helloreverb.com/terms/", 8 | "contact": { 9 | "name": "Wordnik API Team" 10 | }, 11 | "license": { 12 | "name": "MIT" 13 | } 14 | }, 15 | "host": "petstore.swagger.wordnik.com", 16 | "basePath": "/api", 17 | "schemes": [ 18 | "http" 19 | ], 20 | "consumes": [ 21 | "application/json" 22 | ], 23 | "produces": [ 24 | "application/json" 25 | ], 26 | "parameters": { 27 | "idParam": { 28 | "name": "id", 29 | "in": "path", 30 | "description": "ID of pet to fetch", 31 | "required": true, 32 | "type": "integer", 33 | "format": "int64" 34 | }, 35 | "tag": { 36 | "type": "string", 37 | "in": "query", 38 | "required": false 39 | }, 40 | "query": { 41 | "$ref": "#/parameters/tag" 42 | } 43 | }, 44 | "responses": { 45 | "petResponse": { 46 | "description": "pet response", 47 | "schema": { 48 | "$ref": "#/definitions/pet" 49 | } 50 | }, 51 | "stringResponse": { 52 | "descripion": "string response", 53 | "schema": { 54 | "type": "string" 55 | } 56 | }, 57 | "anotherPet": { 58 | "$ref": "#/responses/petResponse" 59 | } 60 | }, 61 | "paths": { 62 | "/": { 63 | "get": { 64 | "operationId": "indexStuff", 65 | "responses": { 66 | "default": { 67 | "$ref": "#/responses/stringResponse" 68 | }, 69 | "200": { 70 | "$ref": "#/responses/anotherPet" 71 | } 72 | } 73 | } 74 | }, 75 | "/pets": { 76 | "get": { 77 | "description": "Returns all pets from the system that the user has access to", 78 | "operationId": "findPets", 79 | "produces": [ 80 | "application/json", 81 | "application/xml", 82 | "text/xml", 83 | "text/html" 84 | ], 85 | "parameters": [ 86 | { 87 | "name": "tags", 88 | "in": "query", 89 | "description": "tags to filter by", 90 | "required": false, 91 | "type": "array", 92 | "items": { 93 | "type": "string" 94 | }, 95 | "collectionFormat": "csv" 96 | }, 97 | { 98 | "name": "limit", 99 | "in": "query", 100 | "description": "maximum number of results to return", 101 | "required": false, 102 | "type": "integer", 103 | "format": "int32" 104 | } 105 | ], 106 | "responses": { 107 | "200": { 108 | "description": "pet response", 109 | "schema": { 110 | "type": "array", 111 | "items": { 112 | "$ref": "#/definitions/pet" 113 | } 114 | } 115 | }, 116 | "default": { 117 | "description": "unexpected error", 118 | "schema": { 119 | "$ref": "#/definitions/errorModel" 120 | } 121 | } 122 | } 123 | }, 124 | "post": { 125 | "description": "Creates a new pet in the store. Duplicates are allowed", 126 | "operationId": "addPet", 127 | "produces": [ 128 | "application/json" 129 | ], 130 | "parameters": [ 131 | { 132 | "name": "pet", 133 | "in": "body", 134 | "description": "Pet to add to the store", 135 | "required": true, 136 | "schema": { 137 | "$ref": "#/definitions/petInput" 138 | } 139 | } 140 | ], 141 | "responses": { 142 | "200": { "$ref": "#/responses/petResponse" }, 143 | "default": { 144 | "description": "unexpected error", 145 | "schema": { 146 | "$ref": "#/definitions/errorModel" 147 | } 148 | } 149 | } 150 | } 151 | }, 152 | "/pets/{id}": { 153 | "get": { 154 | "description": "Returns a user based on a single ID, if the user does not have access to the pet", 155 | "operationId": "findPetById", 156 | "produces": [ 157 | "application/json", 158 | "application/xml", 159 | "text/xml", 160 | "text/html" 161 | ], 162 | "parameters": [ 163 | { 164 | "$ref": "#/parameters/idParam" 165 | } 166 | ], 167 | "responses": { 168 | "200": { 169 | "$ref": "#/responses/petResponse" 170 | }, 171 | "default": { 172 | "description": "unexpected error", 173 | "schema": { 174 | "$ref": "#/definitions/errorModel" 175 | } 176 | } 177 | } 178 | }, 179 | "delete": { 180 | "description": "deletes a single pet based on the ID supplied", 181 | "operationId": "deletePet", 182 | "parameters": [ 183 | { 184 | "$ref": "#/parameters/idParam" 185 | } 186 | ], 187 | "responses": { 188 | "204": { 189 | "description": "pet deleted" 190 | }, 191 | "default": { 192 | "description": "unexpected error", 193 | "schema": { 194 | "$ref": "#/definitions/errorModel" 195 | } 196 | } 197 | } 198 | } 199 | } 200 | }, 201 | "definitions": { 202 | "pet": { 203 | "required": [ 204 | "id", 205 | "name" 206 | ], 207 | "properties": { 208 | "id": { 209 | "type": "integer", 210 | "format": "int64" 211 | }, 212 | "name": { 213 | "type": "string" 214 | }, 215 | "tag": { 216 | "type": "string" 217 | } 218 | } 219 | }, 220 | "petInput": { 221 | "allOf": [ 222 | { 223 | "$ref": "#/definitions/pet" 224 | }, 225 | { 226 | "required": [ 227 | "name" 228 | ], 229 | "properties": { 230 | "id": { 231 | "type": "integer", 232 | "format": "int64" 233 | } 234 | } 235 | } 236 | ] 237 | }, 238 | "errorModel": { 239 | "required": [ 240 | "code", 241 | "message" 242 | ], 243 | "properties": { 244 | "code": { 245 | "type": "integer", 246 | "format": "int32" 247 | }, 248 | "message": { 249 | "type": "string" 250 | } 251 | } 252 | } 253 | } 254 | } -------------------------------------------------------------------------------- /link_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedLinks_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *Link 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedLinks()}, args{"skipParam"}, &Link{Description: "default parameter"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedLinks()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedLinks{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedLinks.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedLinks_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantLink *Link 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedLinks()}, args{"limitParam"}, &Link{Description: "OK"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedLinks()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedLinks{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantLink) { 60 | t.Errorf("OrderedLinks.GetOK() got = %v, want %v", got, tt.wantLink) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedLinks.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedLinks_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *Link 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedLinks()}, args{"getParam", &Link{Description: "Getting OrderedLinks"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedLinks()}, args{"limitParam", &Link{Description: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedLinks()}, args{"", &Link{Description: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedLinks{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedLinks.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedLinks.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedLinks.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedLinks_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *Link) error 115 | } 116 | type foundLink struct { 117 | parameter *Link 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundLink 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedLinks fixture", 128 | fields{buildOrderMapForOrderedLinks()}, 129 | map[string]*foundLink{ 130 | "skipParam": &foundLink{&Link{Description: "default parameter"}, false}, 131 | "limitParam": &foundLink{&Link{Description: "OK"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedLinks", 137 | fields{}, 138 | map[string]*foundLink{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedLinks{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotLink *Link) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotLink) { 150 | t.Fatalf("OrderedLinks.ForEach() for key = %s val = %v, want = %v", key, gotLink, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedLinks.ForEach() for key = %s val = %v, want = %v", key, gotLink, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedLinks.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedLinks.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedLinks_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedLinks fixture", fields{buildOrderMapForOrderedLinks()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedLinks", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedLinks{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedLinks.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedLinks() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedLinks() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &Link{Description: "default parameter"}, 213 | "limitParam": &Link{Description: "OK"}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedLinksFixture() OrderedLinks { 224 | m := OrderedLinks{ 225 | data: buildOrderMapForOrderedLinks(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedExamples_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *Example 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedExamples()}, args{"skipParam"}, &Example{Description: "default parameter"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedExamples()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedExamples{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedExamples.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedExamples_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantExample *Example 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedExamples()}, args{"limitParam"}, &Example{Description: "OK"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedExamples()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedExamples{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantExample) { 60 | t.Errorf("OrderedExamples.GetOK() got = %v, want %v", got, tt.wantExample) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedExamples.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedExamples_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *Example 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedExamples()}, args{"getParam", &Example{Description: "Getting OrderedExamples"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedExamples()}, args{"limitParam", &Example{Description: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedExamples()}, args{"", &Example{Description: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedExamples{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedExamples.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedExamples.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedExamples.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedExamples_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *Example) error 115 | } 116 | type foundExample struct { 117 | parameter *Example 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundExample 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedExamples fixture", 128 | fields{buildOrderMapForOrderedExamples()}, 129 | map[string]*foundExample{ 130 | "skipParam": &foundExample{&Example{Description: "default parameter"}, false}, 131 | "limitParam": &foundExample{&Example{Description: "OK"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedExamples", 137 | fields{}, 138 | map[string]*foundExample{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedExamples{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotExample *Example) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotExample) { 150 | t.Fatalf("OrderedExamples.ForEach() for key = %s val = %v, want = %v", key, gotExample, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedExamples.ForEach() for key = %s val = %v, want = %v", key, gotExample, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedExamples.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedExamples.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedExamples_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedExamples fixture", fields{buildOrderMapForOrderedExamples()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedExamples", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedExamples{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedExamples.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedExamples() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedExamples() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &Example{Description: "default parameter"}, 213 | "limitParam": &Example{Description: "OK"}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedExamplesFixture() OrderedExamples { 224 | m := OrderedExamples{ 225 | data: buildOrderMapForOrderedExamples(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /header_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedHeaders_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *Header 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedHeaders()}, args{"skipParam"}, &Header{Parameter{Description: "default parameter"}}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedHeaders()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedHeaders{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedHeaders.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedHeaders_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantHeader *Header 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedHeaders()}, args{"limitParam"}, &Header{Parameter{Description: "OK"}}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedHeaders()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedHeaders{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantHeader) { 60 | t.Errorf("OrderedHeaders.GetOK() got = %v, want %v", got, tt.wantHeader) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedHeaders.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedHeaders_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *Header 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedHeaders()}, args{"getParam", &Header{Parameter{Description: "Getting OrderedHeaders"}}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedHeaders()}, args{"limitParam", &Header{Parameter{Description: "another OK"}}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedHeaders()}, args{"", &Header{Parameter{Description: "description of item #empty"}}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedHeaders{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedHeaders.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedHeaders.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedHeaders.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedHeaders_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *Header) error 115 | } 116 | type foundHeader struct { 117 | parameter *Header 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundHeader 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedHeaders fixture", 128 | fields{buildOrderMapForOrderedHeaders()}, 129 | map[string]*foundHeader{ 130 | "skipParam": &foundHeader{&Header{Parameter{Description: "default parameter"}}, false}, 131 | "limitParam": &foundHeader{&Header{Parameter{Description: "OK"}}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedHeaders", 137 | fields{}, 138 | map[string]*foundHeader{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedHeaders{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotHeader *Header) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotHeader) { 150 | t.Fatalf("OrderedHeaders.ForEach() for key = %s val = %v, want = %v", key, gotHeader, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedHeaders.ForEach() for key = %s val = %v, want = %v", key, gotHeader, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedHeaders.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedHeaders.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedHeaders_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedHeaders fixture", fields{buildOrderMapForOrderedHeaders()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedHeaders", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedHeaders{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedHeaders.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedHeaders() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedHeaders() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &Header{Parameter{Description: "default parameter"}}, 213 | "limitParam": &Header{Parameter{Description: "OK"}}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedHeadersFixture() OrderedHeaders { 224 | m := OrderedHeaders{ 225 | data: buildOrderMapForOrderedHeaders(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /schema_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedSchemas_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *Schema 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedSchemas()}, args{"skipParam"}, buildSchema("default parameter")}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedSchemas()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedSchemas{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedSchemas.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedSchemas_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantSchema *Schema 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedSchemas()}, args{"limitParam"}, buildSchema("OK"), true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedSchemas()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedSchemas{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantSchema) { 60 | t.Errorf("OrderedSchemas.GetOK() got = %v, want %v", got, tt.wantSchema) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedSchemas.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedSchemas_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *Schema 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedSchemas()}, args{"getParam", buildSchema("Getting OrderedSchemas")}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedSchemas()}, args{"limitParam", buildSchema("another OK")}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedSchemas()}, args{"", buildSchema("description of item #empty")}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedSchemas{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedSchemas.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedSchemas.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedSchemas.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedSchemas_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *Schema) error 115 | } 116 | type foundSchema struct { 117 | parameter *Schema 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundSchema 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedSchemas fixture", 128 | fields{buildOrderMapForOrderedSchemas()}, 129 | map[string]*foundSchema{ 130 | "skipParam": &foundSchema{buildSchema("default parameter"), false}, 131 | "limitParam": &foundSchema{buildSchema("OK"), false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedSchemas", 137 | fields{}, 138 | map[string]*foundSchema{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedSchemas{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotSchema *Schema) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotSchema) { 150 | t.Fatalf("OrderedSchemas.ForEach() for key = %s val = %v, want = %v", key, gotSchema, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedSchemas.ForEach() for key = %s val = %v, want = %v", key, gotSchema, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedSchemas.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedSchemas.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedSchemas_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedSchemas fixture", fields{buildOrderMapForOrderedSchemas()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedSchemas", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedSchemas{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedSchemas.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildSchema(value string) *Schema { 204 | extensions := Extensions{} 205 | extensions.Set("x-some-key", value) 206 | return &Schema{VendorExtensible{extensions}, Reference{}} 207 | } 208 | 209 | func buildEmptyOrderMapForOrderedSchemas() OrderedMap { 210 | return OrderedMap{ 211 | filter: MatchNonEmptyKeys, 212 | } 213 | } 214 | 215 | func buildOrderMapForOrderedSchemas() OrderedMap { 216 | return OrderedMap{ 217 | data: map[string]interface{}{ 218 | "skipParam": buildSchema("default parameter"), 219 | "limitParam": buildSchema("OK"), 220 | }, 221 | keys: []string{ 222 | "skipParam", 223 | "limitParam", 224 | }, 225 | filter: MatchNonEmptyKeys, 226 | } 227 | } 228 | 229 | func buildOrderedSchemasFixture() OrderedSchemas { 230 | m := OrderedSchemas{ 231 | data: buildOrderMapForOrderedSchemas(), 232 | } 233 | 234 | return m 235 | } 236 | -------------------------------------------------------------------------------- /media_type_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedMediaTypes_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *MediaType 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedMediaTypes()}, args{"skipParam"}, &MediaType{Example: "default parameter"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedMediaTypes()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedMediaTypes{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedMediaTypes.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedMediaTypes_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantMediaType *MediaType 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedMediaTypes()}, args{"limitParam"}, &MediaType{Example: "OK"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedMediaTypes()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedMediaTypes{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantMediaType) { 60 | t.Errorf("OrderedMediaTypes.GetOK() got = %v, want %v", got, tt.wantMediaType) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedMediaTypes.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedMediaTypes_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *MediaType 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedMediaTypes()}, args{"getParam", &MediaType{Example: "Getting OrderedMediaTypes"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedMediaTypes()}, args{"limitParam", &MediaType{Example: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedMediaTypes()}, args{"", &MediaType{Example: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedMediaTypes{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedMediaTypes.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedMediaTypes.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedMediaTypes.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedMediaTypes_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *MediaType) error 115 | } 116 | type foundMediaType struct { 117 | parameter *MediaType 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundMediaType 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedMediaTypes fixture", 128 | fields{buildOrderMapForOrderedMediaTypes()}, 129 | map[string]*foundMediaType{ 130 | "skipParam": &foundMediaType{&MediaType{Example: "default parameter"}, false}, 131 | "limitParam": &foundMediaType{&MediaType{Example: "OK"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedMediaTypes", 137 | fields{}, 138 | map[string]*foundMediaType{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedMediaTypes{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotMediaType *MediaType) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotMediaType) { 150 | t.Fatalf("OrderedMediaTypes.ForEach() for key = %s val = %v, want = %v", key, gotMediaType, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedMediaTypes.ForEach() for key = %s val = %v, want = %v", key, gotMediaType, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedMediaTypes.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedMediaTypes.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedMediaTypes_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedMediaTypes fixture", fields{buildOrderMapForOrderedMediaTypes()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedMediaTypes", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedMediaTypes{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedMediaTypes.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedMediaTypes() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedMediaTypes() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &MediaType{Example: "default parameter"}, 213 | "limitParam": &MediaType{Example: "OK"}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedMediaTypesFixture() OrderedMediaTypes { 224 | m := OrderedMediaTypes{ 225 | data: buildOrderMapForOrderedMediaTypes(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /parameter_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedParameters_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *Parameter 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedParameters()}, args{"skipParam"}, &Parameter{Description: "default parameter"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedParameters()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedParameters{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedParameters.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedParameters_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantParameter *Parameter 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedParameters()}, args{"limitParam"}, &Parameter{Description: "OK"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedParameters()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedParameters{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantParameter) { 60 | t.Errorf("OrderedParameters.GetOK() got = %v, want %v", got, tt.wantParameter) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedParameters.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedParameters_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *Parameter 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedParameters()}, args{"getParam", &Parameter{Description: "Getting OrderedParameters"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedParameters()}, args{"limitParam", &Parameter{Description: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedParameters()}, args{"", &Parameter{Description: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedParameters{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedParameters.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedParameters.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedParameters.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedParameters_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *Parameter) error 115 | } 116 | type foundParameter struct { 117 | parameter *Parameter 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundParameter 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedParameters fixture", 128 | fields{buildOrderMapForOrderedParameters()}, 129 | map[string]*foundParameter{ 130 | "skipParam": &foundParameter{&Parameter{Description: "default parameter"}, false}, 131 | "limitParam": &foundParameter{&Parameter{Description: "OK"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedParameters", 137 | fields{}, 138 | map[string]*foundParameter{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedParameters{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotParameter *Parameter) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotParameter) { 150 | t.Fatalf("OrderedParameters.ForEach() for key = %s val = %v, want = %v", key, gotParameter, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedParameters.ForEach() for key = %s val = %v, want = %v", key, gotParameter, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedParameters.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedParameters.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedParameters_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedParameters fixture", fields{buildOrderMapForOrderedParameters()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedParameters", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedParameters{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedParameters.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedParameters() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedParameters() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &Parameter{Description: "default parameter"}, 213 | "limitParam": &Parameter{Description: "OK"}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedParametersFixture() OrderedParameters { 224 | m := OrderedParameters{ 225 | data: buildOrderMapForOrderedParameters(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /encoding_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedEncodings_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *Encoding 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedEncodings()}, args{"historyMetadata"}, &Encoding{ContentType: "application/xml; charset=utf-8"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedEncodings()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedEncodings{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedEncodings.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedEncodings_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantEncoding *Encoding 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedEncodings()}, args{"profileImage"}, &Encoding{ContentType: "image/png, image/jpeg"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedEncodings()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedEncodings{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantEncoding) { 60 | t.Errorf("OrderedEncodings.GetOK() got = %v, want %v", got, tt.wantEncoding) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedEncodings.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedEncodings_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *Encoding 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedEncodings()}, args{"getParam", &Encoding{ContentType: "Getting OrderedEncodings"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedEncodings()}, args{"profileImage", &Encoding{ContentType: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedEncodings()}, args{"", &Encoding{ContentType: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedEncodings{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedEncodings.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedEncodings.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedEncodings.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedEncodings_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *Encoding) error 115 | } 116 | type foundEncoding struct { 117 | parameter *Encoding 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundEncoding 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedEncodings fixture", 128 | fields{buildOrderMapForOrderedEncodings()}, 129 | map[string]*foundEncoding{ 130 | "historyMetadata": &foundEncoding{&Encoding{ContentType: "application/xml; charset=utf-8"}, false}, 131 | "profileImage": &foundEncoding{&Encoding{ContentType: "image/png, image/jpeg"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedEncodings", 137 | fields{}, 138 | map[string]*foundEncoding{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedEncodings{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotEncoding *Encoding) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotEncoding) { 150 | t.Fatalf("OrderedEncodings.ForEach() for key = %s val = %v, want = %v", key, gotEncoding, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedEncodings.ForEach() for key = %s val = %v, want = %v", key, gotEncoding, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedEncodings.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedEncodings.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedEncodings_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedEncodings fixture", fields{buildOrderMapForOrderedEncodings()}, []string{"historyMetadata", "profileImage"}}, 186 | {"Should return empty array when there are no values in OrderedEncodings", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedEncodings{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedEncodings.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedEncodings() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedEncodings() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "historyMetadata": &Encoding{ContentType: "application/xml; charset=utf-8"}, 213 | "profileImage": &Encoding{ContentType: "image/png, image/jpeg"}, 214 | }, 215 | keys: []string{ 216 | "historyMetadata", 217 | "profileImage", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedEncodingsFixture() OrderedEncodings { 224 | m := OrderedEncodings{ 225 | data: buildOrderMapForOrderedEncodings(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /server_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestServerVariables_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *ServerVariable 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForServerVariables()}, args{"skipParam"}, &ServerVariable{Description: "default parameter"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForServerVariables()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &ServerVariables{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("ServerVariables.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestServerVariables_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantServerVariable *ServerVariable 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForServerVariables()}, args{"limitParam"}, &ServerVariable{Description: "OK"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForServerVariables()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &ServerVariables{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantServerVariable) { 60 | t.Errorf("ServerVariables.GetOK() got = %v, want %v", got, tt.wantServerVariable) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("ServerVariables.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestServerVariables_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *ServerVariable 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForServerVariables()}, args{"getParam", &ServerVariable{Description: "Getting ServerVariables"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForServerVariables()}, args{"limitParam", &ServerVariable{Description: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForServerVariables()}, args{"", &ServerVariable{Description: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &ServerVariables{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("ServerVariables.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("ServerVariables.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("ServerVariables.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestServerVariables_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *ServerVariable) error 115 | } 116 | type foundServerVariable struct { 117 | parameter *ServerVariable 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundServerVariable 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for ServerVariables fixture", 128 | fields{buildOrderMapForServerVariables()}, 129 | map[string]*foundServerVariable{ 130 | "skipParam": &foundServerVariable{&ServerVariable{Description: "default parameter"}, false}, 131 | "limitParam": &foundServerVariable{&ServerVariable{Description: "OK"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in ServerVariables", 137 | fields{}, 138 | map[string]*foundServerVariable{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &ServerVariables{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotServerVariable *ServerVariable) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotServerVariable) { 150 | t.Fatalf("ServerVariables.ForEach() for key = %s val = %v, want = %v", key, gotServerVariable, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("ServerVariables.ForEach() for key = %s val = %v, want = %v", key, gotServerVariable, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("ServerVariables.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("ServerVariables.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestServerVariables_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for ServerVariables fixture", fields{buildOrderMapForServerVariables()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in ServerVariables", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &ServerVariables{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("ServerVariables.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForServerVariables() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForServerVariables() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &ServerVariable{Description: "default parameter"}, 213 | "limitParam": &ServerVariable{Description: "OK"}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildServerVariablesFixture() ServerVariables { 224 | m := ServerVariables{ 225 | data: buildOrderMapForServerVariables(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /request_body_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedRequestBodies_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *RequestBody 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedRequestBodies()}, args{"skipParam"}, &RequestBody{Description: "default parameter"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedRequestBodies()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedRequestBodies{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedRequestBodies.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedRequestBodies_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantRequestBody *RequestBody 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedRequestBodies()}, args{"limitParam"}, &RequestBody{Description: "OK"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedRequestBodies()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedRequestBodies{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantRequestBody) { 60 | t.Errorf("OrderedRequestBodies.GetOK() got = %v, want %v", got, tt.wantRequestBody) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedRequestBodies.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedRequestBodies_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *RequestBody 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedRequestBodies()}, args{"getParam", &RequestBody{Description: "Getting OrderedRequestBodies"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedRequestBodies()}, args{"limitParam", &RequestBody{Description: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedRequestBodies()}, args{"", &RequestBody{Description: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedRequestBodies{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedRequestBodies.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedRequestBodies.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedRequestBodies.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedRequestBodies_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *RequestBody) error 115 | } 116 | type foundRequestBody struct { 117 | parameter *RequestBody 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundRequestBody 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedRequestBodies fixture", 128 | fields{buildOrderMapForOrderedRequestBodies()}, 129 | map[string]*foundRequestBody{ 130 | "skipParam": &foundRequestBody{&RequestBody{Description: "default parameter"}, false}, 131 | "limitParam": &foundRequestBody{&RequestBody{Description: "OK"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedRequestBodies", 137 | fields{}, 138 | map[string]*foundRequestBody{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedRequestBodies{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotRequestBody *RequestBody) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotRequestBody) { 150 | t.Fatalf("OrderedRequestBodies.ForEach() for key = %s val = %v, want = %v", key, gotRequestBody, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedRequestBodies.ForEach() for key = %s val = %v, want = %v", key, gotRequestBody, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedRequestBodies.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedRequestBodies.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedRequestBodies_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedRequestBodies fixture", fields{buildOrderMapForOrderedRequestBodies()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedRequestBodies", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedRequestBodies{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedRequestBodies.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedRequestBodies() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedRequestBodies() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &RequestBody{Description: "default parameter"}, 213 | "limitParam": &RequestBody{Description: "OK"}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedRequestBodiesFixture() OrderedRequestBodies { 224 | m := OrderedRequestBodies{ 225 | data: buildOrderMapForOrderedRequestBodies(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /security_scheme_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestOrderedSecuritySchemes_Get(t *testing.T) { 9 | type fields struct { 10 | data OrderedMap 11 | } 12 | type args struct { 13 | key string 14 | } 15 | tests := []struct { 16 | name string 17 | fields fields 18 | args args 19 | want *SecurityScheme 20 | }{ 21 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedSecuritySchemes()}, args{"skipParam"}, &SecurityScheme{Description: "default parameter"}}, 22 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedSecuritySchemes()}, args{"getParam"}, nil}, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | s := &OrderedSecuritySchemes{ 27 | data: tt.fields.data, 28 | } 29 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("OrderedSecuritySchemes.Get() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestOrderedSecuritySchemes_GetOK(t *testing.T) { 37 | type fields struct { 38 | data OrderedMap 39 | } 40 | type args struct { 41 | key string 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | wantSecurityScheme *SecurityScheme 48 | wantOK bool 49 | }{ 50 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedSecuritySchemes()}, args{"limitParam"}, &SecurityScheme{Description: "OK"}, true}, 51 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedSecuritySchemes()}, args{"getParam"}, nil, false}, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | s := &OrderedSecuritySchemes{ 56 | data: tt.fields.data, 57 | } 58 | got, got1 := s.GetOK(tt.args.key) 59 | if !reflect.DeepEqual(got, tt.wantSecurityScheme) { 60 | t.Errorf("OrderedSecuritySchemes.GetOK() got = %v, want %v", got, tt.wantSecurityScheme) 61 | } 62 | if got1 != tt.wantOK { 63 | t.Errorf("OrderedSecuritySchemes.GetOK() got1 = %v, want %v", got1, tt.wantOK) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestOrderedSecuritySchemes_Set(t *testing.T) { 70 | type fields struct { 71 | data OrderedMap 72 | } 73 | type args struct { 74 | key string 75 | val *SecurityScheme 76 | } 77 | tests := []struct { 78 | name string 79 | fields fields 80 | args args 81 | wantOK bool 82 | }{ 83 | {"Should set value when non-existent parameter code is passed", fields{buildOrderMapForOrderedSecuritySchemes()}, args{"getParam", &SecurityScheme{Description: "Getting OrderedSecuritySchemes"}}, true}, 84 | {"Should fail when existent parameter code is passed", fields{buildOrderMapForOrderedSecuritySchemes()}, args{"limitParam", &SecurityScheme{Description: "another OK"}}, false}, 85 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedSecuritySchemes()}, args{"", &SecurityScheme{Description: "description of item #empty"}}, false}, 86 | } 87 | for _, tt := range tests { 88 | t.Run(tt.name, func(t *testing.T) { 89 | s := &OrderedSecuritySchemes{ 90 | data: tt.fields.data, 91 | } 92 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 93 | t.Fatalf("OrderedSecuritySchemes.Set() = %v, want %v", got, tt.wantOK) 94 | } 95 | 96 | if tt.wantOK { 97 | gotVal, gotOK := s.GetOK(tt.args.key) 98 | if !gotOK { 99 | t.Fatalf("OrderedSecuritySchemes.GetOK().OK = %v, want %v", gotOK, true) 100 | } 101 | if !reflect.DeepEqual(gotVal, tt.args.val) { 102 | t.Fatalf("OrderedSecuritySchemes.GetOK().val = %v, want %v", gotVal, tt.args.val) 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func TestOrderedSecuritySchemes_ForEach(t *testing.T) { 110 | type fields struct { 111 | data OrderedMap 112 | } 113 | type args struct { 114 | fn func(string, *SecurityScheme) error 115 | } 116 | type foundSecurityScheme struct { 117 | parameter *SecurityScheme 118 | found bool 119 | } 120 | tests := []struct { 121 | name string 122 | fields fields 123 | wantValInForEach map[string]*foundSecurityScheme 124 | wantErr error 125 | }{ 126 | { 127 | "Should iterate 4 items for OrderedSecuritySchemes fixture", 128 | fields{buildOrderMapForOrderedSecuritySchemes()}, 129 | map[string]*foundSecurityScheme{ 130 | "skipParam": &foundSecurityScheme{&SecurityScheme{Description: "default parameter"}, false}, 131 | "limitParam": &foundSecurityScheme{&SecurityScheme{Description: "OK"}, false}, 132 | }, 133 | nil, 134 | }, 135 | { 136 | "Should return empty array when there are no values in OrderedSecuritySchemes", 137 | fields{}, 138 | map[string]*foundSecurityScheme{}, 139 | nil, 140 | }, 141 | } 142 | for _, tt := range tests { 143 | t.Run(tt.name, func(t *testing.T) { 144 | s := &OrderedSecuritySchemes{ 145 | data: tt.fields.data, 146 | } 147 | err := s.ForEach(func(key string, gotSecurityScheme *SecurityScheme) error { 148 | if wantVal, ok := tt.wantValInForEach[key]; ok { 149 | if !reflect.DeepEqual(wantVal.parameter, gotSecurityScheme) { 150 | t.Fatalf("OrderedSecuritySchemes.ForEach() for key = %s val = %v, want = %v", key, gotSecurityScheme, wantVal.parameter) 151 | } 152 | wantVal.found = true 153 | } else { 154 | t.Fatalf("OrderedSecuritySchemes.ForEach() for key = %s val = %v, want = %v", key, gotSecurityScheme, wantVal) 155 | } 156 | return nil 157 | }) 158 | 159 | if err == nil && tt.wantErr == nil { 160 | // nothing to assert here 161 | } else if err != tt.wantErr { 162 | t.Fatalf("OrderedSecuritySchemes.ForEach() error = %v, wantErr %v", err, tt.wantErr) 163 | } 164 | 165 | if tt.wantErr == nil { 166 | for key2, val2 := range tt.wantValInForEach { 167 | if !val2.found { 168 | t.Fatalf("OrderedSecuritySchemes.ForEach() key = %s not found during foreach()", key2) 169 | } 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | func TestOrderedSecuritySchemes_Keys(t *testing.T) { 177 | type fields struct { 178 | data OrderedMap 179 | } 180 | tests := []struct { 181 | name string 182 | fields fields 183 | wantKeys []string 184 | }{ 185 | {"Should return 2 items for OrderedSecuritySchemes fixture", fields{buildOrderMapForOrderedSecuritySchemes()}, []string{"skipParam", "limitParam"}}, 186 | {"Should return empty array when there are no values in OrderedSecuritySchemes", fields{}, []string{}}, 187 | } 188 | for _, tt := range tests { 189 | t.Run(tt.name, func(t *testing.T) { 190 | s := &OrderedSecuritySchemes{ 191 | data: tt.fields.data, 192 | } 193 | got := s.Keys() 194 | if len(got) != 0 || len(tt.wantKeys) != 0 { 195 | if !reflect.DeepEqual(got, tt.wantKeys) { 196 | t.Errorf("OrderedSecuritySchemes.Keys() = %v, want %v", got, tt.wantKeys) 197 | } 198 | } 199 | }) 200 | } 201 | } 202 | 203 | func buildEmptyOrderMapForOrderedSecuritySchemes() OrderedMap { 204 | return OrderedMap{ 205 | filter: MatchNonEmptyKeys, 206 | } 207 | } 208 | 209 | func buildOrderMapForOrderedSecuritySchemes() OrderedMap { 210 | return OrderedMap{ 211 | data: map[string]interface{}{ 212 | "skipParam": &SecurityScheme{Description: "default parameter"}, 213 | "limitParam": &SecurityScheme{Description: "OK"}, 214 | }, 215 | keys: []string{ 216 | "skipParam", 217 | "limitParam", 218 | }, 219 | filter: MatchNonEmptyKeys, 220 | } 221 | } 222 | 223 | func buildOrderedSecuritySchemesFixture() OrderedSecuritySchemes { 224 | m := OrderedSecuritySchemes{ 225 | data: buildOrderMapForOrderedSecuritySchemes(), 226 | } 227 | 228 | return m 229 | } 230 | -------------------------------------------------------------------------------- /orderedmap.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "strings" 8 | 9 | "github.com/mailru/easyjson" 10 | "github.com/mailru/easyjson/jlexer" 11 | "github.com/mailru/easyjson/jwriter" 12 | ) 13 | 14 | // MapEntry represents a key value pair 15 | type MapEntry struct { 16 | Key string 17 | Value interface{} 18 | } 19 | 20 | // Filter for deciding which keys make it into the sorted map 21 | type Filter func(string) bool 22 | 23 | // MatchAll keys, is used as default filter 24 | func MatchAll(_ string) bool { return true } 25 | 26 | // MatchNonEmptyKeys keys, is used to allow only non empty strings 27 | func MatchNonEmptyKeys(key string) bool { return key != "" } 28 | 29 | // MatchExtension is used as filter for vendor extensions 30 | func MatchExtension(key string) bool { return strings.HasPrefix(key, "x-") } 31 | 32 | // Normalizer is used to normalize keys when writing to a map 33 | type Normalizer func(string) string 34 | 35 | // LowerCaseKeys lowercases keys when looking up in the map 36 | var LowerCaseKeys = strings.ToLower 37 | 38 | // NOPNormalizer passes the key through, used as default 39 | func NOPNormalizer(s string) string { return s } 40 | 41 | // OrderedMap is a map that preserves insertion order 42 | type OrderedMap struct { 43 | filter Filter 44 | normalize Normalizer 45 | data map[string]interface{} 46 | keys []string 47 | } 48 | 49 | // Len of the known keys 50 | func (s *OrderedMap) Len() int { 51 | return len(s.keys) 52 | } 53 | 54 | // GetOK get a value for the specified key, the boolean result indicates if the value was found or not 55 | func (s *OrderedMap) GetOK(key string) (interface{}, bool) { 56 | v, ok := s.data[s.normalizeKey(key)] 57 | return v, ok 58 | } 59 | 60 | // Get get a value for the specified key 61 | func (s *OrderedMap) Get(key string) interface{} { 62 | val, ok := s.data[s.normalizeKey(key)] 63 | if !ok { 64 | return nil 65 | } 66 | return val 67 | } 68 | 69 | func (s *OrderedMap) normalizeKey(key string) string { 70 | if s.normalize == nil { 71 | s.normalize = NOPNormalizer 72 | } 73 | 74 | return s.normalize(key) 75 | } 76 | 77 | func (s *OrderedMap) allows(key string) bool { 78 | if s.filter == nil { 79 | s.filter = MatchAll 80 | } 81 | return s.filter(key) 82 | } 83 | 84 | // Set a value in the map 85 | func (s *OrderedMap) Set(key string, value interface{}) bool { 86 | key = s.normalizeKey(key) 87 | if !s.allows(key) { 88 | return false 89 | } 90 | 91 | if s.data == nil { 92 | s.data = make(map[string]interface{}) 93 | } 94 | _, ok := s.data[key] 95 | s.data[key] = value 96 | if !ok { 97 | s.keys = append(s.keys, key) 98 | } 99 | return !ok 100 | } 101 | 102 | // Delete a value from the map 103 | func (s *OrderedMap) Delete(k string) bool { 104 | key := s.normalizeKey(k) 105 | if !s.allows(key) { 106 | return false 107 | } 108 | 109 | _, ok := s.data[key] 110 | if !ok { 111 | return false 112 | } 113 | 114 | delete(s.data, key) 115 | for i, k := range s.keys { 116 | if k == key { 117 | s.keys = append(s.keys[:i], s.keys[i+1:]...) 118 | } 119 | } 120 | 121 | if len(s.keys) == 0 { 122 | s.data = nil 123 | s.keys = nil 124 | } 125 | return ok 126 | } 127 | 128 | // Keys in the order of addition to the map 129 | func (s *OrderedMap) Keys() []string { 130 | return s.keys[:] 131 | } 132 | 133 | // ForEach executes a function for each value in the map 134 | func (s *OrderedMap) ForEach(fn func(string, interface{}) error) error { 135 | for _, k := range s.Keys() { 136 | val := s.Get(k) 137 | if err := fn(k, val); err != nil { 138 | return err 139 | } 140 | } 141 | return nil 142 | } 143 | 144 | // Values in the order of addition to the map 145 | func (s *OrderedMap) Values() []interface{} { 146 | values := make([]interface{}, len(s.keys)) 147 | for i, k := range s.keys { 148 | values[i] = s.data[k] 149 | } 150 | return values 151 | } 152 | 153 | // Entries in the order of addition to the map 154 | func (s *OrderedMap) Entries() []MapEntry { 155 | values := make([]MapEntry, len(s.keys)) 156 | for i, k := range s.keys { 157 | values[i] = MapEntry{Key: k, Value: s.data[k]} 158 | } 159 | return values 160 | } 161 | 162 | func (s OrderedMap) String() string { 163 | if s.data == nil { 164 | return "" 165 | } 166 | 167 | var b bytes.Buffer 168 | b.WriteByte('{') 169 | b.WriteByte(' ') 170 | first := true 171 | for _, k := range s.keys { 172 | if !first { 173 | b.WriteRune(',') 174 | b.WriteRune(' ') 175 | } 176 | first = false 177 | b.WriteString(k) 178 | b.WriteString(": ") 179 | b.WriteString(fmt.Sprintf("%#v", s.data[k])) 180 | } 181 | if !first { 182 | b.WriteByte(' ') 183 | } 184 | b.WriteByte('}') 185 | return b.String() 186 | } 187 | 188 | // MarshalJSON supports json.Marshaler interface 189 | func (s OrderedMap) MarshalJSON() ([]byte, error) { 190 | w := jwriter.Writer{} 191 | encodeSortedMap(&w, s) 192 | return w.Buffer.BuildBytes(), w.Error 193 | } 194 | 195 | // MarshalEasyJSON supports easyjson.Marshaler interface 196 | func (s OrderedMap) MarshalEasyJSON(w *jwriter.Writer) { 197 | encodeSortedMap(w, s) 198 | } 199 | 200 | // UnmarshalJSON supports json.Unmarshaler interface 201 | func (s *OrderedMap) UnmarshalJSON(data []byte) error { 202 | r := jlexer.Lexer{Data: data} 203 | decodeSortedMap(&r, s) 204 | return r.Error() 205 | } 206 | 207 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 208 | func (s *OrderedMap) UnmarshalEasyJSON(l *jlexer.Lexer) { 209 | decodeSortedMap(l, s) 210 | } 211 | 212 | func encodeSortedMap(out *jwriter.Writer, in OrderedMap) { 213 | if in.data == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 { 214 | out.RawString(`null`) 215 | return 216 | } 217 | 218 | out.RawByte('{') 219 | first := true 220 | for _, k := range in.keys { 221 | _ = first 222 | if !first { 223 | out.RawByte(',') 224 | } 225 | first = false 226 | out.String(k) 227 | out.RawByte(':') 228 | value := in.data[k] 229 | if m, ok := value.(easyjson.Marshaler); ok { 230 | m.MarshalEasyJSON(out) 231 | } else if m, ok := value.(json.Marshaler); ok { 232 | out.Raw(m.MarshalJSON()) 233 | } else { 234 | out.Raw(json.Marshal(value)) 235 | } 236 | } 237 | 238 | out.RawByte('}') 239 | } 240 | 241 | func decodeSortedMap(in *jlexer.Lexer, out *OrderedMap) { 242 | isTopLevel := in.IsStart() 243 | if in.IsNull() { 244 | if isTopLevel { 245 | in.Consumed() 246 | } 247 | in.Skip() 248 | return 249 | } 250 | in.Delim('{') 251 | for !in.IsDelim('}') { 252 | key := string(in.String()) 253 | in.WantColon() 254 | out.Set(key, in.Interface()) 255 | in.WantComma() 256 | } 257 | in.Delim('}') 258 | if isTopLevel { 259 | in.Consumed() 260 | } 261 | } 262 | 263 | // OrderedStrings is a map between a variable name and its value. The value is used for substitution in the server's URL template. 264 | type OrderedStrings struct { 265 | data OrderedMap 266 | } 267 | 268 | // NewOrderedStrings creates a new instance of OrderedStrings with correct filter 269 | func NewOrderedStrings() OrderedStrings { 270 | return OrderedStrings{ 271 | data: OrderedMap{ 272 | filter: MatchNonEmptyKeys, // TODO: check if keys are some regex or just any non empty string 273 | }, 274 | } 275 | } 276 | 277 | // Get gets the security requirement by key 278 | func (s *OrderedStrings) Get(key string) *string { 279 | v := s.data.Get(key) 280 | if v == nil { 281 | return nil 282 | } 283 | return v.(*string) 284 | } 285 | 286 | // GetOK checks if the key exists in the security requirement 287 | func (s *OrderedStrings) GetOK(key string) (*string, bool) { 288 | v, ok := s.data.GetOK(key) 289 | if !ok { 290 | return nil, ok 291 | } 292 | 293 | sr, ok := v.(*string) 294 | return sr, ok 295 | } 296 | 297 | // Set sets the value to the security requirement 298 | func (s *OrderedStrings) Set(key string, val *string) bool { 299 | return s.data.Set(key, val) 300 | } 301 | 302 | // ForEach executes the function for each security requirement 303 | func (s *OrderedStrings) ForEach(fn func(string, *string) error) error { 304 | s.data.ForEach(func(key string, val interface{}) error { 305 | response, _ := val.(*string) 306 | if err := fn(key, response); err != nil { 307 | return err 308 | } 309 | return nil 310 | }) 311 | return nil 312 | } 313 | 314 | // Keys gets the list of keys 315 | func (s *OrderedStrings) Keys() []string { 316 | return s.data.Keys() 317 | } 318 | 319 | // TODO: (s *OrderedStrings) Implement Marshal & Unmarshal -> JSON, YAML 320 | -------------------------------------------------------------------------------- /server_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package spec3 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson22b57fa5DecodeGithubComGoOpenapiSpec3(in *jlexer.Lexer, out *ServerVariables) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | default: 40 | in.SkipRecursive() 41 | } 42 | in.WantComma() 43 | } 44 | in.Delim('}') 45 | if isTopLevel { 46 | in.Consumed() 47 | } 48 | } 49 | func easyjson22b57fa5EncodeGithubComGoOpenapiSpec3(out *jwriter.Writer, in ServerVariables) { 50 | out.RawByte('{') 51 | first := true 52 | _ = first 53 | out.RawByte('}') 54 | } 55 | 56 | // MarshalJSON supports json.Marshaler interface 57 | func (v ServerVariables) MarshalJSON() ([]byte, error) { 58 | w := jwriter.Writer{} 59 | easyjson22b57fa5EncodeGithubComGoOpenapiSpec3(&w, v) 60 | return w.Buffer.BuildBytes(), w.Error 61 | } 62 | 63 | // MarshalEasyJSON supports easyjson.Marshaler interface 64 | func (v ServerVariables) MarshalEasyJSON(w *jwriter.Writer) { 65 | easyjson22b57fa5EncodeGithubComGoOpenapiSpec3(w, v) 66 | } 67 | 68 | // UnmarshalJSON supports json.Unmarshaler interface 69 | func (v *ServerVariables) UnmarshalJSON(data []byte) error { 70 | r := jlexer.Lexer{Data: data} 71 | easyjson22b57fa5DecodeGithubComGoOpenapiSpec3(&r, v) 72 | return r.Error() 73 | } 74 | 75 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 76 | func (v *ServerVariables) UnmarshalEasyJSON(l *jlexer.Lexer) { 77 | easyjson22b57fa5DecodeGithubComGoOpenapiSpec3(l, v) 78 | } 79 | func easyjson22b57fa5DecodeGithubComGoOpenapiSpec31(in *jlexer.Lexer, out *ServerVariable) { 80 | isTopLevel := in.IsStart() 81 | if in.IsNull() { 82 | if isTopLevel { 83 | in.Consumed() 84 | } 85 | in.Skip() 86 | return 87 | } 88 | in.Delim('{') 89 | for !in.IsDelim('}') { 90 | key := in.UnsafeString() 91 | in.WantColon() 92 | if in.IsNull() { 93 | in.Skip() 94 | in.WantComma() 95 | continue 96 | } 97 | switch key { 98 | case "enum": 99 | if in.IsNull() { 100 | in.Skip() 101 | out.Enum = nil 102 | } else { 103 | in.Delim('[') 104 | if out.Enum == nil { 105 | if !in.IsDelim(']') { 106 | out.Enum = make([]string, 0, 4) 107 | } else { 108 | out.Enum = []string{} 109 | } 110 | } else { 111 | out.Enum = (out.Enum)[:0] 112 | } 113 | for !in.IsDelim(']') { 114 | var v1 string 115 | v1 = string(in.String()) 116 | out.Enum = append(out.Enum, v1) 117 | in.WantComma() 118 | } 119 | in.Delim(']') 120 | } 121 | case "default": 122 | out.Default = string(in.String()) 123 | case "description": 124 | out.Description = string(in.String()) 125 | case "extensions": 126 | (out.Extensions).UnmarshalEasyJSON(in) 127 | default: 128 | in.SkipRecursive() 129 | } 130 | in.WantComma() 131 | } 132 | in.Delim('}') 133 | if isTopLevel { 134 | in.Consumed() 135 | } 136 | } 137 | func easyjson22b57fa5EncodeGithubComGoOpenapiSpec31(out *jwriter.Writer, in ServerVariable) { 138 | out.RawByte('{') 139 | first := true 140 | _ = first 141 | if len(in.Enum) != 0 { 142 | const prefix string = ",\"enum\":" 143 | first = false 144 | out.RawString(prefix[1:]) 145 | { 146 | out.RawByte('[') 147 | for v2, v3 := range in.Enum { 148 | if v2 > 0 { 149 | out.RawByte(',') 150 | } 151 | out.String(string(v3)) 152 | } 153 | out.RawByte(']') 154 | } 155 | } 156 | if in.Default != "" { 157 | const prefix string = ",\"default\":" 158 | if first { 159 | first = false 160 | out.RawString(prefix[1:]) 161 | } else { 162 | out.RawString(prefix) 163 | } 164 | out.String(string(in.Default)) 165 | } 166 | if in.Description != "" { 167 | const prefix string = ",\"description\":" 168 | if first { 169 | first = false 170 | out.RawString(prefix[1:]) 171 | } else { 172 | out.RawString(prefix) 173 | } 174 | out.String(string(in.Description)) 175 | } 176 | if true { 177 | const prefix string = ",\"extensions\":" 178 | if first { 179 | first = false 180 | out.RawString(prefix[1:]) 181 | } else { 182 | out.RawString(prefix) 183 | } 184 | (in.Extensions).MarshalEasyJSON(out) 185 | } 186 | out.RawByte('}') 187 | } 188 | 189 | // MarshalJSON supports json.Marshaler interface 190 | func (v ServerVariable) MarshalJSON() ([]byte, error) { 191 | w := jwriter.Writer{} 192 | easyjson22b57fa5EncodeGithubComGoOpenapiSpec31(&w, v) 193 | return w.Buffer.BuildBytes(), w.Error 194 | } 195 | 196 | // MarshalEasyJSON supports easyjson.Marshaler interface 197 | func (v ServerVariable) MarshalEasyJSON(w *jwriter.Writer) { 198 | easyjson22b57fa5EncodeGithubComGoOpenapiSpec31(w, v) 199 | } 200 | 201 | // UnmarshalJSON supports json.Unmarshaler interface 202 | func (v *ServerVariable) UnmarshalJSON(data []byte) error { 203 | r := jlexer.Lexer{Data: data} 204 | easyjson22b57fa5DecodeGithubComGoOpenapiSpec31(&r, v) 205 | return r.Error() 206 | } 207 | 208 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 209 | func (v *ServerVariable) UnmarshalEasyJSON(l *jlexer.Lexer) { 210 | easyjson22b57fa5DecodeGithubComGoOpenapiSpec31(l, v) 211 | } 212 | func easyjson22b57fa5DecodeGithubComGoOpenapiSpec32(in *jlexer.Lexer, out *Server) { 213 | isTopLevel := in.IsStart() 214 | if in.IsNull() { 215 | if isTopLevel { 216 | in.Consumed() 217 | } 218 | in.Skip() 219 | return 220 | } 221 | in.Delim('{') 222 | for !in.IsDelim('}') { 223 | key := in.UnsafeString() 224 | in.WantColon() 225 | if in.IsNull() { 226 | in.Skip() 227 | in.WantComma() 228 | continue 229 | } 230 | switch key { 231 | case "url": 232 | out.URL = string(in.String()) 233 | case "description": 234 | out.Description = string(in.String()) 235 | case "variables": 236 | (out.Variables).UnmarshalEasyJSON(in) 237 | case "extensions": 238 | (out.Extensions).UnmarshalEasyJSON(in) 239 | default: 240 | in.SkipRecursive() 241 | } 242 | in.WantComma() 243 | } 244 | in.Delim('}') 245 | if isTopLevel { 246 | in.Consumed() 247 | } 248 | } 249 | func easyjson22b57fa5EncodeGithubComGoOpenapiSpec32(out *jwriter.Writer, in Server) { 250 | out.RawByte('{') 251 | first := true 252 | _ = first 253 | if in.URL != "" { 254 | const prefix string = ",\"url\":" 255 | first = false 256 | out.RawString(prefix[1:]) 257 | out.String(string(in.URL)) 258 | } 259 | if in.Description != "" { 260 | const prefix string = ",\"description\":" 261 | if first { 262 | first = false 263 | out.RawString(prefix[1:]) 264 | } else { 265 | out.RawString(prefix) 266 | } 267 | out.String(string(in.Description)) 268 | } 269 | if true { 270 | const prefix string = ",\"variables\":" 271 | if first { 272 | first = false 273 | out.RawString(prefix[1:]) 274 | } else { 275 | out.RawString(prefix) 276 | } 277 | (in.Variables).MarshalEasyJSON(out) 278 | } 279 | if true { 280 | const prefix string = ",\"extensions\":" 281 | if first { 282 | first = false 283 | out.RawString(prefix[1:]) 284 | } else { 285 | out.RawString(prefix) 286 | } 287 | (in.Extensions).MarshalEasyJSON(out) 288 | } 289 | out.RawByte('}') 290 | } 291 | 292 | // MarshalJSON supports json.Marshaler interface 293 | func (v Server) MarshalJSON() ([]byte, error) { 294 | w := jwriter.Writer{} 295 | easyjson22b57fa5EncodeGithubComGoOpenapiSpec32(&w, v) 296 | return w.Buffer.BuildBytes(), w.Error 297 | } 298 | 299 | // MarshalEasyJSON supports easyjson.Marshaler interface 300 | func (v Server) MarshalEasyJSON(w *jwriter.Writer) { 301 | easyjson22b57fa5EncodeGithubComGoOpenapiSpec32(w, v) 302 | } 303 | 304 | // UnmarshalJSON supports json.Unmarshaler interface 305 | func (v *Server) UnmarshalJSON(data []byte) error { 306 | r := jlexer.Lexer{Data: data} 307 | easyjson22b57fa5DecodeGithubComGoOpenapiSpec32(&r, v) 308 | return r.Error() 309 | } 310 | 311 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 312 | func (v *Server) UnmarshalEasyJSON(l *jlexer.Lexer) { 313 | easyjson22b57fa5DecodeGithubComGoOpenapiSpec32(l, v) 314 | } 315 | -------------------------------------------------------------------------------- /responses_test.go: -------------------------------------------------------------------------------- 1 | package spec3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | jlexer "github.com/mailru/easyjson/jlexer" 8 | jwriter "github.com/mailru/easyjson/jwriter" 9 | ) 10 | 11 | func TestOrderedResponses_Get(t *testing.T) { 12 | type fields struct { 13 | data OrderedMap 14 | } 15 | type args struct { 16 | key string 17 | } 18 | tests := []struct { 19 | name string 20 | fields fields 21 | args args 22 | want *Response 23 | }{ 24 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedResponses()}, args{"default"}, &Response{Description: "default response"}}, 25 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedResponses()}, args{"201"}, nil}, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | s := &OrderedResponses{ 30 | data: tt.fields.data, 31 | } 32 | if got := s.Get(tt.args.key); !reflect.DeepEqual(got, tt.want) { 33 | t.Errorf("OrderedResponses.Get() = %v, want %v", got, tt.want) 34 | } 35 | }) 36 | } 37 | } 38 | 39 | func TestOrderedResponses_GetOK(t *testing.T) { 40 | type fields struct { 41 | data OrderedMap 42 | } 43 | type args struct { 44 | key string 45 | } 46 | tests := []struct { 47 | name string 48 | fields fields 49 | args args 50 | wantResponse *Response 51 | wantOK bool 52 | }{ 53 | {"Should fetch the item when existent key is passed", fields{buildOrderMapForOrderedResponses()}, args{"200"}, &Response{Description: "OK"}, true}, 54 | {"Should return nil when non-existent key is passed", fields{buildOrderMapForOrderedResponses()}, args{"401"}, nil, false}, 55 | } 56 | for _, tt := range tests { 57 | t.Run(tt.name, func(t *testing.T) { 58 | s := &OrderedResponses{ 59 | data: tt.fields.data, 60 | } 61 | got, got1 := s.GetOK(tt.args.key) 62 | if !reflect.DeepEqual(got, tt.wantResponse) { 63 | t.Errorf("OrderedResponses.GetOK() got = %v, want %v", got, tt.wantResponse) 64 | } 65 | if got1 != tt.wantOK { 66 | t.Errorf("OrderedResponses.GetOK() got1 = %v, want %v", got1, tt.wantOK) 67 | } 68 | }) 69 | } 70 | } 71 | 72 | func TestOrderedResponses_Set(t *testing.T) { 73 | type fields struct { 74 | data OrderedMap 75 | } 76 | type args struct { 77 | key string 78 | val *Response 79 | } 80 | tests := []struct { 81 | name string 82 | fields fields 83 | args args 84 | wantOK bool 85 | }{ 86 | {"Should set value when non-existent response code is passed", fields{buildOrderMapForOrderedResponses()}, args{"201", &Response{Description: "Created"}}, true}, 87 | {"Should fail when existent response code is passed", fields{buildOrderMapForOrderedResponses()}, args{"200", &Response{Description: "another OK"}}, false}, 88 | {"Should fail when empty key is passed", fields{buildOrderMapForOrderedResponses()}, args{"", &Response{Description: "description of item #empty"}}, false}, 89 | } 90 | for _, tt := range tests { 91 | t.Run(tt.name, func(t *testing.T) { 92 | s := &OrderedResponses{ 93 | data: tt.fields.data, 94 | } 95 | if got := s.Set(tt.args.key, tt.args.val); got != tt.wantOK { 96 | t.Fatalf("OrderedResponses.Set() = %v, want %v", got, tt.wantOK) 97 | } 98 | 99 | if tt.wantOK { 100 | gotVal, gotOK := s.GetOK(tt.args.key) 101 | if !gotOK { 102 | t.Fatalf("OrderedResponses.GetOK().OK = %v, want %v", gotOK, true) 103 | } 104 | if !reflect.DeepEqual(gotVal, tt.args.val) { 105 | t.Fatalf("OrderedResponses.GetOK().val = %v, want %v", gotVal, tt.args.val) 106 | } 107 | } 108 | }) 109 | } 110 | } 111 | 112 | func TestOrderedResponses_ForEach(t *testing.T) { 113 | type fields struct { 114 | data OrderedMap 115 | } 116 | type args struct { 117 | fn func(string, *Response) error 118 | } 119 | type foundResponse struct { 120 | response *Response 121 | found bool 122 | } 123 | tests := []struct { 124 | name string 125 | fields fields 126 | wantValInForEach map[string]*foundResponse 127 | wantErr error 128 | }{ 129 | { 130 | "Should iterate 4 items for OrderedResponses fixture", 131 | fields{buildOrderMapForOrderedResponses()}, 132 | map[string]*foundResponse{ 133 | "default": &foundResponse{&Response{Description: "default response"}, false}, 134 | "200": &foundResponse{&Response{Description: "OK"}, false}, 135 | "404": &foundResponse{&Response{Description: "Not found"}, false}, 136 | "500": &foundResponse{&Response{Description: "Internal Error"}, false}, 137 | }, 138 | nil, 139 | }, 140 | { 141 | "Should return empty array when there are no values in OrderedResponses", 142 | fields{}, 143 | map[string]*foundResponse{}, 144 | nil, 145 | }, 146 | } 147 | for _, tt := range tests { 148 | t.Run(tt.name, func(t *testing.T) { 149 | s := &OrderedResponses{ 150 | data: tt.fields.data, 151 | } 152 | err := s.ForEach(func(key string, gotResponse *Response) error { 153 | if wantVal, ok := tt.wantValInForEach[key]; ok { 154 | if !reflect.DeepEqual(wantVal.response, gotResponse) { 155 | t.Fatalf("OrderedResponses.ForEach() for key = %s val = %v, want = %v", key, gotResponse, wantVal.response) 156 | } 157 | wantVal.found = true 158 | } else { 159 | t.Fatalf("OrderedResponses.ForEach() for key = %s val = %v, want = %v", key, gotResponse, wantVal) 160 | } 161 | return nil 162 | }) 163 | 164 | if err == nil && tt.wantErr == nil { 165 | // nothing to assert here 166 | } else if err != tt.wantErr { 167 | t.Fatalf("OrderedResponses.ForEach() error = %v, wantErr %v", err, tt.wantErr) 168 | } 169 | 170 | if tt.wantErr == nil { 171 | for key2, val2 := range tt.wantValInForEach { 172 | if !val2.found { 173 | t.Fatalf("OrderedResponses.ForEach() key = %s not found during foreach()", key2) 174 | } 175 | } 176 | } 177 | }) 178 | } 179 | } 180 | 181 | func TestOrderedResponses_Keys(t *testing.T) { 182 | type fields struct { 183 | data OrderedMap 184 | } 185 | tests := []struct { 186 | name string 187 | fields fields 188 | wantKeys []string 189 | }{ 190 | {"Should return 4 items for OrderedResponses fixture", fields{buildOrderMapForOrderedResponses()}, []string{"default", "200", "404", "500"}}, 191 | {"Should return empty array when there are no values in OrderedResponses", fields{}, []string{}}, 192 | } 193 | for _, tt := range tests { 194 | t.Run(tt.name, func(t *testing.T) { 195 | s := &OrderedResponses{ 196 | data: tt.fields.data, 197 | } 198 | got := s.Keys() 199 | if len(got) != 0 || len(tt.wantKeys) != 0 { 200 | if !reflect.DeepEqual(got, tt.wantKeys) { 201 | t.Errorf("OrderedResponses.Keys() = %v, want %v", got, tt.wantKeys) 202 | } 203 | } 204 | }) 205 | } 206 | } 207 | 208 | func TestOrderedResponses_MarshalJSON(t *testing.T) { 209 | type fields struct { 210 | data OrderedMap 211 | } 212 | tests := []struct { 213 | name string 214 | fields fields 215 | want []byte 216 | wantErr error 217 | }{ 218 | // TODO: Add test cases. 219 | } 220 | for _, tt := range tests { 221 | t.Run(tt.name, func(t *testing.T) { 222 | s := OrderedResponses{ 223 | data: tt.fields.data, 224 | } 225 | got, err := s.MarshalJSON() 226 | if err == nil && tt.wantErr == nil { 227 | // nothing to assert here 228 | } else if err != tt.wantErr { 229 | t.Errorf("OrderedResponses.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) 230 | return 231 | } 232 | if !reflect.DeepEqual(got, tt.want) { 233 | t.Errorf("OrderedResponses.MarshalJSON() = %v, want %v", got, tt.want) 234 | } 235 | }) 236 | } 237 | } 238 | 239 | func TestOrderedResponses_MarshalEasyJSON(t *testing.T) { 240 | type fields struct { 241 | data OrderedMap 242 | } 243 | type args struct { 244 | w *jwriter.Writer 245 | } 246 | tests := []struct { 247 | name string 248 | fields fields 249 | args args 250 | }{ 251 | // TODO: Add test cases. 252 | } 253 | for _, tt := range tests { 254 | t.Run(tt.name, func(t *testing.T) { 255 | s := OrderedResponses{ 256 | data: tt.fields.data, 257 | } 258 | s.MarshalEasyJSON(tt.args.w) 259 | }) 260 | } 261 | } 262 | 263 | func TestOrderedResponses_UnmarshalJSON(t *testing.T) { 264 | type fields struct { 265 | data OrderedMap 266 | } 267 | type args struct { 268 | data []byte 269 | } 270 | tests := []struct { 271 | name string 272 | fields fields 273 | args args 274 | wantErr error 275 | }{ 276 | // TODO: Add test cases. 277 | } 278 | for _, tt := range tests { 279 | t.Run(tt.name, func(t *testing.T) { 280 | s := &OrderedResponses{ 281 | data: tt.fields.data, 282 | } 283 | err := s.UnmarshalJSON(tt.args.data) 284 | if err == nil && tt.wantErr == nil { 285 | // nothing to assert here 286 | } else if err != tt.wantErr { 287 | t.Errorf("OrderedResponses.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) 288 | } 289 | }) 290 | } 291 | } 292 | 293 | func TestOrderedResponses_UnmarshalEasyJSON(t *testing.T) { 294 | type fields struct { 295 | data OrderedMap 296 | } 297 | type args struct { 298 | l *jlexer.Lexer 299 | } 300 | tests := []struct { 301 | name string 302 | fields fields 303 | args args 304 | }{ 305 | // TODO: Add test cases. 306 | } 307 | for _, tt := range tests { 308 | t.Run(tt.name, func(t *testing.T) { 309 | s := &OrderedResponses{ 310 | data: tt.fields.data, 311 | } 312 | s.UnmarshalEasyJSON(tt.args.l) 313 | }) 314 | } 315 | } 316 | 317 | func buildEmptyOrderMapForOrderedResponses() OrderedMap { 318 | return OrderedMap{ 319 | filter: matchResponseCode, 320 | } 321 | } 322 | 323 | func buildOrderMapForOrderedResponses() OrderedMap { 324 | return OrderedMap{ 325 | data: map[string]interface{}{ 326 | "default": &Response{Description: "default response"}, 327 | "200": &Response{Description: "OK"}, 328 | "404": &Response{Description: "Not found"}, 329 | "500": &Response{Description: "Internal Error"}, 330 | }, 331 | keys: []string{ 332 | "default", 333 | "200", 334 | "404", 335 | "500", 336 | }, 337 | filter: matchResponseCode, 338 | } 339 | } 340 | 341 | func buildOrderedResponsesFixture() OrderedResponses { 342 | m := OrderedResponses{ 343 | data: buildOrderMapForOrderedResponses(), 344 | } 345 | 346 | return m 347 | } 348 | --------------------------------------------------------------------------------