Skip to content

Commit 91abe79

Browse files
committed
Add readOnly/writeOnly support. Part 1 of #30
1 parent 06d1664 commit 91abe79

File tree

4 files changed

+88
-10
lines changed

4 files changed

+88
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
- Add `readOnly` and `writeOnly` support to the example generator.
89
- Revamped support for `--validate-server` (short `-s`)
910
- Requires the use of server base path(s) on the client.
1011
- Localhost is now always allowed on all known base paths.

apisprout.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func getTypedExample(mt *openapi3.MediaType) (interface{}, error) {
153153
}
154154

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

example.go

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ import (
66
"github.com/getkin/kin-openapi/openapi3"
77
)
88

9+
// Mode defines a mode of operation for example generation.
10+
type Mode int
11+
12+
const (
13+
// ModeRequest is for the request body (writes to the server)
14+
ModeRequest Mode = iota
15+
// ModeResponse is for the response body (reads from the server)
16+
ModeResponse
17+
)
18+
919
func getSchemaExample(schema *openapi3.Schema) (interface{}, bool) {
1020
if schema.Example != nil {
1121
return schema.Example, true
@@ -66,10 +76,26 @@ func stringFormatExample(format string) string {
6676
return ""
6777
}
6878

79+
// excludeFromMode will exclude a schema if the mode is request and the schema
80+
// is read-only, or if the mode is response and the schema is write only.
81+
func excludeFromMode(mode Mode, schema *openapi3.Schema) bool {
82+
if schema == nil {
83+
return true
84+
}
85+
86+
if mode == ModeRequest && schema.ReadOnly {
87+
return true
88+
} else if mode == ModeResponse && schema.WriteOnly {
89+
return true
90+
}
91+
92+
return false
93+
}
94+
6995
// OpenAPIExample creates an example structure from an OpenAPI 3 schema
7096
// object, which is an extended subset of JSON Schema.
7197
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#schemaObject
72-
func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {
98+
func OpenAPIExample(mode Mode, schema *openapi3.Schema) (interface{}, error) {
7399
if ex, ok := getSchemaExample(schema); ok {
74100
return ex, nil
75101
}
@@ -133,7 +159,7 @@ func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {
133159
example := []interface{}{}
134160

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

152178
for k, v := range schema.Properties {
153-
ex, err := OpenAPIExample(v.Value)
179+
if excludeFromMode(mode, v.Value) {
180+
continue
181+
}
182+
183+
ex, err := OpenAPIExample(mode, v.Value)
154184
if err != nil {
155185
return nil, fmt.Errorf("can't get example for '%s'", k)
156186
}
@@ -160,12 +190,15 @@ func OpenAPIExample(schema *openapi3.Schema) (interface{}, error) {
160190

161191
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Value != nil {
162192
addl := schema.AdditionalProperties.Value
163-
ex, err := OpenAPIExample(addl)
164-
if err != nil {
165-
return nil, fmt.Errorf("can't get example for additional properties")
166-
}
167193

168-
example["additionalPropertyName"] = ex
194+
if !excludeFromMode(mode, addl) {
195+
ex, err := OpenAPIExample(mode, addl)
196+
if err != nil {
197+
return nil, fmt.Errorf("can't get example for additional properties")
198+
}
199+
200+
example["additionalPropertyName"] = ex
201+
}
169202
}
170203

171204
return example, nil

example_test.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"encoding/json"
5+
"strings"
56
"testing"
67

78
"github.com/getkin/kin-openapi/openapi3"
@@ -377,6 +378,45 @@ var schemaTests = []struct {
377378
`{"type": "string", "default": "one", "example": "two"}`,
378379
`"two"`,
379380
},
381+
// ----- Modes -----
382+
{
383+
"Request mode",
384+
`{"type": "object", "required": ["normal", "readOnly", "writeOnly"],
385+
"properties": {
386+
"normal": {
387+
"type": "string"
388+
},
389+
"readOnly": {
390+
"type": "string",
391+
"readOnly": true
392+
},
393+
"writeOnly": {
394+
"type": "string",
395+
"writeOnly": true
396+
}
397+
}
398+
}`,
399+
`{"normal": "string", "writeOnly": "string"}`,
400+
},
401+
{
402+
"Response mode",
403+
`{"type": "object", "required": ["normal", "readOnly", "writeOnly"],
404+
"properties": {
405+
"normal": {
406+
"type": "string"
407+
},
408+
"readOnly": {
409+
"type": "string",
410+
"readOnly": true
411+
},
412+
"writeOnly": {
413+
"type": "string",
414+
"writeOnly": true
415+
}
416+
}
417+
}`,
418+
`{"normal": "string", "readOnly": "string"}`,
419+
},
380420
}
381421

382422
func TestGenExample(t *testing.T) {
@@ -385,7 +425,11 @@ func TestGenExample(t *testing.T) {
385425
schema := &openapi3.Schema{}
386426
err := schema.UnmarshalJSON([]byte(tt.in))
387427
assert.NoError(t, err)
388-
example, err := OpenAPIExample(schema)
428+
m := ModeRequest
429+
if strings.Contains(tt.name, "Response") {
430+
m = ModeResponse
431+
}
432+
example, err := OpenAPIExample(m, schema)
389433

390434
if tt.out == "" {
391435
// Expected to return an error.

0 commit comments

Comments
 (0)