├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bootstrap ├── bootstrap.go └── golang │ ├── content │ ├── Makefile │ ├── README.md │ ├── actions │ │ └── README.md │ ├── config.yml │ ├── generate.gofile │ ├── glide.yaml │ ├── scripts │ │ ├── dependencies.sh │ │ └── generate.sh │ ├── spec │ │ ├── README.md │ │ ├── clients │ │ │ └── README.md │ │ ├── server │ │ │ ├── README.md │ │ │ ├── main.yml │ │ │ ├── templates │ │ │ │ └── README.md │ │ │ └── workflows │ │ │ │ └── README.md │ │ └── templates │ │ │ ├── swagger │ │ │ ├── client │ │ │ │ ├── client.gotmpl │ │ │ │ ├── facade.gotmpl │ │ │ │ ├── parameter.gotmpl │ │ │ │ └── response.gotmpl │ │ │ └── server │ │ │ │ ├── configureapi.gotmpl │ │ │ │ ├── parameter.gotmpl │ │ │ │ ├── responses.gotmpl │ │ │ │ └── views.gotmpl │ │ │ └── workflow │ │ │ └── golang.gotmpl │ └── workflows │ │ └── workflow_shared.gofile │ └── golang.go ├── cmd ├── generate.go ├── init.go └── root.go ├── codon.go ├── compliance ├── clients │ ├── clients.go │ └── mock │ │ └── client │ │ ├── codon_mock_client.go │ │ └── operations │ │ └── operations_client.go ├── config.yml ├── spec │ └── server │ │ ├── main.yml │ │ └── workflows │ │ ├── chain.yml │ │ ├── chain_complete.yml │ │ ├── chain_error.yml │ │ ├── chain_error_complete.yml │ │ ├── complex_output.yml │ │ ├── direct.yml │ │ ├── no_input.yml │ │ ├── no_publish.yml │ │ ├── publish_serial.yml │ │ ├── recursion.yml │ │ ├── simple.yml │ │ └── start.yml └── workflows_test.go ├── flowgen ├── generator │ ├── generate.go │ ├── options.go │ └── process.go ├── languages │ ├── golang.go │ └── shared.go └── shared │ └── spec.go ├── generator ├── generator.go ├── golang │ ├── content │ │ └── clients │ │ │ └── clients.gofile │ ├── golang.go │ └── upstream_swagger.go └── shared │ └── structs.go ├── glide.lock ├── glide.yaml ├── runtime ├── config │ └── config.go ├── mapstructure │ └── mapstructure.go └── templates │ └── templates.go ├── scripts ├── dependencies.sh └── tests.sh └── shared └── spec.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | bootstrap/golang/bindata.go 27 | generator/golang/bindata.go 28 | vendor/ 29 | testing/ 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.8 4 | - 1.9 5 | - master 6 | os: 7 | - linux 8 | before_install: 9 | - sudo add-apt-repository ppa:masterminds/glide -y 10 | - sudo apt-get update -q 11 | - sudo apt-get install glide -y 12 | - git config --global url."https://github.com/".insteadOf 'git@github.com:' 13 | install: 14 | - export GOBIN="$GOPATH/bin" 15 | - export PATH="$PATH:$GOBIN" 16 | - make install 17 | script: 18 | - export GOBIN="$GOPATH/bin" 19 | - export PATH="$PATH:$GOBIN" 20 | - make test 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/zomato/golang:1.19-bullseye 2 | 3 | ENV GOBIN $GOPATH/bin 4 | WORKDIR $GOPATH/src/github.com/grofers/go-codon 5 | COPY . . 6 | 7 | RUN make install 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Grofers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | installdependencies: 2 | ./scripts/dependencies.sh 3 | go get github.com/go-openapi/runtime 4 | go get github.com/tylerb/graceful 5 | go get github.com/jessevdk/go-flags 6 | go get golang.org/x/net/context 7 | go get golang.org/x/net/context/ctxhttp 8 | 9 | installtestdependencies: 10 | go get github.com/stretchr/testify/assert 11 | 12 | igenerate: 13 | go generate 14 | 15 | ibuild: 16 | go build -o codon codon.go 17 | 18 | clean: 19 | go clean 20 | 21 | build: clean installdependencies igenerate ibuild 22 | 23 | iinstall: 24 | go install codon.go 25 | 26 | install: clean installdependencies igenerate iinstall 27 | 28 | itest: 29 | ./scripts/tests.sh 30 | 31 | test: install installtestdependencies itest 32 | 33 | docker_build: 34 | docker build -t docker-hub.grofer.io/consumer/go-codon . 35 | 36 | docker_push: 37 | docker push docker-hub.grofer.io/consumer/go-codon 38 | 39 | .PHONY: installdependencies clean 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-codon 2 | Generates Go server code from a combination of REST and Workflow DSLs. 3 | 4 | [![Build Status](https://travis-ci.org/grofers/go-codon.svg?branch=master)](https://travis-ci.org/grofers/go-codon) 5 | 6 | A codon service has three components: 7 | - `Server`: Accepts and validates HTTP requests 8 | - `Clients`: Clients for upstream services which this service consumes 9 | - `Workflows`: Procedures for every REST endpoint of this server which consume Clients and other custom actions. 10 | 11 | Server and Client side specifications are written in Swagger. Swagger code generation is done through go-swagger. Workflow is written in `Flow`, a Mistral inspired workflow specification in YAML. Its specification can be found [here](https://github.com/grofers/go-codon/wiki/Workflow-DSL-Specification). 12 | 13 | Check out [wiki](https://github.com/grofers/go-codon/wiki) section for more information. Follow [this tutorial](https://github.com/grofers/go-codon/wiki/Codon:-REST-Workflow-Framework) for a very basic example on how to use this tool. 14 | 15 | ## Installation 16 | Set up your Golang development environment ([Getting Started](https://golang.org/doc/install)). Set your `GOPATH` and `GOBIN` directories. Also add `GOBIN` to your `PATH` so that golang tools can be used in command line. 17 | 18 | Download the latest binary from Github releases and put it in your `GOBIN` directory. Or to install from source do: 19 | ```sh 20 | mkdir -p $GOPATH/src/github.com/grofers 21 | cd $GOPATH/src/github.com/grofers 22 | git clone git@github.com:grofers/go-codon.git 23 | cd go-codon 24 | make install 25 | ``` 26 | 27 | ## Example 28 | This is what a workflow looks like (for an API to get posts and the comments for each post concurrently): 29 | ```yaml 30 | name: get_posts_comments 31 | start: 32 | - get_posts 33 | tasks: 34 | get_posts: 35 | action: clients.jplaceholder.get_posts 36 | input: 37 | userId: <%jmes main.userId %> 38 | publish: 39 | posts: <%jmes action %> 40 | on-success: 41 | - get_all_comments: true 42 | get_comments: 43 | action: clients.jplaceholder.get_comments 44 | input: 45 | postId: <%jmes main.postId %> 46 | publish: 47 | comments: <%jmes action %> 48 | get_all_comments: 49 | with-items: <%jmes main.posts %> 50 | loop: 51 | task: get_comments 52 | input: 53 | postId: <%jmes item.id %> 54 | publish: 55 | combined: <%jmes {"post_details":item,"comments":task.comments} %> 56 | output: 57 | body: <%jmes main.combined %> 58 | status_code: 200 59 | ``` 60 | To run this example checkout [examples](https://github.com/grofers/codon-examples). 61 | 62 | ## Projects go-codon would not exist without 63 | (Or just projects I am really thankful for) 64 | - [go-swagger](https://github.com/go-swagger/go-swagger): Provides code generators for client and server side components using [Swagger](https://swagger.io/) specification. 65 | - [go-jmespath](https://github.com/jmespath/go-jmespath): Allows for easy querying and manipulation of json objects in workflows. 66 | - [Pongo2](https://github.com/flosch/pongo2): Django template renderer. Used for templates and workflow expressions in codon. 67 | - [Mistral DSL](https://docs.openstack.org/mistral/latest/): A workflow spec used for infrastructure orchestration. Codon's workflow DSL is inspired from Mistral's but modified for use in REST context. 68 | - [mapstructure](https://github.com/mitchellh/mapstructure) 69 | -------------------------------------------------------------------------------- /bootstrap/bootstrap.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "log" 5 | "github.com/grofers/go-codon/bootstrap/golang" 6 | ) 7 | 8 | type bootstrappable interface { 9 | Bootstrap() bool 10 | } 11 | 12 | var language_map = map[string]bootstrappable { 13 | "golang": &golang.Bootstrapper, 14 | } 15 | 16 | func Bootstrap(lang string) bool { 17 | bs, ok := language_map[lang] 18 | if !ok { 19 | log.Println("Support for language ", lang, " not implemented yet") 20 | } 21 | return bs.Bootstrap() 22 | } 23 | -------------------------------------------------------------------------------- /bootstrap/golang/content/Makefile: -------------------------------------------------------------------------------- 1 | installdependencies: 2 | ./scripts/dependencies.sh 3 | 4 | ibuild: 5 | go build -o {{pascalize .ProjectName}} server/cmd/{{.ProjectName}}-service-server/main.go 6 | 7 | igenerate: 8 | ./scripts/generate.sh 9 | 10 | generate: igenerate 11 | 12 | build: installdependencies ibuild 13 | 14 | .PHONY: installdependencies ibuild igenerate generate build 15 | -------------------------------------------------------------------------------- /bootstrap/golang/content/README.md: -------------------------------------------------------------------------------- 1 | # {{.ProjectName}} 2 | 3 | This service is based on workflow framework and methodology `codon`. 4 | 5 | ## Folders 6 | 7 | * actions: Add all your custom actions to this package. These actions will be available in workflow specification. 8 | 9 | ## Generating the code 10 | 11 | ## Running the service 12 | 13 | ## Running Tests 14 | -------------------------------------------------------------------------------- /bootstrap/golang/content/actions/README.md: -------------------------------------------------------------------------------- 1 | Place your custom actions in this package. These can be called from workflow scripts. -------------------------------------------------------------------------------- /bootstrap/golang/content/config.yml: -------------------------------------------------------------------------------- 1 | endpoints: 2 | # These services based on clients are available in flow dsl 3 | # service_name: 4 | # scheme: http/https 5 | # host: localhost 6 | # client: client_library_name 7 | constants: 8 | # any_key: "any_value" 9 | -------------------------------------------------------------------------------- /bootstrap/golang/content/generate.gofile: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // All generate directives here 4 | //go:generate go-bindata -prefix spec/server/templates/ -pkg bintemplate -o bintemplate/bintemplate.go spec/server/templates/... 5 | -------------------------------------------------------------------------------- /bootstrap/golang/content/glide.yaml: -------------------------------------------------------------------------------- 1 | package: {{ .ProjectPath }} 2 | import: 3 | - package: github.com/cstockton/go-conv 4 | - package: github.com/go-openapi/runtime 5 | - package: github.com/go-openapi/analysis 6 | - package: github.com/go-openapi/errors 7 | - package: github.com/go-openapi/loads 8 | - package: github.com/go-openapi/spec 9 | - package: github.com/go-openapi/strfmt 10 | - package: github.com/go-openapi/swag 11 | - package: github.com/go-openapi/validate 12 | - package: github.com/grofers/go-codon 13 | subpackages: 14 | - runtime 15 | - package: github.com/jmespath/go-jmespath 16 | repo: git@github.com:grofers/go-jmespath.git 17 | - package: github.com/oleiade/reflections 18 | repo: git@github.com:grofers/reflections.git 19 | - package: golang.org/x/net 20 | subpackages: 21 | - context 22 | - context/ctxhttp 23 | - package: github.com/tylerb/graceful 24 | - package: github.com/jessevdk/go-flags 25 | -------------------------------------------------------------------------------- /bootstrap/golang/content/scripts/dependencies.sh: -------------------------------------------------------------------------------- 1 | if ! hash glide 2>/dev/null; then 2 | curl https://glide.sh/get | sh 3 | fi 4 | glide install 5 | -------------------------------------------------------------------------------- /bootstrap/golang/content/scripts/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get go dependencies for generate task first 4 | go get github.com/go-openapi/runtime 5 | go get golang.org/x/net/context 6 | go get golang.org/x/net/context/ctxhttp 7 | go get github.com/tylerb/graceful 8 | go get github.com/jessevdk/go-flags 9 | 10 | codon generate ${ARGS} -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/README.md: -------------------------------------------------------------------------------- 1 | This directory should contain all the specifications for your {{.ProjectName}} service. The following directories further classify the specifications: 2 | 3 | - clients: Contains specs for all the clients available to the service. 4 | - server: Contains the specification for REST interface for this service, the workflow for all the views, and the templates for responses. 5 | - templates: Contains all the templates available to codon's code generator when generating service code. For usual use cases there should be no need to edit these. -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/clients/README.md: -------------------------------------------------------------------------------- 1 | Place all the swagger specifications for your upstream services in this folder. Clients will be generated from those specifications. -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/server/README.md: -------------------------------------------------------------------------------- 1 | This directory should contain specification for service side components in {{.ProjectName}}. The following files/directories further classify the specifications: 2 | 3 | - main.yml: Contains the swagger spec for your REST interface with workflow extensions. 4 | - workflows: These contain the flow DSL describing the different workflows which can be linked to different views in the swagger spec using the workflow extensions. 5 | - templates: Contains all the templates available for post workflow processing. These templates are linked to different views using the template extensions in the swagger spec. -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/server/main.yml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | title: {{.ProjectName}} service 4 | version: "0.0.1" 5 | # TODO: Finish writing the swagger spec for your REST service with 6 | # workflow extensions. -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/server/templates/README.md: -------------------------------------------------------------------------------- 1 | Contains all the templates available for post workflow processing. These templates are linked to different views using the template extensions in the swagger spec. -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/server/workflows/README.md: -------------------------------------------------------------------------------- 1 | These contain the flow DSL describing the different workflows which can be linked to different views in the swagger spec using the workflow extensions. -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/client/client.gotmpl: -------------------------------------------------------------------------------- 1 | package {{ .Name }} 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "net/http" 8 | "github.com/go-openapi/errors" 9 | httptransport "github.com/go-openapi/runtime/client" 10 | "github.com/go-openapi/swag" 11 | "github.com/go-openapi/runtime" 12 | "github.com/go-openapi/validate" 13 | 14 | strfmt "github.com/go-openapi/strfmt" 15 | 16 | {{ range .DefaultImports }}{{ printf "%q" .}} 17 | {{ end }} 18 | {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} 19 | {{ end }} 20 | ) 21 | 22 | // New creates a new {{ humanize .Name }} API client. 23 | func New(transport runtime.ClientTransport, formats strfmt.Registry) *Client { 24 | return &Client{transport: transport, formats: formats} 25 | } 26 | 27 | /* 28 | Client {{ if .Summary }}{{ .Summary }}{{ if .Description }} 29 | 30 | {{ .Description }}{{ end }}{{ else if .Description}}{{ .Description }}{{ else }}for {{ humanize .Name }} API{{ end }} 31 | */ 32 | type Client struct { 33 | transport runtime.ClientTransport 34 | formats strfmt.Registry 35 | } 36 | 37 | {{ range .Operations }}/* 38 | {{ pascalize .Name }} {{ if .Summary }}{{ pluralizeFirstWord (humanize .Summary) }}{{ if .Description }} 39 | 40 | {{ .Description }}{{ end }}{{ else if .Description}}{{ .Description }}{{ else }}{{ humanize .Name }} API{{ end }} 41 | */ 42 | func (a *Client) {{ pascalize .Name }}(all_params map[string]interface{}) {{ if .SuccessResponse }}({{ range .SuccessResponses }}*{{ pascalize .Name }}, {{ end }}{{ end }}error{{ if .SuccessResponse }}){{ end }} { 43 | {{ $length := len .SuccessResponses }} 44 | params, err := New{{ pascalize .Name }}ParamsFromMap(all_params) 45 | if err != nil { 46 | return {{ if .SuccessResponse }}{{ padSurround "nil" "nil" 0 $length }}, {{ end }}err 47 | } 48 | {{ if .Authorized }} 49 | var authInfo runtime.ClientAuthInfoWriter 50 | if _, ok := all_params["BasicAuthUsername"]; ok { 51 | authInfo = httptransport.BasicAuth(all_params["BasicAuthUsername"].(string), all_params["BasicAuthPassword"].(string)) 52 | } 53 | {{ end }} 54 | {{ if .HasStreamingResponse }} 55 | var writer io.Writer 56 | writer = all_params["StreamingWriter"].(io.Writer) 57 | {{ end }} 58 | {{ if .SuccessResponse }}result{{else}}_{{ end }}, err := a.transport.Submit(&runtime.ClientOperation{ 59 | ID: {{ printf "%q" .Name }}, 60 | Method: {{ printf "%q" .Method }}, 61 | PathPattern: {{ printf "%q" .Path }}, 62 | ProducesMediaTypes: {{ printf "%#v" .ProducesMediaTypes }}, 63 | ConsumesMediaTypes: {{ printf "%#v" .ConsumesMediaTypes }}, 64 | Schemes: {{ printf "%#v" .Schemes }}, 65 | Params: params, 66 | Reader: &{{ pascalize .Name }}Reader{formats: a.formats{{ if .HasStreamingResponse }}, writer: writer{{ end }}},{{ if .Authorized }} 67 | AuthInfo: authInfo,{{ end}} 68 | Context: params.Context, 69 | Client: params.HTTPClient, 70 | }) 71 | if err != nil { 72 | return {{ if .SuccessResponse }}{{ padSurround "nil" "nil" 0 $length }}, {{ end }}err 73 | } 74 | {{ if .SuccessResponse }}{{ if eq $length 1 }}return result.(*{{ pascalize .SuccessResponse.Name }}), nil{{ else }}switch value := result.(type) { {{ range $i, $v := .SuccessResponses }} 75 | case *{{ pascalize $v.Name }}: 76 | return {{ padSurround "value" "nil" $i $length }}, nil{{ end }} } 77 | return {{ padSurround "nil" "nil" 0 $length }}, nil{{ end }} 78 | {{ else }}return nil{{ end }} 79 | 80 | } 81 | {{ end }} 82 | 83 | // SetTransport changes the transport on the client 84 | func (a *Client) SetTransport(transport runtime.ClientTransport) { 85 | a.transport = transport 86 | } 87 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/client/facade.gotmpl: -------------------------------------------------------------------------------- 1 | package {{ .Package }} 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | 7 | import ( 8 | "net/http" 9 | "github.com/go-openapi/runtime" 10 | httptransport "github.com/go-openapi/runtime/client" 11 | "github.com/go-openapi/swag" 12 | "github.com/go-openapi/spec" 13 | "github.com/go-openapi/errors" 14 | "github.com/go-openapi/runtime" 15 | 16 | strfmt "github.com/go-openapi/strfmt" 17 | 18 | {{ range .DefaultImports }}{{ printf "%q" .}} 19 | {{ end }} 20 | {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} 21 | {{ end }} 22 | ) 23 | 24 | // Default {{ humanize .Name }} HTTP client. 25 | var Default = NewHTTPClient(nil) 26 | 27 | const ( 28 | // DefaultHost is the default Host 29 | // found in Meta (info) section of spec file 30 | DefaultHost string = {{ printf "%#v" .Host }} 31 | // DefaultBasePath is the default BasePath 32 | // found in Meta (info) section of spec file 33 | DefaultBasePath string = {{ printf "%#v" .BasePath }} 34 | ) 35 | 36 | // DefaultSchemes are the default schemes found in Meta (info) section of spec file 37 | var DefaultSchemes = {{ printf "%#v" .Schemes }} 38 | 39 | // NewHTTPClient creates a new {{ humanize .Name }} HTTP client. 40 | func NewHTTPClient(formats strfmt.Registry) *{{ pascalize .Name }} { 41 | return NewHTTPClientWithConfig(formats, nil) 42 | } 43 | 44 | // NewHTTPClientWithConfigMap creates a new {{ humanize .Name }} HTTP client, 45 | // using a customizable transport config map. 46 | func NewHTTPClientWithConfigMap(formats strfmt.Registry, cfgmap *map[string]string) *{{ pascalize .Name }} { 47 | cfg := DefaultTransportConfig() 48 | if host, ok := (*cfgmap)["host"]; ok { 49 | cfg.Host = host 50 | } 51 | if basepath, ok := (*cfgmap)["base_path"]; ok { 52 | cfg.BasePath = basepath 53 | } 54 | if scheme, ok := (*cfgmap)["scheme"]; ok { 55 | cfg.Schemes = []string{scheme} 56 | } 57 | return NewHTTPClientWithConfig(formats, cfg) 58 | } 59 | 60 | // NewHTTPClientWithConfig creates a new {{ humanize .Name }} HTTP client, 61 | // using a customizable transport config. 62 | func NewHTTPClientWithConfig(formats strfmt.Registry, cfg *TransportConfig) *{{ pascalize .Name }} { 63 | // ensure nullable parameters have default 64 | if formats == nil { 65 | formats = strfmt.Default 66 | } 67 | if cfg == nil { 68 | cfg = DefaultTransportConfig() 69 | } 70 | 71 | // create transport and client 72 | transport := httptransport.New(cfg.Host, cfg.BasePath, cfg.Schemes) 73 | // Hack to make html work 74 | transport.Consumers["text/html"] = runtime.TextConsumer() 75 | transport.Producers["text/html"] = runtime.TextProducer() 76 | // Hack to make forms work. This absurd todo needs to be resolved: https://github.com/go-openapi/runtime/issues/31 77 | transport.Consumers["application/x-www-form-urlencoded"] = runtime.DiscardConsumer 78 | transport.Producers["application/x-www-form-urlencoded"] = runtime.DiscardProducer 79 | return New(transport, formats) 80 | } 81 | 82 | // New creates a new {{ humanize .Name }} client 83 | func New(transport runtime.ClientTransport, formats strfmt.Registry) *{{ pascalize .Name }} { 84 | cli := new({{ pascalize .Name }}) 85 | cli.Transport = transport 86 | {{ range .OperationGroups }} 87 | cli.{{ pascalize .Name }} = {{ .Name }}.New(transport, formats) 88 | {{ end }} 89 | return cli 90 | } 91 | 92 | // DefaultTransportConfig creates a TransportConfig with the 93 | // default settings taken from the meta section of the spec file. 94 | func DefaultTransportConfig() *TransportConfig { 95 | return &TransportConfig { 96 | Host: DefaultHost, 97 | BasePath: DefaultBasePath, 98 | Schemes: DefaultSchemes, 99 | } 100 | } 101 | 102 | // TransportConfig contains the transport related info, 103 | // found in the meta section of the spec file. 104 | type TransportConfig struct { 105 | Host string 106 | BasePath string 107 | Schemes []string 108 | } 109 | 110 | // WithHost overrides the default host, 111 | // provided by the meta section of the spec file. 112 | func (cfg *TransportConfig) WithHost(host string) *TransportConfig { 113 | cfg.Host = host 114 | return cfg 115 | } 116 | 117 | // WithBasePath overrides the default basePath, 118 | // provided by the meta section of the spec file. 119 | func (cfg *TransportConfig) WithBasePath(basePath string) *TransportConfig { 120 | cfg.BasePath = basePath 121 | return cfg 122 | } 123 | 124 | // WithSchemes overrides the default schemes, 125 | // provided by the meta section of the spec file. 126 | func (cfg *TransportConfig) WithSchemes(schemes []string) *TransportConfig { 127 | cfg.Schemes = schemes 128 | return cfg 129 | } 130 | 131 | // {{ pascalize .Name }} is a client for {{ humanize .Name }} 132 | type {{ pascalize .Name }} struct { 133 | {{ range .OperationGroups }} 134 | {{ pascalize .Name }} *{{ snakize .Name }}.Client 135 | {{ end }} 136 | Transport runtime.ClientTransport 137 | } 138 | 139 | 140 | // SetTransport changes the transport on the client and all its subresources 141 | func (c *{{pascalize .Name}}) SetTransport(transport runtime.ClientTransport) { 142 | c.Transport = transport 143 | {{ range .OperationGroups }} 144 | c.{{ pascalize .Name }}.SetTransport(transport) 145 | {{ end }} 146 | } 147 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/client/parameter.gotmpl: -------------------------------------------------------------------------------- 1 | package {{ .Package }} 2 | 3 | // This file was generated by the swagger tool. 4 | // Editing this file might prove futile when you re-run the swagger generate command 5 | 6 | import ( 7 | "os" 8 | "net/http" 9 | 10 | "golang.org/x/net/context" 11 | 12 | "github.com/go-openapi/runtime" 13 | "github.com/go-openapi/swag" 14 | "github.com/go-openapi/errors" 15 | "github.com/go-openapi/validate" 16 | cr "github.com/go-openapi/runtime/client" 17 | 18 | strfmt "github.com/go-openapi/strfmt" 19 | codon_ms "github.com/grofers/go-codon/runtime/mapstructure" 20 | 21 | {{ range .DefaultImports }}{{ printf "%q" .}} 22 | {{ end }} 23 | {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} 24 | {{ end }} 25 | ) 26 | 27 | // New{{ pascalize .Name }}Params creates a new {{ pascalize .Name }}Params object 28 | // with the default values initialized. 29 | func New{{ pascalize .Name }}Params() *{{ pascalize .Name }}Params { 30 | {{ if .Params }}var ( 31 | {{ range .Params }}{{ if .HasDefault }}{{ if not .IsFileParam }}{{ varname .ID}}Default = {{ if .IsPrimitive}}{{.GoType}}({{ end}}{{ printf "%#v" .Default }}{{ if .IsPrimitive }}){{ end }} 32 | {{ end }}{{ end }}{{end}} 33 | ){{ end }} 34 | return &{{ pascalize .Name}}Params{ 35 | {{ range .Params }}{{ if .HasDefault }}{{ pascalize .Name}}: {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (or .IsNullable ) }}&{{ end }}{{ varname .ID }}Default, 36 | {{ end }}{{ end }} 37 | {{ pascalize .TimeoutName }}: cr.DefaultTimeout, 38 | } 39 | } 40 | 41 | // New{{ pascalize .Name }}ParamsWithTimeout creates a new {{ pascalize .Name }}Params object 42 | // with the default values initialized, and the ability to set a timeout on a request 43 | func New{{ pascalize .Name }}ParamsWithTimeout(timeout time.Duration) *{{ pascalize .Name }}Params { 44 | {{ if .Params }}var ( 45 | {{ range .Params }}{{ if .HasDefault }}{{ if not .IsFileParam }}{{ varname .ID}}Default = {{ if .IsPrimitive}}{{.GoType}}({{ end}}{{ printf "%#v" .Default }}{{ if .IsPrimitive }}){{ end }} 46 | {{ end }}{{ end }}{{end}} 47 | ){{ end }} 48 | return &{{ pascalize .Name}}Params{ 49 | {{ range .Params }}{{ if .HasDefault }}{{ pascalize .ID}}: {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (or .IsNullable ) }}&{{ end }}{{ varname .ID }}Default, 50 | {{ end }}{{ end }} 51 | {{ pascalize .TimeoutName }}: timeout, 52 | } 53 | } 54 | 55 | // New{{ pascalize .Name }}ParamsWithContext creates a new {{ pascalize .Name }}Params object 56 | // with the default values initialized, and the ability to set a context for a request 57 | func New{{ pascalize .Name }}ParamsWithContext(ctx context.Context) *{{ pascalize .Name }}Params { 58 | {{ if .Params }}var ( 59 | {{ range .Params }}{{ if .HasDefault }}{{ if not .IsFileParam }}{{ camelize .Name}}Default = {{ if .IsPrimitive}}{{.GoType}}({{ end}}{{ printf "%#v" .Default }}{{ if .IsPrimitive }}){{ end }} 60 | {{ end }}{{ end }}{{end}} 61 | ){{ end }} 62 | return &{{ pascalize .Name}}Params{ 63 | {{ range .Params }}{{ if .HasDefault }}{{ pascalize .Name}}: {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (or .IsNullable ) }}&{{ end }}{{ camelize .Name }}Default, 64 | {{ end }}{{ end }} 65 | Context: ctx, 66 | } 67 | } 68 | 69 | // New{{ pascalize .Name }}ParamsWithHTTPClient creates a new {{ pascalize .Name }}Params object 70 | // with the default values initialized, and the ability to set a custom HTTPClient for a request 71 | func New{{ pascalize .Name }}ParamsWithHTTPClient(client *http.Client) *{{ pascalize .Name }}Params { 72 | {{ if .Params }}var ( 73 | {{ range .Params }}{{ if .HasDefault }}{{ if not .IsFileParam }}{{ camelize .Name}}Default = {{ if .IsPrimitive}}{{.GoType}}({{ end}}{{ printf "%#v" .Default }}{{ if .IsPrimitive }}){{ end }} 74 | {{ end }}{{ end }}{{end}} 75 | ){{ end }} 76 | return &{{ pascalize .Name}}Params{ 77 | {{ range .Params }}{{ if .HasDefault }}{{ pascalize .Name}}: {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (or .IsNullable ) }}&{{ end }}{{ camelize .Name }}Default, 78 | {{ end }}{{ end }}HTTPClient: client, 79 | } 80 | } 81 | 82 | func New{{ pascalize .Name }}ParamsFromMap(params map[string]interface{}) (*{{ pascalize .Name }}Params, error) { 83 | retParams := New{{ pascalize .Name }}Params() 84 | err := codon_ms.Decode(params, &retParams) 85 | if err != nil { 86 | return nil, err 87 | } 88 | // Add millisecond multiplier to timeout 89 | retParams.{{ pascalize .TimeoutName }} = retParams.{{ pascalize .TimeoutName }} * time.Millisecond 90 | return retParams, nil 91 | } 92 | 93 | /*{{ pascalize .Name }}Params contains all the parameters to send to the API endpoint 94 | for the {{ humanize .Name }} operation typically these are written to a http.Request 95 | */ 96 | type {{ pascalize .Name }}Params struct { 97 | 98 | {{ range .Params }}/*{{ pascalize .Name }}{{if .Description }} 99 | {{ .Description }} 100 | 101 | {{ end }}*/ 102 | {{ pascalize .ID }} {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) (or .IsNullable ) }}*{{ end }}{{ if not .IsFileParam }}{{ .GoType }}{{ else }}os.File{{end}} `json:{{.Path}}` 103 | {{ end }} 104 | 105 | {{ pascalize .TimeoutName }} time.Duration `json:"_timeout"` 106 | Context context.Context `json:"-"` 107 | HTTPClient *http.Client `json:"-"` 108 | } 109 | 110 | // With{{ pascalize .TimeoutName }} adds the timeout to the {{ humanize .Name }} params 111 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) With{{ pascalize .TimeoutName }}(timeout time.Duration) *{{ pascalize .Name }}Params { 112 | {{ .ReceiverName }}.Set{{ pascalize .TimeoutName }}(timeout) 113 | return {{ .ReceiverName }} 114 | } 115 | 116 | // Set{{ pascalize .TimeoutName }} adds the timeout to the {{ humanize .Name }} params 117 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) Set{{ pascalize .TimeoutName }}(timeout time.Duration) { 118 | {{ .ReceiverName }}.{{ pascalize .TimeoutName }} = timeout 119 | } 120 | 121 | // WithContext adds the context to the {{ humanize .Name }} params 122 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) WithContext(ctx context.Context) *{{ pascalize .Name }}Params { 123 | {{ .ReceiverName }}.SetContext(ctx) 124 | return {{ .ReceiverName }} 125 | } 126 | 127 | // SetContext adds the context to the {{ humanize .Name }} params 128 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) SetContext(ctx context.Context) { 129 | {{ .ReceiverName }}.Context = ctx 130 | } 131 | 132 | // WithHTTPClient adds the HTTPClient to the {{ humanize .Name }} params 133 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) WithHTTPClient(client *http.Client) *{{ pascalize .Name }}Params { 134 | {{ .ReceiverName }}.SetHTTPClient(client) 135 | return {{ .ReceiverName }} 136 | } 137 | 138 | // SetHTTPClient adds the HTTPClient to the {{ humanize .Name }} params 139 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) SetHTTPClient(client *http.Client) { 140 | {{ .ReceiverName }}.HTTPClient = client 141 | } 142 | 143 | {{ range .Params }} 144 | // With{{ pascalize .ID }} adds the {{ varname .Name }} to the {{ humanize $.Name }} params 145 | func ({{ $.ReceiverName }} *{{ pascalize $.Name }}Params) With{{ pascalize .ID }}({{ varname .Name }} {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (not .IsStream) (or .IsNullable ) }}*{{ end }}{{ if not .IsFileParam }}{{ .GoType }}{{ else }}os.File{{ end }}) *{{ pascalize $.Name }}Params { 146 | {{ $.ReceiverName }}.Set{{ pascalize .ID }}({{ varname .Name }}) 147 | return {{ .ReceiverName }} 148 | } 149 | 150 | // Set{{ pascalize .ID }} adds the {{ camelize .Name }} to the {{ humanize $.Name }} params 151 | func ({{ $.ReceiverName }} *{{ pascalize $.Name }}Params) Set{{ pascalize .ID }}({{ varname .Name }} {{ if and (not .IsArray) (not .IsMap) (not .HasDiscriminator) (not .IsStream) (or .IsNullable ) }}*{{ end }}{{ if not .IsFileParam }}{{ .GoType }}{{ else }}os.File{{ end }}) { 152 | {{ $.ReceiverName }}.{{ pascalize .ID }} = {{ varname .Name }} 153 | } 154 | 155 | {{ end }} 156 | // WriteToRequest writes these params to a swagger request 157 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { 158 | 159 | r.SetTimeout({{ .ReceiverName }}.{{ pascalize .TimeoutName }}) 160 | var res []error 161 | {{range .Params}} 162 | 163 | {{if not (or .IsArray .IsMap .IsBodyParam) }} 164 | {{ if and .IsNullable (not .AllowEmptyValue) }}if {{ .ValueExpression }} != nil { {{ end}} 165 | {{ if .IsQueryParam }} 166 | // query param {{ .Name }} 167 | {{ if .IsNullable }}var qr{{ pascalize .Name }} {{ .GoType }} 168 | if {{ .ValueExpression }} != nil { 169 | qr{{ pascalize .Name }} = *{{ .ValueExpression }} 170 | }{{ else }}qr{{ pascalize .Name }} := {{ .ValueExpression }}{{ end}} 171 | q{{ pascalize .Name}} := {{ if .Formatter }}{{ .Formatter }}(qr{{ pascalize .Name }}){{ else }}qr{{ pascalize .Name }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}{{ if not .AllowEmptyValue }} 172 | if q{{ pascalize .Name }} != "" { {{ end }} 173 | if err := r.SetQueryParam({{ printf "%q" .Name }}, q{{ pascalize .Name }}); err != nil { 174 | return err 175 | } 176 | {{ if not .AllowEmptyValue }}}{{ end }} 177 | {{ else if .IsPathParam }} 178 | // path param {{ .Name }} 179 | if err := r.SetPathParam({{ printf "%q" .Name }}, {{ if .Formatter }}{{ .Formatter }}({{ if .IsNullable }}*{{end}}{{ .ValueExpression }}){{ else }}{{ if .IsNullable }}*{{end}}{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}); err != nil { 180 | return err 181 | } 182 | {{ else if .IsHeaderParam }} 183 | // header param {{ .Name }} 184 | if err := r.SetHeaderParam({{ printf "%q" .Name }}, {{ if .Formatter }}{{ .Formatter }}({{ if .IsNullable }}*{{end}}{{ .ValueExpression }}){{ else }}{{ if .IsNullable }}*{{end}}{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}); err != nil { 185 | return err 186 | } 187 | {{ else if .IsFormParam }} 188 | {{ if .IsFileParam }} 189 | {{ if .IsNullable}} 190 | if {{ .ValueExpression }} != nil { 191 | {{end}} 192 | // form file param {{ .Name }} 193 | if err := r.SetFileParam({{ printf "%q" .Name }}, {{ if not .IsNullable}}&{{end}}{{ .ValueExpression }}); err != nil { 194 | return err 195 | } 196 | {{ if .IsNullable}} 197 | } 198 | {{ end }} 199 | {{ else }} 200 | // form param {{ .Name }} 201 | {{ if .IsNullable }}var fr{{ pascalize .Name }} {{ .GoType }} 202 | if {{ .ValueExpression }} != nil { 203 | fr{{ pascalize .Name }} = *{{ .ValueExpression }} 204 | }{{ else }}fr{{ pascalize .Name }} := {{ .ValueExpression }}{{ end}} 205 | f{{ pascalize .Name}} := {{ if .Formatter }}{{ .Formatter }}(fr{{ pascalize .Name }}){{ else }}fr{{ pascalize .Name }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}}{{ if not .AllowEmptyValue }} 206 | if f{{ pascalize .Name }} != "" { {{ end }} 207 | if err := r.SetFormParam({{ printf "%q" .Name }}, f{{ pascalize .Name }}); err != nil { 208 | return err 209 | } 210 | {{ if not .AllowEmptyValue }}}{{ end }} 211 | {{ end }} 212 | {{ end }} 213 | {{ if and .IsNullable (not .AllowEmptyValue) }}}{{end}} 214 | {{else if .IsArray }} 215 | {{ if not .IsBodyParam }}{{ if .Child }}{{ if or .Child.Formatter .Child.IsCustomFormatter }}var values{{ pascalize .Name }} []string 216 | for _, v := range {{ if and (not .IsArray) (not .IsMap) (not .IsStream) (.IsNullable) }}*{{end}}{{ .ValueExpression }} { 217 | values{{ pascalize .Name }} = append(values{{ pascalize .Name }}, {{ .Child.Formatter }}{{ if .Child.Formatter }}({{ end }}v{{ if .Child.IsCustomFormatter }}.String(){{ end }}{{ if .Child.Formatter }}){{ end }}) 218 | } 219 | {{ else }}values{{ pascalize .Name }} := {{ if and (not .IsArray) (not .IsStream) (not .IsMap) (.IsNullable) }}*{{end}}{{ .ValueExpression }}{{ end }} 220 | {{ else }}values{{ pascalize .Name }} := {{ if and (not .IsArray) (not .IsStream) (not .IsMap) (.IsNullable) }}*{{end}}{{ .ValueExpression }}{{ end }} 221 | joined{{ pascalize .Name}} := swag.JoinByFormat(values{{ pascalize .Name }}, "{{.CollectionFormat}}") 222 | {{ if .IsQueryParam }}// query array param {{ .Name }} 223 | if err := r.SetQueryParam({{ printf "%q" .Name }}, joined{{ pascalize .Name }}...); err != nil { 224 | return err 225 | } 226 | {{ else if and .IsFormParam }}// form array param {{ .Name }} 227 | if err := r.SetFormParam({{ printf "%q" .Name }}, joined{{ pascalize .Name }}...); err != nil { 228 | return err 229 | } 230 | {{ end }}{{ end }} 231 | 232 | {{ end }} 233 | 234 | {{if .IsBodyParam}} 235 | {{ if and .Schema.IsComplexObject .Schema.IsNullable (not .HasDiscriminator) }}if {{ .ValueExpression }} == nil { 236 | {{ .ValueExpression }} = {{ .ZeroValue }} 237 | }{{ end }} 238 | 239 | if err := r.SetBodyParam({{ .ValueExpression }}); err != nil { 240 | return err 241 | } 242 | {{end}} 243 | {{end}} 244 | if len(res) > 0 { 245 | return errors.CompositeValidationError(res...) 246 | } 247 | return nil 248 | } 249 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/client/response.gotmpl: -------------------------------------------------------------------------------- 1 | {{ define "clientresponse" }}// New{{ pascalize .Name }} creates a {{ pascalize .Name }} with default headers values 2 | func New{{ pascalize .Name }}({{ if eq .Code -1 }}code int{{ end }}{{ if .Schema }}{{ if and (eq .Code -1) .Schema.IsStream }}, {{end}}{{ if .Schema.IsStream }}writer io.Writer{{ end }}{{ end }}) *{{ pascalize .Name }} { 3 | return &{{ pascalize .Name }}{ 4 | {{ if eq .Code -1 }}_statusCode: code, 5 | {{ end }}{{ range .Headers }}{{ if .HasDefault }}{{ pascalize .Name }}: {{ printf "%#v" .Default }}, 6 | {{ end }}{{ end }}{{ if .Schema }}{{ if .Schema.IsStream }}Payload: writer, 7 | {{ end }}{{ end }}} 8 | } 9 | 10 | /*{{ pascalize .Name}} handles this case with default header values. 11 | 12 | {{ if .Description }}{{ .Description }}{{else}}{{ pascalize .Name }} {{ humanize .Name }}{{end}} 13 | */ 14 | type {{ pascalize .Name }} struct { 15 | {{ if eq .Code -1 }} 16 | _statusCode int 17 | 18 | {{ end }}{{ range .Headers }}{{ if .Description }}/*{{ .Description }} 19 | */{{ end }} 20 | {{ pascalize .Name }} {{ .GoType }} 21 | {{ end }} 22 | {{ if .Schema }} 23 | Payload {{ if and (not .Schema.IsBaseType) (not .Schema.IsInterface) .Schema.IsComplexObject (not .Schema.IsStream) }}*{{ end }}{{ if (not .Schema.IsStream) }}{{ .Schema.GoType }}{{ else }}io.Writer{{end}} 24 | {{ end }} 25 | }{{ if eq .Code -1 }} 26 | 27 | // Code gets the status code for the {{ humanize .Name }} response 28 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) Code() int { 29 | return {{ .ReceiverName }}._statusCode 30 | } 31 | {{ end }} 32 | 33 | 34 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) Error() string { 35 | return fmt.Sprintf("[{{ upper .Method }} {{ .Path }}][%d] {{ if .Name }}{{ .Name }} {{ else }}unknown error {{ end }}{{ if .Schema }} %+v{{ end }}", {{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}{{ if .Schema }}, o.Payload{{ end }}) 36 | } 37 | 38 | 39 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { 40 | {{ range .Headers }} 41 | // response header {{.Name}} 42 | {{if .Converter }}{{ camelize .Name }}, err := {{ .Converter }}(response.GetHeader("{{ .Name }}")) 43 | if err != nil { 44 | return errors.InvalidType({{ .Path }}, "header", "{{ .GoType }}", response.GetHeader("{{ .Name }}")) 45 | } 46 | {{ .ReceiverName }}.{{ pascalize .Name }} = {{ camelize .Name }} 47 | {{ else if .IsCustomFormatter }} 48 | {{ camelize .Name }}, err := formats.Parse({{ printf "%q" .SwaggerFormat }}, response.GetHeader("{{ .Name }}")) 49 | if err != nil { 50 | return errors.InvalidType({{ .Path }}, "header", "{{ .GoType }}", response.GetHeader("{{ .Name }}")) 51 | } 52 | {{ .ReceiverName }}.{{ pascalize .Name }} = *({{ camelize .Name }}.(*{{ .GoType }})) 53 | {{ else}}{{ .ReceiverName }}.{{ pascalize .Name }} = response.GetHeader("{{ .Name }}") 54 | {{end}} 55 | {{ end }} 56 | {{ if .Schema }} 57 | {{ if .Schema.IsBaseType }} 58 | // response payload as interface type 59 | payload, err := {{ .ModelsPackage }}.Unmarshal{{ stripPackage .Schema.GoType .ModelsPackage }}{{ if .Schema.IsArray}}Slice{{ end }}(response.Body(), consumer) 60 | if err != nil { 61 | return err 62 | } 63 | {{ .ReceiverName }}.Payload = payload 64 | {{ else if .Schema.IsComplexObject }} 65 | {{ .ReceiverName }}.Payload = new({{ .Schema.GoType }}) 66 | {{ end }}{{ if not .Schema.IsBaseType }} 67 | // response payload 68 | if err := consumer.Consume(response.Body(), {{ if not (or .Schema.IsComplexObject .Schema.IsStream) }}&{{ end}}{{ .ReceiverName }}.Payload); err != nil && err != io.EOF { 69 | return err 70 | } 71 | {{ end }}{{ end }} 72 | return nil 73 | } 74 | {{ end }}package {{ .Package }} 75 | 76 | // This file was generated by the swagger tool. 77 | // Editing this file might prove futile when you re-run the swagger generate command 78 | 79 | 80 | import ( 81 | "io" 82 | "net/http" 83 | 84 | "github.com/go-openapi/runtime" 85 | "github.com/go-openapi/swag" 86 | "github.com/go-openapi/errors" 87 | "github.com/go-openapi/validate" 88 | "github.com/go-openapi/runtime" 89 | 90 | strfmt "github.com/go-openapi/strfmt" 91 | 92 | {{ range .DefaultImports }}{{ printf "%q" .}} 93 | {{ end }} 94 | {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} 95 | {{ end }} 96 | ) 97 | 98 | // {{ pascalize .Name }}Reader is a Reader for the {{ pascalize .Name }} structure. 99 | type {{ pascalize .Name }}Reader struct { 100 | formats strfmt.Registry{{ if .HasStreamingResponse }} 101 | writer io.Writer{{ end }} 102 | } 103 | 104 | // ReadResponse reads a server response into the received {{ .ReceiverName }}. 105 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Reader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { 106 | {{ if .Responses}}switch response.Code() { 107 | {{ end }}{{ range .Responses }} 108 | case {{ .Code }}: 109 | result := New{{ pascalize .Name }}({{ if .Schema }}{{ if .Schema.IsStream }}{{ $.ReceiverName }}.writer{{ end }}{{ end }}) 110 | if err := result.readResponse(response, consumer, {{ $.ReceiverName }}.formats); err != nil { 111 | return nil, err 112 | } 113 | return {{ if .IsSuccess }}result, nil{{else}}nil, result{{end}} 114 | {{end}}{{ if .DefaultResponse }}{{ with .DefaultResponse }} 115 | {{ if $.Responses}}default: 116 | {{ end }}result := New{{ pascalize .Name }}(response.Code(){{ if .Schema }}{{ if .Schema.IsStream }}, {{ $.ReceiverName }}.writer{{ end }}{{ end }}) 117 | if err := result.readResponse(response, consumer, {{ $.ReceiverName }}.formats); err != nil { 118 | return nil, err 119 | } 120 | if response.Code() / 100 == 2 { 121 | return result, nil 122 | } 123 | return nil, result{{ end }}{{else}} 124 | {{ if $.Responses}}default: 125 | {{ end }}return nil, runtime.NewAPIError("unknown error", response, response.Code()){{ end }} 126 | {{ if .Responses}}}{{ end }} 127 | } 128 | 129 | {{ range .Responses }} 130 | {{ template "clientresponse" . }} 131 | {{ end }} 132 | {{ if .DefaultResponse }} 133 | {{ template "clientresponse" .DefaultResponse }} 134 | {{ end }} 135 | 136 | {{ range .ExtraSchemas }} 137 | /*{{ pascalize .Name }} {{ template "docstring" . }} 138 | swagger:model {{ .Name }} 139 | */ 140 | {{ template "schema" . }} 141 | {{ end }} 142 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/server/configureapi.gotmpl: -------------------------------------------------------------------------------- 1 | package {{ .APIPackage }} 2 | 3 | import ( 4 | "crypto/tls" 5 | "net/http" 6 | "log" 7 | 8 | errors "github.com/go-openapi/errors" 9 | runtime "github.com/go-openapi/runtime" 10 | graceful "github.com/tylerb/graceful" 11 | "golang.org/x/net/context" 12 | 13 | {{range .DefaultImports}}{{printf "%q" .}} 14 | {{end}} 15 | {{range $key, $value := .Imports}}{{$key}} {{ printf "%q" $value}} 16 | {{end}} 17 | "github.com/grofers/go-codon/runtime/templates" 18 | ) 19 | {{ $package := .Package }} 20 | 21 | // This file is safe to edit. Once it exists it will not be overwritten 22 | {{ with .GenOpts }} 23 | //go:generate swagger generate server --target {{ .TargetPath }} --name {{ .Name }} --spec {{ .SpecPath }} 24 | {{- if .APIPackage }}{{ if ne .APIPackage "operations" }} --api-package {{ .APIPackage }}{{ end }}{{ end }} 25 | {{- if .ModelPackage }}{{ if ne .ModelPackage "models" }} --model-package {{ .ModelPackage }}{{ end }}{{ end }} 26 | {{- if .ServerPackage }}{{ if ne .ServerPackage "restapi"}} --server-package {{ .ServerPackage }}{{ end }}{{ end }} 27 | {{- if .ClientPackage }}{{ if ne .ClientPackage "client" }} --client-package {{ .ClientPackage }}{{ end }}{{ end }} 28 | {{- if .TemplateDir }} --template-dir {{ .TemplateDir }}{{ end }} 29 | {{- range .Operations }} --operation {{ . }}{{ end }} 30 | {{- range .Tags }} --tags {{ . }}{{ end }} 31 | {{- if .Principal }} --principal {{ .Principal }}{{ end }} 32 | {{- if .DefaultScheme }}{{ if ne .DefaultScheme "http" }} --default-scheme {{ .DefaultScheme }}{{ end }}{{ end }} 33 | {{- range .Models }} --model {{ . }}{{ end }} 34 | {{- if or (not .IncludeModel) (not .IncludeValidator) }} --skip-models{{ end }} 35 | {{- if or (not .IncludeHandler) (not .IncludeParameters ) (not .IncludeResponses) }} --skip-operations{{ end }} 36 | {{- if not .IncludeSupport }} --skip-support{{ end }} 37 | {{- if not .IncludeMain }} --exclude-main{{ end }} 38 | {{- if .ExcludeSpec }} --exclude-spec{{ end }} 39 | {{- if .DumpData }} --dump-data{{ end }} 40 | {{- if .WithContext }} --with-context{{ end }} 41 | {{ end }} 42 | func configureFlags(api *{{.Package}}.{{ pascalize .Name }}API) { 43 | // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... } 44 | } 45 | 46 | func configureAPI(api *{{.Package}}.{{ pascalize .Name }}API) http.Handler { 47 | // configure the api here 48 | api.ServeError = errors.ServeError 49 | 50 | // Set your custom logger if needed. Default one is log.Printf 51 | // Expected interface func(string, ...interface{}) 52 | // 53 | // Example: 54 | // s.api.Logger = log.Printf 55 | 56 | {{ range .Consumes }}{{ if .Implementation }}api.{{ pascalize .Name }}Consumer = {{ .Implementation }} 57 | {{else}}api.{{ pascalize .Name }}Consumer = runtime.ConsumerFunc(func(r io.Reader, target interface{}) error { 58 | return errors.NotImplemented("{{.Name}} consumer has not yet been implemented") 59 | }){{end}} 60 | {{end}} 61 | {{ range .Produces }}{{ if .Implementation }}api.{{ pascalize .Name }}Producer = {{ .Implementation }} 62 | {{else}}api.{{ pascalize .Name }}Producer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error { 63 | return errors.NotImplemented("{{.Name}} producer has not yet been implemented") 64 | }){{end}} 65 | {{end}} 66 | {{range .SecurityDefinitions}} 67 | {{if .IsBasicAuth}} 68 | // Applies when the Authorization header is set with the Basic scheme 69 | api.{{ pascalize .ID }}Auth = func(user string, pass string) ({{if not ( eq .Principal "interface{}" )}}*{{ end }}{{.Principal}}, error) { 70 | return nil, errors.NotImplemented("basic auth ({{ .ID }}) has not yet been implemented") 71 | } 72 | {{end}}{{if .IsAPIKeyAuth}} 73 | // Applies when the "{{ .Name }}" {{ .Source }} is set 74 | api.{{ pascalize .ID }}Auth = func(token string) ({{if not ( eq .Principal "interface{}" )}}*{{ end }}{{.Principal}}, error) { 75 | return nil, errors.NotImplemented("api key auth ({{ .ID }}) {{.Name}} from {{.Source}} param [{{ .Name }}] has not yet been implemented") 76 | } 77 | {{end}}{{if .IsOAuth2}} 78 | api.{{ pascalize .ID }}Auth = func(token string, scopes []string) ({{if not ( eq .Principal "interface{}" )}}*{{ end }}{{.Principal}}, error) { 79 | return nil, errors.NotImplemented("oauth2 bearer auth ({{ .ID }}) has not yet been implemented") 80 | } 81 | {{end}} 82 | {{end}} 83 | configureViews(api) 84 | 85 | api.ServerShutdown = func() { } 86 | 87 | return setupGlobalMiddleware(api.Serve(setupMiddlewares)) 88 | } 89 | 90 | // The TLS configuration before HTTPS server starts. 91 | func configureTLS(tlsConfig *tls.Config) { 92 | // Make all necessary changes to the TLS configuration here. 93 | } 94 | 95 | // As soon as server is initialized but not run yet, this function will be called. 96 | // If you need to modify a config, store server instance to stop it individually later, this is the place. 97 | // This function can be called multiple times, depending on the number of serving schemes. 98 | // scheme value will be set accordingly: "http", "https" or "unix" 99 | func configureServer(s *graceful.Server, scheme string) { 100 | templates.Init(bintemplate.AssetNames, bintemplate.Asset) 101 | } 102 | 103 | // The middleware configuration is for the handler executors. These do not apply to the swagger.json document. 104 | // The middleware executes after routing but before authentication, binding and validation 105 | func setupMiddlewares(handler http.Handler) http.Handler { 106 | return handler 107 | } 108 | 109 | // The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document. 110 | // So this is a good place to plug in a panic handling middleware, logging and metrics 111 | func setupGlobalMiddleware(handler http.Handler) http.Handler { 112 | return handler 113 | } 114 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/server/parameter.gotmpl: -------------------------------------------------------------------------------- 1 | {{ define "sliceparamvalidator"}} 2 | {{ if or .MinItems .MaxItems }} 3 | {{ camelize .Name }}Size := int64(len({{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{ .ValueExpression }})) 4 | {{ end }} 5 | {{ if .MinItems }} 6 | if err := validate.MinItems({{ .Path }}, {{ printf "%q" .Location }}, {{ camelize .Name }}Size, {{ .MinItems }}); err != nil { 7 | return err 8 | } 9 | {{ end }} 10 | {{ if .MaxItems }} 11 | if err := validate.MaxItems({{ .Path }}, {{ printf "%q" .Location }}, {{ camelize .Name }}Size, {{.MaxItems}}); err != nil { 12 | return err 13 | } 14 | {{ end }} 15 | {{ if .UniqueItems }} 16 | if err := validate.UniqueItems({{ .Path }}, {{ printf "%q" .Location }}, {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{ .ValueExpression }}); err != nil { 17 | return err 18 | } 19 | {{ end }} 20 | {{ if .Enum }} 21 | if err := validate.Enum({{ .Path }}, {{ printf "%q" .Location }}, {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{ .ValueExpression }}, {{ .Enum }}); err != nil { 22 | return err 23 | } 24 | {{ end }} 25 | {{ end }} 26 | {{ define "propertyparamvalidator" }} 27 | {{ if .IsPrimitive }}{{ template "validationPrimitive" . }}{{ end }} 28 | {{ if .IsCustomFormatter }} 29 | if err := validate.FormatOf({{.Path}}, "{{.Location}}", "{{.SwaggerFormat}}", {{.ValueExpression}}.String(), formats); err != nil { 30 | return err 31 | }{{ end }} 32 | {{ if .IsArray }}{{ template "sliceparamvalidator" . }}{{ end -}} 33 | {{ end }} 34 | {{define "bindprimitiveparam" }} 35 | {{ end }} 36 | {{ define "sliceparambinder" }} 37 | var {{ varname .Child.ValueExpression }}R {{ .GoType }} 38 | for {{ if or .Child.HasValidations .Child.Converter .Child.IsCustomFormatter }}{{ .IndexVar }}{{ else }}_{{ end }}, {{ varname .Child.ValueExpression }}V := range {{ varname .Child.ValueExpression }}C { 39 | {{ if or .Child.IsArray -}} 40 | {{ .Child.Child.ValueExpression }}C := swag.SplitByFormat({{ varname .Child.ValueExpression }}V, {{ printf "%q" .Child.CollectionFormat }}) 41 | {{ template "sliceparambinder" .Child }} 42 | {{- else -}} 43 | {{ if .Child.Converter -}} 44 | {{ varname .Child.ValueExpression }}, err := {{ .Child.Converter }}({{ varname .Child.ValueExpression }}V) 45 | if err != nil { 46 | return errors.InvalidType({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, "{{ .Child.GoType }}", {{ varname .Child.ValueExpression }}) 47 | } 48 | {{- else if .Child.IsCustomFormatter -}} 49 | {{ varname .Child.ValueExpression }}, err := formats.Parse({{ varname .Child.ValueExpression }}V) 50 | if err != nil { 51 | return errors.InvalidType({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, "{{ .Child.GoType }}", {{ varname .Child.ValueExpression }}) 52 | } 53 | {{- else -}} 54 | {{ varname .Child.ValueExpression }} := {{ varname .Child.ValueExpression }}V 55 | {{ end }} 56 | {{- end }} 57 | 58 | {{ template "propertyparamvalidator" .Child }} 59 | {{ varname .Child.ValueExpression }}R = append({{ varname .Child.ValueExpression }}R, {{ varname .Child.ValueExpression }}) 60 | } 61 | {{ end }} 62 | package {{ .Package }} 63 | 64 | // This file was generated by the swagger tool. 65 | // Editing this file might prove futile when you re-run the swagger generate command 66 | 67 | import ( 68 | "net/http" 69 | 70 | "github.com/go-openapi/errors" 71 | "github.com/go-openapi/validate" 72 | "github.com/go-openapi/runtime" 73 | "github.com/go-openapi/runtime/middleware" 74 | "github.com/go-openapi/swag" 75 | 76 | strfmt "github.com/go-openapi/strfmt" 77 | 78 | {{ range .DefaultImports }}{{ printf "%q" .}} 79 | {{ end }} 80 | {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} 81 | {{ end }} 82 | ) 83 | 84 | // New{{ pascalize .Name }}Params creates a new {{ pascalize .Name }}Params object 85 | // with the default values initialized. 86 | func New{{ pascalize .Name }}Params() {{ pascalize .Name }}Params { 87 | var ( 88 | {{ range .Params }}{{ if .HasDefault }}{{ if not .IsFileParam }}{{ varname .ID}}Default = {{ if .IsPrimitive}}{{.GoType}}({{ end}}{{ printf "%#v" .Default }}{{ if .IsPrimitive }}){{ end }} 89 | {{ end }}{{ end }}{{end}} 90 | ) 91 | return {{ pascalize .Name }}Params{ 92 | Params: map[string]interface{} { 93 | {{ range .Params }}{{ if .HasDefault }} 94 | {{.Path}}: {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}&{{ end }}{{ varname .ID }}Default, 95 | {{ end }}{{ end }} 96 | }, 97 | } 98 | } 99 | 100 | // {{ pascalize .Name }}Params contains all the bound params for the {{ humanize .Name }} operation 101 | // typically these are obtained from a http.Request 102 | // 103 | // swagger:parameters {{ .Name }} 104 | type {{ pascalize .Name }}Params struct { 105 | 106 | // HTTP Request Object 107 | HTTPRequest *http.Request 108 | 109 | Params map[string]interface{} 110 | } 111 | 112 | // GetParamsMap returns the parameters in a map format with original names 113 | // as keys. 114 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) GetParamsMap() (map[string]interface{}, error) { 115 | return o.Params, nil 116 | } 117 | 118 | // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface 119 | // for simple values it will use straight method calls 120 | func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { 121 | var res []error 122 | {{ .ReceiverName }}.HTTPRequest = r 123 | 124 | {{ if .HasQueryParams }}qs := runtime.Values(r.URL.Query()){{ end }} 125 | 126 | {{ if .HasFormParams }}if err := r.ParseMultipartForm(32 << 20); err != nil { 127 | if err != http.ErrNotMultipart { 128 | return err 129 | } else if err := r.ParseForm(); err != nil { 130 | return err 131 | } 132 | }{{ if .HasFormValueParams }} 133 | fds := runtime.Values(r.Form) 134 | {{ end }}{{ end }} 135 | 136 | {{ range .Params }} 137 | {{ if not .IsArray }}{{ if .IsQueryParam }}q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, _ := qs.GetOK({{ .Path }}) 138 | if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, route.Formats); err != nil { 139 | res = append(res, err) 140 | } 141 | {{ else if .IsPathParam }}r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, _ := route.Params.GetOK({{ .Path }}) 142 | if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, route.Formats); err != nil { 143 | res = append(res, err) 144 | } 145 | {{ else if .IsHeaderParam }}if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r.Header[http.CanonicalHeaderKey({{ .Path }})], true, route.Formats); err != nil { 146 | res = append(res, err) 147 | } 148 | {{ else if .IsFormParam }}{{if .IsFileParam }}{{ camelize .Name }}, {{ camelize .Name }}Header, err := r.FormFile({{ .Path }}) 149 | if err != nil { 150 | res = append(res, errors.New(400, "reading file %q failed: %v", {{ printf "%q" (camelize .Name) }}, err)) 151 | } else { 152 | {{ .ReceiverName }}.Params["{{ .Name }}"] = runtime.File{Data: {{ camelize .Name }}, Header: {{ camelize .Name }}Header} 153 | } 154 | {{ else }}fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, _ := fds.GetOK({{ .Path }}) 155 | if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, route.Formats); err != nil { 156 | res = append(res, err) 157 | } 158 | {{ end }}{{ end }} 159 | {{ else if .IsArray }}{{ if .IsQueryParam }}q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, _ := qs.GetOK({{ .Path }}) 160 | if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, route.Formats); err != nil { 161 | res = append(res, err) 162 | } 163 | {{ else if and .IsFormParam }}fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, _ := fds.GetOK({{ .Path }}) 164 | if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, route.Formats); err != nil { 165 | res = append(res, err) 166 | } 167 | {{ end }}{{ end }} 168 | 169 | {{ if and .IsBodyParam .Schema }}if runtime.HasBody(r) { 170 | {{ if .Schema.IsStream }}{{ .ReceiverName }}.Params["{{ .Name }}"] = r.Body 171 | {{ else }}defer r.Body.Close() 172 | {{ if and .Schema.IsBaseType .Schema.IsExported }}body, err := {{ .ModelsPackage }}.Unmarshal{{ stripPackage .GoType .ModelsPackage }}{{ if .IsArray }}Slice{{ end }}(r.Body, route.Consumer) 173 | if err != nil { {{ if .Required }} 174 | if err == io.EOF { 175 | err = errors.Required({{ .Path }}, {{ printf "%q" .Location }}) 176 | } 177 | {{ end }}res = append(res, err) 178 | {{ else }}var body {{ .GoType }} 179 | if err := route.Consumer.Consume(r.Body, &body); err != nil { {{ if .Required }} 180 | if err == io.EOF { 181 | res = append(res, errors.Required({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }})) 182 | } else { {{ end }} 183 | res = append(res, errors.NewParseError({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, "", err)){{ if .Required }} 184 | } 185 | {{ end }} 186 | {{ end }}} else { 187 | {{ if .IsArray }}{{ if .Child }}{{ if (and (not .Schema.IsInterface) (or .Child.IsAliased .Child.IsComplexObject)) }}for _, {{ .IndexVar }}{{ .ReceiverName }} := range {{ .ReceiverName }}.Params["{{ .Name }}"] { 188 | if err := {{ .IndexVar }}{{ .ReceiverName }}.Validate(route.Formats); err != nil { 189 | res = append(res, err) 190 | break 191 | } 192 | } 193 | {{ end }}{{ end }}{{ else if (and (not .Schema.IsInterface) (or .Schema.IsAliased .Schema.IsComplexObject)) }}if err := body.Validate(route.Formats); err != nil { 194 | res = append(res, err) 195 | } 196 | {{ end }} 197 | if len(res) == 0 { 198 | {{ .ReceiverName }}.Params["{{ .Name }}"] = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body 199 | } 200 | } 201 | {{ end }} 202 | }{{ if .Required }} else { 203 | res = append(res, errors.Required({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }})) 204 | } {{ end }} 205 | 206 | {{ end }} 207 | {{ end }} 208 | if len(res) > 0 { 209 | return errors.CompositeValidationError(res...) 210 | } 211 | return nil 212 | } 213 | 214 | {{ $className := (pascalize .Name) }} 215 | {{ range .Params }} 216 | {{ $receiverName := .ReceiverName }} 217 | {{ if not (or .IsBodyParam .IsFileParam) }} 218 | {{ if or .IsPrimitive .IsCustomFormatter }} 219 | func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(rawData []string, hasKey bool, formats strfmt.Registry) error { 220 | {{ if and (not .IsPathParam) .Required }}if !hasKey { 221 | return errors.Required({{ .Path }}, {{ printf "%q" .Location }}) 222 | } 223 | {{ end }}var raw string 224 | if len(rawData) > 0 { 225 | raw = rawData[len(rawData)-1] 226 | } 227 | {{ if and (not .IsPathParam) .Required (not .AllowEmptyValue) }}if err := validate.RequiredString({{ .Path }}, {{ printf "%q" .Location }}, raw); err != nil { 228 | return err 229 | } 230 | {{ else if and ( not .IsPathParam ) (or (not .Required) .AllowEmptyValue) }}if raw == "" { // empty values pass all other validations 231 | {{ if .HasDefault }}var {{ camelize .Name}}Default {{ if not .IsFileParam }}{{ .GoType }}{{ else }}os.File{{end}} = {{ if .IsPrimitive}}{{.GoType}}({{ end}}{{ printf "%#v" .Default }}{{ if .IsPrimitive }}){{ end }} 232 | {{$receiverName}}.Params[{{.Path}}] = {{ if and (not .IsArray) (not .HasDiscriminator) (or .IsNullable ) (not .IsStream) }}&{{ end }}{{ camelize .Name }}Default 233 | {{ end }}return nil 234 | } 235 | {{ end }} 236 | {{ if .Converter }}value, err := {{ .Converter }}(raw) 237 | if err != nil { 238 | return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, raw) 239 | } 240 | {{$receiverName}}.Params[{{.Path}}] = {{ if .IsNullable }}&{{ end }}value 241 | {{ else if .IsCustomFormatter }}value, err := formats.Parse({{ printf "%q" .SwaggerFormat }}, raw) 242 | if err != nil { 243 | return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, raw) 244 | } 245 | {{$receiverName}}.Params[{{.Path}}] = {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsFileParam) (not .IsStream) (not .IsNullable) }}*{{ end }}(value.(*{{ .GoType }})) 246 | {{else}}{{$receiverName}}.Params[{{.Path}}] = {{ if .IsNullable }}&{{ end }}raw 247 | {{ end }} 248 | {{if .HasValidations }}if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil { 249 | return err 250 | } 251 | {{ end }} 252 | return nil 253 | } 254 | {{else if .IsArray}} 255 | func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(rawData []string, hasKey bool, formats strfmt.Registry) error { 256 | {{if .Required }}if !hasKey { 257 | return errors.Required({{ .Path }}, {{ printf "%q" .Location }}) 258 | } 259 | {{ end }} 260 | {{ if eq .CollectionFormat "multi" }}{{ varname .Child.ValueExpression }}C := rawData{{ else }}var qv{{ pascalize .Name }} string 261 | if len(rawData) > 0 { 262 | qv{{ pascalize .Name }} = rawData[len(rawData) - 1] 263 | } 264 | 265 | {{ varname .Child.ValueExpression }}C := swag.SplitByFormat(qv{{ pascalize .Name }}, {{ printf "%q" .CollectionFormat }}){{ end }} 266 | {{if and .Required (not .AllowEmptyValue) }} 267 | if len({{ varname .Child.ValueExpression }}C) == 0 { 268 | return errors.Required({{ .Path }}, {{ printf "%q" .Location }}) 269 | } 270 | {{ end }} 271 | {{ if not .Required }}{{ if .HasDefault }}defValue := swag.SplitByFormat({{ .Default }}, {{ printf "%q" .CollectionFormat }}) 272 | if len({{ varname .Child.ValueExpression }}C) == 0 && len(defValue) > 0 { 273 | {{$receiverName}}.Params[{{.Path}}] = defValue 274 | {{ else }}if len({{ varname .Child.ValueExpression }}C) == 0 { 275 | return nil{{ end }} 276 | }{{ end }} 277 | {{ template "sliceparambinder" . }} 278 | {{$receiverName}}.Params[{{.Path}}] = {{ varname .Child.ValueExpression }}R 279 | {{ if .HasSliceValidations }}if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil { 280 | return err 281 | } 282 | {{ end }} 283 | 284 | return nil 285 | } 286 | {{ end }} 287 | {{ if or .HasValidations .HasSliceValidations }} 288 | func ({{ .ReceiverName }} *{{ $className }}Params) validate{{ pascalize .ID }}(formats strfmt.Registry) error { 289 | {{ template "propertyparamvalidator" . }} 290 | return nil 291 | } 292 | {{ end }} 293 | {{ end }} 294 | {{ end }} 295 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/server/responses.gotmpl: -------------------------------------------------------------------------------- 1 | {{ define "serverheaderbuilder" }} 2 | {{ if not .IsArray }}{{ template "simpleserverheaderbuilder" . }}{{ else }}{{ template "sliceserverheaderbuilder" . }}{{ end }} 3 | {{- end }} 4 | {{ define "simpleserverheaderbuilder" }} 5 | {{ if .IsNullable -}} 6 | var {{ varname .ID }} string 7 | if {{ .ReceiverName }}.Headers.{{ pascalize .ID }} != nil { 8 | {{ varname .ID }} = {{ if .Formatter }}{{ .Formatter }}(*{{ .ReceiverName }}.Headers.{{ pascalize .ID }}){{ else }}{{ if not .IsCustomFormatter }}*{{ end }}{{ .ReceiverName }}.Headers.{{ pascalize .ID }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}} 9 | } 10 | {{ else }}{{ varname .ID }} := {{ if .Formatter }}{{ .Formatter }}({{ .ReceiverName }}.Headers.{{ pascalize .ID }}){{ else }}{{ .ReceiverName }}.Headers.{{ pascalize .ID }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}} 11 | {{ end -}} 12 | if {{ varname .ID }} != "" { 13 | rw.Header().Set({{ printf "%q" .Name }}, {{ varname .ID }}) 14 | } 15 | {{ end }} 16 | {{ define "sliceitemserverheaderbuilder" }} 17 | {{ if .IsNullable -}} 18 | var {{ .ValueExpression }}S string 19 | if {{ .ValueExpression }} != nil { 20 | {{ .ValueExpression }}S = {{ if .Formatter }}{{ .Formatter }}(*{{ .ValueExpression }}){{ else }}*{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}} 21 | } 22 | {{ else -}} 23 | {{ .ValueExpression }}S := {{ if .Formatter }}{{ .Formatter }}({{ .ValueExpression }}){{ else }}{{ .ValueExpression }}{{ if .IsCustomFormatter }}.String(){{end}}{{end}} 24 | {{ end -}} 25 | if {{ .ValueExpression }}S != "" { 26 | {{ .ValueExpression }}R = append({{ .ValueExpression }}R, {{ .ValueExpression }}S) 27 | } 28 | {{ end }} 29 | {{define "sliceserverheaderbuilder" }} 30 | var {{ varname .Child.ValueExpression }}R []string 31 | for _, {{ varname .Child.ValueExpression }} := range {{ .ValueExpression }} { 32 | {{- if not .Child.IsArray }}{{ template "sliceitemserverheaderbuilder" .Child }}{{ else }}{{ template "sliceserverheaderbuilder" .Child }}{{ end -}} 33 | } 34 | 35 | {{ if not .Child.Parent -}} 36 | {{ varname .ID }} := swag.JoinByFormat({{ varname .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }}) 37 | if len({{ varname .ID }}) > 0 { 38 | hv := {{ varname .ID }}[0] 39 | if hv != "" { 40 | rw.Header().Set({{ printf "%q" .Name }}, hv) 41 | } 42 | } 43 | {{ else -}} 44 | {{ .ValueExpression }}S := swag.JoinByFormat({{ varname .Child.ValueExpression }}R, {{ printf "%q" .CollectionFormat }}) 45 | if len({{ .ValueExpression }}S) > 0 { 46 | {{ .ValueExpression }}Ss := {{ .ValueExpression }}S[0] 47 | if {{ .ValueExpression }}Ss != "" { 48 | {{ .ValueExpression }}R = append({{ .ValueExpression }}R, {{ .ValueExpression }}Ss) 49 | } 50 | } 51 | {{ end -}} 52 | {{ end -}} 53 | {{ define "serverresponse" }} 54 | {{ if ne .Code -1 }}// HTTP code for type {{ pascalize .Name}} 55 | const {{ pascalize .Name}}Code int = {{ .Code }}{{ end }} 56 | 57 | type {{ pascalize .Name }}Headers struct { 58 | {{ range .Headers }}/*{{if .Description }}{{ .Description }}{{ end }}{{ if .Required }} 59 | Required: true{{ end }}{{ if .Maximum }} 60 | Maximum: {{ if .ExclusiveMaximum }}< {{ end }}{{ .Maximum }}{{ end }}{{ if .Minimum }} 61 | Minimum: {{ if .ExclusiveMinimum }}> {{ end }}{{ .Minimum }}{{ end }}{{ if .MultipleOf }} 62 | Multiple Of: {{ .MultipleOf }}{{ end }}{{ if .MaxLength }} 63 | Max Length: {{ .MaxLength }}{{ end }}{{ if .MinLength }} 64 | Min Length: {{ .MinLength }}{{ end }}{{ if .Pattern }} 65 | Pattern: {{ .Pattern }}{{ end }}{{ if .MaxItems }} 66 | Max Items: {{ .MaxItems }}{{ end }}{{ if .MinItems }} 67 | Min Items: {{ .MinItems }}{{ end }}{{ if .UniqueItems }} 68 | Unique: true{{ end }}{{ if .HasDefault }} 69 | Default: {{ printf "%#v" .Default }}{{ end }} 70 | */ 71 | {{ pascalize .Name }} {{ .GoType }} `json:"{{.Name}}{{ if not .Required }},omitempty{{ end }}"` 72 | {{ end }} 73 | } 74 | 75 | /*{{ if .Description }}{{ pascalize .Name }} {{ .Description }}{{else}}{{ pascalize .Name }} {{ humanize .Name }}{{end}} 76 | 77 | swagger:response {{ camelize .Name }} 78 | */ 79 | type {{ pascalize .Name }} struct { 80 | {{ if eq .Code -1 }} 81 | _statusCode int 82 | {{ end }} 83 | {{ if .Schema }}{{ with .Schema }} 84 | /*{{if .Description }}{{ .Description }}{{ end }}{{ if .Maximum }} 85 | Maximum: {{ if .ExclusiveMaximum }}< {{ end }}{{ .Maximum }}{{ end }}{{ if .Minimum }} 86 | Minimum: {{ if .ExclusiveMinimum }}> {{ end }}{{ .Minimum }}{{ end }}{{ if .MultipleOf }} 87 | Multiple Of: {{ .MultipleOf }}{{ end }}{{ if .MaxLength }} 88 | Max Length: {{ .MaxLength }}{{ end }}{{ if .MinLength }} 89 | Min Length: {{ .MinLength }}{{ end }}{{ if .Pattern }} 90 | Pattern: {{ .Pattern }}{{ end }}{{ if .MaxItems }} 91 | Max Items: {{ .MaxItems }}{{ end }}{{ if .MinItems }} 92 | Min Items: {{ .MinItems }}{{ end }}{{ if .UniqueItems }} 93 | Unique: true{{ end }} 94 | In: Body 95 | */{{ end }} 96 | Payload {{ if and (not .Schema.IsBaseType) .Schema.IsComplexObject }}*{{ end }}{{ .Schema.GoType }} `json:"body,omitempty"` 97 | {{ end }} 98 | Headers {{ pascalize .Name }}Headers `json:"headers,omitempty"` 99 | } 100 | 101 | // New{{ pascalize .Name }}Headers creates {{ pascalize .Name }}Headers with default headers 102 | func New{{ pascalize .Name }}Headers() *{{ pascalize .Name }}Headers { 103 | return &{{ pascalize .Name }}Headers { 104 | {{ range .Headers }}{{ if .HasDefault }}{{ pascalize .Name }}: {{ printf "%#v" .Default }},{{ end }} 105 | {{ end }}} 106 | } 107 | 108 | // New{{ pascalize .Name }}HeadersFromObj creates {{ pascalize .Name }}Headers from a map or struct with defaults 109 | func New{{ pascalize .Name }}HeadersFromObj(headers_obj interface{}) *{{ pascalize .Name }}Headers { 110 | headers := New{{ pascalize .Name }}Headers() 111 | codon_ms.Decode(headers_obj, headers) 112 | return headers 113 | } 114 | 115 | // New{{ pascalize .Name }} creates {{ pascalize .Name }} with default headers values 116 | func New{{ pascalize .Name }}({{ if eq .Code -1 }}code int{{ end }}) *{{ pascalize .Name }} { {{ if eq .Code -1 }} 117 | if code <= 0 { 118 | code = 500 119 | } 120 | {{ end }} 121 | return &{{ pascalize .Name }}{ 122 | {{ if eq .Code -1 }}_statusCode: code, 123 | {{ end }}Headers: *New{{ pascalize .Name }}Headers(), 124 | } 125 | } 126 | 127 | {{ if eq .Code -1 }} 128 | // WithStatusCode adds the status to the {{ humanize .Name }} response 129 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) WithStatusCode(code int) *{{ pascalize .Name }} { 130 | {{ .ReceiverName }}._statusCode = code 131 | return {{ .ReceiverName }} 132 | } 133 | 134 | // SetStatusCode sets the status to the {{ humanize .Name }} response 135 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) SetStatusCode(code int) { 136 | {{ .ReceiverName }}._statusCode = code 137 | } 138 | {{ end }}{{ range .Headers }} 139 | // With{{ pascalize .Name }} adds the {{ camelize .Name }} to the {{ humanize $.Name }} response 140 | func ({{ $.ReceiverName }} *{{ pascalize $.Name }}) With{{ pascalize .Name }}({{ varname .Name }} {{ .GoType}}) *{{ pascalize $.Name }} { 141 | {{ $.ReceiverName }}.Headers.{{ pascalize .Name }} = {{ varname .Name }} 142 | return {{ .ReceiverName }} 143 | } 144 | 145 | // Set{{ pascalize .Name }} sets the {{ camelize .Name }} to the {{ humanize $.Name }} response 146 | func ({{ $.ReceiverName }} *{{ pascalize $.Name }}) Set{{ pascalize .Name }}({{ varname .Name }} {{ .GoType}}) { 147 | {{ $.ReceiverName }}.Headers.{{ pascalize .Name }} = {{ varname .Name }} 148 | } 149 | {{ end }}{{ if .Schema }} 150 | // WithPayload adds the payload to the {{ humanize .Name }} response 151 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) WithPayload(payload {{ if and .Schema.IsComplexObject (not .Schema.IsBaseType) }}*{{ end }}{{ .Schema.GoType }}) *{{ pascalize .Name }} { 152 | {{ .ReceiverName }}.Payload = payload 153 | return {{ .ReceiverName }} 154 | } 155 | 156 | // SetPayload sets the payload to the {{ humanize .Name }} response 157 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) SetPayload(payload {{ if and .Schema.IsComplexObject (not .Schema.IsBaseType) }}*{{ end }}{{ .Schema.GoType }}) { 158 | {{ .ReceiverName }}.Payload = payload 159 | } 160 | {{ end }} 161 | 162 | // WriteResponse to the client 163 | func ({{ .ReceiverName }} *{{ pascalize .Name }}) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { 164 | {{ range .Headers }} 165 | // response header {{.Name}} 166 | {{ template "serverheaderbuilder" . -}} 167 | {{ end }} 168 | rw.WriteHeader({{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}){{ if .Schema }}{{ if .Schema.IsComplexObject }} 169 | if {{ .ReceiverName }}.Payload != nil { {{ end }} 170 | payload := {{ .ReceiverName }}.Payload{{ if .Schema.IsArray }} 171 | if payload == nil { 172 | payload = {{ .Schema.Zero }} 173 | } 174 | {{ end }} 175 | {{ if (index .Extensions "x-template") }} 176 | context := map[string]interface{} { 177 | "body": payload, 178 | } 179 | if err := templates.Execute("{{(index .Extensions "x-template" "location")}}", context, rw); err != nil { 180 | panic(err) 181 | } 182 | {{ else }} 183 | if err := producer.Produce(rw, payload); err != nil { 184 | panic(err) // let the recovery middleware deal with this 185 | } 186 | {{ end }} 187 | {{ if .Schema.IsComplexObject }} } {{ end }}{{ end }} 188 | } 189 | {{ end }}package {{ .Package }} 190 | 191 | // This file was generated by the swagger tool. 192 | // Editing this file might prove futile when you re-run the swagger generate command 193 | 194 | 195 | import ( 196 | "net/http" 197 | "github.com/go-openapi/runtime" 198 | "github.com/go-openapi/swag" 199 | "github.com/go-openapi/errors" 200 | "github.com/go-openapi/validate" 201 | "github.com/go-openapi/runtime" 202 | 203 | {{ range .DefaultImports }}{{ printf "%q" .}} 204 | {{ end }} 205 | {{ range $key, $value := .Imports }}{{ $key }} {{ printf "%q" $value }} 206 | {{ end }} 207 | "github.com/grofers/go-codon/runtime/templates" 208 | codon_ms "github.com/grofers/go-codon/runtime/mapstructure" 209 | ) 210 | 211 | {{ range .Responses }} 212 | {{ template "serverresponse" . }} 213 | {{ end }} 214 | {{ if .DefaultResponse }} 215 | {{ template "serverresponse" .DefaultResponse }} 216 | {{ end }} 217 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/swagger/server/views.gotmpl: -------------------------------------------------------------------------------- 1 | package {{ .APIPackage }} 2 | 3 | import ( 4 | "log" 5 | 6 | middleware "github.com/go-openapi/runtime/middleware" 7 | 8 | conv "github.com/cstockton/go-conv" 9 | 10 | {{range .DefaultImports}}{{printf "%q" .}} 11 | {{end}} 12 | {{range $key, $value := .Imports}}{{$key}} {{ printf "%q" $value}} 13 | {{end}} 14 | {{range $key, $value := .GenOpts.Imports}}{{$key}} {{ printf "%q" $value}} 15 | {{end}} 16 | ) 17 | {{ $package := .Package }} 18 | 19 | func configureViews(api *{{.Package}}.{{ pascalize .Name }}API) { 20 | {{range .Operations}}api.{{if ne .Package $package}}{{pascalize .Package}}{{end}}{{ pascalize .Name }}Handler = {{.Package}}.{{ pascalize .Name }}HandlerFunc(func({{ if .WithContext }}ctx context.Context, {{ end }}params {{.Package}}.{{ pascalize .Name }}Params{{if .Authorized}}, principal {{if not ( eq .Principal "interface{}" )}}*{{ end }}{{.Principal}}{{end}}) middleware.Responder { 21 | {{ $workflow := (index .Extensions "x-workflow") }} 22 | {{if $workflow}} 23 | var_map, err := params.GetParamsMap() 24 | log.Println("[INFO] Request received for {{if ne .Package $package}}{{ .Package}}{{end}}.{{pascalize .Name}}, params: ", var_map) 25 | if err != nil { 26 | log.Println("[WARN] Failed to parse params for request. Error: ", err) 27 | {{ if .FailResponse }}return &operations.{{ pascalize .FailResponse.Name }}{ 28 | }{{ else }}return &operations.{{ pascalize .SuccessResponse.Name }}{ 29 | }{{ end }} 30 | } 31 | 32 | resp := workflows.{{ pascalize $workflow }}(var_map).(map[string]interface{}) 33 | 34 | status_code, _ := conv.Int(resp["status_code"]) 35 | 36 | headers, headers_ok := resp["headers"] 37 | if !headers_ok { 38 | headers = map[string]interface{} {} 39 | } 40 | 41 | switch status_code { 42 | {{ range .Responses }}case {{.Code}}: 43 | return &operations.{{ pascalize .Name }}{ 44 | {{ if .Schema }}Payload: resp["body"], 45 | {{ end }} 46 | Headers: *operations.New{{ pascalize .Name }}HeadersFromObj(headers), 47 | } 48 | {{ end }}default: 49 | {{ if .FailResponse }}return &operations.{{ pascalize .FailResponse.Name }}{ 50 | }{{ else }}return &operations.{{ pascalize .SuccessResponse.Name }}{ 51 | }{{ end }} 52 | } 53 | {{ else }} 54 | return middleware.NotImplemented("operation {{if ne .Package $package}}{{ .Package}}{{end}}.{{pascalize .Name}} has not yet been implemented") 55 | {{ end }} 56 | }) 57 | {{end}} 58 | } 59 | -------------------------------------------------------------------------------- /bootstrap/golang/content/spec/templates/workflow/golang.gotmpl: -------------------------------------------------------------------------------- 1 | {{define "outputmapexpander"}}map[string]interface{} { 2 | {{range $key, $value := .Children}} 3 | {{if eq $value.Type "map"}} 4 | "{{$key}}": {{template "outputmapexpander" $value}}, 5 | {{else}} 6 | "{{$key}}": maybe({{$value.FlowName}}_expr_{{$value.ExpressionSrno}}(expr_map)), 7 | {{end}} 8 | {{end}} 9 | }{{end}} 10 | package workflows 11 | {{if .OrigSpec.References.go}} 12 | import ( 13 | config "github.com/grofers/go-codon/runtime/config" 14 | {{range $index, $element := .OrigSpec.References.go}} 15 | {{$index}} "{{$element}}" 16 | {{end}} 17 | ) 18 | {{end}} 19 | 20 | {{range $expr, $expr_obj := .ExpressionMap}} 21 | {{if eq $expr_obj.Type "jmes"}}var {{$.OrigSpec.Name}}_eval_{{$expr_obj.Srno}} *jmespath.JMESPath{{end}}{{if eq $expr_obj.Type "jngo"}}var {{$.OrigSpec.Name}}_eval_{{$expr_obj.Srno}} *pongo2.Template{{end}}{{end}} 22 | 23 | func init() { 24 | {{range $expr, $expr_obj := .ExpressionMap}} 25 | {{if eq $expr_obj.Type "jmes"}}{{$.OrigSpec.Name}}_eval_{{$expr_obj.Srno}} = jmespath.MustCompile("{{escapestring $expr_obj.Raw}}"){{end}}{{if eq $expr_obj.Type "jngo"}}{{$.OrigSpec.Name}}_eval_{{$expr_obj.Srno}} = pongo2.Must(pongo2.FromString("{{escapestring $expr_obj.Raw}}")){{end}}{{end}} 26 | } 27 | 28 | type {{printf "%s_data" $.OrigSpec.Name }} struct { 29 | var_map map[string]interface{} 30 | var_map_mutex sync.RWMutex 31 | {{range $index, $element := $.OrigSpec.Tasks}} 32 | {{if gt $element.Join 0}} 33 | {{printf "%s__jc" $index}} joincoordinator 34 | {{end}} 35 | {{end}} 36 | } 37 | 38 | func {{ pascalize .OrigSpec.Name }}(var_map map[string]interface{}) interface{} { 39 | var err interface{} 40 | err = nil 41 | 42 | flow_data := &{{printf "%s_data" $.OrigSpec.Name }}{ 43 | var_map: var_map, 44 | var_map_mutex: sync.RWMutex{}, 45 | } 46 | 47 | {{ if eq (len .OrigSpec.Start) 1 }} 48 | {{printf "task_%s__%s" $.OrigSpec.Name (index $.OrigSpec.Start 0)}}(flow_data) 49 | {{ else if gt (len .OrigSpec.Start) 1 }} 50 | var wg sync.WaitGroup 51 | wg.Add({{len .OrigSpec.Start}}) 52 | {{ range $index, $element := .OrigSpec.Start }} 53 | go func() { 54 | defer func () { 55 | if r := recover(); r != nil { 56 | err = r 57 | } 58 | wg.Done() 59 | }() 60 | {{printf "task_%s__%s" $.OrigSpec.Name $element}}(flow_data) 61 | }() 62 | {{ end }} 63 | wg.Wait() 64 | {{ end }} 65 | 66 | if err != nil { 67 | panic(err) 68 | } 69 | 70 | expr_map := make(map[string]interface{}) 71 | expr_map["main"] = flow_data.var_map 72 | expr_map["constants"] = config.YmlConfig.Constants 73 | 74 | defer flow_data.var_map_mutex.RUnlock() 75 | flow_data.var_map_mutex.RLock() 76 | {{if eq .LanguageSpec.OutputObj.Type "map"}} 77 | ret_map := {{template "outputmapexpander" .LanguageSpec.OutputObj}} 78 | {{else}} 79 | ret_map := maybe({{$.OrigSpec.Name}}_expr_{{.LanguageSpec.OutputObj.ExpressionSrno}}(expr_map)) 80 | {{end}} 81 | 82 | return ret_map 83 | } 84 | 85 | {{range $index, $element := .OrigSpec.Tasks}} 86 | func {{printf "task_%s__%s" $.OrigSpec.Name $index}}(flow_data *{{printf "%s_data" $.OrigSpec.Name }}) error { 87 | {{if gt $element.Join 0}} 88 | defer flow_data.{{printf "%s__jc" $index}}.fin_mutex.Unlock() 89 | if flow_data.{{printf "%s__jc" $index}}.execute({{$element.Join}}) { 90 | return nil 91 | } 92 | {{end}} 93 | expr_map := make(map[string]interface{}) 94 | expr_map["main"] = flow_data.var_map 95 | expr_map["constants"] = config.YmlConfig.Constants 96 | 97 | var err error 98 | is_error := false 99 | 100 | {{ if $element.Action }} 101 | arg_map := make(map[string]interface{}) 102 | {{if gt (len $element.Input) 0}} 103 | func() { 104 | defer flow_data.var_map_mutex.RUnlock() 105 | flow_data.var_map_mutex.RLock() 106 | {{range $index2, $element2 := $element.Input}} 107 | arg_map["{{$index2}}"], _ = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2).Srno }}(expr_map){{end}} 108 | }() 109 | {{end}} 110 | {{if $element.Timeout}}arg_map["_timeout"] = maybe({{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element.Timeout).Srno }}(expr_map)){{end}} 111 | 112 | {{ if eq (index $.ActionMap $element.Action).Type "clients" }} 113 | log.Println("[DEBUG] Sending a request to {{ (index $.ActionMap $element.Action).Pascalized }} API") 114 | resp, rerr := {{ (index $.ActionMap $element.Action).Pascalized }}(arg_map) 115 | err = rerr 116 | if err == nil { 117 | resp_map := resp.Payload 118 | log.Println("[DEBUG] Received response from {{ (index $.ActionMap $element.Action).Pascalized }} API: ", resp_map) 119 | expr_map["action"] = resp_map 120 | } 121 | {{ else }} 122 | resp, rerr := {{ (index $.ActionMap $element.Action).Pascalized }}(arg_map) 123 | err = rerr 124 | if err == nil { 125 | expr_map["action"] = resp 126 | } 127 | {{ end }} 128 | 129 | {{ else if $element.WithItems }} 130 | var perr interface{} = nil 131 | var wg sync.WaitGroup 132 | var iterlist_interface interface{} 133 | var iterlist []interface{} 134 | var_maps := []map[string]interface{} {} 135 | err_objs := []*error {} 136 | 137 | func() { 138 | defer flow_data.var_map_mutex.RUnlock() 139 | flow_data.var_map_mutex.RLock() 140 | iterlist_interface, err = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element.WithItems).Srno }}(expr_map) 141 | }() 142 | 143 | func() { 144 | if err != nil { 145 | return 146 | } 147 | var ok bool 148 | iterlist, ok = iterlist_interface.([]interface{}) 149 | if !ok { 150 | err = fmt.Errorf("Error casting iteration list into array: %s\n", iterlist_interface) 151 | return 152 | } 153 | 154 | wg.Add(len(iterlist)) 155 | 156 | for _, element := range iterlist { 157 | expr_map["item"] = element 158 | new_var_map := make(map[string]interface{}) 159 | var nil_err error 160 | var err_obj *error 161 | err_obj = &nil_err 162 | {{if gt (len $element.Loop.Input) 0}} 163 | func() { 164 | defer flow_data.var_map_mutex.RUnlock() 165 | flow_data.var_map_mutex.RLock() 166 | {{range $index2, $element2 := $element.Loop.Input}} 167 | new_var_map["{{$index2}}"], _ = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2).Srno }}(expr_map){{end}} 168 | }() 169 | {{end}} 170 | var_maps = append(var_maps, new_var_map) 171 | err_objs = append(err_objs, err_obj) 172 | 173 | 174 | go func() { 175 | var terr error 176 | defer func() { 177 | if r := recover(); r != nil { 178 | perr = r 179 | } 180 | if terr != nil { 181 | (*err_obj) = terr 182 | is_error = true 183 | err = terr 184 | } 185 | wg.Done() 186 | }() 187 | terr = {{printf "task_%s__%s" $.OrigSpec.Name $element.Loop.TaskName}}(&{{printf "%s_data" $.OrigSpec.Name }} { 188 | var_map: new_var_map, 189 | var_map_mutex: sync.RWMutex{}, 190 | }) 191 | }() 192 | } 193 | expr_map["item"] = nil 194 | 195 | wg.Wait() 196 | }() 197 | 198 | if perr != nil { 199 | panic(perr) 200 | } 201 | {{ end }} 202 | 203 | {{ if $element.Action }} 204 | if err != nil { 205 | log.Println("[WARN] Failed to get response from {{ (index $.ActionMap $element.Action).Pascalized }} API: ", resp, " Err: ", err, " Args: ", arg_map) 206 | is_error = true 207 | err_val := reflect.ValueOf(err) 208 | if err_val.Kind() == reflect.Struct { 209 | err_payload := err_val.FieldByName("Payload") 210 | if err_payload.IsValid() { 211 | expr_map["action"] = err_payload.Interface() 212 | } 213 | } else { 214 | expr_map["action_exception"] = err 215 | } 216 | } 217 | {{ else if $element.WithItems }} 218 | if err != nil { 219 | is_error = true 220 | } 221 | {{ end }} 222 | 223 | {{ if $element.WithItems }} 224 | {{ if $element.Loop.ErrorPublishList }} 225 | func() { 226 | defer flow_data.var_map_mutex.Unlock() 227 | flow_data.var_map_mutex.Lock() 228 | {{range $index2, $element2 := $element.Loop.ErrorPublishList}} 229 | flow_data.var_map["{{$element2.VariableName}}"] = []interface{}{} 230 | {{end}} 231 | for index, element := range iterlist { 232 | expr_map["item"] = element 233 | var err_obj *error 234 | err_obj = err_objs[index] 235 | if err_obj != nil && *err_obj != nil { 236 | err_val := reflect.ValueOf(*err_obj) 237 | if err_val.Kind() == reflect.Struct { 238 | err_payload := err_val.FieldByName("Payload") 239 | if err_payload.IsValid() { 240 | expr_map["task"] = err_payload.Interface() 241 | } 242 | } else { 243 | expr_map["task_exception"] = *err_obj 244 | } 245 | } 246 | {{range $index2, $element2 := $element.Loop.ErrorPublishList}} 247 | flow_data.var_map["{{$element2.VariableName}}"] = append(flow_data.var_map["{{$element2.VariableName}}"].([]interface{}), maybe({{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map))) 248 | {{end}} 249 | } 250 | }() 251 | {{ end }} 252 | {{ if $element.Loop.PublishList }} 253 | func() { 254 | defer flow_data.var_map_mutex.Unlock() 255 | flow_data.var_map_mutex.Lock() 256 | {{range $index2, $element2 := $element.Loop.PublishList}} 257 | flow_data.var_map["{{$element2.VariableName}}"] = []interface{}{} 258 | {{end}} 259 | for index, element := range iterlist { 260 | expr_map["item"] = element 261 | expr_map["task"] = var_maps[index] 262 | var err_obj *error 263 | err_obj = err_objs[index] 264 | if err_obj != nil && *err_obj != nil { 265 | expr_map["task"] = nil 266 | } 267 | {{range $index2, $element2 := $element.Loop.PublishList}} 268 | flow_data.var_map["{{$element2.VariableName}}"] = append(flow_data.var_map["{{$element2.VariableName}}"].([]interface{}), maybe({{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map))) 269 | {{end}} 270 | } 271 | }() 272 | {{ end }} 273 | {{ end }} 274 | {{if gt (len $element.ErrorPublishList) 0}} 275 | if is_error { 276 | func() { 277 | defer flow_data.var_map_mutex.Unlock() 278 | flow_data.var_map_mutex.Lock() 279 | {{range $index2, $element2 := $element.ErrorPublishList}} 280 | flow_data.var_map["{{$element2.VariableName}}"], _ = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map) 281 | {{end}} 282 | }() 283 | } 284 | {{ end }} 285 | {{if gt (len $element.PublishList) 0}} 286 | if !is_error { 287 | func() { 288 | defer flow_data.var_map_mutex.Unlock() 289 | flow_data.var_map_mutex.Lock() 290 | {{range $index2, $element2 := $element.PublishList}} 291 | flow_data.var_map["{{$element2.VariableName}}"], _ = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map) 292 | {{end}} 293 | }() 294 | } 295 | {{ end }} 296 | 297 | {{range $index2, $element2 := $element.OnSuccessList}} 298 | var expr{{$element2.Srno}} interface{} 299 | var e{{$element2.Srno}} error 300 | {{end}} 301 | {{range $index2, $element2 := $element.OnErrorList}} 302 | var eexpr{{$element2.Srno}} interface{} 303 | var ee{{$element2.Srno}} error 304 | {{end}} 305 | {{if gt (len $element.OnErrorList) 0}} 306 | if is_error { 307 | func() { 308 | defer flow_data.var_map_mutex.RUnlock() 309 | flow_data.var_map_mutex.RLock() 310 | {{range $index2, $element2 := $element.OnErrorList}} 311 | eexpr{{$element2.Srno}}, ee{{$element2.Srno}} = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map){{end}} 312 | }() 313 | } 314 | {{end}} 315 | {{if gt (len $element.OnSuccessList) 0}} 316 | if !is_error { 317 | func() { 318 | defer flow_data.var_map_mutex.RUnlock() 319 | flow_data.var_map_mutex.RLock() 320 | {{range $index2, $element2 := $element.OnSuccessList}} 321 | expr{{$element2.Srno}}, e{{$element2.Srno}} = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map){{end}} 322 | }() 323 | } 324 | {{end}} 325 | 326 | {{ if gt (len $element.OnErrorList) 0 }} 327 | if is_error { 328 | {{if eq (len $element.OnErrorList) 1}} 329 | if ee1 == nil && eexpr1.(bool) { 330 | {{printf "task_%s__%s" $.OrigSpec.Name (index $element.OnErrorList 0).TaskName}}(flow_data) 331 | } 332 | {{else if gt (len $element.OnErrorList) 1}} 333 | var wg sync.WaitGroup 334 | var wgerr interface{} 335 | wgerr = nil 336 | {{range $index2, $element2 := $element.OnErrorList}} 337 | if ee{{$element2.Srno}} == nil && eexpr{{$element2.Srno}}.(bool) { 338 | wg.Add(1) 339 | go func() { 340 | defer func () { 341 | if r := recover(); r != nil { 342 | wgerr = r 343 | } 344 | wg.Done() 345 | }() 346 | {{printf "task_%s__%s" $.OrigSpec.Name $element2.TaskName}}(flow_data) 347 | }() 348 | } 349 | {{end}} 350 | wg.Wait() 351 | {{end}} 352 | } 353 | {{ end }} 354 | {{ if gt (len $element.OnSuccessList) 0 }} 355 | if !is_error { 356 | {{if eq (len $element.OnSuccessList) 1}} 357 | if e1 == nil && expr1.(bool) { 358 | {{printf "task_%s__%s" $.OrigSpec.Name (index $element.OnSuccessList 0).TaskName}}(flow_data) 359 | } 360 | {{else if gt (len $element.OnSuccessList) 1}} 361 | var wg sync.WaitGroup 362 | var wgerr interface{} 363 | wgerr = nil 364 | {{range $index2, $element2 := $element.OnSuccessList}} 365 | if e{{$element2.Srno}} == nil && expr{{$element2.Srno}}.(bool) { 366 | wg.Add(1) 367 | go func() { 368 | defer func () { 369 | if r := recover(); r != nil { 370 | wgerr = r 371 | } 372 | wg.Done() 373 | }() 374 | {{printf "task_%s__%s" $.OrigSpec.Name $element2.TaskName}}(flow_data) 375 | }() 376 | } 377 | {{end}} 378 | wg.Wait() 379 | {{end}} 380 | } 381 | {{ end }} 382 | 383 | {{range $index2, $element2 := $element.OnCompleteList}} 384 | onc_expr{{$element2.Srno}}, onc_e{{$element2.Srno}} := {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map) 385 | if onc_e{{$element2.Srno}} == nil && onc_expr{{$element2.Srno}}.(bool) { 386 | {{printf "task_%s__%s" $.OrigSpec.Name $element2.TaskName}}(flow_data) 387 | } 388 | {{end}} 389 | 390 | {{if gt (len $element.CompletePublishList) 0}} 391 | func() { 392 | defer flow_data.var_map_mutex.Unlock() 393 | flow_data.var_map_mutex.Lock() 394 | {{range $index2, $element2 := $element.CompletePublishList}} 395 | flow_data.var_map["{{$element2.VariableName}}"], _ = {{$.OrigSpec.Name}}_expr_{{ (index $.ExpressionMap $element2.ExpressionName).Srno }}(expr_map) 396 | {{end}} 397 | }() 398 | {{end}} 399 | 400 | if is_error { 401 | return err 402 | } else { 403 | return nil 404 | } 405 | } 406 | {{end}} 407 | 408 | {{range $expr, $expr_obj := .ExpressionMap}} 409 | // {{$expr}} -> {{$expr_obj}} 410 | func {{$.OrigSpec.Name}}_expr_{{$expr_obj.Srno}}(var_map map[string]interface{}) (result interface{}, err error) { 411 | {{if eq $expr_obj.Type "json"}}err = json.Unmarshal([]byte("{{escapestring $expr_obj.Raw}}"), &result){{else if eq $expr_obj.Type "jmes"}}result, err = {{$.OrigSpec.Name}}_eval_{{$expr_obj.Srno}}.Search(var_map){{else if eq $expr_obj.Type "jngo"}}var t_result string 412 | t_result, err = {{$.OrigSpec.Name}}_eval_{{$expr_obj.Srno}}.Execute(pongo2.Context(var_map)) 413 | if err != nil { 414 | result = nil 415 | return 416 | } 417 | err = json.Unmarshal([]byte(t_result), &result){{end}} 418 | if err == nil { 419 | result = resolvePointers(result) 420 | } 421 | return 422 | } 423 | {{end}} 424 | -------------------------------------------------------------------------------- /bootstrap/golang/content/workflows/workflow_shared.gofile: -------------------------------------------------------------------------------- 1 | package workflows 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | ) 7 | 8 | type joincoordinator struct { 9 | // WaitGroup used to wait for all joins 10 | wg sync.WaitGroup 11 | // Mutex used to coordinate wg initialization 12 | mutex sync.RWMutex 13 | // Mutex used to coordinate task syncronization 14 | fin_mutex sync.RWMutex 15 | // Mark wg as initialized 16 | initialized bool 17 | // Mark join as claimed by a task 18 | // Only one task may claim to be the executor after join 19 | claimed bool 20 | } 21 | 22 | func (jc *joincoordinator) execute(num int) bool { 23 | func () { 24 | defer jc.mutex.Unlock() 25 | jc.mutex.Lock() 26 | if !jc.initialized { 27 | jc.wg.Add(num) 28 | jc.initialized = true 29 | } 30 | }() 31 | 32 | join_to_exit := false 33 | func () { 34 | jc.wg.Done() 35 | jc.wg.Wait() 36 | defer jc.mutex.Unlock() 37 | jc.mutex.Lock() 38 | if jc.claimed { 39 | join_to_exit = true 40 | return 41 | } else { 42 | jc.fin_mutex.Lock() 43 | } 44 | jc.claimed = true 45 | }() 46 | if join_to_exit { 47 | jc.fin_mutex.Lock() 48 | return true 49 | } 50 | return false 51 | } 52 | 53 | func maybe(obj interface{}, err error) interface{} { 54 | if err != nil { 55 | return nil 56 | } 57 | return obj 58 | } 59 | 60 | func resolvePointers(obj interface{}) interface{} { 61 | if obj == nil { 62 | return obj 63 | } 64 | obj_val := reflect.ValueOf(obj) 65 | for obj_val.Kind() == reflect.Ptr { 66 | obj_val = obj_val.Elem() 67 | } 68 | if obj_val.CanInterface() { 69 | return obj_val.Interface() 70 | } else { 71 | return nil 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /bootstrap/golang/golang.go: -------------------------------------------------------------------------------- 1 | package golang 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "log" 7 | "text/template" 8 | "strings" 9 | shared "github.com/grofers/go-codon/shared" 10 | ) 11 | 12 | // var templates = template.Must(template.ParseGlob("*")) 13 | 14 | type bootstrapper struct { 15 | CurrentDirPath string 16 | CurrentDirName string 17 | ProjectName string 18 | ProjectPath string 19 | } 20 | 21 | func (bs *bootstrapper) UpdateCurrentDirPath() error { 22 | pwd, err := os.Getwd() 23 | if err != nil { 24 | log.Println(err) 25 | return err 26 | } 27 | bs.CurrentDirPath = pwd 28 | log.Println("Updated working directory:", pwd) 29 | _, bs.CurrentDirName = filepath.Split(pwd) 30 | bs.ProjectName = bs.CurrentDirName 31 | log.Println("Working with project name:", bs.ProjectName) 32 | 33 | bs.ProjectPath, err = shared.BaseImport(bs.CurrentDirPath) 34 | if err != nil { 35 | log.Println(err) 36 | return err 37 | } 38 | 39 | return nil 40 | } 41 | 42 | func (bs *bootstrapper) process_templates() error { 43 | for _, asset := range AssetNames() { 44 | t := template.New(asset) 45 | 46 | // If the content being templated is a template itself 47 | if strings.HasSuffix(asset, ".gotmpl") { 48 | t = t.Delims("{|{", "}|}") 49 | } 50 | 51 | t = t.Funcs(template.FuncMap{ 52 | "pascalize": shared.Pascalize, 53 | }) 54 | 55 | t, err := t.Parse(string(MustAsset(asset))) 56 | if err != nil { 57 | log.Println("Failed to get asset:", err) 58 | return err 59 | } 60 | 61 | var new_asset_path string 62 | if strings.HasSuffix(asset, ".gofile") { 63 | new_asset_path = filepath.Join(bs.CurrentDirPath, strings.TrimSuffix(asset, ".gofile") + ".go") 64 | } else { 65 | new_asset_path = filepath.Join(bs.CurrentDirPath, asset) 66 | } 67 | base_path, _ := filepath.Split(new_asset_path) 68 | 69 | err = os.MkdirAll(base_path, 0755) 70 | if err != nil { 71 | log.Println("Failed to create file:", err) 72 | return err 73 | } 74 | 75 | var perms os.FileMode 76 | if strings.HasSuffix(asset, ".sh") { 77 | perms = 0775 78 | } else { 79 | perms = 0666 80 | } 81 | 82 | f, err := os.OpenFile(new_asset_path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perms) 83 | defer f.Close() 84 | if err != nil { 85 | log.Println("Failed to create file:", err) 86 | return err 87 | } 88 | 89 | err = t.Execute(f, bs) 90 | if err != nil { 91 | log.Println("Failed to execute template:", err) 92 | return err 93 | } 94 | } 95 | return nil 96 | } 97 | 98 | func (bs *bootstrapper) Bootstrap() bool { 99 | log.Println("Bootstrapping a codon project in golang ...") 100 | 101 | if err := bs.UpdateCurrentDirPath(); err != nil { 102 | return false 103 | } 104 | 105 | if err := bs.process_templates(); err != nil { 106 | return false 107 | } 108 | 109 | return true 110 | } 111 | 112 | var Bootstrapper = bootstrapper{} 113 | -------------------------------------------------------------------------------- /cmd/generate.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "log" 6 | "github.com/spf13/cobra" 7 | 8 | codon_generator "github.com/grofers/go-codon/generator" 9 | ) 10 | 11 | var ( 12 | generateClients bool 13 | generateWorkflow bool 14 | generateServer bool 15 | ) 16 | 17 | // generateCmd represents the generate command 18 | var generateCmd = &cobra.Command{ 19 | Use: "generate", 20 | Short: "Generates service code alongside codon service specification", 21 | Long: `After you have run init on your project folder you should edit 22 | the specification and configuration files according to your project. Run 23 | this command to finally generate code according to the specification. Your 24 | working directory must be your project directory. Not specifying any 25 | generator flags will generate everything.`, 26 | Run: func(cmd *cobra.Command, args []string) { 27 | if !generateClients && !generateWorkflow && !generateServer { 28 | generateClients = true 29 | generateWorkflow = true 30 | generateServer = true 31 | } 32 | opts := codon_generator.GenOpts { 33 | Language: "golang", 34 | GenerateClients: generateClients, 35 | GenerateWorkflow: generateWorkflow, 36 | GenerateServer: generateServer, 37 | } 38 | if codon_generator.Generate(opts) { 39 | log.Println("Generate successful") 40 | } else { 41 | log.Println("Generate failed") 42 | os.Exit(1) 43 | } 44 | }, 45 | } 46 | 47 | func init() { 48 | RootCmd.AddCommand(generateCmd) 49 | 50 | // Here you will define your flags and configuration settings. 51 | 52 | // Cobra supports Persistent Flags which will work for this command 53 | // and all subcommands, e.g.: 54 | // generateCmd.PersistentFlags().String("foo", "", "A help for foo") 55 | 56 | // Cobra supports local flags which will only run when this command 57 | // is called directly, e.g.: 58 | generateCmd.Flags().BoolVarP(&generateClients, "clients", "c", false, "Generate clients") 59 | generateCmd.Flags().BoolVarP(&generateWorkflow, "workflow", "w", false, "Generate workflow") 60 | generateCmd.Flags().BoolVarP(&generateServer, "server", "s", false, "Generate server") 61 | } 62 | -------------------------------------------------------------------------------- /cmd/init.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "log" 6 | "github.com/spf13/cobra" 7 | 8 | codon_bootstrap "github.com/grofers/go-codon/bootstrap" 9 | ) 10 | 11 | // initCmd represents the init command 12 | var initCmd = &cobra.Command{ 13 | Use: "init", 14 | Short: "Bootstraps an empty project folder for use with codon.", 15 | Long: `Sets up your project folder with proper folder structure and 16 | config/specification files to be used to generate service code. Run this 17 | command to begin your project. Your working directory must be your project 18 | directory.`, 19 | Run: func(cmd *cobra.Command, args []string) { 20 | if codon_bootstrap.Bootstrap("golang") { 21 | log.Println("Bootstrap successful") 22 | } else { 23 | log.Println("Bootstrap failed") 24 | os.Exit(1) 25 | } 26 | }, 27 | } 28 | 29 | func init() { 30 | RootCmd.AddCommand(initCmd) 31 | 32 | // Here you will define your flags and configuration settings. 33 | 34 | // Cobra supports Persistent Flags which will work for this command 35 | // and all subcommands, e.g.: 36 | // initCmd.PersistentFlags().String("foo", "", "A help for foo") 37 | 38 | // Cobra supports local flags which will only run when this command 39 | // is called directly, e.g.: 40 | // initCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 41 | 42 | } 43 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | var cfgFile string 12 | 13 | // RootCmd represents the base command when called without any subcommands 14 | var RootCmd = &cobra.Command{ 15 | Use: "codon", 16 | Short: "Generate workflow based web API", 17 | Long: `Codon drives workflow based web api development. It automates 18 | code generation from design to implementation as much as possible.`, 19 | // Uncomment the following line if your bare application 20 | // has an action associated with it: 21 | // Run: func(cmd *cobra.Command, args []string) { }, 22 | } 23 | 24 | // Execute adds all child commands to the root command sets flags appropriately. 25 | // This is called by main.main(). It only needs to happen once to the rootCmd. 26 | func Execute() { 27 | if err := RootCmd.Execute(); err != nil { 28 | fmt.Println(err) 29 | os.Exit(-1) 30 | } 31 | } 32 | 33 | func init() { 34 | cobra.OnInitialize(initConfig) 35 | 36 | // Here you will define your flags and configuration settings. 37 | // Cobra supports Persistent Flags, which, if defined here, 38 | // will be global for your application. 39 | 40 | // RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.go-codon.yaml)") 41 | // Cobra also supports local flags, which will only run 42 | // when this action is called directly. 43 | // RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 44 | } 45 | 46 | // initConfig reads in config file and ENV variables if set. 47 | func initConfig() { 48 | if cfgFile != "" { // enable ability to specify config file via flag 49 | viper.SetConfigFile(cfgFile) 50 | } 51 | 52 | viper.SetConfigName(".go-codon") // name of config file (without extension) 53 | viper.AddConfigPath("$HOME") // adding home directory as first search path 54 | viper.AutomaticEnv() // read in environment variables that match 55 | 56 | // If a config file is found, read it in. 57 | if err := viper.ReadInConfig(); err == nil { 58 | fmt.Println("Using config file:", viper.ConfigFileUsed()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /codon.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/grofers/go-codon/cmd" 4 | 5 | // All the generate directives here 6 | //go:generate go-bindata -prefix bootstrap/golang/content/ -pkg golang -o bootstrap/golang/bindata.go bootstrap/golang/content/... 7 | //go:generate go-bindata -prefix generator/golang/content/ -pkg golang -o generator/golang/bindata.go generator/golang/content/... 8 | 9 | func main() { 10 | cmd.Execute() 11 | } 12 | -------------------------------------------------------------------------------- /compliance/clients/clients.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import ( 4 | mock_client "github.com/grofers/go-codon/testing/clients/mock/client" 5 | ) 6 | 7 | 8 | var Mock = mock_client.NewHTTPClientWithConfigMap(nil, nil).Operations 9 | -------------------------------------------------------------------------------- /compliance/clients/mock/client/codon_mock_client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | strfmt "github.com/go-openapi/strfmt" 5 | "github.com/grofers/go-codon/testing/clients/mock/client/operations" 6 | ) 7 | 8 | func NewHTTPClientWithConfigMap(formats strfmt.Registry, cfgmap map[string]string) *CodonMock { 9 | return New() 10 | } 11 | 12 | func New() *CodonMock { 13 | cli := new(CodonMock) 14 | cli.Operations = operations.New() 15 | return cli 16 | } 17 | 18 | type CodonMock struct { 19 | Operations *operations.Client 20 | } 21 | -------------------------------------------------------------------------------- /compliance/clients/mock/client/operations/operations_client.go: -------------------------------------------------------------------------------- 1 | package operations 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | conv "github.com/cstockton/go-conv" 8 | ) 9 | 10 | func New() *Client { 11 | return &Client{} 12 | } 13 | 14 | type Client struct { 15 | } 16 | 17 | type GetMockOK struct { 18 | Payload interface{} 19 | } 20 | 21 | type GetMockError struct { 22 | Payload interface{} 23 | } 24 | 25 | func (o *GetMockError) Error() string { 26 | return fmt.Sprintf("[GET /{unknown}][%d] %+v", 400, o.Payload) 27 | } 28 | 29 | func (a *Client) GetSuccess(all_params map[string]interface{}) (*GetMockOK, error) { 30 | wait, ok := all_params["wait"] 31 | if ok { 32 | wait_val, err := conv.Int64(wait) 33 | if err != nil { 34 | wait_val = int64(0) 35 | } 36 | time.Sleep(time.Duration(wait_val) * time.Millisecond) 37 | } 38 | delete(all_params, "wait") 39 | delete(all_params, "_timeout") 40 | return &GetMockOK { 41 | Payload: all_params, 42 | }, nil 43 | } 44 | 45 | func (a *Client) GetFailure(all_params map[string]interface{}) (*GetMockOK, error) { 46 | return nil, errors.New("Mock Error") 47 | } 48 | 49 | func (a *Client) GetFailurePayload(all_params map[string]interface{}) (*GetMockOK, error) { 50 | return nil, &GetMockError { 51 | Payload: all_params, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /compliance/config.yml: -------------------------------------------------------------------------------- 1 | endpoints: 2 | constants: 3 | any_key: "any_value" 4 | -------------------------------------------------------------------------------- /compliance/spec/server/main.yml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | title: Codon flowgen compliance test 4 | version: "1.0.0" 5 | basePath: / 6 | produces: 7 | - application/json 8 | paths: 9 | /direct: 10 | get: 11 | x-workflow: get_health 12 | responses: 13 | 200: 14 | description: Feed from API Response 15 | schema: 16 | $ref: '#/definitions/ObjectResponse' 17 | definitions: 18 | ObjectResponse: 19 | type: object 20 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/chain.yml: -------------------------------------------------------------------------------- 1 | name: get_chain 2 | 3 | start: 4 | - first_task 5 | 6 | tasks: 7 | first_task: 8 | action: clients.mock.get_success 9 | publish: 10 | - first_val: "\"success-first\"" 11 | publish-on-error: 12 | - first_val: "\"error-first\"" 13 | on-success: 14 | - second_task: <%jmes `true` %> 15 | on-error: 16 | - third_task: <%jmes `true` %> 17 | second_task: 18 | publish: 19 | - second_val: "\"success-second\"" 20 | third_task: 21 | publish: 22 | - second_val: "\"error-second\"" 23 | 24 | output: 25 | body: 26 | first_val: <%jmes main.first_val %> 27 | second_val: <%jmes main.second_val %> 28 | status_code: <%jmes `200` %> 29 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/chain_complete.yml: -------------------------------------------------------------------------------- 1 | name: get_chain_complete 2 | 3 | start: 4 | - first_task 5 | 6 | tasks: 7 | first_task: 8 | action: clients.mock.get_success 9 | publish: 10 | - first_val: "\"success-first\"" 11 | publish-on-error: 12 | - first_val: "\"error-first\"" 13 | publish-on-complete: 14 | - third_val: "\"complete-first\"" 15 | on-success: 16 | - second_task: <%jmes `true` %> 17 | on-error: 18 | - third_task: <%jmes `true` %> 19 | on-complete: 20 | - fourth_task: <%jmes `true` %> 21 | second_task: 22 | publish: 23 | - second_val: "\"success-second\"" 24 | - third_val: "\"success-second\"" 25 | - fourth_val: "\"success-second\"" 26 | third_task: 27 | publish: 28 | - second_val: "\"error-second\"" 29 | - third_val: "\"error-second\"" 30 | - fourth_val: "\"error-second\"" 31 | fourth_task: 32 | publish: 33 | - fourth_val: "\"complete-fourth\"" 34 | 35 | output: 36 | body: 37 | first_val: <%jmes main.first_val %> 38 | second_val: <%jmes main.second_val %> 39 | third_val: <%jmes main.third_val %> 40 | fourth_val: <%jmes main.fourth_val %> 41 | status_code: <%jmes `200` %> 42 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/chain_error.yml: -------------------------------------------------------------------------------- 1 | name: get_chain_error 2 | 3 | start: 4 | - first_task 5 | 6 | tasks: 7 | first_task: 8 | action: clients.mock.get_failure 9 | publish: 10 | - first_val: "\"success-first\"" 11 | publish-on-error: 12 | - first_val: "\"error-first\"" 13 | on-success: 14 | - second_task: <%jmes `true` %> 15 | on-error: 16 | - third_task: <%jmes `true` %> 17 | second_task: 18 | publish: 19 | - second_val: "\"success-second\"" 20 | third_task: 21 | publish: 22 | - second_val: "\"error-second\"" 23 | 24 | output: 25 | body: 26 | first_val: <%jmes main.first_val %> 27 | second_val: <%jmes main.second_val %> 28 | status_code: <%jmes `200` %> 29 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/chain_error_complete.yml: -------------------------------------------------------------------------------- 1 | name: get_chain_error_complete 2 | 3 | start: 4 | - first_task 5 | 6 | tasks: 7 | first_task: 8 | action: clients.mock.get_failure 9 | publish: 10 | - first_val: "\"success-first\"" 11 | publish-on-error: 12 | - first_val: "\"error-first\"" 13 | publish-on-complete: 14 | - third_val: "\"complete-first\"" 15 | on-success: 16 | - second_task: <%jmes `true` %> 17 | on-error: 18 | - third_task: <%jmes `true` %> 19 | on-complete: 20 | - fourth_task: <%jmes `true` %> 21 | second_task: 22 | publish: 23 | - second_val: "\"success-second\"" 24 | - third_val: "\"success-second\"" 25 | - fourth_val: "\"success-second\"" 26 | third_task: 27 | publish: 28 | - second_val: "\"error-second\"" 29 | - third_val: "\"error-second\"" 30 | - fourth_val: "\"error-second\"" 31 | fourth_task: 32 | publish: 33 | - fourth_val: "\"complete-fourth\"" 34 | 35 | output: 36 | body: 37 | first_val: <%jmes main.first_val %> 38 | second_val: <%jmes main.second_val %> 39 | third_val: <%jmes main.third_val %> 40 | fourth_val: <%jmes main.fourth_val %> 41 | status_code: <%jmes `200` %> 42 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/complex_output.yml: -------------------------------------------------------------------------------- 1 | name: complex_output 2 | 3 | output: 4 | body: 5 | dict: 6 | a: '"a"' 7 | b: 1 8 | c: true 9 | d: "\"true\"" 10 | list: '[10,20,30]' 11 | # TODO: YAML lists are not supported yet! 12 | # - 10 13 | # - 20 14 | # - 30 15 | # list_obj: 16 | # - k1: '"v1"' 17 | # k2: '"v2"' 18 | # - k1: '"v3"' 19 | # k2: '"v4"' 20 | status_code: <%jmes `200` %> 21 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/direct.yml: -------------------------------------------------------------------------------- 1 | name: get_direct 2 | 3 | output: 4 | body: <%jmes `"OK"` %> 5 | status_code: <%jmes `200` %> 6 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/no_input.yml: -------------------------------------------------------------------------------- 1 | name: no_input 2 | 3 | start: 4 | - first_task 5 | 6 | tasks: 7 | first_task: 8 | action: clients.mock.get_success 9 | timeout: 1000 10 | publish: 11 | - success_reply: <%jmes action %> 12 | 13 | output: 14 | body: <%jmes main.success_reply %> 15 | status_code: <%jmes `200` %> 16 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/no_publish.yml: -------------------------------------------------------------------------------- 1 | name: no_publish 2 | 3 | description: To test that publish is optional 4 | 5 | start: 6 | - simple_task 7 | 8 | tasks: 9 | simple_task: 10 | action: clients.mock.get_success 11 | on-success: 12 | - simple_task2: <%jmes `true` %> 13 | simple_task2: 14 | action: clients.mock.get_failure 15 | on-error: 16 | - simple_task3: <%jmes `true` %> 17 | simple_task3: 18 | on-success: 19 | - simple_task4: <%jmes `true` %> 20 | simple_task4: 21 | on-failure: 22 | - simple_task5: <%jmes `true` %> 23 | simple_task5: 24 | publish: 25 | - val: 10 26 | 27 | output: 28 | body: 29 | val: <%jmes main.val %> 30 | status_code: <%jmes `200` %> 31 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/publish_serial.yml: -------------------------------------------------------------------------------- 1 | name: publish_serial 2 | 3 | start: 4 | - simple_task 5 | 6 | tasks: 7 | simple_task: 8 | publish: 9 | - retval: 1 10 | - retval: 2 11 | - retval: 3 12 | - retval: 4 13 | - retval: 5 14 | - retval: 6 15 | - retval: 7 16 | - retval: 8 17 | - retval: 9 18 | 19 | output: 20 | body: 21 | retval: <%jmes main.retval %> 22 | status_code: <%jmes `200` %> 23 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/recursion.yml: -------------------------------------------------------------------------------- 1 | name: recursion 2 | 3 | description: To test recursion of tasks. Tasks calling themselves. 4 | 5 | start: 6 | - add_till_10 7 | 8 | tasks: 9 | add_till_10: 10 | on-success: 11 | - add_1: <%jmes main.val < `10` %> 12 | add_1: 13 | publish: 14 | # TODO: We need to resolve pointers before sending 15 | # to jmes path. Functions will break otherwise. 16 | # - val: <%jmes sum([to_number(main.val), `1`]) %> 17 | - val: <%jmes `10` %> 18 | on-success: 19 | - add_1: <%jmes main.val < `10` %> 20 | 21 | output: 22 | body: 23 | val: <%jmes main.val %> 24 | status_code: <%jmes `200` %> 25 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/simple.yml: -------------------------------------------------------------------------------- 1 | name: get_simple 2 | 3 | start: 4 | - simple_task 5 | 6 | tasks: 7 | simple_task: 8 | action: clients.mock.get_success 9 | input: 10 | string_val: <%jmes main.string_val %> 11 | boolean_val: <%jmes main.boolean_val %> 12 | int_val: <%jmes main.int_val %> 13 | float_val: <%jmes main.float_val %> 14 | timeout: 1000 15 | publish: 16 | - success_reply: <%jmes action %> 17 | 18 | output: 19 | body: <%jmes main.success_reply %> 20 | status_code: <%jmes `200` %> 21 | -------------------------------------------------------------------------------- /compliance/spec/server/workflows/start.yml: -------------------------------------------------------------------------------- 1 | name: start_test 2 | 3 | start: 4 | - simple_task1 5 | - simple_task2 6 | 7 | # Since get_success has a sleep of 5ms both these tasks 8 | # should have been started with original values. After 9 | # execution the values should have been exchanged. If 10 | # these tasks are not called concurrently, both values 11 | # would be the same. 12 | tasks: 13 | simple_task1: 14 | action: clients.mock.get_success 15 | input: 16 | val: <%jmes main.val2 %> 17 | wait: 5 18 | publish: 19 | - val1: <%jmes action.val %> 20 | simple_task2: 21 | action: clients.mock.get_success 22 | input: 23 | val: <%jmes main.val1 %> 24 | wait: 5 25 | publish: 26 | - val2: <%jmes action.val %> 27 | 28 | output: 29 | body: 30 | val1: <%jmes main.val1 %> 31 | val2: <%jmes main.val2 %> 32 | status_code: <%jmes `200` %> 33 | -------------------------------------------------------------------------------- /compliance/workflows_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | "github.com/grofers/go-codon/testing/workflows" 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func deepcopyMap(src map[string]interface{}) map[string]interface{} { 10 | dst := map[string]interface{} {} 11 | for k, v := range src { 12 | dst[k] = v 13 | } 14 | return dst 15 | } 16 | 17 | func TestDirect(t *testing.T) { 18 | var_map := map[string]interface{} {} 19 | var_map_c := deepcopyMap(var_map) 20 | result_i := workflows.GetDirect(var_map_c) 21 | result, ok := result_i.(map[string]interface{}) 22 | if !ok { 23 | t.FailNow() 24 | } 25 | assert.Equal(t, result["body"], "OK", "Response body not OK") 26 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 27 | } 28 | 29 | func TestSimple(t *testing.T) { 30 | var_map := map[string]interface{} { 31 | "string_val": "test_val", 32 | "boolean_val": true, 33 | "int_val": int64(10), 34 | "float_val": float64(1.11), 35 | } 36 | var_map_c := deepcopyMap(var_map) 37 | result_i := workflows.GetSimple(var_map_c) 38 | result, ok := result_i.(map[string]interface{}) 39 | if !ok { 40 | t.FailNow() 41 | } 42 | assert.Equal(t, result["body"], var_map, "Response body not OK") 43 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 44 | } 45 | 46 | func TestNoInput(t *testing.T) { 47 | var_map := map[string]interface{} {} 48 | var_map_c := deepcopyMap(var_map) 49 | result_i := workflows.NoInput(var_map_c) 50 | result, ok := result_i.(map[string]interface{}) 51 | if !ok { 52 | t.FailNow() 53 | } 54 | assert.Equal(t, result["body"], var_map, "Response body not OK") 55 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 56 | } 57 | 58 | func TestChain(t *testing.T) { 59 | assert_map := map[string]interface{} { 60 | "first_val": "success-first", 61 | "second_val": "success-second", 62 | } 63 | var_map := map[string]interface{} {} 64 | result_i := workflows.GetChain(var_map) 65 | result, ok := result_i.(map[string]interface{}) 66 | if !ok { 67 | t.FailNow() 68 | } 69 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 70 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 71 | } 72 | 73 | func TestChainError(t *testing.T) { 74 | assert_map := map[string]interface{} { 75 | "first_val": "error-first", 76 | "second_val": "error-second", 77 | } 78 | var_map := map[string]interface{} {} 79 | result_i := workflows.GetChainError(var_map) 80 | result, ok := result_i.(map[string]interface{}) 81 | if !ok { 82 | t.FailNow() 83 | } 84 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 85 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 86 | } 87 | 88 | func TestChainComplete(t *testing.T) { 89 | assert_map := map[string]interface{} { 90 | "first_val": "success-first", 91 | "second_val": "success-second", 92 | "third_val": "complete-first", 93 | "fourth_val": "complete-fourth", 94 | } 95 | var_map := map[string]interface{} {} 96 | result_i := workflows.GetChainComplete(var_map) 97 | result, ok := result_i.(map[string]interface{}) 98 | if !ok { 99 | t.FailNow() 100 | } 101 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 102 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 103 | } 104 | 105 | func TestChainErrorComplete(t *testing.T) { 106 | assert_map := map[string]interface{} { 107 | "first_val": "error-first", 108 | "second_val": "error-second", 109 | "third_val": "complete-first", 110 | "fourth_val": "complete-fourth", 111 | } 112 | var_map := map[string]interface{} {} 113 | result_i := workflows.GetChainErrorComplete(var_map) 114 | result, ok := result_i.(map[string]interface{}) 115 | if !ok { 116 | t.FailNow() 117 | } 118 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 119 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 120 | } 121 | 122 | func TestPublishSerial(t *testing.T) { 123 | assert_map := map[string]interface{} { 124 | "retval": float64(9), 125 | } 126 | var_map := map[string]interface{} {} 127 | result_i := workflows.PublishSerial(var_map) 128 | result, ok := result_i.(map[string]interface{}) 129 | if !ok { 130 | t.FailNow() 131 | } 132 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 133 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 134 | } 135 | 136 | func TestStartConcurrency(t *testing.T) { 137 | assert_map := map[string]interface{} { 138 | "val1": 2, 139 | "val2": 1, 140 | } 141 | var_map := map[string]interface{} { 142 | "val1": 1, 143 | "val2": 2, 144 | } 145 | result_i := workflows.StartTest(var_map) 146 | result, ok := result_i.(map[string]interface{}) 147 | if !ok { 148 | t.FailNow() 149 | } 150 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 151 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 152 | } 153 | 154 | func TestComplexOutput(t *testing.T) { 155 | assert_map := map[string]interface{} { 156 | "dict": map[string]interface{} { 157 | "a": "a", 158 | "b": float64(1), 159 | "c": true, 160 | "d": "true", 161 | }, 162 | "list": []interface{} {float64(10),float64(20),float64(30)}, 163 | } 164 | var_map := deepcopyMap(assert_map) 165 | result_i := workflows.ComplexOutput(var_map) 166 | result, ok := result_i.(map[string]interface{}) 167 | if !ok { 168 | t.FailNow() 169 | } 170 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 171 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 172 | } 173 | 174 | func TestNoPublish(t *testing.T) { 175 | assert_map := map[string]interface{} { 176 | "val": 1, 177 | } 178 | var_map := deepcopyMap(assert_map) 179 | result_i := workflows.NoPublish(var_map) 180 | result, ok := result_i.(map[string]interface{}) 181 | if !ok { 182 | t.FailNow() 183 | } 184 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 185 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 186 | } 187 | 188 | func TestRecursion(t *testing.T) { 189 | assert_map := map[string]interface{} { 190 | "val": float64(10), 191 | } 192 | var_map := map[string]interface{} { 193 | "val": 1, 194 | } 195 | result_i := workflows.Recursion(var_map) 196 | result, ok := result_i.(map[string]interface{}) 197 | if !ok { 198 | t.FailNow() 199 | } 200 | assert.Equal(t, result["body"], assert_map, "Response body not OK") 201 | assert.Equal(t, result["status_code"], float64(200), "Response code not 200") 202 | } 203 | -------------------------------------------------------------------------------- /flowgen/generator/generate.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "errors" 5 | "github.com/grofers/go-codon/flowgen/languages" 6 | "github.com/grofers/go-codon/flowgen/shared" 7 | ) 8 | 9 | func Generate(language string, opts *GenOpts, post_spec *shared.PostSpec) error { 10 | var err error 11 | var gen languages.Generator 12 | switch language { 13 | case "go": 14 | gen = &languages.GoGenerator { 15 | Data: post_spec, 16 | Dest: opts.Dest, 17 | Templates: opts.Templates, 18 | } 19 | default: 20 | gen = nil 21 | } 22 | if gen == nil { 23 | err = errors.New("Language not implemented yet") 24 | } else { 25 | err = gen.Generate() 26 | } 27 | return err 28 | } 29 | -------------------------------------------------------------------------------- /flowgen/generator/options.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type GenOpts struct { 8 | Spec string 9 | Dest string 10 | Templates string 11 | } 12 | 13 | func (o *GenOpts) EnsureDefaults() { 14 | if o.Spec == "" { 15 | if _, err := os.Stat("flow.yml"); err == nil { 16 | o.Spec = "flow.yml" 17 | } else { 18 | o.Spec = "flow.yaml" 19 | } 20 | } 21 | if o.Dest == "" { 22 | o.Dest = "flow.go" 23 | } 24 | } 25 | 26 | func (o *GenOpts) Verify() error { 27 | if _, err := os.Stat(o.Spec); os.IsNotExist(err) { 28 | return err 29 | } 30 | 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /flowgen/generator/process.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "github.com/grofers/go-codon/flowgen/shared" 5 | ) 6 | 7 | func Process(opts *GenOpts) error { 8 | opts.EnsureDefaults() 9 | if err := opts.Verify(); err != nil { 10 | return err 11 | } 12 | 13 | spec, err := shared.ReadSpec(opts.Spec) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | spec_ptr := &spec 19 | 20 | post_spec, err := spec_ptr.Process() 21 | if err != nil { 22 | return err 23 | } 24 | 25 | err = Generate("go", opts, &post_spec) 26 | if err != nil { 27 | return err 28 | } 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /flowgen/languages/golang.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | import ( 4 | flowgen_shared "github.com/grofers/go-codon/flowgen/shared" 5 | shared "github.com/grofers/go-codon/shared" 6 | "text/template" 7 | "os" 8 | "fmt" 9 | "path/filepath" 10 | // "strings" 11 | html_template "html/template" 12 | // "fmt" 13 | ) 14 | 15 | type GoGenerator struct { 16 | Data *flowgen_shared.PostSpec 17 | Dest string 18 | Templates string 19 | BaseImport string 20 | } 21 | 22 | type OutputObj struct { 23 | Type string 24 | Children map[string]OutputObj 25 | ExpressionSrno int 26 | FlowName string 27 | } 28 | 29 | func (g GoGenerator) getOutputObj(output interface{}) OutputObj { 30 | outputObj := OutputObj{} 31 | switch output_val := output.(type) { 32 | case map[interface{}]interface{}: 33 | outputObj.Type = "map" 34 | outputObj.Children = map[string]OutputObj{} 35 | for key, val := range output_val { 36 | outputObj.Children[key.(string)] = g.getOutputObj(val) 37 | } 38 | default: 39 | outputObj.Type = "value" 40 | output_val_str := fmt.Sprintf("%v", output_val) 41 | outputObj.ExpressionSrno = g.Data.ExpressionMap[output_val_str].Srno 42 | } 43 | outputObj.FlowName = g.Data.OrigSpec.Name 44 | return outputObj 45 | } 46 | 47 | func (g *GoGenerator) postProcess() error { 48 | g.Data.LanguageSpec["OutputObj"] = g.getOutputObj(g.Data.OrigSpec.Output) 49 | g.Data.LanguageSpec["ErrorOutputObj"] = g.getOutputObj(g.Data.OrigSpec.ErrorOutput) 50 | 51 | if _, ok := g.Data.OrigSpec.References["go"]; !ok { 52 | g.Data.OrigSpec.References["go"] = make(map[string]string) 53 | } 54 | 55 | pwd, err := os.Getwd() 56 | if err != nil { 57 | return err 58 | } 59 | import_path, err := shared.BaseImport(pwd) 60 | if err != nil { 61 | return err 62 | } 63 | g.BaseImport = import_path 64 | 65 | // Add imports required for expressions 66 | for _, expr_obj := range g.Data.ExpressionMap { 67 | switch expr_obj.Type { 68 | case "json": 69 | g.Data.OrigSpec.References["go"]["json"] = "encoding/json" 70 | case "jmes": 71 | g.Data.OrigSpec.References["go"]["jmespath"] = "github.com/jmespath/go-jmespath" 72 | case "jngo": 73 | g.Data.OrigSpec.References["go"]["pongo2"] = "github.com/flosch/pongo2" 74 | } 75 | } 76 | 77 | // Add imports required for actions 78 | for _, action_obj := range g.Data.ActionMap { 79 | action_obj_type := action_obj.Type 80 | if action_obj_type == "" { 81 | continue 82 | } 83 | // If import location is not custom defined we assume 84 | // it is present in base import path 85 | if _, ok := g.Data.OrigSpec.References["go"][action_obj_type]; !ok { 86 | g.Data.OrigSpec.References["go"][action_obj_type] = g.BaseImport + "/" + action_obj_type 87 | } 88 | } 89 | 90 | return nil 91 | } 92 | 93 | func (g *GoGenerator) Generate() error { 94 | err := g.postProcess() 95 | if err != nil { 96 | return err 97 | } 98 | 99 | template_location := filepath.Join(g.Templates, "golang.gotmpl") 100 | 101 | tmpl, err := template.New("golang.gotmpl").Funcs(template.FuncMap{ 102 | "escapestring": html_template.JSEscapeString, 103 | "pascalize": shared.Pascalize, 104 | }).ParseFiles(template_location) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | dest_base, _ := filepath.Split(g.Dest) 110 | 111 | err = os.MkdirAll(dest_base, 0755) 112 | if err != nil { 113 | return err 114 | } 115 | 116 | f, err := os.Create(g.Dest) 117 | defer f.Close() 118 | if err != nil { 119 | return err 120 | } 121 | 122 | err = tmpl.Execute(f, g.Data) 123 | if err != nil { 124 | return err 125 | } 126 | 127 | return nil 128 | } 129 | -------------------------------------------------------------------------------- /flowgen/languages/shared.go: -------------------------------------------------------------------------------- 1 | package languages 2 | 3 | type Generator interface { 4 | Generate() error 5 | } 6 | -------------------------------------------------------------------------------- /flowgen/shared/spec.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "log" 5 | "fmt" 6 | "strings" 7 | "encoding/json" 8 | "regexp" 9 | "io/ioutil" 10 | "gopkg.in/yaml.v2" 11 | jmespath "github.com/jmespath/go-jmespath" 12 | pongo2 "github.com/flosch/pongo2" 13 | shared "github.com/grofers/go-codon/shared" 14 | conv "github.com/cstockton/go-conv" 15 | ) 16 | 17 | type PostSpec struct { 18 | //The original spec read from yaml 19 | OrigSpec *Spec 20 | // Compiled list of all the expressions in spec 21 | ExpressionMap map[string]Expression 22 | // Compiles list of actions 23 | ActionMap map[string]Action 24 | //Variables added by language level processing 25 | LanguageSpec map[string]interface{} 26 | } 27 | 28 | type Spec struct { 29 | Name string `yaml:"name"` 30 | Start []string `yaml:"start"` 31 | Tasks map[string]Task `yaml:"tasks"` 32 | Output interface{} `yaml:"output"` 33 | ErrorOutput interface{} `yaml:"output-on-error"` 34 | References map[string]map[string]string `yaml:"references"` 35 | } 36 | 37 | type Task struct { 38 | Join int `yaml:"join"` 39 | Action string `yaml:"action"` 40 | Input map[string]string `yaml:"input"` 41 | PublishRaw interface{} `yaml:"publish"` 42 | Publish map[string]string `yaml:"-"` 43 | PublishList []PublishObj `yaml:"-"` 44 | ErrorPublishRaw interface{} `yaml:"publish-on-error"` 45 | ErrorPublish map[string]string `yaml:"-"` 46 | ErrorPublishList []PublishObj `yaml:"-"` 47 | CompletePublishRaw interface{} `yaml:"publish-on-complete"` 48 | CompletePublish map[string]string `yaml:"-"` 49 | CompletePublishList []PublishObj `yaml:"-"` 50 | OnError []map[string]string `yaml:"on-error"` 51 | OnErrorList []TodoObj `yaml:"-"` 52 | OnSuccess []map[string]string `yaml:"on-success"` 53 | OnSuccessList []TodoObj `yaml:"-"` 54 | OnComplete []map[string]string `yaml:"on-complete"` 55 | OnCompleteList []TodoObj `yaml:"-"` 56 | Timeout string `yaml:"timeout"` 57 | WithItems string `yaml:"with-items"` 58 | Loop LoopInfo `yaml:"loop"` 59 | } 60 | 61 | type LoopInfo struct { 62 | TaskName string `yaml:"task"` 63 | Input map[string]string `yaml:"input"` 64 | PublishRaw interface{} `yaml:"publish"` 65 | Publish map[string]string `yaml:"-"` 66 | PublishList []PublishObj `yaml:"-"` 67 | ErrorPublishRaw interface{} `yaml:"publish-on-error"` 68 | ErrorPublish map[string]string `yaml:"-"` 69 | ErrorPublishList []PublishObj `yaml:"-"` 70 | } 71 | 72 | type TodoObj struct { 73 | TaskName string 74 | ExpressionName string 75 | Srno int 76 | } 77 | 78 | type PublishObj struct { 79 | VariableName string 80 | ExpressionName string 81 | Srno int 82 | } 83 | 84 | type Expression struct { 85 | Type string 86 | Raw string 87 | Srno int 88 | } 89 | 90 | type Action struct { 91 | Type string 92 | Raw string 93 | Pascalized string 94 | } 95 | 96 | func ReadSpec(filename string) (Spec, error) { 97 | filename_data, err := ioutil.ReadFile(filename) 98 | if err != nil { 99 | return Spec{}, err 100 | } 101 | 102 | var t Spec 103 | 104 | err = yaml.Unmarshal([]byte(filename_data), &t) 105 | if err != nil { 106 | return Spec{}, err 107 | } 108 | 109 | return t, nil 110 | } 111 | 112 | func (s *Spec) Process() (PostSpec, error) { 113 | var err error 114 | ps := PostSpec {} 115 | ps.OrigSpec = s 116 | if s.References == nil { 117 | s.References = make(map[string]map[string]string) 118 | } 119 | ps.LanguageSpec = make(map[string]interface{}) 120 | 121 | err = s.setLists() 122 | if err != nil { 123 | return PostSpec {}, err 124 | } 125 | 126 | ps.ExpressionMap, err = s.getExpressionMap() 127 | if err != nil { 128 | return PostSpec {}, err 129 | } 130 | 131 | ps.ActionMap, err = s.getActionMap() 132 | if err != nil { 133 | return PostSpec {}, err 134 | } 135 | 136 | return ps, nil 137 | } 138 | 139 | func (s *Spec) setLists() error { 140 | var err error 141 | for task_name, _ := range s.Tasks { 142 | task_obj := s.Tasks[task_name] 143 | task_obj.OnErrorList, err = task_obj.getOnErrorList() 144 | if err != nil { 145 | return err 146 | } 147 | task_obj.OnSuccessList, err = task_obj.getOnSuccessList() 148 | if err != nil { 149 | return err 150 | } 151 | task_obj.OnCompleteList, err = task_obj.getOnCompleteList() 152 | if err != nil { 153 | return err 154 | } 155 | task_obj.PublishList, err = task_obj.getPublishList() 156 | if err != nil { 157 | return err 158 | } 159 | task_obj.Publish = task_obj.tryPublishMap() 160 | task_obj.ErrorPublishList, err = task_obj.getErrorPublishList() 161 | if err != nil { 162 | return err 163 | } 164 | task_obj.ErrorPublish = task_obj.tryErrorPublishMap() 165 | task_obj.CompletePublishList, err = task_obj.getCompletePublishList() 166 | if err != nil { 167 | return err 168 | } 169 | task_obj.CompletePublish = task_obj.tryCompletePublishMap() 170 | if task_obj.WithItems != "" { 171 | task_obj.Loop.PublishList, err = task_obj.Loop.getPublishList() 172 | if err != nil { 173 | return err 174 | } 175 | task_obj.Loop.Publish = task_obj.Loop.tryPublishMap() 176 | task_obj.Loop.ErrorPublishList, err = task_obj.Loop.getErrorPublishList() 177 | if err != nil { 178 | return err 179 | } 180 | task_obj.Loop.ErrorPublish = task_obj.Loop.tryErrorPublishMap() 181 | } 182 | s.Tasks[task_name] = task_obj 183 | } 184 | return nil 185 | } 186 | 187 | func (s *Spec) getActionMap() (map[string]Action, error) { 188 | all_actions := make(map[string]Action) 189 | 190 | for _, task_obj := range s.Tasks { 191 | action_obj, err := processAction(task_obj.Action) 192 | if err != nil { 193 | log.Println("Unable to process action:", task_obj.Action) 194 | return nil, err 195 | } 196 | all_actions[task_obj.Action] = action_obj 197 | } 198 | 199 | return all_actions, nil 200 | } 201 | 202 | func (s *Spec) appendExpression(all_exprs map[string]Expression, expr *string, counter *int) error { 203 | if _, ok := all_exprs[*expr]; ok { 204 | return nil 205 | } 206 | expr_obj, err := s.processExpression(*expr) 207 | if err != nil { 208 | log.Println("Unable to process expression:", *expr) 209 | return err 210 | } 211 | expr_obj.Srno = *counter 212 | (*counter)++ 213 | all_exprs[*expr] = expr_obj 214 | return nil 215 | } 216 | 217 | func (s *Spec) getExpressionMap() (map[string]Expression, error) { 218 | all_exprs := make(map[string]Expression) 219 | counter := 1 220 | 221 | for _, task := range s.Tasks { 222 | for _, expr := range task.Input { 223 | err := s.appendExpression(all_exprs, &expr, &counter) 224 | if err != nil { 225 | return nil, err 226 | } 227 | } 228 | for _, publish_obj := range task.PublishList { 229 | expr := publish_obj.ExpressionName 230 | err := s.appendExpression(all_exprs, &expr, &counter) 231 | if err != nil { 232 | return nil, err 233 | } 234 | } 235 | for _, publish_obj := range task.ErrorPublishList { 236 | expr := publish_obj.ExpressionName 237 | err := s.appendExpression(all_exprs, &expr, &counter) 238 | if err != nil { 239 | return nil, err 240 | } 241 | } 242 | for _, publish_obj := range task.CompletePublishList { 243 | expr := publish_obj.ExpressionName 244 | err := s.appendExpression(all_exprs, &expr, &counter) 245 | if err != nil { 246 | return nil, err 247 | } 248 | } 249 | for _, task_obj := range task.OnErrorList { 250 | expr := task_obj.ExpressionName 251 | err := s.appendExpression(all_exprs, &expr, &counter) 252 | if err != nil { 253 | return nil, err 254 | } 255 | } 256 | for _, task_obj := range task.OnSuccessList { 257 | expr := task_obj.ExpressionName 258 | err := s.appendExpression(all_exprs, &expr, &counter) 259 | if err != nil { 260 | return nil, err 261 | } 262 | } 263 | for _, task_obj := range task.OnCompleteList { 264 | expr := task_obj.ExpressionName 265 | err := s.appendExpression(all_exprs, &expr, &counter) 266 | if err != nil { 267 | return nil, err 268 | } 269 | } 270 | if task.WithItems != "" { 271 | expr := task.WithItems 272 | err := s.appendExpression(all_exprs, &expr, &counter) 273 | if err != nil { 274 | return nil, err 275 | } 276 | for _, expr := range task.Loop.Input { 277 | err := s.appendExpression(all_exprs, &expr, &counter) 278 | if err != nil { 279 | return nil, err 280 | } 281 | } 282 | for _, task_obj := range task.Loop.PublishList { 283 | expr := task_obj.ExpressionName 284 | err := s.appendExpression(all_exprs, &expr, &counter) 285 | if err != nil { 286 | return nil, err 287 | } 288 | } 289 | for _, task_obj := range task.Loop.ErrorPublishList { 290 | expr := task_obj.ExpressionName 291 | err := s.appendExpression(all_exprs, &expr, &counter) 292 | if err != nil { 293 | return nil, err 294 | } 295 | } 296 | } 297 | if task.Timeout != "" { 298 | expr := task.Timeout 299 | err := s.appendExpression(all_exprs, &expr, &counter) 300 | if err != nil { 301 | return nil, err 302 | } 303 | } 304 | } 305 | 306 | var exprs []string = make([]string, 0) 307 | exprs = extractExpressionsFromMap(s.Output, exprs) 308 | for _, expr := range exprs { 309 | if _, ok := all_exprs[expr]; ok { 310 | continue 311 | } 312 | expr_obj, err := s.processExpression(expr) 313 | if err != nil { 314 | log.Println("Unable to process expression:", expr) 315 | return nil, err 316 | } 317 | expr_obj.Srno = counter 318 | counter++ 319 | all_exprs[expr] = expr_obj 320 | } 321 | 322 | exprs = make([]string, 0) 323 | exprs = extractExpressionsFromMap(s.ErrorOutput, exprs) 324 | for _, expr := range exprs { 325 | if _, ok := all_exprs[expr]; ok { 326 | continue 327 | } 328 | expr_obj, err := s.processExpression(expr) 329 | if err != nil { 330 | log.Println("Unable to process expression:", expr) 331 | return nil, err 332 | } 333 | expr_obj.Srno = counter 334 | counter++ 335 | all_exprs[expr] = expr_obj 336 | } 337 | 338 | return all_exprs, nil 339 | } 340 | 341 | func extractExpressionsFromMap(expr_imap interface{}, exprs []string) []string { 342 | switch expr_map := expr_imap.(type) { 343 | case map[string]interface{}: 344 | for _, val := range expr_map { 345 | switch val_type := val.(type) { 346 | case map[interface{}]interface{}: 347 | exprs = extractExpressionsFromMap(val_type, exprs) 348 | default: 349 | val_type_str := fmt.Sprintf("%v", val_type) 350 | exprs = append(exprs, val_type_str) 351 | } 352 | } 353 | case map[interface{}]interface{}: 354 | for _, val := range expr_map { 355 | switch val_type := val.(type) { 356 | case map[interface{}]interface{}: 357 | exprs = extractExpressionsFromMap(val_type, exprs) 358 | default: 359 | val_type_str := fmt.Sprintf("%v", val_type) 360 | exprs = append(exprs, val_type_str) 361 | } 362 | } 363 | case string: 364 | val_type_str := fmt.Sprintf("%v", expr_map) 365 | exprs = append(exprs, val_type_str) 366 | } 367 | return exprs 368 | } 369 | 370 | func (t Task) getOnErrorList() (todo_list []TodoObj, err error) { 371 | todo_list, err = createTodoList(t.OnError) 372 | return 373 | } 374 | 375 | func (t Task) getOnSuccessList() (todo_list []TodoObj, err error) { 376 | todo_list, err = createTodoList(t.OnSuccess) 377 | return 378 | } 379 | 380 | func (t Task) getOnCompleteList() (todo_list []TodoObj, err error) { 381 | todo_list, err = createTodoList(t.OnComplete) 382 | return 383 | } 384 | 385 | func (t Task) getPublishList() (publish_list []PublishObj, err error) { 386 | publish_list, err = createPublishList(t.PublishRaw) 387 | return 388 | } 389 | 390 | func (t Task) tryPublishMap() (publish_map map[string]string) { 391 | publish_map = createPublishMap(t.PublishList) 392 | return 393 | } 394 | 395 | func (t Task) getErrorPublishList() (publish_list []PublishObj, err error) { 396 | publish_list, err = createPublishList(t.ErrorPublishRaw) 397 | return 398 | } 399 | 400 | func (t Task) tryErrorPublishMap() (publish_map map[string]string) { 401 | publish_map = createPublishMap(t.ErrorPublishList) 402 | return 403 | } 404 | 405 | func (t Task) getCompletePublishList() (publish_list []PublishObj, err error) { 406 | publish_list, err = createPublishList(t.CompletePublishRaw) 407 | return 408 | } 409 | 410 | func (t Task) tryCompletePublishMap() (publish_map map[string]string) { 411 | publish_map = createPublishMap(t.CompletePublishList) 412 | return 413 | } 414 | 415 | func (t LoopInfo) getPublishList() (publish_list []PublishObj, err error) { 416 | publish_list, err = createPublishList(t.PublishRaw) 417 | return 418 | } 419 | 420 | func (t LoopInfo) tryPublishMap() (publish_map map[string]string) { 421 | publish_map = createPublishMap(t.PublishList) 422 | return 423 | } 424 | 425 | func (t LoopInfo) getErrorPublishList() (publish_list []PublishObj, err error) { 426 | publish_list, err = createPublishList(t.ErrorPublishRaw) 427 | return 428 | } 429 | 430 | func (t LoopInfo) tryErrorPublishMap() (publish_map map[string]string) { 431 | publish_map = createPublishMap(t.ErrorPublishList) 432 | return 433 | } 434 | 435 | var expr_regex = regexp.MustCompile("<%(?P[^ ]*) (?P[^ ].*) %>") 436 | 437 | func (s *Spec) processExpression(expr string) (Expression, error) { 438 | type_expr := expr_regex.FindStringSubmatch(expr) 439 | ret_expr := Expression{} 440 | if type_expr != nil { 441 | ret_expr.Type = type_expr[1] 442 | ret_expr.Raw = type_expr[2] 443 | if ret_expr.Type == "" { 444 | ret_expr.Type = "yaql" 445 | } 446 | switch ret_expr.Type { 447 | // Just testing compile 448 | case "jmes": 449 | _, err := jmespath.Compile(ret_expr.Raw) 450 | if err != nil { 451 | return Expression{}, err 452 | } 453 | case "jngo": 454 | // Disabling pongo2 validity test. pongo2 does not allow parsing 455 | // unknown filters which are registered in the generated project. 456 | // TODO: Add missing filters before performing validity check? 457 | pongo2.FromString(ret_expr.Raw) 458 | // _, err := pongo2.FromString(ret_expr.Raw) 459 | // if err != nil { 460 | // return Expression{}, err 461 | // } 462 | default: 463 | return Expression{}, fmt.Errorf("Expression of this type is not supported: %v", expr) 464 | } 465 | return ret_expr, nil 466 | } else { 467 | ret_expr.Type = "json" 468 | ret_expr.Raw = expr 469 | var res interface{} 470 | err := json.Unmarshal([]byte(expr), &res) 471 | if err != nil { 472 | return Expression{}, err 473 | } 474 | return ret_expr, nil 475 | } 476 | } 477 | 478 | func processAction(action string) (Action, error) { 479 | ret_action := Action{} 480 | 481 | action_elems := strings.Split(action, ".") 482 | for i := 1; i < len(action_elems); i++ { 483 | action_elems[i] = shared.Pascalize(action_elems[i]) 484 | } 485 | ret_action.Pascalized = strings.Join(action_elems, ".") 486 | ret_action.Type = action_elems[0] 487 | ret_action.Raw = action 488 | 489 | return ret_action, nil 490 | } 491 | 492 | func createPublishMap(publish_list []PublishObj) map[string]string { 493 | publish_map := make(map[string]string) 494 | for _, publish_obj := range publish_list { 495 | publish_map[publish_obj.VariableName] = publish_obj.ExpressionName 496 | } 497 | return publish_map 498 | } 499 | 500 | func createPublishList(map_list interface{}) ([]PublishObj, error) { 501 | if map_list == nil { 502 | return []PublishObj {}, nil 503 | } 504 | 505 | counter := 1 506 | switch map_list_v := map_list.(type) { 507 | case map[interface{}]interface{}: 508 | retlist := make([]PublishObj, len(map_list_v)) 509 | for ct, ce_i := range map_list_v { 510 | ce, err := conv.String(ce_i) 511 | if err != nil { 512 | return nil, fmt.Errorf("Invalid expression value in publis list: %v", map_list_v) 513 | } 514 | retlist[counter-1] = PublishObj{ 515 | Srno: counter, 516 | VariableName: ct.(string), 517 | ExpressionName: ce, 518 | } 519 | counter++ 520 | } 521 | return retlist, nil 522 | case []interface{}: 523 | retlist := make([]PublishObj, len(map_list_v)) 524 | for _, task_map_i := range map_list_v { 525 | task_map := task_map_i.(map[interface{}]interface{}) 526 | if len(task_map) != 1 { 527 | return nil, fmt.Errorf("Each entry in todo must have only one key-value pair: %v", map_list_v) 528 | } 529 | for ct, ce_i := range task_map { 530 | ce, err := conv.String(ce_i) 531 | if err != nil { 532 | return nil, fmt.Errorf("Invalid expression value in publis list: %v", map_list_v) 533 | } 534 | retlist[counter-1] = PublishObj{ 535 | Srno: counter, 536 | VariableName: ct.(string), 537 | ExpressionName: ce, 538 | } 539 | counter++ 540 | break 541 | } 542 | } 543 | return retlist, nil 544 | default: 545 | return nil, fmt.Errorf("Publish list must be a list of variables: %v, Type: %T", map_list, map_list) 546 | } 547 | } 548 | 549 | func createTodoList(map_list []map[string]string) ([]TodoObj, error) { 550 | counter := 1 551 | retlist := make([]TodoObj, len(map_list)) 552 | for _, task_map := range map_list { 553 | if len(task_map) != 1 { 554 | return nil, fmt.Errorf("Each entry in todo must have only one key-value pair: %v", map_list) 555 | } 556 | for ct, ce := range task_map { 557 | retlist[counter-1] = TodoObj{ 558 | Srno: counter, 559 | TaskName: ct, 560 | ExpressionName: ce, 561 | } 562 | counter++ 563 | break 564 | } 565 | } 566 | return retlist, nil 567 | } 568 | -------------------------------------------------------------------------------- /generator/generator.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "log" 5 | "github.com/grofers/go-codon/generator/shared" 6 | "github.com/grofers/go-codon/generator/golang" 7 | ) 8 | 9 | type GenOpts shared.GenOpts 10 | 11 | type generatable interface { 12 | Generate(shared.GenOpts) bool 13 | } 14 | 15 | var language_map = map[string]generatable { 16 | "golang": &golang.Generator, 17 | } 18 | 19 | func Generate(opts GenOpts) bool { 20 | bs, ok := language_map[opts.Language] 21 | if !ok { 22 | log.Println("Support for language ", opts.Language, " not implemented yet") 23 | } 24 | return bs.Generate(shared.GenOpts(opts)) 25 | } 26 | -------------------------------------------------------------------------------- /generator/golang/content/clients/clients.gofile: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import ( 4 | {{if .ClientEndpoints}}config "github.com/grofers/go-codon/runtime/config"{{end}} 5 | {{ range $client, $used := .ClientsUsed }} 6 | {{if $used}}{{$client}}_client "{{ index $.ClientImports $client }}"{{end}}{{end}} 7 | ) 8 | 9 | {{ range $key, $value := .ClientEndpoints }} 10 | var {{ index $.ClientEndpointGoNames $key }} = {{$value.client}}_client.NewHTTPClientWithConfigMap(nil, config.GetEndpoint("{{$key}}")).Operations{{ end }} 11 | -------------------------------------------------------------------------------- /generator/golang/golang.go: -------------------------------------------------------------------------------- 1 | package golang 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "log" 7 | "strings" 8 | "io/ioutil" 9 | "path/filepath" 10 | "text/template" 11 | imports "golang.org/x/tools/imports" 12 | 13 | "github.com/go-openapi/swag" 14 | 15 | gen_shared "github.com/grofers/go-codon/generator/shared" 16 | codon_shared "github.com/grofers/go-codon/shared" 17 | config "github.com/grofers/go-codon/runtime/config" 18 | 19 | flowgen "github.com/grofers/go-codon/flowgen/generator" 20 | ) 21 | 22 | // Important: Dont use lists, use maps 23 | // While go randomizes iteration results, template does not. 24 | // So we let template ensure idiomatic (and sorted) results. 25 | type generator struct { 26 | CurrentSpecFile string 27 | CurrentSpecFilePath string 28 | CurrentAPIName string 29 | CurrentDirTarget string 30 | CurrentDirPath string 31 | CurrentDirName string 32 | ProjectName string 33 | ClientImports map[string]string 34 | ClientEndpoints map[string]map[string]string 35 | ClientsUsed map[string]bool 36 | ClientEndpointGoNames map[string]string 37 | WorkflowsBasePath string 38 | } 39 | 40 | var upstream_generator_list = map[int]func(*generator)bool { 41 | codon_shared.SWAGGER: GenerateUpstreamSwagger, 42 | codon_shared.UNKNOWN: GenerateUnknown, 43 | } 44 | 45 | var service_generator_list = map[int]func(*generator)bool { 46 | codon_shared.SWAGGER: GenerateServiceSwagger, 47 | codon_shared.UNKNOWN: GenerateUnknown, 48 | } 49 | 50 | func (gen *generator) Init() { 51 | gen.ClientImports = make(map[string]string) 52 | gen.ClientEndpoints = make(map[string]map[string]string) 53 | gen.ClientsUsed = make(map[string]bool) 54 | gen.ClientEndpointGoNames = make(map[string]string) 55 | } 56 | 57 | func (gen *generator) UpdateCurrentDirPath() error { 58 | pwd, err := os.Getwd() 59 | if err != nil { 60 | log.Println(err) 61 | return err 62 | } 63 | gen.CurrentDirPath = pwd 64 | log.Println("Updated working directory:", pwd) 65 | _, gen.CurrentDirName = filepath.Split(pwd) 66 | gen.ProjectName = gen.CurrentDirName 67 | log.Println("Working with project name:", gen.ProjectName) 68 | return nil 69 | } 70 | 71 | func (gen *generator) process_templates() error { 72 | for _, asset := range AssetNames() { 73 | t := template.New(asset) 74 | 75 | // If the content being templated is a template itself 76 | if strings.HasSuffix(asset, ".gotmpl") { 77 | t = t.Delims("{|{", "}|}") 78 | } 79 | 80 | t, err := t.Parse(string(MustAsset(asset))) 81 | if err != nil { 82 | log.Println("Failed to get asset:", err) 83 | return err 84 | } 85 | 86 | var new_asset_path string 87 | if strings.HasSuffix(asset, ".gofile") { 88 | new_asset_path = filepath.Join(gen.CurrentDirPath, strings.TrimSuffix(asset, ".gofile") + ".go") 89 | } else { 90 | new_asset_path = filepath.Join(gen.CurrentDirPath, asset) 91 | } 92 | base_path, _ := filepath.Split(new_asset_path) 93 | 94 | err = os.MkdirAll(base_path, 0755) 95 | if err != nil { 96 | log.Println("Failed to create file:", err) 97 | return err 98 | } 99 | 100 | f, err := os.Create(new_asset_path) 101 | defer f.Close() 102 | if err != nil { 103 | log.Println("Failed to create file:", err) 104 | return err 105 | } 106 | 107 | err = t.Execute(f, gen) 108 | if err != nil { 109 | log.Println("Failed to execute template:", err) 110 | return err 111 | } 112 | } 113 | return nil 114 | } 115 | 116 | func (gen *generator) process_config() error { 117 | gen.ClientEndpoints = config.YmlConfig.Endpoints 118 | 119 | for endpoint_name, endpoint := range gen.ClientEndpoints { 120 | client, ok := endpoint["client"] 121 | if !ok { 122 | continue 123 | } 124 | gen.ClientsUsed[client] = true 125 | gen.ClientEndpointGoNames[endpoint_name] = swag.ToGoName(endpoint_name) 126 | } 127 | 128 | return nil 129 | } 130 | 131 | func GenerateUnknown(gen *generator) bool { 132 | log.Println("Ignoring unknown file format for:", gen.CurrentSpecFile) 133 | return true 134 | } 135 | 136 | func (gen *generator) GenerateDynamic() bool { 137 | err := exec.Command("go", "generate").Run() 138 | if err != nil { 139 | log.Println("Could not run `go generate` command. Bindata not generated.") 140 | log.Fatalln("Please run it yourself and then run `codon generate --no-bindata`") 141 | return false 142 | } 143 | return true 144 | } 145 | 146 | func (gen *generator) GenerateUpstream() bool { 147 | // Get list of all the files in spec/clients 148 | files, err := ioutil.ReadDir("spec/clients") 149 | if err != nil { 150 | log.Println(err) 151 | return false 152 | } 153 | 154 | for _, file := range files { 155 | gen.CurrentSpecFile = file.Name() 156 | gen.CurrentSpecFilePath = filepath.Join("spec/clients", file.Name()) 157 | log.Println("Processing upstream spec: ", file.Name()) 158 | if file.IsDir() { 159 | log.Println(file.Name(), "is a directory. Ignoring.") 160 | continue 161 | } 162 | spec_type := codon_shared.DetectFileSpec(gen.CurrentSpecFilePath) 163 | gen_func := upstream_generator_list[spec_type] 164 | if ok := gen_func(gen); !ok { 165 | log.Println("Failed to generate code for spec", file.Name()) 166 | return false 167 | } 168 | } 169 | return true 170 | } 171 | 172 | func (gen *generator) GenerateContent() bool { 173 | if err := gen.process_config(); err != nil { 174 | log.Println(err) 175 | return false 176 | } 177 | if err := gen.process_templates(); err != nil { 178 | log.Println(err) 179 | return false 180 | } 181 | return true 182 | } 183 | 184 | func (gen *generator) GenerateService() bool { 185 | spec_type := codon_shared.DetectFileSpec("spec/server/main.yml") 186 | gen_func := service_generator_list[spec_type] 187 | if ok := gen_func(gen); !ok { 188 | log.Println("Failed to generate code for spec/server/main.yml") 189 | return false 190 | } 191 | return true 192 | } 193 | 194 | func (gen *generator) generateWorkflows(prefix string, dest string) bool { 195 | files, err := ioutil.ReadDir(prefix) 196 | if err != nil { 197 | log.Println(err) 198 | return false 199 | } 200 | 201 | for _, file := range files { 202 | if file.IsDir() { 203 | new_prefix := filepath.Join(prefix, file.Name()) 204 | if !gen.generateWorkflows(new_prefix, dest) { 205 | return false 206 | } 207 | continue 208 | } 209 | gen.CurrentSpecFile = file.Name() 210 | gen.CurrentSpecFilePath = filepath.Join(prefix, file.Name()) 211 | if !strings.HasSuffix(gen.CurrentSpecFile, ".yml") && !strings.HasSuffix(gen.CurrentSpecFile, ".yaml") { 212 | continue 213 | } 214 | log.Println("Processing workflow spec: ", file.Name()) 215 | rel_path, err2 := filepath.Rel(gen.WorkflowsBasePath, gen.CurrentSpecFilePath) 216 | if err2 != nil { 217 | log.Println(err2) 218 | return false 219 | } 220 | rel_path = filepath.Clean(rel_path) 221 | filename := strings.Replace(rel_path, "/", "_", -1) 222 | filename = strings.TrimSuffix(filename, ".yaml") 223 | filename = strings.TrimSuffix(filename, ".yml") 224 | filename = filename + ".go" 225 | 226 | opts := &flowgen.GenOpts{ 227 | Spec: gen.CurrentSpecFilePath, 228 | Dest: filepath.Join(dest, filename), 229 | Templates: "spec/templates/workflow/", 230 | } 231 | err2 = flowgen.Process(opts) 232 | if err2 != nil { 233 | log.Println("Failed to generate workflow for", file.Name()) 234 | log.Println(err2) 235 | return false 236 | } 237 | err2 = formatFunc(opts.Dest) 238 | if err2 != nil { 239 | log.Println("Failed to format workflow file", file.Name()) 240 | log.Println(err2) 241 | return false 242 | } 243 | } 244 | return true 245 | } 246 | 247 | func (gen *generator) GenerateWorkflow() bool { 248 | gen.WorkflowsBasePath = "spec/server/workflows" 249 | return gen.generateWorkflows(gen.WorkflowsBasePath, "workflows") 250 | } 251 | 252 | func (gen *generator) Generate(opts gen_shared.GenOpts) bool { 253 | gen.Init() 254 | log.Println("Generating a codon project in golang ...") 255 | 256 | if err := gen.UpdateCurrentDirPath(); err != nil { 257 | return false 258 | } 259 | 260 | if !gen.GenerateDynamic() { 261 | return false 262 | } 263 | 264 | if opts.GenerateClients { 265 | if !gen.GenerateUpstream() { 266 | return false 267 | } 268 | 269 | if !gen.GenerateContent() { 270 | return false 271 | } 272 | } 273 | 274 | if opts.GenerateWorkflow { 275 | if !gen.GenerateWorkflow() { 276 | return false 277 | } 278 | } 279 | 280 | if opts.GenerateServer { 281 | if !gen.GenerateService() { 282 | return false 283 | } 284 | } 285 | 286 | return true 287 | } 288 | 289 | var Generator = generator{} 290 | 291 | func formatFunc(filename string) error { 292 | content, err := ioutil.ReadFile(filename) 293 | if err != nil { 294 | return err 295 | } 296 | 297 | opts := new(imports.Options) 298 | opts.TabIndent = true 299 | opts.TabWidth = 2 300 | opts.Fragment = true 301 | opts.Comments = true 302 | 303 | new_content, err := imports.Process(filename, content, opts) 304 | if err != nil { 305 | return err 306 | } 307 | 308 | err = ioutil.WriteFile(filename, new_content, os.FileMode(0755)) 309 | if err != nil { 310 | return err 311 | } 312 | return nil 313 | } 314 | -------------------------------------------------------------------------------- /generator/golang/upstream_swagger.go: -------------------------------------------------------------------------------- 1 | package golang 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | "path/filepath" 8 | 9 | swagger_generator "github.com/go-swagger/go-swagger/generator" 10 | shared "github.com/grofers/go-codon/shared" 11 | ) 12 | 13 | func GenerateUpstreamSwagger(gen *generator) bool { 14 | gen.CurrentAPIName = strings.TrimSuffix(gen.CurrentSpecFile, ".yml") 15 | gen.CurrentAPIName = strings.TrimSuffix(gen.CurrentAPIName, ".yaml") 16 | 17 | gen.CurrentDirTarget = fmt.Sprintf("clients/%s/", gen.CurrentAPIName) 18 | 19 | opts := &swagger_generator.GenOpts{ 20 | APIPackage: "operations", 21 | ModelPackage: "models", 22 | ServerPackage: "restapi", 23 | ClientPackage: "client", 24 | Principal: "", 25 | DefaultScheme: "http", 26 | DefaultProduces: "application/json", 27 | IncludeModel: true, 28 | IncludeValidator: true, 29 | IncludeHandler: true, 30 | IncludeParameters: true, 31 | IncludeResponses: true, 32 | ValidateSpec: true, 33 | Tags: []string{}, 34 | IncludeSupport: true, 35 | DumpData: false, 36 | Spec: gen.CurrentSpecFilePath, 37 | Target: gen.CurrentDirTarget, 38 | TemplateDir: "spec/templates/swagger/", 39 | } 40 | if err := opts.EnsureDefaults(true); err != nil { 41 | log.Println(err) 42 | return false 43 | } 44 | if err := swagger_generator.GenerateClient("", []string{}, []string{}, opts); err != nil { 45 | log.Println(err) 46 | return false 47 | } 48 | 49 | import_path, err := shared.BaseImport(filepath.Join(gen.CurrentDirTarget, opts.ClientPackage)) 50 | if err != nil { 51 | log.Println(err) 52 | return false 53 | } 54 | gen.ClientImports[gen.CurrentAPIName] = import_path 55 | 56 | return true 57 | } 58 | 59 | func GenerateServiceSwagger(gen *generator) bool { 60 | // swagger generate server -f spec/server/main.yml -t server -T spec/templates/swagger/ 61 | gen.CurrentDirTarget = "server" 62 | gen.CurrentSpecFilePath = "spec/server/main.yml" 63 | 64 | opts := &swagger_generator.GenOpts{ 65 | IncludeModel: true, 66 | IncludeValidator: true, 67 | IncludeHandler: true, 68 | IncludeParameters: true, 69 | IncludeResponses: true, 70 | IncludeURLBuilder: true, 71 | IncludeMain: true, 72 | IncludeSupport: true, 73 | ExcludeSpec: false, 74 | DumpData: false, 75 | WithContext: false, 76 | ValidateSpec: true, 77 | Spec: gen.CurrentSpecFilePath, 78 | APIPackage: "operations", 79 | ModelPackage: "models", 80 | ServerPackage: "restapi", 81 | ClientPackage: "client", 82 | Principal: "", 83 | Target: gen.CurrentDirTarget, 84 | DefaultScheme: "http", 85 | DefaultProduces: "", 86 | Tags: []string{}, 87 | TemplateDir: "spec/templates/swagger/", 88 | Models: []string{}, 89 | Operations: []string{}, 90 | Name: "", 91 | FlagStrategy: "go-flags", 92 | CompatibilityMode: "modern", 93 | } 94 | if err := opts.EnsureDefaults(false); err != nil { 95 | log.Println(err) 96 | return false 97 | } 98 | 99 | // TODO: Add support for template config 100 | // Adding views template to configuration 101 | opts.Sections.Application = append(opts.Sections.Application, swagger_generator.TemplateOpts{ 102 | Name: "views", 103 | Source: "asset:serverViews", 104 | Target: "{{ joinFilePath .Target .ServerPackage }}", 105 | FileName: "views.go", 106 | }) 107 | 108 | if opts.Imports == nil { 109 | opts.Imports = make(map[string]string) 110 | } 111 | workflow_import, err := shared.BaseImport("workflows") 112 | if err != nil { 113 | log.Println(err) 114 | return false 115 | } 116 | opts.Imports["workflows"] = workflow_import 117 | 118 | if err := swagger_generator.GenerateServer("", []string{}, []string{}, opts); err != nil { 119 | log.Println(err) 120 | return false 121 | } 122 | 123 | return true 124 | } 125 | -------------------------------------------------------------------------------- /generator/shared/structs.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | type GenOpts struct { 4 | Language string 5 | GenerateClients bool 6 | GenerateWorkflow bool 7 | GenerateServer bool 8 | } 9 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 8ddb4e3a60070e3fd8c5a5b756935164eb5ce707de793f1f20882f52fd56e310 2 | updated: 2017-06-14T00:51:27.20336848+05:30 3 | imports: 4 | - name: github.com/asaskevich/govalidator 5 | version: 2c14e1cca90af49b3b21fe2d7aaa8cc7f9da2ff8 6 | repo: https://github.com/asaskevich/govalidator 7 | - name: github.com/cstockton/go-conv 8 | version: 3b644f69dbf732338c8aaea4125d08ef4ff9fef1 9 | subpackages: 10 | - internal/refconv 11 | - internal/refutil 12 | - name: github.com/flosch/pongo2 13 | version: 1d0f0d3af150c4a65dfd424d742f7374819e7d29 14 | - name: github.com/fsnotify/fsnotify 15 | version: 4da3e2cfbabc9f751898f250b49f2439785783a1 16 | - name: github.com/go-openapi/analysis 17 | version: d5a75b7d751ca3f11ad5d93cfe97405f2c3f6a47 18 | repo: https://github.com/go-openapi/analysis 19 | - name: github.com/go-openapi/errors 20 | version: fc3f73a224499b047eda7191e5d22e1e9631e86f 21 | repo: https://github.com/go-openapi/errors 22 | - name: github.com/go-openapi/inflect 23 | version: b1f6470ffb9c552dc105dd869f16e36ba86ba7d0 24 | repo: https://github.com/go-openapi/inflect 25 | - name: github.com/go-openapi/jsonpointer 26 | version: 779f45308c19820f1a69e9a4cd965f496e0da10f 27 | repo: https://github.com/go-openapi/jsonpointer 28 | - name: github.com/go-openapi/jsonreference 29 | version: 36d33bfe519efae5632669801b180bf1a245da3b 30 | repo: https://github.com/go-openapi/jsonreference 31 | - name: github.com/go-openapi/loads 32 | version: 6bb6486231e079ea125c0f39994ed3d0c53399ed 33 | repo: https://github.com/go-openapi/loads 34 | subpackages: 35 | - fmts 36 | - name: github.com/go-openapi/runtime 37 | version: e66a4c4406028a04ddafd6002c378ffd3db7e52b 38 | repo: https://github.com/go-openapi/runtime 39 | - name: github.com/go-openapi/spec 40 | version: e51c28f07047ad90caff03f6450908720d337e0c 41 | repo: https://github.com/go-openapi/spec 42 | - name: github.com/go-openapi/strfmt 43 | version: 93a31ef21ac23f317792fff78f9539219dd74619 44 | repo: https://github.com/go-openapi/strfmt 45 | - name: github.com/go-openapi/swag 46 | version: aa30237cf993e01e1a1e467eead45d1ce5ad155e 47 | - name: github.com/go-openapi/validate 48 | version: 035dcd74f1f61e83debe1c22950dc53556e7e4b2 49 | repo: https://github.com/go-openapi/validate 50 | - name: github.com/go-swagger/go-swagger 51 | version: 0925bd9bdbce8c826211a417b520e203744dca63 52 | repo: git://github.com/grofers/go-swagger.git 53 | subpackages: 54 | - generator 55 | - name: github.com/hashicorp/hcl 56 | version: 630949a3c5fa3c613328e1b8256052cbc2327c9b 57 | subpackages: 58 | - hcl/ast 59 | - hcl/parser 60 | - hcl/scanner 61 | - hcl/strconv 62 | - hcl/token 63 | - json/parser 64 | - json/scanner 65 | - json/token 66 | - name: github.com/inconshreveable/mousetrap 67 | version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 68 | - name: github.com/jmespath/go-jmespath 69 | version: 00188b135398219b940eafb0c41bd5ade35490e8 70 | repo: git://github.com/grofers/go-jmespath.git 71 | - name: github.com/juju/errors 72 | version: 6f54ff6318409d31ff16261533ce2c8381a4fd5d 73 | - name: github.com/kr/pretty 74 | version: cfb55aafdaf3ec08f0db22699ab822c50091b1c4 75 | repo: https://github.com/kr/pretty 76 | - name: github.com/kr/text 77 | version: 7cafcd837844e784b526369c9bce262804aebc60 78 | repo: https://github.com/kr/text 79 | - name: github.com/magiconair/properties 80 | version: 51463bfca2576e06c62a8504b5c0f06d61312647 81 | - name: github.com/mailru/easyjson 82 | version: 2af9a745a611440bab0528e5ac19b2805a1c50eb 83 | subpackages: 84 | - buffer 85 | - jlexer 86 | - jwriter 87 | - name: github.com/mitchellh/mapstructure 88 | version: 53818660ed4955e899c0bcafa97299a388bd7c8e 89 | - name: github.com/oleiade/reflections 90 | version: bfcc97ebb7fe8fa8ccfe23554901ead8794bae07 91 | repo: git://github.com/grofers/reflections.git 92 | - name: github.com/pelletier/go-buffruneio 93 | version: c37440a7cf42ac63b919c752ca73a85067e05992 94 | repo: https://github.com/pelletier/go-buffruneio 95 | - name: github.com/pelletier/go-toml 96 | version: fe206efb84b2bc8e8cfafe6b4c1826622be969e3 97 | - name: github.com/PuerkitoBio/purell 98 | version: b938d81255b5473c57635324295cb0fe398c7a58 99 | repo: https://github.com/PuerkitoBio/purell 100 | - name: github.com/PuerkitoBio/urlesc 101 | version: bbf7a2afc14f93e1e0a5c06df524fbd75e5031e5 102 | repo: https://github.com/PuerkitoBio/urlesc 103 | - name: github.com/spf13/afero 104 | version: 9be650865eab0c12963d8753212f4f9c66cdcf12 105 | subpackages: 106 | - mem 107 | - name: github.com/spf13/cast 108 | version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 109 | - name: github.com/spf13/cobra 110 | version: 10f6b9d7e1631a54ad07c5c0fb71c28a1abfd3c2 111 | - name: github.com/spf13/jwalterweatherman 112 | version: fa7ca7e836cf3a8bb4ebf799f472c12d7e903d66 113 | - name: github.com/spf13/pflag 114 | version: 2300d0f8576fe575f71aaa5b9bbe4e1b0dc2eb51 115 | - name: github.com/spf13/viper 116 | version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2 117 | - name: golang.org/x/net 118 | version: d1e1b351919c6738fdeb9893d5c998b161464f0c 119 | repo: https://go.googlesource.com/net 120 | subpackages: 121 | - context 122 | - idna 123 | - netutil 124 | - name: golang.org/x/sys 125 | version: f3918c30c5c2cb527c0b071a27c35120a6c0719a 126 | repo: https://go.googlesource.com/sys 127 | subpackages: 128 | - unix 129 | - name: golang.org/x/text 130 | version: f4b4367115ec2de254587813edaa901bc1c723a8 131 | repo: https://go.googlesource.com/text 132 | subpackages: 133 | - secure/bidirule 134 | - transform 135 | - unicode/bidi 136 | - unicode/norm 137 | - width 138 | - name: golang.org/x/tools 139 | version: 9bf174b4d3fab31065b140e4a69c6e6b17746806 140 | repo: https://go.googlesource.com/tools 141 | subpackages: 142 | - go/ast/astutil 143 | - go/buildutil 144 | - go/loader 145 | - imports 146 | - name: gopkg.in/mgo.v2 147 | version: 3f83fa5005286a7fe593b055f0d7771a7dce4655 148 | repo: https://gopkg.in/mgo.v2 149 | subpackages: 150 | - bson 151 | - internal/json 152 | - name: gopkg.in/yaml.v2 153 | version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b 154 | testImports: [] 155 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/grofers/go-codon 2 | import: 3 | - package: github.com/go-openapi/swag 4 | - package: github.com/go-swagger/go-swagger 5 | repo: git://github.com/grofers/go-swagger.git 6 | subpackages: 7 | - generator 8 | - package: github.com/jmespath/go-jmespath 9 | repo: git://github.com/grofers/go-jmespath.git 10 | - package: github.com/oleiade/reflections 11 | repo: git://github.com/grofers/reflections.git 12 | - package: github.com/spf13/cobra 13 | - package: github.com/spf13/viper 14 | - package: gopkg.in/yaml.v2 15 | - package: github.com/cstockton/go-conv 16 | - package: github.com/mitchellh/mapstructure 17 | -------------------------------------------------------------------------------- /runtime/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "gopkg.in/yaml.v2" 7 | "errors" 8 | "strings" 9 | ) 10 | 11 | type Config struct { 12 | Endpoints map[string]map[string]string `yaml:"endpoints"` 13 | Constants map[string]interface{} `yaml:"constants"` 14 | } 15 | 16 | func (cfg *Config) GetEndpoint(endpoint string) *map[string]string { 17 | if endpoint_cfg, ok := cfg.Endpoints[endpoint]; ok { 18 | return &endpoint_cfg 19 | } else { 20 | return nil 21 | } 22 | } 23 | 24 | func (cfg *Config) GetConstant(name string) (interface{}, error) { 25 | if val, ok := cfg.Constants[name]; ok { 26 | return val, nil 27 | } else { 28 | return nil, errors.New("Constant not found") 29 | } 30 | } 31 | 32 | func (cfg *Config) GetConstantPath(path string) (interface{}, error) { 33 | path_s := strings.Split(path, ".") 34 | obj, err := cfg.GetConstant(path_s[0]) 35 | if err != nil { 36 | return nil, err 37 | } 38 | for i:=1; i/dev/null; then 2 | curl https://glide.sh/get | sh 3 | fi 4 | glide install 5 | 6 | go get -u github.com/jteeuwen/go-bindata/... 7 | -------------------------------------------------------------------------------- /scripts/tests.sh: -------------------------------------------------------------------------------- 1 | rm -rf testing 2 | 3 | mkdir testing 4 | cd testing 5 | codon init 6 | cd .. 7 | 8 | rsync -a compliance/ testing/ 9 | 10 | cd testing 11 | make generate 12 | # Patch clients with mock 13 | rm -rf clients 14 | cd .. 15 | rsync -a compliance/clients/ testing/clients/ 16 | 17 | cd testing 18 | go test 19 | 20 | cd .. 21 | rm -rf testing 22 | -------------------------------------------------------------------------------- /shared/spec.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "os" 5 | "log" 6 | "errors" 7 | "strings" 8 | "io/ioutil" 9 | "path/filepath" 10 | "gopkg.in/yaml.v2" 11 | goruntime "runtime" 12 | "github.com/go-openapi/swag" 13 | ) 14 | 15 | 16 | const ( 17 | SWAGGER = iota 18 | UNKNOWN = -1 19 | ) 20 | 21 | 22 | func DetectFileSpec(path string) int { 23 | _, filename := filepath.Split(path) 24 | if strings.HasSuffix(filename, ".yml") || strings.HasSuffix(filename, ".yaml") { 25 | yamlFile, err := ioutil.ReadFile(path) 26 | if err != nil { 27 | log.Println(err) 28 | return UNKNOWN 29 | } 30 | c := map[string]interface{}{} 31 | err = yaml.Unmarshal(yamlFile, &c) 32 | if err != nil { 33 | log.Println(err) 34 | return UNKNOWN 35 | } 36 | if _, ok := c["swagger"]; ok { 37 | return SWAGGER 38 | } 39 | return UNKNOWN 40 | } else { 41 | return UNKNOWN 42 | } 43 | } 44 | 45 | 46 | // Copyright 2015 go-swagger maintainers 47 | // Licensed under the Apache License, Version 2.0 (the "License"); 48 | // you may not use this file except in compliance with the License. 49 | // You may obtain a copy of the License at 50 | // 51 | // http://www.apache.org/licenses/LICENSE-2.0 52 | // 53 | // Unless required by applicable law or agreed to in writing, software 54 | // distributed under the License is distributed on an "AS IS" BASIS, 55 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 56 | // See the License for the specific language governing permissions and 57 | // limitations under the License. 58 | func BaseImport(tgt string) (string, error) { 59 | // Modified error reporting structure to match go-codon's 60 | p, err := filepath.Abs(tgt) 61 | if err != nil { 62 | return "", err 63 | } 64 | 65 | var pth string 66 | for _, gp := range filepath.SplitList(os.Getenv("GOPATH")) { 67 | pp := filepath.Join(filepath.Clean(gp), "src") 68 | var np, npp string 69 | if goruntime.GOOS == "windows" { 70 | np = strings.ToLower(p) 71 | npp = strings.ToLower(pp) 72 | } 73 | if strings.HasPrefix(np, npp) { 74 | pth, err = filepath.Rel(pp, p) 75 | if err != nil { 76 | return "", err 77 | } 78 | break 79 | } 80 | } 81 | 82 | if pth == "" { 83 | return "", errors.New("target must reside inside a location in the $GOPATH/src") 84 | } 85 | return pth, nil 86 | } 87 | 88 | func Pascalize(arg string) string { 89 | if len(arg) == 0 || arg[0] > '9' { 90 | return swag.ToGoName(arg) 91 | } 92 | if arg[0] == '+' { 93 | return swag.ToGoName("Plus " + arg[1:]) 94 | } 95 | if arg[0] == '-' { 96 | return swag.ToGoName("Minus " + arg[1:]) 97 | } 98 | 99 | return swag.ToGoName("Nr " + arg) 100 | } 101 | --------------------------------------------------------------------------------