[go-experimental] Support selection and formatting of endpoint servers (#4755)

* [Go] Support server URLs in runtime

* [Go] Regenerate experimental clients

* Rename Url to URL

* Remove BaseBase from go-experimental
This commit is contained in:
Jiri Kuncar
2019-12-18 02:41:52 +01:00
committed by Jim Schubert
parent 83cb4206cd
commit 617904c876
23 changed files with 1040 additions and 246 deletions

View File

@@ -34,6 +34,47 @@ Put the package under your project folder and add the following in import:
import sw "./{{packageName}}"
```
## Configuration of Server URL
Default configuration comes with `Servers` field that contains server objects as defined in the OpenAPI specification.
### Select Server Configuration
For using other server than the one defined on index 0 set context value `sw.ContextServerIndex` of type `int`.
```golang
ctx := context.WithValue(context.Background(), sw.ContextServerIndex, 1)
```
### Templated Server URL
Templated server URL is formatted using default variables from configuration or from context value `sw.ContextServerVariables` of type `map[string]string`.
```golang
ctx := context.WithValue(context.Background(), sw.ContextServerVariables, map[string]string{
"basePath": "v2",
})
```
Note, enum values are always validated and all unused variables are silently ignored.
### URLs Configuration per Operation
Each operation can use different server URL defined using `OperationServers` map in the `Configuration`.
An operation is uniquely identifield by `"{classname}Service.{nickname}"` string.
Similar rules for overriding default operation server index and variables applies by using `sw.ContextOperationServerIndices` and `sw.ContextOperationServerVariables` context maps.
```
ctx := context.WithValue(context.Background(), sw.ContextOperationServerIndices, map[string]int{
"{classname}Service.{nickname}": 2,
})
ctx = context.WithValue(context.Background(), sw.ContextOperationServerVariables, map[string]map[string]string{
"{classname}Service.{nickname}": {
"port": "8443",
},
})
```
## Documentation for API Endpoints
All URIs are relative to *{{basePath}}*

View File

@@ -76,10 +76,13 @@ func (a *{{{classname}}}Service) {{{nickname}}}(ctx _context.Context{{#hasParams
{{/returnType}}
)
// create path and map variables
localVarPath := a.client.cfg.BasePath + "{{{path}}}"{{#pathParams}}
localVarPath = strings.Replace(localVarPath, "{"+"{{baseName}}"+"}", _neturl.QueryEscape(parameterToString({{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) , -1)
{{/pathParams}}
localBasePath, err := a.client.cfg.ServerURLWithContext(ctx, "{{{classname}}}Service.{{{nickname}}}")
if err != nil {
return {{#returnType}}localVarReturnValue, {{/returnType}}nil, GenericOpenAPIError{error: err.Error()}
}
localVarPath := localBasePath + "{{{path}}}"{{#pathParams}}
localVarPath = strings.Replace(localVarPath, "{"+"{{baseName}}"+"}", _neturl.QueryEscape(parameterToString({{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) , -1){{/pathParams}}
localVarHeaderParams := make(map[string]string)
localVarQueryParams := _neturl.Values{}

View File

@@ -186,11 +186,6 @@ func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) {
return resp, err
}
// ChangeBasePath changes base path to allow switching to mocks
func (c *APIClient) ChangeBasePath(path string) {
c.cfg.BasePath = path
}
// Allow modification of underlying config for alternate implementations and testing
// Caution: modifying the configuration while live can cause data races and potentially unwanted behavior
func (c *APIClient) GetConfig() *Configuration {

View File

@@ -2,6 +2,7 @@
package {{packageName}}
import (
"context"
"fmt"
"net/http"
"strings"
@@ -29,6 +30,18 @@ var (
// ContextAPIKeys takes a string apikey as authentication for the request
ContextAPIKeys = contextKey("apiKeys")
// ContextServerIndex uses a server configuration from the index.
ContextServerIndex = contextKey("serverIndex")
// ContextOperationServerIndices uses a server configuration from the index mapping.
ContextOperationServerIndices = contextKey("serverOperationIndices")
// ContextServerVariables overrides a server configuration variables.
ContextServerVariables = contextKey("serverVariables")
// ContextOperationServerVariables overrides a server configuration variables using operation specific values.
ContextOperationServerVariables = contextKey("serverOperationVariables")
)
// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth
@@ -52,35 +65,38 @@ type ServerVariable struct {
// ServerConfiguration stores the information about a server
type ServerConfiguration struct {
Url string
URL string
Description string
Variables map[string]ServerVariable
}
// ServerConfigurations stores multiple ServerConfiguration items
type ServerConfigurations []ServerConfiguration
// Configuration stores the configuration of the API client
type Configuration struct {
BasePath string `json:"basePath,omitempty"`
Host string `json:"host,omitempty"`
Scheme string `json:"scheme,omitempty"`
DefaultHeader map[string]string `json:"defaultHeader,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
Debug bool `json:"debug,omitempty"`
Servers []ServerConfiguration
HTTPClient *http.Client
Host string `json:"host,omitempty"`
Scheme string `json:"scheme,omitempty"`
DefaultHeader map[string]string `json:"defaultHeader,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
Debug bool `json:"debug,omitempty"`
Servers ServerConfigurations
OperationServers map[string]ServerConfigurations
HTTPClient *http.Client
}
// NewConfiguration returns a new Configuration object
func NewConfiguration() *Configuration {
cfg := &Configuration{
BasePath: "{{{basePath}}}",
DefaultHeader: make(map[string]string),
UserAgent: "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/go{{/httpUserAgent}}",
Debug: false,
DefaultHeader: make(map[string]string),
UserAgent: "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/go{{/httpUserAgent}}",
Debug: false,
{{#servers}}
{{#-first}}
Servers: []ServerConfiguration{{
Servers: ServerConfigurations{
{{/-first}}
Url: "{{{url}}}",
{
URL: "{{{url}}}",
Description: "{{{description}}}{{^description}}No description provided{{/description}}",
{{#variables}}
{{#-first}}
@@ -108,6 +124,49 @@ func NewConfiguration() *Configuration {
},
{{/-last}}
{{/servers}}
{{#apiInfo}}
OperationServers: map[string]ServerConfigurations{
{{#apis}}
{{#operations}}
{{#operation}}
{{#servers}}
{{#-first}}
"{{{classname}}}Service.{{{nickname}}}": {
{{/-first}}
{
URL: "{{{url}}}",
Description: "{{{description}}}{{^description}}No description provided{{/description}}",
{{#variables}}
{{#-first}}
Variables: map[string]ServerVariable{
{{/-first}}
"{{{name}}}": ServerVariable{
Description: "{{{description}}}{{^description}}No description provided{{/description}}",
DefaultValue: "{{{defaultValue}}}",
{{#enumValues}}
{{#-first}}
EnumValues: []string{
{{/-first}}
"{{{.}}}",
{{#-last}}
},
{{/-last}}
{{/enumValues}}
},
{{#-last}}
},
{{/-last}}
{{/variables}}
},
{{#-last}}
},
{{/-last}}
{{/servers}}
{{/operation}}
{{/operations}}
{{/apis}}
},
{{/apiInfo}}
}
return cfg
}
@@ -117,13 +176,13 @@ func (c *Configuration) AddDefaultHeader(key string, value string) {
c.DefaultHeader[key] = value
}
// ServerUrl returns URL based on server settings
func (c *Configuration) ServerUrl(index int, variables map[string]string) (string, error) {
if index < 0 || len(c.Servers) <= index {
return "", fmt.Errorf("Index %v out of range %v", index, len(c.Servers) - 1)
// URL formats template on a index using given variables
func (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) {
if index < 0 || len(sc) <= index {
return "", fmt.Errorf("Index %v out of range %v", index, len(sc)-1)
}
server := c.Servers[index]
url := server.Url
server := sc[index]
url := server.URL
// go through variables and replace placeholders
for name, variable := range server.Variables {
@@ -144,3 +203,84 @@ func (c *Configuration) ServerUrl(index int, variables map[string]string) (strin
}
return url, nil
}
// ServerURL returns URL based on server settings
func (c *Configuration) ServerURL(index int, variables map[string]string) (string, error) {
return c.Servers.URL(index, variables)
}
func getServerIndex(ctx context.Context) (int, error) {
si := ctx.Value(ContextServerIndex)
if si != nil {
if index, ok := si.(int); ok {
return index, nil
}
return 0, reportError("Invalid type %T should be int", si)
}
return 0, nil
}
func getServerOperationIndex(ctx context.Context, endpoint string) (int, error) {
osi := ctx.Value(ContextOperationServerIndices)
if osi != nil {
if operationIndices, ok := osi.(map[string]int); !ok {
return 0, reportError("Invalid type %T should be map[string]int", osi)
} else {
index, ok := operationIndices[endpoint]
if ok {
return index, nil
}
}
}
return getServerIndex(ctx)
}
func getServerVariables(ctx context.Context) (map[string]string, error) {
sv := ctx.Value(ContextServerVariables)
if sv != nil {
if variables, ok := sv.(map[string]string); ok {
return variables, nil
}
return nil, reportError("ctx value of ContextServerVariables has invalid type %T should be map[string]string", sv)
}
return nil, nil
}
func getServerOperationVariables(ctx context.Context, endpoint string) (map[string]string, error) {
osv := ctx.Value(ContextOperationServerVariables)
if osv != nil {
if operationVariables, ok := osv.(map[string]map[string]string); !ok {
return nil, reportError("ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string", osv)
} else {
variables, ok := operationVariables[endpoint]
if ok {
return variables, nil
}
}
}
return getServerVariables(ctx)
}
// ServerURLWithContext returns a new server URL given an endpoint
func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint string) (string, error) {
sc, ok := c.OperationServers[endpoint]
if !ok {
sc = c.Servers
}
if ctx == nil {
return sc.URL(0, nil)
}
index, err := getServerOperationIndex(ctx, endpoint)
if err != nil {
return "", err
}
variables, err := getServerOperationVariables(ctx, endpoint)
if err != nil {
return "", err
}
return sc.URL(index, variables)
}