diff --git a/.apigentools-info b/.apigentools-info index 93e58ec8db2..48882359e77 100644 --- a/.apigentools-info +++ b/.apigentools-info @@ -4,13 +4,13 @@ "spec_versions": { "v1": { "apigentools_version": "1.4.1.dev6", - "regenerated": "2021-03-23 10:51:41.342590", - "spec_repo_commit": "34cf37f" + "regenerated": "2021-03-23 14:02:27.115937", + "spec_repo_commit": "7e667f1" }, "v2": { "apigentools_version": "1.4.1.dev6", - "regenerated": "2021-03-23 10:51:49.967652", - "spec_repo_commit": "34cf37f" + "regenerated": "2021-03-23 14:02:34.801926", + "spec_repo_commit": "7e667f1" } } } \ No newline at end of file diff --git a/.generator/templates/.travis.yml b/.generator/templates/.travis.yml new file mode 100644 index 00000000000..f5cb2ce9a5a --- /dev/null +++ b/.generator/templates/.travis.yml @@ -0,0 +1,8 @@ +language: go + +install: + - go get -d -v . + +script: + - go build -v ./ + diff --git a/.generator/templates/README.mustache b/.generator/templates/README.mustache new file mode 100644 index 00000000000..4e9577633d4 --- /dev/null +++ b/.generator/templates/README.mustache @@ -0,0 +1,221 @@ +# Go API client for {{packageName}} + +{{#appDescriptionWithNewLines}} +{{{appDescriptionWithNewLines}}} +{{/appDescriptionWithNewLines}} + +## Overview +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client. + +- API version: {{appVersion}} +- Package version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Installation + +Install the following dependencies: + +```shell +go get github.com/stretchr/testify/assert +go get golang.org/x/oauth2 +go get golang.org/x/net/context +``` + +Put the package under your project folder and add the following in import: + +```golang +import sw "./{{packageName}}" +``` + +To use a proxy, set the environment variable `HTTP_PROXY`: + +```golang +os.Setenv("HTTP_PROXY", "http://proxy_name:proxy_port") +``` + +## 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}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation For Models + +{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} + +## Documentation For Authorization + +{{^authMethods}} Endpoints do not require authorization. +{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}} +{{#authMethods}} + +### {{{name}}} + +{{#isApiKey}} +- **Type**: API key +- **API key parameter name**: {{{keyParamName}}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} + +Note, each API key must be added to a map of `map[string]APIKey` where the key is: {{keyParamName}} and passed in as the auth context for each request. + +{{/isApiKey}} +{{#isBasic}} +{{#isBasicBearer}} +- **Type**: HTTP Bearer token authentication + +Example + +```golang +auth := context.WithValue(context.Background(), sw.ContextAccessToken, "BEARERTOKENSTRING") +r, err := client.Service.Operation(auth, args) +``` + +{{/isBasicBearer}} +{{#isBasicBasic}} +- **Type**: HTTP basic authentication + +Example + +```golang +auth := context.WithValue(context.Background(), sw.ContextBasicAuth, sw.BasicAuth{ + UserName: "username", + Password: "password", +}) +r, err := client.Service.Operation(auth, args) +``` + +{{/isBasicBasic}} +{{#isHttpSignature}} +- **Type**: HTTP signature authentication + +Example + +```golang + authConfig := sw.HttpSignatureAuth{ + KeyId: "my-key-id", + PrivateKeyPath: "rsa.pem", + Passphrase: "my-passphrase", + SigningScheme: sw.HttpSigningSchemeHs2019, + SignedHeaders: []string{ + sw.HttpSignatureParameterRequestTarget, // The special (request-target) parameter expresses the HTTP request target. + sw.HttpSignatureParameterCreated, // Time when request was signed, formatted as a Unix timestamp integer value. + "Host", // The Host request header specifies the domain name of the server, and optionally the TCP port number. + "Date", // The date and time at which the message was originated. + "Content-Type", // The Media type of the body of the request. + "Digest", // A cryptographic digest of the request body. + }, + SigningAlgorithm: sw.HttpSigningAlgorithmRsaPSS, + SignatureMaxValidity: 5 * time.Minute, + } + var authCtx context.Context + var err error + if authCtx, err = authConfig.ContextWithValue(context.Background()); err != nil { + // Process error + } + r, err = client.Service.Operation(auth, args) + +``` +{{/isHttpSignature}} +{{/isBasic}} +{{#isOAuth}} + +- **Type**: OAuth +- **Flow**: {{{flow}}} +- **Authorization URL**: {{{authorizationUrl}}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - **{{{scope}}}**: {{{description}}} +{{/scopes}} + +Example + +```golang +auth := context.WithValue(context.Background(), sw.ContextAccessToken, "ACCESSTOKENSTRING") +r, err := client.Service.Operation(auth, args) +``` + +Or via OAuth2 module to automatically refresh tokens and perform user authentication. + +```golang +import "golang.org/x/oauth2" + +/* Perform OAuth2 round trip request and obtain a token */ + +tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token) +auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource) +r, err := client.Service.Operation(auth, args) +``` + +{{/isOAuth}} +{{/authMethods}} + +## Documentation for Utility Methods + +Due to the fact that model structure members are all pointers, this package contains +a number of utility functions to easily obtain pointers to values of basic types. +Each of these functions takes a value of the given basic type and returns a pointer to it: + +* `PtrBool` +* `PtrInt` +* `PtrInt32` +* `PtrInt64` +* `PtrFloat` +* `PtrFloat32` +* `PtrFloat64` +* `PtrString` +* `PtrTime` + +## Author + +{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}} +{{/-last}}{{/apis}}{{/apiInfo}} diff --git a/.generator/templates/api.mustache b/.generator/templates/api.mustache new file mode 100644 index 00000000000..215fef248a5 --- /dev/null +++ b/.generator/templates/api.mustache @@ -0,0 +1,394 @@ +{{>partial_header}} +package {{packageName}} + +{{#operations}} +import ( + "bytes" + _context "context" + _fmt "fmt" + _ioutil "io/ioutil" + _log "log" + _nethttp "net/http" + _neturl "net/url" +{{#imports}} "{{import}}" +{{/imports}} +) + +// Linger please +var ( + _ _context.Context +) +{{#generateInterfaces}} + +type {{classname}} interface { + {{#operation}} + + /* + * {{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}} + {{#notes}} + * {{{unescapedNotes}}} + {{/notes}} + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().{{#pathParams}} + * @param {{paramName}}{{#description}} {{{.}}}{{/description}}{{/pathParams}} + * @return {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request + */ + {{{nickname}}}(ctx _context.Context{{#pathParams}}, {{paramName}} {{{dataType}}}{{/pathParams}}) {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request + + /* + * {{nickname}}Execute executes the request{{#returnType}} + * @return {{{.}}}{{/returnType}} + */ + {{nickname}}Execute(r {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request) ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error) + {{/operation}} +} +{{/generateInterfaces}} + +// {{classname}}Service {{classname}} service +type {{classname}}Service service +{{#operation}} + +type {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request struct { + ctx _context.Context{{#generateInterfaces}} + ApiService {{classname}} +{{/generateInterfaces}}{{^generateInterfaces}} + ApiService *{{classname}}Service +{{/generateInterfaces}} +{{#allParams}} + {{paramName}} {{^isPathParam}}*{{/isPathParam}}{{{dataType}}} +{{/allParams}} +} +{{#allParams}}{{^isPathParam}} +func (r {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request) {{vendorExtensions.x-export-param-name}}({{paramName}} {{{dataType}}}) {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request { + r.{{paramName}} = &{{paramName}} + return r +}{{/isPathParam}}{{/allParams}} + +func (r {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request) Execute() ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error) { + return r.ApiService.{{nickname}}Execute(r) +} + +/* + * {{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}} +{{#notes}} + * {{{unescapedNotes}}} +{{/notes}} + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().{{#pathParams}} + * @param {{paramName}}{{#description}} {{{.}}}{{/description}}{{/pathParams}} + * @return {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request + */ +func (a *{{{classname}}}Service) {{{nickname}}}(ctx _context.Context{{#pathParams}}, {{paramName}} {{{dataType}}}{{/pathParams}}) {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request { + return {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request{ + ApiService: a, + ctx: ctx, + {{#pathParams}} + {{paramName}}: {{paramName}}, + {{/pathParams}} + } +} + +/* + * Execute executes the request{{#returnType}} + * @return {{{.}}}{{/returnType}} + */ +func (a *{{{classname}}}Service) {{nickname}}Execute(r {{#structPrefix}}{{&classname}}{{/structPrefix}}Api{{operationId}}Request) ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.Method{{httpMethod}} + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + {{#returnType}} + localVarReturnValue {{{.}}} + {{/returnType}} + ) + + {{#vendorExtensions.x-unstable}} + operationId := "{{operationId}}" + if r.ApiService.client.cfg.IsUnstableOperationEnabled(operationId) { + _log.Printf("WARNING: Using unstable operation '%s'", operationId); + } else { + return {{#returnType}} localVarReturnValue, {{/returnType}}nil, GenericOpenAPIError{error: _fmt.Sprintf("Unstable operation '%s' is disabled", operationId)} + } + {{/vendorExtensions.x-unstable}} + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.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.PathEscape(parameterToString(r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")), -1){{/pathParams}} + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + {{#allParams}} + {{#required}} + {{^isPathParam}} + if r.{{paramName}} == nil { + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} is required and must be specified") + } + {{/isPathParam}} + {{#minItems}} + if len({{^isPathParam}}*{{/isPathParam}}r.{{paramName}}) < {{minItems}} { + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} must have at least {{minItems}} elements") + } + {{/minItems}} + {{#maxItems}} + if len({{^isPathParam}}*{{/isPathParam}}r.{{paramName}}) > {{maxItems}} { + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} must have less than {{maxItems}} elements") + } + {{/maxItems}} + {{#minLength}} + if strlen({{^isPathParam}}*{{/isPathParam}}r.{{paramName}}) < {{minLength}} { + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} must have at least {{minLength}} elements") + } + {{/minLength}} + {{#maxLength}} + if strlen({{^isPathParam}}*{{/isPathParam}}r.{{paramName}}) > {{maxLength}} { + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} must have less than {{maxLength}} elements") + } + {{/maxLength}} + {{#minimum}} + {{#isString}} + {{paramName}}Txt, err := atoi({{^isPathParam}}*{{/isPathParam}}r.{{paramName}}) + if {{paramName}}Txt < {{minimum}} { + {{/isString}} + {{^isString}} + if {{^isPathParam}}*{{/isPathParam}}r.{{paramName}} < {{minimum}} { + {{/isString}} + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} must be greater than {{minimum}}") + } + {{/minimum}} + {{#maximum}} + {{#isString}} + {{paramName}}Txt, err := atoi({{^isPathParam}}*{{/isPathParam}}r.{{paramName}}) + if {{paramName}}Txt > {{maximum}} { + {{/isString}} + {{^isString}} + if {{^isPathParam}}*{{/isPathParam}}r.{{paramName}} > {{maximum}} { + {{/isString}} + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} must be less than {{maximum}}") + } + {{/maximum}} + {{/required}} + {{/allParams}} + + {{#queryParams}} + {{#required}} + {{#isCollectionFormatMulti}} + { + t := *r.{{paramName}} + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + localVarQueryParams.Add("{{baseName}}", parameterToString(s.Index(i), "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + } + } else { + localVarQueryParams.Add("{{baseName}}", parameterToString(t, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + } + } + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + localVarQueryParams.Add("{{baseName}}", parameterToString(*r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + {{/isCollectionFormatMulti}} + {{/required}} + {{^required}} + if r.{{paramName}} != nil { + {{#isCollectionFormatMulti}} + t := *r.{{paramName}} + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + localVarQueryParams.Add("{{baseName}}", parameterToString(s.Index(i), "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + } + } else { + localVarQueryParams.Add("{{baseName}}", parameterToString(t, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + } + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + localVarQueryParams.Add("{{baseName}}", parameterToString(*r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + {{/isCollectionFormatMulti}} + } + {{/required}} + {{/queryParams}} + // to determine the Content-Type header +{{=<% %>=}} + localVarHTTPContentTypes := []string{<%#consumes%>"<%&mediaType%>"<%^-last%>, <%/-last%><%/consumes%>} +<%={{ }}=%> + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header +{{=<% %>=}} + localVarHTTPHeaderAccepts := []string{<%#produces%>"<%&mediaType%>"<%^-last%>, <%/-last%><%/produces%>} +<%={{ }}=%> + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + + // Set Operation-ID header for telemetry + localVarHeaderParams["DD-OPERATION-ID"] = "{{operationId}}" + +{{#headerParams}} + {{#required}} + localVarHeaderParams["{{baseName}}"] = parameterToString(*r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}") + {{/required}} + {{^required}} + if r.{{paramName}} != nil { + localVarHeaderParams["{{baseName}}"] = parameterToString(*r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}") + } + {{/required}} +{{/headerParams}} +{{#formParams}} +{{#isFile}} + localVarFormFileName = "{{baseName}}" +{{#required}} + localVarFile := *r.{{paramName}} +{{/required}} +{{^required}} + var localVarFile {{dataType}} + if r.{{paramName}} != nil { + localVarFile = *r.{{paramName}} + } +{{/required}} + if localVarFile != nil { + fbs, _ := _ioutil.ReadAll(localVarFile) + localVarFileBytes = fbs + localVarFileName = localVarFile.Name() + localVarFile.Close() + } +{{/isFile}} +{{^isFile}} +{{#required}} + localVarFormParams.Add("{{baseName}}", parameterToString(*r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) +{{/required}} +{{^required}} +{{#isModel}} + if r.{{paramName}} != nil { + paramJson, err := parameterToJson(*r.{{paramName}}) + if err != nil { + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, err + } + localVarFormParams.Add("{{baseName}}", paramJson) + } +{{/isModel}} +{{^isModel}} + if r.{{paramName}} != nil { + localVarFormParams.Add("{{baseName}}", parameterToString(*r.{{paramName}}, "{{#collectionFormat}}{{collectionFormat}}{{/collectionFormat}}")) + } +{{/isModel}} +{{/required}} +{{/isFile}} +{{/formParams}} +{{#bodyParams}} + // body params + localVarPostBody = r.{{paramName}} +{{/bodyParams}} +{{#authMethods}} +{{#isApiKey}} +{{^isKeyInCookie}} + if r.ctx != nil { + // API Key Authentication + if auth, ok := r.ctx.Value(ContextAPIKeys).(map[string]APIKey); ok { + {{#vendorExtensions.x-auth-id-alias}} + if apiKey, ok := auth["{{.}}"]; ok { + var key string + if prefix, ok := auth["{{name}}"]; ok && prefix.Prefix != "" { + key = prefix.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + {{/vendorExtensions.x-auth-id-alias}} + {{^vendorExtensions.x-auth-id-alias}} + if apiKey, ok := auth["{{name}}"]; ok { + var key string + if apiKey.Prefix != "" { + key = apiKey.Prefix + " " + apiKey.Key + } else { + key = apiKey.Key + } + {{/vendorExtensions.x-auth-id-alias}} + {{#isKeyInHeader}} + localVarHeaderParams["{{keyParamName}}"] = key + {{/isKeyInHeader}} + {{#isKeyInQuery}} + localVarQueryParams.Add("{{keyParamName}}", key) + {{/isKeyInQuery}} + } + } + } +{{/isKeyInCookie}} +{{/isApiKey}} +{{/authMethods}} + req, err := a.client.PrepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return {{#returnType}}localVarReturnValue, {{/returnType}}nil, err + } + + localVarHTTPResponse, err := a.client.CallAPI(req) + if err != nil || localVarHTTPResponse == nil { + return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = _ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + {{#responses}} + {{#dataType}} + {{^is1xx}} + {{^is2xx}} + {{^wildcard}} + if localVarHTTPResponse.StatusCode == {{{code}}} { + {{/wildcard}} + var v {{{dataType}}} + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, newErr + } + newErr.model = v + {{^-last}} + return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, newErr + {{/-last}} + {{^wildcard}} + } + {{/wildcard}} + {{/is2xx}} + {{/is1xx}} + {{/dataType}} + {{/responses}} + return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, newErr + } + + {{#returnType}} + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, newErr + } + + {{/returnType}} + return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, nil +} +{{/operation}} +{{/operations}} diff --git a/.generator/templates/api_doc.mustache b/.generator/templates/api_doc.mustache new file mode 100644 index 00000000000..ef8e1ef953e --- /dev/null +++ b/.generator/templates/api_doc.mustache @@ -0,0 +1,134 @@ +# {{invokerPackage}}\{{classname}}{{#description}} + +{{description}}{{/description}} + +All URIs are relative to *{{basePath}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +## {{{operationId}}} + +> {{#returnType}}{{{.}}} {{/returnType}}{{{operationId}}}(ctx{{#pathParams}}, {{paramName}}{{/pathParams}}){{#allParams}}{{^isPathParam}}.{{vendorExtensions.x-export-param-name}}({{paramName}}){{/isPathParam}}{{/allParams}}.Execute() + +{{{summary}}}{{#notes}} + +{{{unespacedNotes}}}{{/notes}} + +### Example + +```go +package main + +import ( + "context" +{{#returnType}} + "encoding/json" +{{/returnType}} + "fmt" + "os" +{{#vendorExtensions.x-go-import}} +{{{vendorExtensions.x-go-import}}} +{{/vendorExtensions.x-go-import}} + {{goImportAlias}} "{{goImport}}" +) + +func main() { + {{#hasAuthMethods}} + ctx := context.WithValue( + context.Background(), + datadog.ContextAPIKeys, + map[string]datadog.APIKey{ + {{#authMethods}} + {{#isApiKey}} + {{^isKeyInCookie}} + "{{#vendorExtensions.x-auth-id-alias}}{{.}}{{/vendorExtensions.x-auth-id-alias}}{{^vendorExtensions.x-auth-id-alias}}{{name}}{{/vendorExtensions.x-auth-id-alias}}": { + Key: os.Getenv("{{#vendorExtensions.x-env-name}}{{.}}{{/vendorExtensions.x-env-name}}{{^vendorExtensions.x-env-name}}{{name}}{{/vendorExtensions.x-env-name}}"), + }, + {{/isKeyInCookie}} + {{/isApiKey}} + {{/authMethods}} + }, + ) + {{/hasAuthMethods}} + {{^hasAuthMethods}} + ctx := context.Background() + {{/hasAuthMethods}} + + if site, ok := os.LookupEnv("DD_SITE"); ok { + ctx = context.WithValue( + ctx, + datadog.ContextServerVariables, + map[string]string{"site": site}, + ) + } + + {{#allParams}} + {{paramName}} := {{{vendorExtensions.x-go-example}}} // {{{dataType}}} | {{{description}}}{{^required}} (optional){{/required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}} + {{/allParams}} + + configuration := {{goImportAlias}}.NewConfiguration() + {{#vendorExtensions.x-unstable}} + configuration.SetUnstableOperationEnabled("{{operationId}}", true) + {{/vendorExtensions.x-unstable}} + + api_client := {{goImportAlias}}.NewAPIClient(configuration) + {{#returnType}} + resp, r, err := api_client.{{classname}}.{{operationId}}(ctx{{#pathParams}}, {{paramName}}{{/pathParams}}){{#allParams}}{{^isPathParam}}.{{vendorExtensions.x-export-param-name}}({{paramName}}){{/isPathParam}}{{/allParams}}.Execute() + {{/returnType}} + {{^returnType}} + r, err := api_client.{{classname}}.{{operationId}}(ctx{{#pathParams}}, {{paramName}}{{/pathParams}}){{#allParams}}{{^isPathParam}}.{{vendorExtensions.x-export-param-name}}({{paramName}}){{/isPathParam}}{{/allParams}}.Execute() + {{/returnType}} + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `{{classname}}.{{operationId}}``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + {{#returnType}} + // response from `{{operationId}}`: {{{.}}} + response_content, _ := json.MarshalIndent(resp, "", " ") + fmt.Fprintf(os.Stdout, "Response from {{classname}}.{{operationId}}:\n%s\n", response_content) + {{/returnType}} +} +``` + +### Path Parameters + +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#pathParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- +**ctx** | **context.Context** | context for authentication, logging, cancellation, deadlines, tracing, etc.{{/-last}}{{/pathParams}}{{#pathParams}} +**{{paramName}}** | {{^isPrimitiveType}}{{^isFile}}[{{/isFile}}{{/isPrimitiveType}}**{{dataType}}**{{^isPrimitiveType}}{{^isFile}}]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}} | {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/pathParams}} + +### Other Parameters + +Other parameters are passed through a pointer to a api{{{nickname}}}Request struct via the builder pattern +{{#allParams}}{{#-last}} + +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}{{#allParams}} +{{^isPathParam}} **{{paramName}}** | {{#isContainer}}{{#isArray}}{{#items}}{{^isPrimitiveType}}{{^isFile}}[{{/isFile}}{{/isPrimitiveType}}**[]{{dataType}}**{{^isPrimitiveType}}{{^isFile}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isFile}}{{/isPrimitiveType}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{^isPrimitiveType}}{{^isFile}}[{{/isFile}}{{/isPrimitiveType}}**map[string]{{dataType}}**{{^isPrimitiveType}}{{^isFile}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isFile}}{{/isPrimitiveType}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{^isPrimitiveType}}{{^isFile}}[{{/isFile}}{{/isPrimitiveType}}**{{dataType}}**{{^isPrimitiveType}}{{^isFile}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isFile}}{{/isPrimitiveType}}{{/isContainer}} | {{description}} | {{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}{{/isPathParam}}{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{{returnType}}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{{returnType}}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}} (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{{name}}}](../README.md#{{{name}}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + +- **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} +- **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + +{{/operation}} +{{/operations}} diff --git a/.generator/templates/client.mustache b/.generator/templates/client.mustache new file mode 100644 index 00000000000..eb8952c840a --- /dev/null +++ b/.generator/templates/client.mustache @@ -0,0 +1,598 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "bytes" + "context" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "log" + "mime/multipart" + "net/http" + "net/http/httputil" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "golang.org/x/oauth2" + {{#withAWSV4Signature}} + awsv4 "github.com/aws/aws-sdk-go/aws/signer/v4" + awscredentials "github.com/aws/aws-sdk-go/aws/credentials" + {{/withAWSV4Signature}} +) + +var ( + jsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:vnd\.[^;]+\+)?json)`) + xmlCheck = regexp.MustCompile(`(?i:(?:application|text)/xml)`) +) + +// APIClient manages communication with the {{appName}} API v{{version}} +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services +{{#apiInfo}} +{{#apis}} +{{#operations}} + + {{#generateInterfaces}} + {{classname}} {{classname}} + {{/generateInterfaces}} + {{^generateInterfaces}} + {{classname}} *{{classname}}Service + {{/generateInterfaces}} +{{/operations}} +{{/apis}} +{{/apiInfo}} +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + +{{#apiInfo}} + // API Services +{{#apis}} +{{#operations}} + c.{{classname}} = (*{{classname}}Service)(&c.common) +{{/operations}} +{{/apis}} +{{/apiInfo}} + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insenstive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.ToLower(a) == strings.ToLower(needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } else if t, ok := obj.(time.Time); ok { + return t.Format(time.RFC3339) + } + + return fmt.Sprintf("%v", obj) +} + +// helper for converting interface{} parameters to json strings +func parameterToJson(obj interface{}) (string, error) { + jsonBuf, err := json.Marshal(obj) + if err != nil { + return "", err + } + return string(jsonBuf), err +} + +// CallAPI do the request. +func (c *APIClient) CallAPI(request *http.Request) (*http.Response, error) { + if c.cfg.Debug { + dump, err := httputil.DumpRequestOut(request, true) + if err != nil { + return nil, err + } +{{#hasAuthMethods}} + // Strip any api keys from the response being logged + keys, ok := request.Context().Value(ContextAPIKeys).(map[string]APIKey) + if keys != nil && ok { + for _, apiKey := range keys { + valueRegex := regexp.MustCompile(fmt.Sprintf("(?m)%s",apiKey.Key)) + dump = valueRegex.ReplaceAll(dump, []byte("REDACTED")) + } + } +{{/hasAuthMethods}} + log.Printf("\n%s\n", string(dump)) + } + + resp, err := c.cfg.HTTPClient.Do(request) + if err != nil { + return resp, err + } + + if c.cfg.Debug { + dump, err := httputil.DumpResponse(resp, true) + if err != nil { + return resp, err + } + log.Printf("\n%s\n", string(dump)) + } + return resp, err +} + +// 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 { + return c.cfg +} + +// PrepareRequest build the request +func (c *APIClient) PrepareRequest( + ctx context.Context, + path string, method string, + postBody interface{}, + headerParams map[string]string, + queryParams url.Values, + formParams url.Values, + formFileName string, + fileName string, + fileBytes []byte) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form parameters and file if available. + if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + if len(fileBytes) > 0 && fileName != "" { + w.Boundary() + //_, fileNm := filepath.Split(fileName) + part, err := w.CreateFormFile(formFileName, filepath.Base(fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(fileBytes) + if err != nil { + return nil, err + } + } + + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { + if body != nil { + return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") + } + body = &bytes.Buffer{} + body.WriteString(formParams.Encode()) + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Override request host, if applicable + if c.cfg.Host != "" { + url.Host = c.cfg.Host + } + + // Override request scheme, if applicable + if c.cfg.Scheme != "" { + url.Scheme = c.cfg.Scheme + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = query.Encode() + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers.Set(h, v) + } + localVarRequest.Header = headers + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } + + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer "+auth) + } + + {{#withAWSV4Signature}} + // AWS Signature v4 Authentication + if auth, ok := ctx.Value(ContextAWSv4).(AWSv4); ok { + creds := awscredentials.NewStaticCredentials(auth.AccessKey, auth.SecretKey, auth.SessionToken) + signer := awsv4.NewSigner(creds) + var reader *strings.Reader + if body == nil { + reader = strings.NewReader("") + } else { + reader = strings.NewReader(body.String()) + } + + // Define default values for region and service to maintain backward compatibility + region := auth.Region + if region == "" { + region = "eu-west-2" + } + service := auth.Service + if service == "" { + service = "oapi" + } + + timestamp := time.Now() + _, err := signer.Sign(localVarRequest, reader, service, region, timestamp) + if err != nil { + return nil, err + } + } + {{/withAWSV4Signature}} + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } +{{#hasHttpSignatureMethods}} + if ctx != nil { + // HTTP Signature Authentication. All request headers must be set (including default headers) + // because the headers may be included in the signature. + if auth, ok := ctx.Value(ContextHttpSignatureAuth).(HttpSignatureAuth); ok { + err = SignRequest(ctx, localVarRequest, auth) + if err != nil { + return nil, err + } + } + } +{{/hasHttpSignatureMethods}} + return localVarRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if len(b) == 0 { + return nil + } + if s, ok := v.(*string); ok { + *s = string(b) + return nil + } + if xmlCheck.MatchString(contentType) { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } + if jsonCheck.MatchString(contentType) { + if actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas + if unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined + if err = unmarshalObj.UnmarshalJSON(b); err != nil { + return err + } + } else { + return errors.New("Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") + } + } else if err = json.Unmarshal(b, v); err != nil { // simple model + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if fp, ok := body.(**os.File); ok { + _, err = bodyBuf.ReadFrom(*fp) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + err = xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } else { + expires = now.Add(lifetime) + } + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericOpenAPIError Provides access to the body, error and model on returned errors. +type GenericOpenAPIError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericOpenAPIError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericOpenAPIError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericOpenAPIError) Model() interface{} { + return e.model +} diff --git a/.generator/templates/configuration.mustache b/.generator/templates/configuration.mustache new file mode 100644 index 00000000000..87db1aa226d --- /dev/null +++ b/.generator/templates/configuration.mustache @@ -0,0 +1,374 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "context" + "fmt" + "log" + "net/http" + "runtime" + "strings" + + client "github.com/DataDog/datadog-api-client-go" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes an oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKeys takes a string apikey as authentication for the request + ContextAPIKeys = contextKey("apiKeys") + + {{#withAWSV4Signature}} + // ContextAWSv4 takes an Access Key and a Secret Key for signing AWS Signature v4 + ContextAWSv4 = contextKey("awsv4") + + {{/withAWSV4Signature}} + // ContextHttpSignatureAuth takes HttpSignatureAuth as authentication for the request. + ContextHttpSignatureAuth = contextKey("httpsignature") + + // 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 +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +{{#withAWSV4Signature}} +// AWSv4 provides AWS Signature to a request passed via context using ContextAWSv4 +// https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html +type AWSv4 struct { + AccessKey string + SecretKey string + SessionToken string + Region string + Service string +} + +{{/withAWSV4Signature}} +// ServerVariable stores the information about a server variable +type ServerVariable struct { + Description string + DefaultValue string + EnumValues []string +} + +// ServerConfiguration stores the information about a server +type ServerConfiguration struct { + 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 { + 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 + unstableOperations map[string]bool +} + +// NewConfiguration returns a new Configuration object +func NewConfiguration() *Configuration { + cfg := &Configuration{ + DefaultHeader: make(map[string]string), + UserAgent: getUserAgent(), + Debug: false, + {{#servers}} + {{#-first}} + Servers: ServerConfigurations{ + {{/-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}} + {{#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}} + unstableOperations: map[string]bool{ + {{#apiInfo}} + {{#apis}} + {{#operations}} + {{#operation}} + {{#vendorExtensions.x-unstable}} + "{{operationId}}": false, + {{/vendorExtensions.x-unstable}} + {{/operation}} + {{/operations}} + {{/apis}} + {{/apiInfo}} + }, + } + return cfg +} + +// AddDefaultHeader adds a new HTTP header to the default header in the request +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} + +// 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 := sc[index] + url := server.URL + + // go through variables and replace placeholders + for name, variable := range server.Variables { + if value, ok := variables[name]; ok { + found := bool(len(variable.EnumValues) == 0) + for _, enumValue := range variable.EnumValues { + if value == enumValue { + found = true + } + } + if !found { + return "", fmt.Errorf("The variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues) + } + url = strings.Replace(url, "{"+name+"}", value, -1) + } else { + url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1) + } + } + 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) +} + +// GetUnstableOperations returns a slice with all unstable operation Ids +func (c *Configuration) GetUnstableOperations() []string { + ids := make([]string, len(c.unstableOperations)) + for id := range c.unstableOperations { + ids = append(ids, id) + } + return ids +} + +// SetUnstableOperationEnabled sets an unstable operation as enabled (true) or disabled (false) +// This function accepts operation ID as an argument - this is the name of the method on the API class, e.g. "CreateFoo" +// Returns true if the operation is marked as unstable and thus was enabled/disabled, false otherwise +func (c *Configuration) SetUnstableOperationEnabled(operation string, enabled bool) bool { + if _, ok := c.unstableOperations[operation]; ok { + c.unstableOperations[operation] = enabled + return true + } + log.Printf("WARNING: '%s' is not an unstable operation, can't enable/disable", operation) + return false +} + +// IsUnstableOperation determines whether an operation is an unstable operation. +// This function accepts operation ID as an argument - this is the name of the method on the API class, e.g. "CreateFoo" +func (c *Configuration) IsUnstableOperation(operation string) bool { + _, present := c.unstableOperations[operation] + return present +} + +// IsUnstableOperationEnabled determines whether an unstable operation is enabled. +// This function accepts operation ID as an argument - this is the name of the method on the API class, e.g. "CreateFoo" +// Returns true if the operation is unstable and it is enabled, false otherwise +func (c *Configuration) IsUnstableOperationEnabled(operation string) bool { + if enabled, present := c.unstableOperations[operation]; present { + return enabled + } else { + log.Printf("WARNING: '%s' is not an unstable operation, is always enabled", operation) + } + return false +} + +func getUserAgent() string { + return fmt.Sprintf( + "datadog-api-client-go/%s (go %s; os %s; arch %s)", + client.Version, + runtime.Version(), + runtime.GOOS, + runtime.GOARCH, + ) +} diff --git a/.generator/templates/git_push.sh.mustache b/.generator/templates/git_push.sh.mustache new file mode 100755 index 00000000000..8b3f689c912 --- /dev/null +++ b/.generator/templates/git_push.sh.mustache @@ -0,0 +1,58 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-pestore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="{{{gitHost}}}" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="{{{gitUserId}}}" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="{{{gitRepoId}}}" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="{{{releaseNote}}}" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=`git remote` +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:${GIT_TOKEN}@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' + diff --git a/.generator/templates/gitignore.mustache b/.generator/templates/gitignore.mustache new file mode 100644 index 00000000000..daf913b1b34 --- /dev/null +++ b/.generator/templates/gitignore.mustache @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/.generator/templates/go.mod.mustache b/.generator/templates/go.mod.mustache new file mode 100644 index 00000000000..97fceb03a92 --- /dev/null +++ b/.generator/templates/go.mod.mustache @@ -0,0 +1,10 @@ +module {{gitHost}}/{{gitUserId}}/{{gitRepoId}}{{#isGoSubmodule}}/{{packageName}}{{/isGoSubmodule}} + +go 1.13 + +require ( + golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99 + {{#withAWSV4Signature}} + github.com/aws/aws-sdk-go v1.34.14 + {{/withAWSV4Signature}} +) diff --git a/.generator/templates/go.sum b/.generator/templates/go.sum new file mode 100644 index 00000000000..734252e6815 --- /dev/null +++ b/.generator/templates/go.sum @@ -0,0 +1,13 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/.generator/templates/model.mustache b/.generator/templates/model.mustache new file mode 100644 index 00000000000..684af1d33c6 --- /dev/null +++ b/.generator/templates/model.mustache @@ -0,0 +1,20 @@ +{{>partial_header}} +package {{packageName}} + +{{#models}} +import ( + "encoding/json" +{{#imports}} + "{{import}}" +{{/imports}} +) + +{{#model}} +{{#isEnum}} +{{>model_enum}} +{{/isEnum}} +{{^isEnum}} +{{#oneOf}}{{#-first}}{{>model_oneof}}{{/-first}}{{/oneOf}}{{^oneOf}}{{#anyOf}}{{#-first}}{{>model_anyof}}{{/-first}}{{/anyOf}}{{^anyOf}}{{>model_simple}}{{/anyOf}}{{/oneOf}} +{{/isEnum}} +{{/model}} +{{/models}} diff --git a/.generator/templates/model_anyof.mustache b/.generator/templates/model_anyof.mustache new file mode 100644 index 00000000000..5dfa75302f1 --- /dev/null +++ b/.generator/templates/model_anyof.mustache @@ -0,0 +1,76 @@ +// {{classname}}{{#description}} {{{description}}}{{/description}}{{^description}} struct for {{{classname}}}{{/description}} +type {{classname}} struct { + {{#anyOf}} + {{{.}}} *{{{.}}} + {{/anyOf}} +} + +// Unmarshal JSON data into any of the pointers in the struct +func (dst *{{classname}}) UnmarshalJSON(data []byte) error { + var err error + {{#isNullable}} + // this object is nullable so check if the payload is null or empty string + if string(data) == "" || string(data) == "{}" { + return nil + } + + {{/isNullable}} + {{#discriminator}} + {{#mappedModels}} + {{#-first}} + // use discriminator value to speed up the lookup + var jsonDict map[string]interface{} + err := json.Unmarshal(data, &jsonDict) + if err != nil { + return fmt.Errorf("Failed to unmarshal JSON into map for the discrimintor lookup.") + } + + {{/-first}} + // check if the discriminator value is '{{{mappingName}}}' + if jsonDict["{{{propertyBaseName}}}"] == "{{{mappingName}}}" { + // try to unmarshal JSON data into {{{modelName}}} + err = json.Unmarshal(data, &dst.{{{modelName}}}); + if err == nil { + json{{{modelName}}}, _ := json.Marshal(dst.{{{modelName}}}) + if string(json{{{modelName}}}) == "{}" { // empty struct + dst.{{{modelName}}} = nil + } else { + return nil // data stored in dst.{{{modelName}}}, return on the first match + } + } else { + dst.{{{modelName}}} = nil + } + } + + {{/mappedModels}} + {{/discriminator}} + {{#anyOf}} + // try to unmarshal JSON data into {{{.}}} + err = json.Unmarshal(data, &dst.{{{.}}}); + if err == nil { + json{{{.}}}, _ := json.Marshal(dst.{{{.}}}) + if string(json{{{.}}}) == "{}" { // empty struct + dst.{{{.}}} = nil + } else { + return nil // data stored in dst.{{{.}}}, return on the first match + } + } else { + dst.{{{.}}} = nil + } + + {{/anyOf}} + return fmt.Errorf("Data failed to match schemas in anyOf({{classname}})") +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src *{{classname}}) MarshalJSON() ([]byte, error) { +{{#anyOf}} + if src.{{{.}}} != nil { + return json.Marshal(&src.{{{.}}}) + } + +{{/anyOf}} + return nil, nil // no data in anyOf schemas +} + +{{>nullable_model}} diff --git a/.generator/templates/model_doc.mustache b/.generator/templates/model_doc.mustache new file mode 100644 index 00000000000..439e695b1f5 --- /dev/null +++ b/.generator/templates/model_doc.mustache @@ -0,0 +1,97 @@ +{{#models}}{{#model}}# {{classname}} + +{{^isEnum}} +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vendorExtensions.x-is-one-of-interface}} +**{{classname}}Interface** | **interface { {{#discriminator}}{{propertyGetter}}() {{propertyType}}{{/discriminator}} }** | An interface that can hold any of the proper implementing types | +{{/vendorExtensions.x-is-one-of-interface}} +{{^vendorExtensions.x-is-one-of-interface}} +{{#vars}}**{{name}}** | {{^required}}Pointer to {{/required}}{{#isContainer}}{{#isArray}}{{#items}}{{^isPrimitiveType}}{{^isFile}}[{{/isFile}}{{/isPrimitiveType}}**[]{{dataType}}**{{^isPrimitiveType}}{{^isFile}}]({{dataType}}.md){{/isFile}}{{/isPrimitiveType}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{^isPrimitiveType}}{{^isFile}}[{{/isFile}}{{/isPrimitiveType}}**map[string]{{dataType}}**{{^isPrimitiveType}}{{^isFile}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isFile}}{{/isPrimitiveType}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{^isPrimitiveType}}{{^isFile}}{{^isDateTime}}[{{/isDateTime}}{{/isFile}}{{/isPrimitiveType}}**{{dataType}}**{{^isPrimitiveType}}{{^isFile}}{{^isDateTime}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isDateTime}}{{/isFile}}{{/isPrimitiveType}}{{/isContainer}} | {{description}} | {{^required}}[optional] {{/required}}{{#isReadOnly}}[readonly] {{/isReadOnly}}{{#defaultValue}}[default to {{{.}}}]{{/defaultValue}} +{{/vars}} +{{/vendorExtensions.x-is-one-of-interface}} + +## Methods + +{{^vendorExtensions.x-is-one-of-interface}} +### New{{classname}} + +`func New{{classname}}({{#vars}}{{#required}}{{nameInCamelCase}} {{dataType}}, {{/required}}{{/vars}}) *{{classname}}` + +New{{classname}} instantiates a new {{classname}} object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### New{{classname}}WithDefaults + +`func New{{classname}}WithDefaults() *{{classname}}` + +New{{classname}}WithDefaults instantiates a new {{classname}} object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +{{#vars}} +### Get{{name}} + +`func (o *{{classname}}) Get{{name}}() {{vendorExtensions.x-go-base-type}}` + +Get{{name}} returns the {{name}} field if non-nil, zero value otherwise. + +### Get{{name}}Ok + +`func (o *{{classname}}) Get{{name}}Ok() (*{{vendorExtensions.x-go-base-type}}, bool)` + +Get{{name}}Ok returns a tuple with the {{name}} field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### Set{{name}} + +`func (o *{{classname}}) Set{{name}}(v {{vendorExtensions.x-go-base-type}})` + +Set{{name}} sets {{name}} field to given value. + +{{^required}} +### Has{{name}} + +`func (o *{{classname}}) Has{{name}}() bool` + +Has{{name}} returns a boolean if a field has been set. +{{/required}} + +{{#isNullable}} +### Set{{name}}Nil + +`func (o *{{classname}}) Set{{name}}Nil(b bool)` + + Set{{name}}Nil sets the value for {{name}} to be an explicit nil + +### Unset{{name}} +`func (o *{{classname}}) Unset{{name}}()` + +Unset{{name}} ensures that no value is present for {{name}}, not even an explicit nil +{{/isNullable}} +{{/vars}} +{{#vendorExtensions.x-implements}} + +### As{{{.}}} + +`func (s *{{classname}}) As{{{.}}}() {{{.}}}` + +Convenience method to wrap this instance of {{classname}} in {{{.}}} +{{/vendorExtensions.x-implements}} +{{/vendorExtensions.x-is-one-of-interface}} +{{/isEnum}} +{{#isEnum}} +## Enum + +{{#allowableValues}}{{#enumVars}} +* `{{name}}` (value: `{{{value}}}`) +{{/enumVars}}{{/allowableValues}} +{{/isEnum}} + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + +{{/model}}{{/models}} diff --git a/.generator/templates/model_enum.mustache b/.generator/templates/model_enum.mustache new file mode 100644 index 00000000000..11dfbfc7d33 --- /dev/null +++ b/.generator/templates/model_enum.mustache @@ -0,0 +1,100 @@ +// {{{classname}}} {{#description}}{{{.}}}{{/description}}{{^description}}the model '{{{classname}}}'{{/description}} +type {{{classname}}} {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}} + +// List of {{{name}}} +const ( + {{#allowableValues}} + {{#enumVars}} + {{^-first}} + {{/-first}} + {{#enumClassPrefix}}{{{classname.toUpperCase}}}_{{/enumClassPrefix}}{{name}} {{{classname}}} = {{{value}}} + {{/enumVars}} + {{/allowableValues}} +) + +var allowed{{{classname}}}EnumValues = []{{{classname}}}{ + {{#allowableValues}} + {{#enumVars}} + {{{value}}}, + {{/enumVars}} + {{/allowableValues}} +} + +func (v *{{{classname}}}) UnmarshalJSON(src []byte) error { + var value {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}} + err := json.Unmarshal(src, &value) + if err != nil { + return err + } + enumTypeValue := {{{classname}}}(value) + for _, existing := range allowed{{{classname}}}EnumValues { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + return fmt.Errorf("%+v is not a valid {{classname}}", value) +} + +// New{{{classname}}}FromValue returns a pointer to a valid {{{classname}}} +// for the value passed as argument, or an error if the value passed is not allowed by the enum +func New{{{classname}}}FromValue(v {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}}) (*{{{classname}}}, error) { + ev := {{{classname}}}(v) + if ev.IsValid() { + return &ev, nil + } else { + return nil, fmt.Errorf("invalid value '%v' for {{{classname}}}: valid values are %v", v, allowed{{{classname}}}EnumValues) + } +} + +// IsValid return true if the value is valid for the enum, false otherwise +func (v {{{classname}}}) IsValid() bool { + for _, existing := range allowed{{{classname}}}EnumValues { + if existing == v { + return true + } + } + return false +} + +// Ptr returns reference to {{{name}}} value +func (v {{{classname}}}) Ptr() *{{{classname}}} { + return &v +} + +type Nullable{{{classname}}} struct { + value *{{{classname}}} + isSet bool +} + +func (v Nullable{{classname}}) Get() *{{classname}} { + return v.value +} + +func (v *Nullable{{classname}}) Set(val *{{classname}}) { + v.value = val + v.isSet = true +} + +func (v Nullable{{classname}}) IsSet() bool { + return v.isSet +} + +func (v *Nullable{{classname}}) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullable{{classname}}(val *{{classname}}) *Nullable{{classname}} { + return &Nullable{{classname}}{value: val, isSet: true} +} + +func (v Nullable{{{classname}}}) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *Nullable{{{classname}}}) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/.generator/templates/model_oneof.mustache b/.generator/templates/model_oneof.mustache new file mode 100644 index 00000000000..a0322e34074 --- /dev/null +++ b/.generator/templates/model_oneof.mustache @@ -0,0 +1,109 @@ +// {{classname}} - {{#description}}{{{description}}}{{/description}}{{^description}}struct for {{{classname}}}{{/description}} +type {{classname}} struct { + {{#oneOf}} + {{{.}}} *{{{.}}} + {{/oneOf}} +} + +{{#oneOf}} +// {{{.}}}As{{classname}} is a convenience function that returns {{{.}}} wrapped in {{classname}} +func {{{.}}}As{{classname}}(v *{{{.}}}) {{classname}} { + return {{classname}}{ {{{.}}}: v} +} + +{{/oneOf}} + +// Unmarshal JSON data into one of the pointers in the struct +func (dst *{{classname}}) UnmarshalJSON(data []byte) error { + var err error + {{#isNullable}} + // this object is nullable so check if the payload is null or empty string + if string(data) == "" || string(data) == "{}" { + return nil + } + + {{/isNullable}} + {{#useOneOfDiscriminatorLookup}} + {{#discriminator}} + {{#mappedModels}} + {{#-first}} + // use discriminator value to speed up the lookup + var jsonDict map[string]interface{} + err = json.Unmarshal(data, &jsonDict) + if err != nil { + return fmt.Errorf("Failed to unmarshal JSON into map for the discrimintor lookup.") + } + + {{/-first}} + // check if the discriminator value is '{{{mappingName}}}' + if jsonDict["{{{propertyBaseName}}}"] == "{{{mappingName}}}" { + // try to unmarshal JSON data into {{{modelName}}} + err = json.Unmarshal(data, &dst.{{{modelName}}}) + if err == nil { + return nil // data stored in dst.{{{modelName}}}, return on the first match + } else { + dst.{{{modelName}}} = nil + return fmt.Errorf("Failed to unmarshal {{classname}} as {{{modelName}}}: %s", err.Error()) + } + } + + {{/mappedModels}} + {{/discriminator}} + return nil + {{/useOneOfDiscriminatorLookup}} + {{^useOneOfDiscriminatorLookup}} + match := 0 + {{#oneOf}} + // try to unmarshal data into {{{.}}} + err = json.Unmarshal(data, &dst.{{{.}}}) + if err == nil { + json{{{.}}}, _ := json.Marshal(dst.{{{.}}}) + if string(json{{{.}}}) == "{}" { // empty struct + dst.{{{.}}} = nil + } else { + match++ + } + } else { + dst.{{{.}}} = nil + } + + {{/oneOf}} + if match > 1 { // more than 1 match + // reset to nil + {{#oneOf}} + dst.{{{.}}} = nil + {{/oneOf}} + + return fmt.Errorf("Data matches more than one schema in oneOf({{classname}})") + } else if match == 1 { + return nil // exactly one match + } else { // no match + return fmt.Errorf("Data failed to match schemas in oneOf({{classname}})") + } + {{/useOneOfDiscriminatorLookup}} +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src {{classname}}) MarshalJSON() ([]byte, error) { +{{#oneOf}} + if src.{{{.}}} != nil { + return json.Marshal(&src.{{{.}}}) + } + +{{/oneOf}} + return nil, nil // no data in oneOf schemas +} + +// Get the actual instance +func (obj *{{classname}}) GetActualInstance() (interface{}) { +{{#oneOf}} + if obj.{{{.}}} != nil { + return obj.{{{.}}} + } + +{{/oneOf}} + // all schemas are nil + return nil +} + +{{>nullable_model}} diff --git a/.generator/templates/model_simple.mustache b/.generator/templates/model_simple.mustache new file mode 100644 index 00000000000..da651274308 --- /dev/null +++ b/.generator/templates/model_simple.mustache @@ -0,0 +1,391 @@ +// {{classname}}{{#description}} {{{description}}}{{/description}}{{^description}} struct for {{{classname}}}{{/description}} +type {{classname}} struct { +{{#parent}} +{{^isMap}} +{{^isArray}} + {{{parent}}} +{{/isArray}} +{{/isMap}} +{{#isArray}} + Items {{{parent}}} +{{/isArray}} +{{/parent}} +{{#vars}} +{{^-first}} +{{/-first}} +{{#description}} + // {{{description}}} +{{/description}} + {{name}} {{^required}}{{^isNullable}}*{{/isNullable}}{{/required}}{{{dataType}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"{{#withXml}} xml:"{{baseName}}{{#isXmlAttribute}},attr{{/isXmlAttribute}}"{{/withXml}}{{#vendorExtensions.x-go-custom-tag}} {{{.}}}{{/vendorExtensions.x-go-custom-tag}}` +{{/vars}} +{{#isAdditionalPropertiesTrue}} + AdditionalProperties map[string]interface{} +{{/isAdditionalPropertiesTrue}} +} + +{{#isAdditionalPropertiesTrue}} +type _{{{classname}}} {{{classname}}} + +{{/isAdditionalPropertiesTrue}} +// New{{classname}} instantiates a new {{classname}} object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func New{{classname}}({{#requiredVars}}{{nameInCamelCase}} {{dataType}}{{^-last}}, {{/-last}}{{/requiredVars}}) *{{classname}} { + this := {{classname}}{} +{{#allVars}} +{{#required}} + this.{{name}} = {{nameInCamelCase}} +{{/required}} +{{^required}} +{{#defaultValue}} +{{^vendorExtensions.x-golang-is-container}} +{{#isNullable}} + var {{nameInCamelCase}} {{{datatypeWithEnum}}} = {{{.}}} + this.{{name}} = *New{{{dataType}}}(&{{nameInCamelCase}}) +{{/isNullable}} +{{^isNullable}} + var {{nameInCamelCase}} {{{dataType}}} = {{{.}}} + this.{{name}} = &{{nameInCamelCase}} +{{/isNullable}} +{{/vendorExtensions.x-golang-is-container}} +{{/defaultValue}} +{{/required}} +{{/allVars}} + return &this +} + +// New{{classname}}WithDefaults instantiates a new {{classname}} object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func New{{classname}}WithDefaults() *{{classname}} { + this := {{classname}}{} +{{#vars}} +{{#defaultValue}} +{{^vendorExtensions.x-golang-is-container}} +{{#isNullable}} +{{!we use datatypeWithEnum here, since it will represent the non-nullable name of the datatype, e.g. int64 for NullableInt64}} + var {{nameInCamelCase}} {{{datatypeWithEnum}}} = {{{.}}} + this.{{name}} = *New{{{dataType}}}(&{{nameInCamelCase}}) +{{/isNullable}} +{{^isNullable}} + var {{nameInCamelCase}} {{{dataType}}} = {{{.}}} + this.{{name}} = {{^required}}&{{/required}}{{nameInCamelCase}} +{{/isNullable}} +{{/vendorExtensions.x-golang-is-container}} +{{/defaultValue}} +{{/vars}} + return &this +} + +{{#vars}} +{{#required}} +// Get{{name}} returns the {{name}} field value +{{#isNullable}} +// If the value is explicit nil, the zero value for {{vendorExtensions.x-go-base-type}} will be returned +{{/isNullable}} +func (o *{{classname}}) Get{{name}}() {{vendorExtensions.x-go-base-type}} { + if o == nil{{#isNullable}}{{^vendorExtensions.x-golang-is-container}} || o.{{name}}.Get() == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} { + var ret {{vendorExtensions.x-go-base-type}} + return ret + } + +{{#isNullable}} +{{#vendorExtensions.x-golang-is-container}} + return o.{{name}} +{{/vendorExtensions.x-golang-is-container}} +{{^vendorExtensions.x-golang-is-container}} + return *o.{{name}}.Get() +{{/vendorExtensions.x-golang-is-container}} +{{/isNullable}} +{{^isNullable}} + return o.{{name}} +{{/isNullable}} +} + +// Get{{name}}Ok returns a tuple with the {{name}} field value +// and a boolean to check if the value has been set. +{{#isNullable}} +// NOTE: If the value is an explicit nil, `nil, true` will be returned +{{/isNullable}} +func (o *{{classname}}) Get{{name}}Ok() (*{{vendorExtensions.x-go-base-type}}, bool) { + if o == nil {{#isNullable}}{{#vendorExtensions.x-golang-is-container}}|| o.{{name}} == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} { + return nil, false + } +{{#isNullable}} +{{#vendorExtensions.x-golang-is-container}} + return &o.{{name}}, true +{{/vendorExtensions.x-golang-is-container}} +{{^vendorExtensions.x-golang-is-container}} + return o.{{name}}.Get(), o.{{name}}.IsSet() +{{/vendorExtensions.x-golang-is-container}} +{{/isNullable}} +{{^isNullable}} + return &o.{{name}}, true +{{/isNullable}} +} + +// Set{{name}} sets field value +func (o *{{classname}}) Set{{name}}(v {{vendorExtensions.x-go-base-type}}) { +{{#isNullable}} +{{#vendorExtensions.x-golang-is-container}} + o.{{name}} = v +{{/vendorExtensions.x-golang-is-container}} +{{^vendorExtensions.x-golang-is-container}} + o.{{name}}.Set(&v) +{{/vendorExtensions.x-golang-is-container}} +{{/isNullable}} +{{^isNullable}} + o.{{name}} = v +{{/isNullable}} +} + +{{/required}} +{{^required}} +// Get{{name}} returns the {{name}} field value if set, zero value otherwise{{#isNullable}} (both if not set or set to explicit null){{/isNullable}}. +func (o *{{classname}}) Get{{name}}() {{vendorExtensions.x-go-base-type}} { + if o == nil {{^isNullable}}|| o.{{name}} == nil{{/isNullable}}{{#isNullable}}{{^vendorExtensions.x-golang-is-container}}|| o.{{name}}.Get() == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} { + var ret {{vendorExtensions.x-go-base-type}} + return ret + } +{{#isNullable}} +{{#vendorExtensions.x-golang-is-container}} + return o.{{name}} +{{/vendorExtensions.x-golang-is-container}} +{{^vendorExtensions.x-golang-is-container}} + return *o.{{name}}.Get() +{{/vendorExtensions.x-golang-is-container}} +{{/isNullable}} +{{^isNullable}} + return *o.{{name}} +{{/isNullable}} +} + +// Get{{name}}Ok returns a tuple with the {{name}} field value if set, nil otherwise +// and a boolean to check if the value has been set. +{{#isNullable}} +// NOTE: If the value is an explicit nil, `nil, true` will be returned +{{/isNullable}} +func (o *{{classname}}) Get{{name}}Ok() (*{{vendorExtensions.x-go-base-type}}, bool) { + if o == nil {{^isNullable}}|| o.{{name}} == nil{{/isNullable}}{{#isNullable}}{{#vendorExtensions.x-golang-is-container}}|| o.{{name}} == nil{{/vendorExtensions.x-golang-is-container}}{{/isNullable}} { + return nil, false + } +{{#isNullable}} +{{#vendorExtensions.x-golang-is-container}} + return &o.{{name}}, true +{{/vendorExtensions.x-golang-is-container}} +{{^vendorExtensions.x-golang-is-container}} + return o.{{name}}.Get(), o.{{name}}.IsSet() +{{/vendorExtensions.x-golang-is-container}} +{{/isNullable}} +{{^isNullable}} + return o.{{name}}, true +{{/isNullable}} +} + +// Has{{name}} returns a boolean if a field has been set. +func (o *{{classname}}) Has{{name}}() bool { + if o != nil && {{^isNullable}}o.{{name}} != nil{{/isNullable}}{{#isNullable}}{{#vendorExtensions.x-golang-is-container}}o.{{name}} != nil{{/vendorExtensions.x-golang-is-container}}{{^vendorExtensions.x-golang-is-container}}o.{{name}}.IsSet(){{/vendorExtensions.x-golang-is-container}}{{/isNullable}} { + return true + } + + return false +} + +// Set{{name}} gets a reference to the given {{dataType}} and assigns it to the {{name}} field. +func (o *{{classname}}) Set{{name}}(v {{vendorExtensions.x-go-base-type}}) { +{{#isNullable}} +{{#vendorExtensions.x-golang-is-container}} + o.{{name}} = v +{{/vendorExtensions.x-golang-is-container}} +{{^vendorExtensions.x-golang-is-container}} + o.{{name}}.Set(&v) +{{/vendorExtensions.x-golang-is-container}} +{{/isNullable}} +{{^isNullable}} + o.{{name}} = &v +{{/isNullable}} +} +{{#isNullable}} +{{^vendorExtensions.x-golang-is-container}} +// Set{{name}}Nil sets the value for {{name}} to be an explicit nil +func (o *{{classname}}) Set{{name}}Nil() { + o.{{name}}.Set(nil) +} + +// Unset{{name}} ensures that no value is present for {{name}}, not even an explicit nil +func (o *{{classname}}) Unset{{name}}() { + o.{{name}}.Unset() +} +{{/vendorExtensions.x-golang-is-container}} +{{/isNullable}} + +{{/required}} +{{/vars}} +func (o {{classname}}) MarshalJSON() ([]byte, error) { + toSerialize := {{#isArray}}make([]interface{}, len(o.Items)){{/isArray}}{{^isArray}}map[string]interface{}{}{{/isArray}} + {{#parent}} + {{^isMap}} + {{^isArray}} + serialized{{parent}}, err{{parent}} := json.Marshal(o.{{parent}}) + if err{{parent}} != nil { + return []byte{}, err{{parent}} + } + err{{parent}} = json.Unmarshal([]byte(serialized{{parent}}), &toSerialize) + if err{{parent}} != nil { + return []byte{}, err{{parent}} + } + {{/isArray}} + {{/isMap}} + {{#isArray}} + for i, item := range o.Items { + toSerialize[i] = item + } + {{/isArray}} + {{/parent}} + {{#vars}} + {{! if argument is nullable, only serialize it if it is set}} + {{#isNullable}} + {{#vendorExtensions.x-golang-is-container}} + {{! support for container fields is not ideal at this point because of lack of Nullable* types}} + if o.{{name}} != nil { + toSerialize["{{baseName}}"] = o.{{name}} + } + {{/vendorExtensions.x-golang-is-container}} + {{^vendorExtensions.x-golang-is-container}} + if {{#required}}true{{/required}}{{^required}}o.{{name}}.IsSet(){{/required}} { + toSerialize["{{baseName}}"] = o.{{name}}.Get() + } + {{/vendorExtensions.x-golang-is-container}} + {{/isNullable}} + {{! if argument is not nullable, don't set it if it is nil}} + {{^isNullable}} + if {{#required}}true{{/required}}{{^required}}o.{{name}} != nil{{/required}} { + toSerialize["{{baseName}}"] = o.{{name}} + } + {{/isNullable}} + {{/vars}} + {{#isAdditionalPropertiesTrue}} + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + {{/isAdditionalPropertiesTrue}} + return json.Marshal(toSerialize) +} + +{{#isAdditionalPropertiesTrue}} +func (o *{{{classname}}}) UnmarshalJSON(bytes []byte) (err error) { +{{#parent}} +{{^isMap}} + type {{classname}}WithoutEmbeddedStruct struct { + {{#vars}} + {{^-first}} + {{/-first}} + {{#description}} + // {{{description}}} + {{/description}} + {{name}} {{^required}}{{^isNullable}}*{{/isNullable}}{{/required}}{{{dataType}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"{{#withXml}} xml:"{{baseName}}{{#isXmlAttribute}},attr{{/isXmlAttribute}}"{{/withXml}}{{#vendorExtensions.x-go-custom-tag}} {{{.}}}{{/vendorExtensions.x-go-custom-tag}}` + {{/vars}} + } + + var{{{classname}}}WithoutEmbeddedStruct := {{{classname}}}WithoutEmbeddedStruct{} + + err = json.Unmarshal(bytes, &var{{{classname}}}WithoutEmbeddedStruct) + if err == nil { + var{{{classname}}} := _{{{classname}}}{} + {{#vars}} + var{{{classname}}}.{{{name}}} = var{{{classname}}}WithoutEmbeddedStruct.{{{name}}} + {{/vars}} + *o = {{{classname}}}(var{{{classname}}}) + } else { + return err + } + + var{{{classname}}} := _{{{classname}}}{} + + err = json.Unmarshal(bytes, &var{{{classname}}}) + if err == nil { + o.{{{parent}}} = var{{{classname}}}.{{{parent}}} + } else { + return err + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + {{#vars}} + delete(additionalProperties, "{{{baseName}}}") + {{/vars}} + + // remove fields from embedded structs + reflect{{{parent}}} := reflect.ValueOf(o.{{{parent}}}) + for i := 0; i < reflect{{{parent}}}.Type().NumField(); i++ { + t := reflect{{{parent}}}.Type().Field(i) + + if jsonTag := t.Tag.Get("json"); jsonTag != "" { + fieldName := "" + if commaIdx := strings.Index(jsonTag, ","); commaIdx > 0 { + fieldName = jsonTag[:commaIdx] + } else { + fieldName = jsonTag + } + if fieldName != "AdditionalProperties" { + delete(additionalProperties, fieldName) + } + } + } + + o.AdditionalProperties = additionalProperties + } + + return err +{{/isMap}} +{{#isMap}} + var{{{classname}}} := _{{{classname}}}{} + + if err = json.Unmarshal(bytes, &var{{{classname}}}); err == nil { + *o = {{{classname}}}(var{{{classname}}}) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + {{#vars}} + delete(additionalProperties, "{{{baseName}}}") + {{/vars}} + o.AdditionalProperties = additionalProperties + } + + return err +{{/isMap}} +{{/parent}} +{{^parent}} + var{{{classname}}} := _{{{classname}}}{} + + if err = json.Unmarshal(bytes, &var{{{classname}}}); err == nil { + *o = {{{classname}}}(var{{{classname}}}) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + {{#vars}} + delete(additionalProperties, "{{{baseName}}}") + {{/vars}} + o.AdditionalProperties = additionalProperties + } + + return err +{{/parent}} +} + +{{/isAdditionalPropertiesTrue}} +{{#isArray}} +func (o *{{{classname}}}) UnmarshalJSON(bytes []byte) (err error) { + return json.Unmarshal(bytes, &o.Items) +} + +{{/isArray}} +{{>nullable_model}} diff --git a/.generator/templates/nullable_model.mustache b/.generator/templates/nullable_model.mustache new file mode 100644 index 00000000000..20d35769130 --- /dev/null +++ b/.generator/templates/nullable_model.mustache @@ -0,0 +1,35 @@ +type Nullable{{{classname}}} struct { + value *{{{classname}}} + isSet bool +} + +func (v Nullable{{classname}}) Get() *{{classname}} { + return v.value +} + +func (v *Nullable{{classname}}) Set(val *{{classname}}) { + v.value = val + v.isSet = true +} + +func (v Nullable{{classname}}) IsSet() bool { + return v.isSet +} + +func (v *Nullable{{classname}}) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullable{{classname}}(val *{{classname}}) *Nullable{{classname}} { + return &Nullable{{classname}}{value: val, isSet: true} +} + +func (v Nullable{{{classname}}}) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *Nullable{{{classname}}}) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/.generator/templates/openapi.mustache b/.generator/templates/openapi.mustache new file mode 100644 index 00000000000..51ebafb0187 --- /dev/null +++ b/.generator/templates/openapi.mustache @@ -0,0 +1 @@ +{{{openapi-yaml}}} \ No newline at end of file diff --git a/.generator/templates/partial_header.mustache b/.generator/templates/partial_header.mustache new file mode 100644 index 00000000000..94c9ae11b74 --- /dev/null +++ b/.generator/templates/partial_header.mustache @@ -0,0 +1,9 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ +{{#withGoCodegenComment}} + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. +{{/withGoCodegenComment}} diff --git a/.generator/templates/response.mustache b/.generator/templates/response.mustache new file mode 100644 index 00000000000..1a8765bae8f --- /dev/null +++ b/.generator/templates/response.mustache @@ -0,0 +1,38 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "net/http" +) + +// APIResponse stores the API response returned by the server. +type APIResponse struct { + *http.Response `json:"-"` + Message string `json:"message,omitempty"` + // Operation is the name of the OpenAPI operation. + Operation string `json:"operation,omitempty"` + // RequestURL is the request URL. This value is always available, even if the + // embedded *http.Response is nil. + RequestURL string `json:"url,omitempty"` + // Method is the HTTP method used for the request. This value is always + // available, even if the embedded *http.Response is nil. + Method string `json:"method,omitempty"` + // Payload holds the contents of the response body (which may be nil or empty). + // This is provided here as the raw response.Body() reader will have already + // been drained. + Payload []byte `json:"-"` +} + +// NewAPIResponse returns a new APIResonse object. +func NewAPIResponse(r *http.Response) *APIResponse { + + response := &APIResponse{Response: r} + return response +} + +// NewAPIResponseWithError returns a new APIResponse object with the provided error message. +func NewAPIResponseWithError(errorMessage string) *APIResponse { + + response := &APIResponse{Message: errorMessage} + return response +} diff --git a/.generator/templates/signing.mustache b/.generator/templates/signing.mustache new file mode 100644 index 00000000000..22477a06f38 --- /dev/null +++ b/.generator/templates/signing.mustache @@ -0,0 +1,430 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "bytes" + "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/textproto" + "os" + "strings" + "time" +) + +const ( + // Constants for HTTP signature parameters. + // The '(request-target)' parameter concatenates the lowercased :method, an + // ASCII space, and the :path pseudo-headers. + HttpSignatureParameterRequestTarget string = "(request-target)" + // The '(created)' parameter expresses when the signature was + // created. The value MUST be a Unix timestamp integer value. + HttpSignatureParameterCreated string = "(created)" + // The '(expires)' parameter expresses when the signature ceases to + // be valid. The value MUST be a Unix timestamp integer value. + HttpSignatureParameterExpires string = "(expires)" +) + +const ( + // Constants for HTTP headers. + // The 'Host' header, as defined in RFC 2616, section 14.23. + HttpHeaderHost string = "Host" + // The 'Date' header. + HttpHeaderDate string = "Date" + // The digest header, as defined in RFC 3230, section 4.3.2. + HttpHeaderDigest string = "Digest" + // The HTTP Authorization header, as defined in RFC 7235, section 4.2. + HttpHeaderAuthorization string = "Authorization" +) + +const ( + // Specifies the Digital Signature Algorithm is derived from metadata + // associated with 'keyId'. Supported DSA algorithms are RSASSA-PKCS1-v1_5, + // RSASSA-PSS, and ECDSA. + // The hash is SHA-512. + // This is the default value. + HttpSigningSchemeHs2019 string = "hs2019" + // Use RSASSA-PKCS1-v1_5 with SHA-512 hash. Deprecated. + HttpSigningSchemeRsaSha512 string = "rsa-sha512" + // Use RSASSA-PKCS1-v1_5 with SHA-256 hash. Deprecated. + HttpSigningSchemeRsaSha256 string = "rsa-sha256" + + // RFC 8017 section 7.2 + // Calculate the message signature using RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5. + // PKCSV1_5 is deterministic. The same message and key will produce an identical + // signature value each time. + HttpSigningAlgorithmRsaPKCS1v15 string = "RSASSA-PKCS1-v1_5" + // Calculate the message signature using probabilistic signature scheme RSASSA-PSS. + // PSS is randomized and will produce a different signature value each time. + HttpSigningAlgorithmRsaPSS string = "RSASSA-PSS" +) + +var supportedSigningSchemes = map[string]bool{ + HttpSigningSchemeHs2019: true, + HttpSigningSchemeRsaSha512: true, + HttpSigningSchemeRsaSha256: true, +} + + +// HttpSignatureAuth provides HTTP signature authentication to a request passed +// via context using ContextHttpSignatureAuth. +// An 'Authorization' header is calculated by creating a hash of select headers, +// and optionally the body of the HTTP request, then signing the hash value using +// a private key which is available to the client. +// +// SignedHeaders specifies the list of HTTP headers that are included when generating +// the message signature. +// The two special signature headers '(request-target)' and '(created)' SHOULD be +// included in SignedHeaders. +// The '(created)' header expresses when the signature was created. +// The '(request-target)' header is a concatenation of the lowercased :method, an +// ASCII space, and the :path pseudo-headers. +// +// For example, SignedHeaders can be set to: +// (request-target) (created) date host digest +// +// When SignedHeaders is not specified, the client defaults to a single value, '(created)', +// in the list of HTTP headers. +// When SignedHeaders contains the 'Digest' value, the client performs the following operations: +// 1. Calculate a digest of request body, as specified in RFC3230, section 4.3.2. +// 2. Set the 'Digest' header in the request body. +// 3. Include the 'Digest' header and value in the HTTP signature. +type HttpSignatureAuth struct { + KeyId string // A key identifier. + PrivateKeyPath string // The path to the private key. + Passphrase string // The passphrase to decrypt the private key, if the key is encrypted. + SigningScheme string // The signature scheme, when signing HTTP requests. Supported value is 'hs2019'. + // The signature algorithm, when signing HTTP requests. + // Supported values are RSASSA-PKCS1-v1_5, RSASSA-PSS. + SigningAlgorithm string + SignedHeaders []string // A list of HTTP headers included when generating the signature for the message. + // SignatureMaxValidity specifies the maximum duration of the signature validity. + // The value is used to set the '(expires)' signature parameter in the HTTP request. + // '(expires)' is set to '(created)' plus the value of the SignatureMaxValidity field. + // To specify the '(expires)' signature parameter, set 'SignatureMaxValidity' and add '(expires)' to 'SignedHeaders'. + SignatureMaxValidity time.Duration + privateKey crypto.PrivateKey // The private key used to sign HTTP requests. +} + +// SetPrivateKey accepts a private key string and sets it. +func (h *HttpSignatureAuth) SetPrivateKey(privateKey string) error { + return h.parsePrivateKey([]byte(privateKey)) +} + +// ContextWithValue validates the HttpSignatureAuth configuration parameters and returns a context +// suitable for HTTP signature. An error is returned if the HttpSignatureAuth configuration parameters +// are invalid. +func (h *HttpSignatureAuth) ContextWithValue(ctx context.Context) (context.Context, error) { + if h.KeyId == "" { + return nil, fmt.Errorf("Key ID must be specified") + } + if h.PrivateKeyPath == "" && h.privateKey == nil { + return nil, fmt.Errorf("Private key path must be specified") + } + if _, ok := supportedSigningSchemes[h.SigningScheme]; !ok { + return nil, fmt.Errorf("Invalid signing scheme: '%v'", h.SigningScheme) + } + m := make(map[string]bool) + for _, h := range h.SignedHeaders { + if strings.ToLower(h) == strings.ToLower(HttpHeaderAuthorization) { + return nil, fmt.Errorf("Signed headers cannot include the 'Authorization' header") + } + m[h] = true + } + if len(m) != len(h.SignedHeaders) { + return nil, fmt.Errorf("List of signed headers cannot have duplicate names") + } + if h.SignatureMaxValidity < 0 { + return nil, fmt.Errorf("Signature max validity must be a positive value") + } + if err := h.loadPrivateKey(); err != nil { + return nil, err + } + return context.WithValue(ctx, ContextHttpSignatureAuth, *h), nil +} + +// GetPublicKey returns the public key associated with this HTTP signature configuration. +func (h *HttpSignatureAuth) GetPublicKey() (crypto.PublicKey, error) { + if h.privateKey == nil { + if err := h.loadPrivateKey(); err != nil { + return nil, err + } + } + switch key := h.privateKey.(type) { + case *rsa.PrivateKey: + return key.Public(), nil + case *ecdsa.PrivateKey: + return key.Public(), nil + default: + // Do not change '%T' to anything else such as '%v'! + // The value of the private key must not be returned. + return nil, fmt.Errorf("Unsupported key: %T", h.privateKey) + } +} + +// loadPrivateKey reads the private key from the file specified in the HttpSignatureAuth. +// The key is loaded only when privateKey is not already set. +func (h *HttpSignatureAuth) loadPrivateKey() (err error) { + if h.privateKey != nil { + return nil + } + var file *os.File + file, err = os.Open(h.PrivateKeyPath) + if err != nil { + return fmt.Errorf("Cannot load private key '%s'. Error: %v", h.PrivateKeyPath, err) + } + defer func() { + err = file.Close() + }() + var priv []byte + priv, err = ioutil.ReadAll(file) + if err != nil { + return err + } + return h.parsePrivateKey(priv) +} + +// parsePrivateKey decodes privateKey byte array to crypto.PrivateKey type. +func (h *HttpSignatureAuth) parsePrivateKey(priv []byte) error { + pemBlock, _ := pem.Decode(priv) + if pemBlock == nil { + // No PEM data has been found. + return fmt.Errorf("File '%s' does not contain PEM data", h.PrivateKeyPath) + } + var privKey []byte + var err error + if x509.IsEncryptedPEMBlock(pemBlock) { + // The PEM data is encrypted. + privKey, err = x509.DecryptPEMBlock(pemBlock, []byte(h.Passphrase)) + if err != nil { + // Failed to decrypt PEM block. Because of deficiencies in the encrypted-PEM format, + // it's not always possibleto detect an incorrect password. + return err + } + } else { + privKey = pemBlock.Bytes + } + switch pemBlock.Type { + case "RSA PRIVATE KEY": + if h.privateKey, err = x509.ParsePKCS1PrivateKey(privKey); err != nil { + return err + } + case "EC PRIVATE KEY", "PRIVATE KEY": + // https://tools.ietf.org/html/rfc5915 section 4. + if h.privateKey, err = x509.ParsePKCS8PrivateKey(privKey); err != nil { + return err + } + default: + return fmt.Errorf("Key '%s' is not supported", pemBlock.Type) + } + return nil +} + +// SignRequest signs the request using HTTP signature. +// See https://datatracker.ietf.org/doc/draft-cavage-http-signatures/ +// +// Do not add, remove or change headers that are included in the SignedHeaders +// after SignRequest has been invoked; this is because the header values are +// included in the signature. Any subsequent alteration will cause a signature +// verification failure. +// If there are multiple instances of the same header field, all +// header field values associated with the header field MUST be +// concatenated, separated by a ASCII comma and an ASCII space +// ', ', and used in the order in which they will appear in the +// transmitted HTTP message. +func SignRequest( + ctx context.Context, + r *http.Request, + auth HttpSignatureAuth) error { + + if auth.privateKey == nil { + return fmt.Errorf("Private key is not set") + } + now := time.Now() + date := now.UTC().Format(http.TimeFormat) + // The 'created' field expresses when the signature was created. + // The value MUST be a Unix timestamp integer value. See 'HTTP signature' section 2.1.4. + created := now.Unix() + + var h crypto.Hash + var err error + var prefix string + var expiresUnix float64 + + if auth.SignatureMaxValidity < 0 { + return fmt.Errorf("Signature validity must be a positive value") + } + if auth.SignatureMaxValidity > 0 { + e := now.Add(auth.SignatureMaxValidity) + expiresUnix = float64(e.Unix()) + float64(e.Nanosecond()) / float64(time.Second) + } + // Determine the cryptographic hash to be used for the signature and the body digest. + switch auth.SigningScheme { + case HttpSigningSchemeRsaSha512, HttpSigningSchemeHs2019: + h = crypto.SHA512 + prefix = "SHA-512=" + case HttpSigningSchemeRsaSha256: + // This is deprecated and should no longer be used. + h = crypto.SHA256 + prefix = "SHA-256=" + default: + return fmt.Errorf("Unsupported signature scheme: %v", auth.SigningScheme) + } + if !h.Available() { + return fmt.Errorf("Hash '%v' is not available", h) + } + + // Build the "(request-target)" signature header. + var sb bytes.Buffer + fmt.Fprintf(&sb, "%s %s", strings.ToLower(r.Method), r.URL.EscapedPath()) + if r.URL.RawQuery != "" { + // The ":path" pseudo-header field includes the path and query parts + // of the target URI (the "path-absolute" production and optionally a + // '?' character followed by the "query" production (see Sections 3.3 + // and 3.4 of [RFC3986] + fmt.Fprintf(&sb, "?%s", r.URL.RawQuery) + } + requestTarget := sb.String() + sb.Reset() + + // Build the string to be signed. + signedHeaders := auth.SignedHeaders + if len(signedHeaders) == 0 { + signedHeaders = []string{HttpSignatureParameterCreated} + } + // Validate the list of signed headers has no duplicates. + m := make(map[string]bool) + for _, h := range signedHeaders { + m[h] = true + } + if len(m) != len(signedHeaders) { + return fmt.Errorf("List of signed headers must not have any duplicates") + } + hasCreatedParameter := false + hasExpiresParameter := false + for i, header := range signedHeaders { + header = strings.ToLower(header) + var value string + switch header { + case strings.ToLower(HttpHeaderAuthorization): + return fmt.Errorf("Cannot include the 'Authorization' header as a signed header.") + case HttpSignatureParameterRequestTarget: + value = requestTarget + case HttpSignatureParameterCreated: + value = fmt.Sprintf("%d", created) + hasCreatedParameter = true + case HttpSignatureParameterExpires: + if auth.SignatureMaxValidity.Nanoseconds() == 0 { + return fmt.Errorf("Cannot set '(expires)' signature parameter. SignatureMaxValidity is not configured.") + } + value = fmt.Sprintf("%.3f", expiresUnix) + hasExpiresParameter = true + case "date": + value = date + r.Header.Set(HttpHeaderDate, date) + case "digest": + // Calculate the digest of the HTTP request body. + // Calculate body digest per RFC 3230 section 4.3.2 + bodyHash := h.New() + if r.Body != nil { + // Make a copy of the body io.Reader so that we can read the body to calculate the hash, + // then one more time when marshaling the request. + var body io.Reader + body, err = r.GetBody() + if err != nil { + return err + } + if _, err = io.Copy(bodyHash, body); err != nil { + return err + } + } + d := bodyHash.Sum(nil) + value = prefix + base64.StdEncoding.EncodeToString(d) + r.Header.Set(HttpHeaderDigest, value) + case "host": + value = r.Host + r.Header.Set(HttpHeaderHost, r.Host) + default: + var ok bool + var v []string + canonicalHeader := textproto.CanonicalMIMEHeaderKey(header) + if v, ok = r.Header[canonicalHeader]; !ok { + // If a header specified in the headers parameter cannot be matched with + // a provided header in the message, the implementation MUST produce an error. + return fmt.Errorf("Header '%s' does not exist in the request", canonicalHeader) + } + // If there are multiple instances of the same header field, all + // header field values associated with the header field MUST be + // concatenated, separated by a ASCII comma and an ASCII space + // `, `, and used in the order in which they will appear in the + // transmitted HTTP message. + value = strings.Join(v, ", ") + } + if i > 0 { + fmt.Fprintf(&sb, "\n") + } + fmt.Fprintf(&sb, "%s: %s", header, value) + } + if expiresUnix != 0 && !hasExpiresParameter { + return fmt.Errorf("SignatureMaxValidity is specified, but '(expired)' parameter is not present") + } + msg := []byte(sb.String()) + msgHash := h.New() + if _, err = msgHash.Write(msg); err != nil { + return err + } + d := msgHash.Sum(nil) + + var signature []byte + switch key := auth.privateKey.(type) { + case *rsa.PrivateKey: + switch auth.SigningAlgorithm { + case HttpSigningAlgorithmRsaPKCS1v15: + signature, err = rsa.SignPKCS1v15(rand.Reader, key, h, d) + case "", HttpSigningAlgorithmRsaPSS: + signature, err = rsa.SignPSS(rand.Reader, key, h, d, nil) + default: + return fmt.Errorf("Unsupported signing algorithm: '%s'", auth.SigningAlgorithm) + } + case *ecdsa.PrivateKey: + signature, err = key.Sign(rand.Reader, d, h) + case ed25519.PrivateKey: // requires go 1.13 + signature, err = key.Sign(rand.Reader, msg, crypto.Hash(0)) + default: + return fmt.Errorf("Unsupported private key") + } + if err != nil { + return err + } + + sb.Reset() + for i, header := range signedHeaders { + if i > 0 { + sb.WriteRune(' ') + } + sb.WriteString(strings.ToLower(header)) + } + headers_list := sb.String() + sb.Reset() + fmt.Fprintf(&sb, `Signature keyId="%s",algorithm="%s",`, auth.KeyId, auth.SigningScheme) + if hasCreatedParameter { + fmt.Fprintf(&sb, "created=%d,", created) + } + if hasExpiresParameter { + fmt.Fprintf(&sb, "expires=%.3f,", expiresUnix) + } + fmt.Fprintf(&sb, `headers="%s",signature="%s"`, headers_list, base64.StdEncoding.EncodeToString(signature)) + authStr := sb.String() + r.Header.Set(HttpHeaderAuthorization, authStr) + return nil +} diff --git a/.generator/templates/utils.mustache b/.generator/templates/utils.mustache new file mode 100644 index 00000000000..5ae789a996c --- /dev/null +++ b/.generator/templates/utils.mustache @@ -0,0 +1,319 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "encoding/json" + "time" +) + +// PtrBool is a helper routine that returns a pointer to given boolean value. +func PtrBool(v bool) *bool { return &v } + +// PtrInt is a helper routine that returns a pointer to given integer value. +func PtrInt(v int) *int { return &v } + +// PtrInt32 is a helper routine that returns a pointer to given integer value. +func PtrInt32(v int32) *int32 { return &v } + +// PtrInt64 is a helper routine that returns a pointer to given integer value. +func PtrInt64(v int64) *int64 { return &v } + +// PtrFloat32 is a helper routine that returns a pointer to given float value. +func PtrFloat32(v float32) *float32 { return &v } + +// PtrFloat64 is a helper routine that returns a pointer to given float value. +func PtrFloat64(v float64) *float64 { return &v } + +// PtrString is a helper routine that returns a pointer to given string value. +func PtrString(v string) *string { return &v } + +// PtrTime is helper routine that returns a pointer to given Time value. +func PtrTime(v time.Time) *time.Time { return &v } + +type NullableBool struct { + value *bool + isSet bool +} + +func (v NullableBool) Get() *bool { + return v.value +} + +func (v *NullableBool) Set(val *bool) { + v.value = val + v.isSet = true +} + +func (v NullableBool) IsSet() bool { + return v.isSet +} + +func (v *NullableBool) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBool(val *bool) *NullableBool { + return &NullableBool{value: val, isSet: true} +} + +func (v NullableBool) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBool) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt struct { + value *int + isSet bool +} + +func (v NullableInt) Get() *int { + return v.value +} + +func (v *NullableInt) Set(val *int) { + v.value = val + v.isSet = true +} + +func (v NullableInt) IsSet() bool { + return v.isSet +} + +func (v *NullableInt) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt(val *int) *NullableInt { + return &NullableInt{value: val, isSet: true} +} + +func (v NullableInt) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt32 struct { + value *int32 + isSet bool +} + +func (v NullableInt32) Get() *int32 { + return v.value +} + +func (v *NullableInt32) Set(val *int32) { + v.value = val + v.isSet = true +} + +func (v NullableInt32) IsSet() bool { + return v.isSet +} + +func (v *NullableInt32) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt32(val *int32) *NullableInt32 { + return &NullableInt32{value: val, isSet: true} +} + +func (v NullableInt32) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt32) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt64 struct { + value *int64 + isSet bool +} + +func (v NullableInt64) Get() *int64 { + return v.value +} + +func (v *NullableInt64) Set(val *int64) { + v.value = val + v.isSet = true +} + +func (v NullableInt64) IsSet() bool { + return v.isSet +} + +func (v *NullableInt64) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt64(val *int64) *NullableInt64 { + return &NullableInt64{value: val, isSet: true} +} + +func (v NullableInt64) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt64) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableFloat32 struct { + value *float32 + isSet bool +} + +func (v NullableFloat32) Get() *float32 { + return v.value +} + +func (v *NullableFloat32) Set(val *float32) { + v.value = val + v.isSet = true +} + +func (v NullableFloat32) IsSet() bool { + return v.isSet +} + +func (v *NullableFloat32) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableFloat32(val *float32) *NullableFloat32 { + return &NullableFloat32{value: val, isSet: true} +} + +func (v NullableFloat32) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableFloat32) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableFloat64 struct { + value *float64 + isSet bool +} + +func (v NullableFloat64) Get() *float64 { + return v.value +} + +func (v *NullableFloat64) Set(val *float64) { + v.value = val + v.isSet = true +} + +func (v NullableFloat64) IsSet() bool { + return v.isSet +} + +func (v *NullableFloat64) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableFloat64(val *float64) *NullableFloat64 { + return &NullableFloat64{value: val, isSet: true} +} + +func (v NullableFloat64) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableFloat64) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableString struct { + value *string + isSet bool +} + +func (v NullableString) Get() *string { + return v.value +} + +func (v *NullableString) Set(val *string) { + v.value = val + v.isSet = true +} + +func (v NullableString) IsSet() bool { + return v.isSet +} + +func (v *NullableString) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableString(val *string) *NullableString { + return &NullableString{value: val, isSet: true} +} + +func (v NullableString) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableString) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableTime struct { + value *time.Time + isSet bool +} + +func (v NullableTime) Get() *time.Time { + return v.value +} + +func (v *NullableTime) Set(val *time.Time) { + v.value = val + v.isSet = true +} + +func (v NullableTime) IsSet() bool { + return v.isSet +} + +func (v *NullableTime) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTime(val *time.Time) *NullableTime { + return &NullableTime{value: val, isSet: true} +} + +func (v NullableTime) MarshalJSON() ([]byte, error) { + return v.value.MarshalJSON() +} + +func (v *NullableTime) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +}