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  }