github.com/aavshr/aws-sdk-go@v1.41.3/private/model/api/example.go (about) 1 //go:build codegen 2 // +build codegen 3 4 package api 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "os" 11 "sort" 12 "strings" 13 "text/template" 14 15 "github.com/aavshr/aws-sdk-go/private/util" 16 ) 17 18 type Examples map[string][]Example 19 20 // ExamplesDefinition is the structural representation of the examples-1.json file 21 type ExamplesDefinition struct { 22 *API `json:"-"` 23 Examples Examples `json:"examples"` 24 } 25 26 // Example is a single entry within the examples-1.json file. 27 type Example struct { 28 API *API `json:"-"` 29 Operation *Operation `json:"-"` 30 OperationName string `json:"-"` 31 Index string `json:"-"` 32 Builder examplesBuilder `json:"-"` 33 VisitedErrors map[string]struct{} `json:"-"` 34 Title string `json:"title"` 35 Description string `json:"description"` 36 ID string `json:"id"` 37 Comments Comments `json:"comments"` 38 Input map[string]interface{} `json:"input"` 39 Output map[string]interface{} `json:"output"` 40 } 41 42 type Comments struct { 43 Input map[string]interface{} `json:"input"` 44 Output map[string]interface{} `json:"output"` 45 } 46 47 var exampleFuncMap = template.FuncMap{ 48 "commentify": commentify, 49 "wrap": wrap, 50 "generateExampleInput": generateExampleInput, 51 "generateTypes": generateTypes, 52 } 53 54 var exampleCustomizations = map[string]template.FuncMap{} 55 56 var exampleTmpls = template.Must(template.New("example").Funcs(exampleFuncMap).Parse(` 57 {{ generateTypes . }} 58 {{ commentify (wrap .Title 80) }} 59 // 60 {{ commentify (wrap .Description 80) }} 61 func Example{{ .API.StructName }}_{{ .MethodName }}() { 62 svc := {{ .API.PackageName }}.New(session.New()) 63 input := {{ generateExampleInput . }} 64 65 result, err := svc.{{ .OperationName }}(input) 66 if err != nil { 67 if aerr, ok := err.(awserr.Error); ok { 68 switch aerr.Code() { 69 {{ range $_, $ref := .Operation.ErrorRefs -}} 70 {{ if not ($.HasVisitedError $ref) -}} 71 case {{ .API.PackageName }}.{{ $ref.Shape.ErrorCodeName }}: 72 fmt.Println({{ .API.PackageName }}.{{ $ref.Shape.ErrorCodeName }}, aerr.Error()) 73 {{ end -}} 74 {{ end -}} 75 default: 76 fmt.Println(aerr.Error()) 77 } 78 } else { 79 // Print the error, cast err to awserr.Error to get the Code and 80 // Message from an error. 81 fmt.Println(err.Error()) 82 } 83 return 84 } 85 86 fmt.Println(result) 87 } 88 `)) 89 90 // Names will return the name of the example. This will also be the name of the operation 91 // that is to be tested. 92 func (exs Examples) Names() []string { 93 names := make([]string, 0, len(exs)) 94 for k := range exs { 95 names = append(names, k) 96 } 97 98 sort.Strings(names) 99 return names 100 } 101 102 func (exs Examples) GoCode() string { 103 buf := bytes.NewBuffer(nil) 104 for _, opName := range exs.Names() { 105 examples := exs[opName] 106 for _, ex := range examples { 107 buf.WriteString(util.GoFmt(ex.GoCode())) 108 buf.WriteString("\n") 109 } 110 } 111 return buf.String() 112 } 113 114 // ExampleCode will generate the example code for the given Example shape. 115 // TODO: Can delete 116 func (ex Example) GoCode() string { 117 var buf bytes.Buffer 118 m := exampleFuncMap 119 if fMap, ok := exampleCustomizations[ex.API.PackageName()]; ok { 120 m = fMap 121 } 122 tmpl := exampleTmpls.Funcs(m) 123 if err := tmpl.ExecuteTemplate(&buf, "example", &ex); err != nil { 124 panic(err) 125 } 126 127 return strings.TrimSpace(buf.String()) 128 } 129 130 func generateExampleInput(ex Example) string { 131 if ex.Operation.HasInput() { 132 return fmt.Sprintf("&%s{\n%s\n}", 133 ex.Builder.GoType(&ex.Operation.InputRef, true), 134 ex.Builder.BuildShape(&ex.Operation.InputRef, ex.Input, false), 135 ) 136 } 137 return "" 138 } 139 140 // generateTypes will generate no types for default examples, but customizations may 141 // require their own defined types. 142 func generateTypes(ex Example) string { 143 return "" 144 } 145 146 // correctType will cast the value to the correct type when printing the string. 147 // This is due to the json decoder choosing numbers to be floats, but the shape may 148 // actually be an int. To counter this, we pass the shape's type and properly do the 149 // casting here. 150 func correctType(memName string, t string, value interface{}) string { 151 if value == nil { 152 return "" 153 } 154 155 v := "" 156 switch value.(type) { 157 case string: 158 v = value.(string) 159 case int: 160 v = fmt.Sprintf("%d", value.(int)) 161 case float64: 162 if t == "integer" || t == "long" || t == "int64" { 163 v = fmt.Sprintf("%d", int(value.(float64))) 164 } else { 165 v = fmt.Sprintf("%f", value.(float64)) 166 } 167 case bool: 168 v = fmt.Sprintf("%t", value.(bool)) 169 } 170 171 return convertToCorrectType(memName, t, v) 172 } 173 174 func convertToCorrectType(memName, t, v string) string { 175 return fmt.Sprintf("%s: %s,\n", memName, getValue(t, v)) 176 } 177 178 func getValue(t, v string) string { 179 if t[0] == '*' { 180 t = t[1:] 181 } 182 switch t { 183 case "string": 184 return fmt.Sprintf("aws.String(%q)", v) 185 case "integer", "long", "int64": 186 return fmt.Sprintf("aws.Int64(%s)", v) 187 case "float", "float64", "double": 188 return fmt.Sprintf("aws.Float64(%s)", v) 189 case "boolean": 190 return fmt.Sprintf("aws.Bool(%s)", v) 191 default: 192 panic("Unsupported type: " + t) 193 } 194 } 195 196 // AttachExamples will create a new ExamplesDefinition from the examples file 197 // and reference the API object. 198 func (a *API) AttachExamples(filename string) error { 199 p := ExamplesDefinition{API: a} 200 201 f, err := os.Open(filename) 202 defer f.Close() 203 if err != nil { 204 return err 205 } 206 err = json.NewDecoder(f).Decode(&p) 207 if err != nil { 208 return fmt.Errorf("failed to decode %s, err: %v", filename, err) 209 } 210 211 return p.setup() 212 } 213 214 var examplesBuilderCustomizations = map[string]examplesBuilder{ 215 "wafregional": NewWAFregionalExamplesBuilder(), 216 } 217 218 func (p *ExamplesDefinition) setup() error { 219 var builder examplesBuilder 220 ok := false 221 if builder, ok = examplesBuilderCustomizations[p.API.PackageName()]; !ok { 222 builder = NewExamplesBuilder() 223 } 224 225 filteredExamples := make(Examples) 226 227 for _, n := range p.Examples.Names() { 228 examples := p.Examples[n] 229 n = p.ExportableName(n) 230 if _, ok := p.API.Operations[n]; !ok { 231 continue 232 } 233 filteredExamples[n] = make([]Example, 0, len(examples)) 234 for i, e := range examples { 235 e.OperationName = n 236 e.API = p.API 237 e.Index = fmt.Sprintf("shared%02d", i) 238 239 e.Builder = builder 240 241 e.VisitedErrors = map[string]struct{}{} 242 op := p.API.Operations[e.OperationName] 243 e.Operation = op 244 245 filteredExamples[n] = append(filteredExamples[n], e) 246 } 247 } 248 249 p.Examples = filteredExamples 250 p.API.Examples = p.Examples 251 252 return nil 253 } 254 255 var exampleHeader = template.Must(template.New("exampleHeader").Parse(` 256 import ( 257 {{ .Builder.Imports .API }} 258 ) 259 260 var _ time.Duration 261 var _ strings.Reader 262 var _ aws.Config 263 264 func parseTime(layout, value string) *time.Time { 265 t, err := time.Parse(layout, value) 266 if err != nil { 267 panic(err) 268 } 269 return &t 270 } 271 272 `)) 273 274 type exHeader struct { 275 Builder examplesBuilder 276 API *API 277 } 278 279 // ExamplesGoCode will return a code representation of the entry within the 280 // examples.json file. 281 func (a *API) ExamplesGoCode() string { 282 var buf bytes.Buffer 283 var builder examplesBuilder 284 ok := false 285 if builder, ok = examplesBuilderCustomizations[a.PackageName()]; !ok { 286 builder = NewExamplesBuilder() 287 } 288 289 if err := exampleHeader.ExecuteTemplate(&buf, "exampleHeader", &exHeader{builder, a}); err != nil { 290 panic(err) 291 } 292 293 code := a.Examples.GoCode() 294 if len(code) == 0 { 295 return "" 296 } 297 298 buf.WriteString(code) 299 return buf.String() 300 } 301 302 // TODO: In the operation docuentation where we list errors, this needs to be done 303 // there as well. 304 func (ex *Example) HasVisitedError(errRef *ShapeRef) bool { 305 errName := errRef.Shape.ErrorCodeName() 306 _, ok := ex.VisitedErrors[errName] 307 ex.VisitedErrors[errName] = struct{}{} 308 return ok 309 } 310 311 func (ex *Example) MethodName() string { 312 return fmt.Sprintf("%s_%s", ex.OperationName, ex.Index) 313 }