Skip to content

Commit

Permalink
Add readOnly/writeOnly support. Part 1 of #30
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgtaylor committed Mar 13, 2019
1 parent 06d1664 commit 91abe79
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Add `readOnly` and `writeOnly` support to the example generator.
- Revamped support for `--validate-server` (short `-s`)
- Requires the use of server base path(s) on the client.
- Localhost is now always allowed on all known base paths.
Expand Down
2 changes: 1 addition & 1 deletion apisprout.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func getTypedExample(mt *openapi3.MediaType) (interface{}, error) {
}

if mt.Schema != nil {
return OpenAPIExample(mt.Schema.Value)
return OpenAPIExample(ModeResponse, mt.Schema.Value)
}
// TODO: generate data from JSON schema, if no examples available?

Expand Down
49 changes: 41 additions & 8 deletions example.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import (
"github.com/getkin/kin-openapi/openapi3"
)

// Mode defines a mode of operation for example generation.
type Mode int

const (
// ModeRequest is for the request body (writes to the server)
ModeRequest Mode = iota
// ModeResponse is for the response body (reads from the server)
ModeResponse
)

func getSchemaExample(schema *openapi3.Schema) (interface{}, bool) {
if schema.Example != nil {
return schema.Example, true
Expand Down Expand Up @@ -66,10 +76,26 @@ func stringFormatExample(format string) string {
return ""
}

// excludeFromMode will exclude a schema if the mode is request and the schema
// is read-only, or if the mode is response and the schema is write only.
func excludeFromMode(mode Mode, schema *openapi3.Schema) bool {
if schema == nil {
return true
}

if mode == ModeRequest && schema.ReadOnly {
return true
} else if mode == ModeResponse && schema.WriteOnly {
return true
}

return false
}

// OpenAPIExample creates an example structure from an OpenAPI 3 schema
// object, which is an extended subset of JSON Schema.
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#schemaObject
func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {
func OpenAPIExample(mode Mode, schema *openapi3.Schema) (interface{}, error) {
if ex, ok := getSchemaExample(schema); ok {
return ex, nil
}
Expand Down Expand Up @@ -133,7 +159,7 @@ func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {
example := []interface{}{}

if schema.Items != nil && schema.Items.Value != nil {
ex, err := OpenAPIExample(schema.Items.Value)
ex, err := OpenAPIExample(mode, schema.Items.Value)
if err != nil {
return nil, fmt.Errorf("can't get example for array item")
}
Expand All @@ -150,7 +176,11 @@ func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {
example := map[string]interface{}{}

for k, v := range schema.Properties {
ex, err := OpenAPIExample(v.Value)
if excludeFromMode(mode, v.Value) {
continue
}

ex, err := OpenAPIExample(mode, v.Value)
if err != nil {
return nil, fmt.Errorf("can't get example for '%s'", k)
}
Expand All @@ -160,12 +190,15 @@ func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {

if schema.AdditionalProperties != nil && schema.AdditionalProperties.Value != nil {
addl := schema.AdditionalProperties.Value
ex, err := OpenAPIExample(addl)
if err != nil {
return nil, fmt.Errorf("can't get example for additional properties")
}

example["additionalPropertyName"] = ex
if !excludeFromMode(mode, addl) {
ex, err := OpenAPIExample(mode, addl)
if err != nil {
return nil, fmt.Errorf("can't get example for additional properties")
}

example["additionalPropertyName"] = ex
}
}

return example, nil
Expand Down
46 changes: 45 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"encoding/json"
"strings"
"testing"

"github.com/getkin/kin-openapi/openapi3"
Expand Down Expand Up @@ -377,6 +378,45 @@ var schemaTests = []struct {
`{"type": "string", "default": "one", "example": "two"}`,
`"two"`,
},
// ----- Modes -----
{
"Request mode",
`{"type": "object", "required": ["normal", "readOnly", "writeOnly"],
"properties": {
"normal": {
"type": "string"
},
"readOnly": {
"type": "string",
"readOnly": true
},
"writeOnly": {
"type": "string",
"writeOnly": true
}
}
}`,
`{"normal": "string", "writeOnly": "string"}`,
},
{
"Response mode",
`{"type": "object", "required": ["normal", "readOnly", "writeOnly"],
"properties": {
"normal": {
"type": "string"
},
"readOnly": {
"type": "string",
"readOnly": true
},
"writeOnly": {
"type": "string",
"writeOnly": true
}
}
}`,
`{"normal": "string", "readOnly": "string"}`,
},
}

func TestGenExample(t *testing.T) {
Expand All @@ -385,7 +425,11 @@ func TestGenExample(t *testing.T) {
schema := &openapi3.Schema{}
err := schema.UnmarshalJSON([]byte(tt.in))
assert.NoError(t, err)
example, err := OpenAPIExample(schema)
m := ModeRequest
if strings.Contains(tt.name, "Response") {
m = ModeResponse
}
example, err := OpenAPIExample(m, schema)

if tt.out == "" {
// Expected to return an error.
Expand Down

0 comments on commit 91abe79

Please sign in to comment.