-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgenerator_raml.go
177 lines (145 loc) · 4.38 KB
/
generator_raml.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package apitest
import (
"encoding/json"
"fmt"
"reflect"
"time"
"github.com/alecthomas/jsonschema"
"github.com/seesawlabs/raml"
"gopkg.in/yaml.v2"
)
type ramlGenerator struct {
seed raml.APIDefinition
}
// NewRamlGenerator creates an instance of RAML generator
// seed as used as a source of initial data for resulting doc
func NewRamlGenerator(seed raml.APIDefinition) IDocGenerator {
generator := &ramlGenerator{
seed: seed,
}
generator.seed.RAMLVersion = "0.8"
generator.seed.Resources = map[string]raml.Resource{}
return generator
}
func (g *ramlGenerator) Generate(tests []IApiTest) ([]byte, error) {
doc := g.seed // copy seed
for _, test := range tests {
// path MUST begin with '/'
path := test.Path()
if path[0] != '/' {
path = "/" + path
}
resource, ok := doc.Resources[path]
if !ok { // new resource created by default
resource.UriParameters = map[string]raml.NamedParameter{}
}
m := raml.Method{
Responses: map[raml.HTTPCode]raml.Response{},
Headers: map[raml.HTTPHeader]raml.Header{},
QueryParameters: map[string]raml.NamedParameter{},
Description: test.Description(),
Name: test.Method(),
}
processedHeaderParams := map[string]interface{}{}
processedPathParams := map[string]interface{}{}
processedQueryParams := map[string]interface{}{}
for _, testCase := range test.TestCases() {
m.Description = testCase.Description
for key, param := range testCase.PathParams {
if _, ok := processedPathParams[key]; ok {
continue
}
uriParam := generateRamlNamedParameter(key, param)
processedPathParams[key] = nil
resource.UriParameters[key] = uriParam
}
for key, param := range testCase.Headers {
if _, ok := processedHeaderParams[key]; ok {
continue
}
h := generateRamlNamedParameter(key, param)
m.Headers[raml.HTTPHeader(key)] = raml.Header(h)
processedHeaderParams[key] = nil
}
for key, param := range testCase.QueryParams {
if _, ok := processedQueryParams[key]; ok {
continue
}
queryParam := generateRamlNamedParameter(key, param)
processedQueryParams[key] = nil
m.QueryParameters[key] = queryParam
}
response := raml.Response{}
response.Description = testCase.Description
response.HTTPCode = raml.HTTPCode(testCase.ExpectedHttpCode)
if testCase.ExpectedData != nil {
schema := jsonschema.Reflect(testCase.ExpectedData)
// TODO: marshal data according to MIME type, coming soon with RAML 1.0
schemaBytes, _ := json.MarshalIndent(schema, "", " ")
response.Bodies.DefaultSchema = string(schemaBytes)
// TODO: marshal data according to MIME type, coming soon with RAML 1.0
exampleBytes, _ := json.MarshalIndent(testCase.ExpectedData, "", " ")
response.Bodies.DefaultExample = string(exampleBytes)
}
m.Responses[raml.HTTPCode(testCase.ExpectedHttpCode)] = response
}
// TODO: check if path has already assigned an method to some other test
// return error if so
switch test.Method() {
case "GET":
resource.Get = &m
case "POST":
resource.Post = &m
case "PATCH":
resource.Patch = &m
case "DELETE":
resource.Delete = &m
case "PUT":
resource.Put = &m
case "HEAD":
resource.Head = &m
}
doc.Resources[path] = resource
}
generatedDoc, err := yaml.Marshal(doc)
if err == nil {
header := []byte(fmt.Sprintf("#%%RAML %s\n", doc.RAMLVersion))
generatedDoc = append(header, generatedDoc...)
}
return generatedDoc, err
}
func generateRamlNamedParameter(paramKey string, param Param) raml.NamedParameter {
return raml.NamedParameter{
Name: paramKey,
Description: param.Description,
Required: param.Required,
Default: param.Value,
Type: resolveRamlType(param.Value),
}
}
func resolveRamlType(data interface{}) string {
switch data.(type) {
case []byte:
return "string"
case time.Time, *time.Time:
return "date"
default:
val := reflect.ValueOf(data)
tpe := val.Type()
switch tpe.Kind() {
case reflect.Bool:
return "boolean"
case reflect.String:
return "string"
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
fallthrough
case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64:
return "integer"
case reflect.Float32, reflect.Float64:
return "number"
case reflect.Ptr:
return resolveRamlType(reflect.Indirect(val).Interface())
}
}
return ""
}