(Feat): Initial Commit
Some checks failed
Build & Test / build-test (push) Has been cancelled
Build & Test / swagger-codegen-cli (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled

This commit is contained in:
2026-07-03 19:41:31 +05:30
commit 7e940c83a7
461 changed files with 45002 additions and 0 deletions

22
api/README.md Normal file
View File

@@ -0,0 +1,22 @@
# `/api`
OpenAPI/Swagger specs, JSON schema files, protocol definition files.
Use `make swagger` do generate `/internal/types/**/*.go` from these specs. You may use `make watch-swagger` while writing these schemas to automatically execute `make swagger` every time there are changes (especially useful if you have `http://localhost:8081/` opened in your browser while mutating these specs).
The final `/api/swagger.yml` is auto-generated from the following specs:
1. The skeleton spec at `/api/config/main.yml`,
2. all path specs living within `/api/paths/*.yml` and
3. any `/api/definitions/*.yml` referenced by in step 1 and 2.
Further reading:
* [Swagger v2 specification](https://swagger.io/specification/v2/)
* [go-swagger](https://github.com/go-swagger/go-swagger)
* [go-swagger: Schema Generation Rules](https://github.com/go-swagger/go-swagger/blob/master/docs/use/models/schemas.md)
Related examples regarding this project layout:
* https://github.com/golang-standards/project-layout/blob/master/api/README.md
* https://github.com/kubernetes/kubernetes/tree/master/api
* https://github.com/moby/moby/tree/master/api

View File

@@ -0,0 +1,16 @@
layout:
models:
- name: definition
source: asset:model
target: "{{ joinFilePath .Target .ModelPackage }}"
file_name: "{{ (snakize (pascalize .Name)) }}.go"
operations:
- name: parameters
source: asset:serverParameter
target: "{{ joinFilePath .Target .ServerPackage .Package }}"
file_name: "{{ (snakize (pascalize .Name)) }}_parameters.go"
application:
- name: builder
source: asset:serverBuilder
target: "{{ joinFilePath .Target .ServerPackage }}"
file_name: "spec_handlers.go"

37
api/config/main.yml Normal file
View File

@@ -0,0 +1,37 @@
# This is our base swagger file and the primary mixin target.
# Everything in definitions|paths/*.yml will be mixed through
# and finally flattened into the actual swagger.yml in this dir.
consumes:
- application/json
produces:
- application/json
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
description: API documentation
paths: {}
securityDefinitions:
Bearer:
type: apiKey
name: Authorization
in: header
description: |-
Access token for application access, **must** include "Bearer " prefix.
Example: `Bearer b4a94a42-3ea2-4af3-9699-8bcbfee6e6d2`
x-keyPrefix: "Bearer "
Management:
type: apiKey
in: query
description: Management secret, used for monitoring and infrastructure related calls
name: mgmt-secret
definitions:
# Any definitions that are not yet used within paths/*.yml are automatically removed from the resulting swagger.yml.
# You may reference some definitions that you *always* want to be included here.
# --
# Always include orderDir so we can test binding it to db queries directly.
orderDir:
type: string
enum:
- asc
- desc

View File

@@ -0,0 +1,122 @@
// https://github.com/swagger-api/swagger-ui/blob/master/docker/configurator/translator.js
// Based on commit in 02758b8125dbf38763cfd5d4f91c7c803e9bd0ad
// Nov 08, 2018
// see "AAA change" for applied changes
// Converts an object of environment variables into a Swagger UI config object
const configSchema = require("./variables")
const defaultBaseConfig = {
url: {
value: "https://petstore.swagger.io/v2/swagger.json",
schema: {
type: "string",
base: true
}
},
dom_id: {
value: "#swagger-ui",
schema: {
type: "string",
base: true
}
},
deepLinking: {
value: "true",
schema: {
type: "boolean",
base: true
}
},
presets: {
value: `[\n SwaggerUIBundle.presets.apis,\n SwaggerUIStandalonePreset\n]`,
schema: {
type: "array",
base: true
}
},
plugins: {
value: `[\n SwaggerUIBundle.plugins.DownloadUrl\n]`,
schema: {
type: "array",
base: true
}
},
layout: {
value: "StandaloneLayout",
schema: {
type: "string",
base: true
}
},
// AAA change START: interspect requests to 8081 and instead pass them to 8080 where our server runs
requestInterceptor: {
value: `function (request) {
request.url = request.url.split(":8081/").join(":8080/");
console.log("[requestInterceptor] :8080", request);
return request;
}`,
schema: {
type: "function",
base: true
}
}
// AAA change END
}
function objectToKeyValueString(env, { injectBaseConfig = false, schema = configSchema, baseConfig = defaultBaseConfig } = {}) {
let valueStorage = injectBaseConfig ? Object.assign({}, baseConfig) : {}
const keys = Object.keys(env)
// Compute an intermediate representation that holds candidate values and schemas.
//
// This is useful for deduping between multiple env keys that set the same
// config variable.
keys.forEach(key => {
const varSchema = schema[key]
const value = env[key]
if (!varSchema) return
if (varSchema.onFound) {
varSchema.onFound()
}
const storageContents = valueStorage[varSchema.name]
if (storageContents) {
if (varSchema.legacy === true && !storageContents.schema.base) {
// If we're looking at a legacy var, it should lose out to any already-set value
// except for base values
return
}
delete valueStorage[varSchema.name]
}
valueStorage[varSchema.name] = {
value,
schema: varSchema
}
})
// Compute a key:value string based on valueStorage's contents.
let result = ""
Object.keys(valueStorage).forEach(key => {
const value = valueStorage[key]
const escapedName = /[^a-zA-Z0-9]/.test(key) ? `"${key}"` : key
if (value.schema.type === "string") {
result += `${escapedName}: "${value.value}",\n`
} else {
result += `${escapedName}: ${value.value === "" ? `undefined` : value.value},\n`
}
})
return result.trim()
}
module.exports = objectToKeyValueString

195
api/definitions/auth.yml Normal file
View File

@@ -0,0 +1,195 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths: {}
definitions:
User:
type: object
required:
- id
properties:
id:
type: string
format: uuid4
description: ID of user
example: 891d37d3-c74f-493e-aea8-af73efd92016
GetUserInfoResponse:
type: object
required:
- sub
- updated_at
properties:
sub:
type: string
description: ID of user
example: 82ebdfad-c586-4407-a873-4cc1c33d56fc
updated_at:
type: integer
description: Unix timestamp the user's info was last updated at
example: 1591960808
email:
type: string
format: email
description: Email address of user, if available
maxLength: 255
example: user@example.com
scopes:
type: array
items:
type: string
enum:
- "app"
- "cms"
description: Auth-Scopes of the user, if available
example: ["app"]
PostChangePasswordPayload:
type: object
required:
- currentPassword
- newPassword
properties:
currentPassword:
description: Current password of user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
newPassword:
description: New password to set for user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
PostForgotPasswordCompletePayload:
type: object
required:
- token
- password
properties:
password:
description: New password to set for user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
token:
description: Password reset token sent via email
type: string
format: uuid4
example: ec16f032-3c44-4148-bbcc-45557466fa74
PostForgotPasswordPayload:
type: object
required:
- username
properties:
username:
description: Username to initiate password reset for
type: string
format: email
maxLength: 255
minLength: 1
example: user@example.com
PostLoginPayload:
type: object
required:
- username
- password
properties:
password:
description: Password of user to authenticate as
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
username:
description: Username of user to authenticate as
type: string
format: email
maxLength: 255
minLength: 1
example: user@example.com
PostLoginResponse:
type: object
required:
- access_token
- token_type
- expires_in
- refresh_token
properties:
access_token:
description: Access token required for accessing protected API endpoints
type: string
format: uuid4
example: c1247d8d-0d65-41c4-bc86-ec041d2ac437
expires_in:
description: Access token expiry in seconds
type: integer
format: int64
example: 86400
refresh_token:
description: Refresh token for refreshing the access token once it expires
type: string
format: uuid4
example: 1dadb3bd-50d8-485d-83a3-6111392568f0
token_type:
description: "Type of access token, will always be `bearer`"
type: string
example: bearer
RegisterResponse:
type: object
required:
- requiresConfirmation
properties:
requiresConfirmation:
description: Indicates whether the registration process requires email confirmation
type: boolean
example: true
PostLogoutPayload:
type: object
properties:
refresh_token:
description: Optional refresh token to delete while logging out
type: string
format: uuid4
example: 700ebed3-40f7-4211-bc83-a89b22b9875e
PostRefreshPayload:
type: object
required:
- refresh_token
properties:
refresh_token:
description: Refresh token to use for retrieving new token set
type: string
format: uuid4
example: 7503cd8a-c921-4368-a32d-6c1d01d86da9
PostRegisterPayload:
type: object
required:
- username
- password
properties:
password:
description: Password to register with
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
username:
description: Username to register with
type: string
format: email
maxLength: 255
minLength: 1
example: user@example.com
DeleteUserAccountPayload:
type: object
required:
- currentPassword
properties:
currentPassword:
description: Current password of user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple

View File

@@ -0,0 +1,47 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths: {}
definitions:
Paginatable:
type: object
required:
- limit
- offset
- total
properties:
limit:
type: integer
description: Actual limit applied to request
offset:
type: integer
description: Actual offset applied to request
total:
type: integer
description: Total number of records available
parameters:
offsetParam:
type: integer
in: query
name: offset
description: Offset used for pagination, number of records to skip
default: 0
minimum: 0
limitParam:
type: integer
in: query
name: limit
description: Limit used for pagination, number of records to retrieve
default: 50
minimum: 1
maximum: 500
orderDirParam:
type: string
in: query
name: orderDir
description: Direction of order applied, defaults to `asc` if omitted. `asc` will sort `NULL` values at the end of the list.
enum:
- asc
- desc
default: asc

View File

@@ -0,0 +1,79 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths: {}
definitions:
PublicHTTPErrorType:
type: string
description: Type of error returned, should be used for client-side error handling
enum:
- generic
# push
- PUSH_TOKEN_ALREADY_EXISTS
- OLD_PUSH_TOKEN_NOT_FOUND
# files
- ZERO_FILE_SIZE
# auth
- USER_DEACTIVATED
- INVALID_PASSWORD
- NOT_LOCAL_USER
- TOKEN_NOT_FOUND
- TOKEN_EXPIRED
- USER_ALREADY_EXISTS
- MALFORMED_TOKEN
- LAST_AUTHENTICATED_AT_EXCEEDED
- MISSING_SCOPES
PublicHTTPError:
type: object
required:
- status
- type
- title
properties:
detail:
description: "More detailed, human-readable, optional explanation of the error"
type: string
example: User is lacking permission to access this resource
status:
description: HTTP status code returned for the error
type: integer
format: int64
maximum: 599
minimum: 100
x-go-name: Code
example: 403
title:
description: "Short, human-readable description of the error"
type: string
example: Forbidden
type:
$ref: "#/definitions/PublicHTTPErrorType"
PublicHTTPValidationError:
allOf:
- $ref: "#/definitions/PublicHTTPError"
type: object
required:
- validationErrors
properties:
validationErrors:
description: List of errors received while validating payload against schema
type: array
items:
$ref: "#/definitions/HTTPValidationErrorDetail"
HTTPValidationErrorDetail:
type: object
required:
- key
- in
- error
properties:
error:
description: Error describing field validation failure
type: string
in:
description: Indicates how the invalid field was provided
type: string
key:
description: Key of field failing validation
type: string

28
api/definitions/push.yml Normal file
View File

@@ -0,0 +1,28 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths: {}
definitions:
PutUpdatePushTokenPayload:
type: object
required:
- newToken
- provider
properties:
newToken:
description: New push token for given provider.
type: string
maxLength: 500
example: 1c91e550-8167-439c-8021-dee7de2f7e96
oldToken:
description: Old token that can be deleted if present.
type: string
maxLength: 500
example: 495179de-b771-48f0-aab2-8d23701b0f02
x-nullable: true
provider:
description: Identifier of the provider the token is for (eg. "fcm", "apn"). Currently only "fcm" is supported.
type: string
maxLength: 500
example: fcm

289
api/paths/auth.yml Normal file
View File

@@ -0,0 +1,289 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
responses:
InvalidPasswordResponse:
description: "PublicHTTPValidationError, type `INVALID_PASSWORD`"
schema:
$ref: ../definitions/errors.yml#/definitions/PublicHTTPValidationError
AuthUnauthorizedResponse:
description: PublicHTTPError
schema:
$ref: ../definitions/errors.yml#/definitions/PublicHTTPError
AuthForbiddenResponse:
description: "PublicHTTPError, type `USER_DEACTIVATED`/`NOT_LOCAL_USER`"
schema:
$ref: ../definitions/errors.yml#/definitions/PublicHTTPError
ValidationError:
description: PublicHTTPValidationError
schema:
$ref: "../definitions/errors.yml#/definitions/PublicHTTPValidationError"
parameters:
registrationTokenParam:
type: string
format: uuid4
in: path
name: registrationToken
description: Registration token to complete the registration process
required: true
paths:
/api/v1/auth/change-password:
post:
security:
- Bearer: []
description: |-
After successful password change, all current access and refresh tokens are
invalidated and a new set of auth tokens is returned
tags:
- auth
summary: Change local user's password
operationId: PostChangePasswordRoute
parameters:
- name: Payload
in: body
schema:
$ref: ../definitions/auth.yml#/definitions/PostChangePasswordPayload
responses:
"200":
description: PostLoginResponse
schema:
$ref: ../definitions/auth.yml#/definitions/PostLoginResponse
"400":
$ref: "#/responses/InvalidPasswordResponse"
"401":
$ref: "#/responses/AuthUnauthorizedResponse"
"403":
$ref: "#/responses/AuthForbiddenResponse"
/api/v1/auth/forgot-password:
post:
description: |-
Initiates a password reset for a local user, sending an email with a password
reset link to the provided email address if a user account exists. Will always
succeed, even if no user was found in order to prevent user enumeration
tags:
- auth
summary: Initiate password reset for local user
operationId: PostForgotPasswordRoute
parameters:
- name: Payload
in: body
schema:
$ref: "../definitions/auth.yml#/definitions/PostForgotPasswordPayload"
responses:
"204":
description: Success
"400":
$ref: "#/responses/ValidationError"
/api/v1/auth/forgot-password/complete:
post:
description: |-
Completes a password reset for a local user, using the password reset token sent via email
to confirm user access, setting the new password if successful. All current access and refresh
tokens are invalidated and a new set of auth tokens is returned
tags:
- auth
summary: Completes password reset for local user
operationId: PostForgotPasswordCompleteRoute
parameters:
- name: Payload
in: body
schema:
$ref: "../definitions/auth.yml#/definitions/PostForgotPasswordCompletePayload"
responses:
"200":
description: PostLoginResponse
schema:
$ref: "../definitions/auth.yml#/definitions/PostLoginResponse"
"400":
$ref: "#/responses/InvalidPasswordResponse"
"403":
$ref: "#/responses/AuthForbiddenResponse"
"404":
description: "PublicHTTPError, type `TOKEN_NOT_FOUND`"
schema:
$ref: "../definitions/errors.yml#/definitions/PublicHTTPError"
"409":
description: "PublicHTTPError, type `TOKEN_EXPIRED`"
schema:
$ref: "../definitions/errors.yml#/definitions/PublicHTTPError"
/api/v1/auth/login:
post:
description: Returns an access and refresh token on successful authentication
tags:
- auth
summary: Login with local user
operationId: PostLoginRoute
parameters:
- name: Payload
in: body
schema:
$ref: "../definitions/auth.yml#/definitions/PostLoginPayload"
responses:
"200":
description: PostLoginResponse
schema:
$ref: "../definitions/auth.yml#/definitions/PostLoginResponse"
"400":
$ref: "#/responses/ValidationError"
"401":
$ref: "#/responses/AuthUnauthorizedResponse"
"403":
description: "PublicHTTPError, type `USER_DEACTIVATED`"
schema:
$ref: "../definitions/errors.yml#/definitions/PublicHTTPError"
/api/v1/auth/logout:
post:
security:
- Bearer: []
description: |-
Logs the local user out, destroying the provided access token.
A refresh token can optionally be provided, destroying it as well if found.
tags:
- auth
summary: Log out local user
operationId: PostLogoutRoute
parameters:
- name: Payload
in: body
schema:
$ref: "../definitions/auth.yml#/definitions/PostLogoutPayload"
responses:
"204":
description: Success
"400":
$ref: "#/responses/ValidationError"
"401":
$ref: "#/responses/AuthUnauthorizedResponse"
/api/v1/auth/refresh:
post:
description: |-
Returns a fresh set of access and refresh tokens if a valid refresh token was provided.
The old refresh token used to authenticate the request will be invalidated.
tags:
- auth
summary: Refresh tokens
operationId: PostRefreshRoute
parameters:
- name: Payload
in: body
schema:
$ref: "../definitions/auth.yml#/definitions/PostRefreshPayload"
responses:
"200":
description: PostLoginResponse
schema:
$ref: "../definitions/auth.yml#/definitions/PostLoginResponse"
"400":
$ref: "#/responses/ValidationError"
"401":
$ref: "#/responses/AuthUnauthorizedResponse"
"403":
description: "PublicHTTPError, type `USER_DEACTIVATED`"
schema:
$ref: "../definitions/errors.yml#/definitions/PublicHTTPError"
/api/v1/auth/register:
post:
description: |
Registers a local user. If the registration process requires a confirmation
the status code `202` is returned without auth tokens. Afterwards the registration needs to be confirmed
using the `POST /api/v1/auth/register/{registrationToken}` endpoint.
tags:
- auth
summary: Registers a local user
operationId: PostRegisterRoute
parameters:
- name: Payload
in: body
schema:
$ref: "../definitions/auth.yml#/definitions/PostRegisterPayload"
responses:
"200":
description: PostLoginResponse
schema:
$ref: "../definitions/auth.yml#/definitions/PostLoginResponse"
"202":
description: RegisterResponse
schema:
$ref: "../definitions/auth.yml#/definitions/RegisterResponse"
"400":
$ref: "#/responses/InvalidPasswordResponse"
"409":
description: "PublicHTTPError, type `USER_ALREADY_EXISTS`"
schema:
$ref: "../definitions/errors.yml#/definitions/PublicHTTPError"
get:
summary: Page to complete registration
tags:
- auth
operationId: GetCompleteRegisterRoute
parameters:
- name: token
in: query
type: string
format: uuid4
description: Registration token to complete the registration process
required: true
produces:
- text/html
responses:
"200":
description: Page to complete registration
/api/v1/auth/register/{registrationToken}:
post:
summary: Complete registration
description: |-
Completes registration for a local user
tags:
- auth
operationId: PostCompleteRegisterRoute
parameters:
- $ref: "#/parameters/registrationTokenParam"
responses:
"200":
description: PostLoginResponse
schema:
$ref: "../definitions/auth.yml#/definitions/PostLoginResponse"
"401":
description: Unauthorized
/api/v1/auth/userinfo:
get:
summary: Get user info
description: |-
Returns user information compatible with the OpenID Connect Core 1.0 specification.
Information returned depends on the requesting user as some data is only available if an app user profile exists.
security:
- Bearer: []
operationId: GetUserInfoRoute
tags:
- auth
responses:
"200":
description: GetUserInfoResponse
schema:
$ref: "../definitions/auth.yml#/definitions/GetUserInfoResponse"
/api/v1/auth/account:
delete:
summary: Delete user account
description: |-
Completely delete the user account. This action is irreversible and therefore requires additional password authentication.
security:
- Bearer: []
operationId: DeleteUserAccountRoute
parameters:
- name: Payload
in: body
schema:
$ref: ../definitions/auth.yml#/definitions/DeleteUserAccountPayload
tags:
- auth
responses:
"204":
description: NoContent
"400":
$ref: "#/responses/InvalidPasswordResponse"
"401":
$ref: "#/responses/AuthUnauthorizedResponse"
"403":
$ref: "#/responses/AuthForbiddenResponse"

71
api/paths/common.yml Normal file
View File

@@ -0,0 +1,71 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths:
/swagger.yml:
get:
summary: Get swagger.yml
operationId: GetSwaggerRoute
produces:
- text/plain
description: |-
OpenAPI Specification ver. 2 (fka Swagger)
Returns our handcrafted and validated `swagger.yml`.
tags:
- common
responses:
"200":
description: OK
/-/ready:
get:
summary: Get ready (readiness probe)
operationId: GetReadyRoute
produces:
- text/plain
description: |-
This endpoint returns 200 when the service is ready to serve traffic.
Does read-only probes apart from the general server ready state.
Note that /-/ready is typically public (and not shielded by a mgmt-secret), we thus prevent information leakage here and only return `"Ready."`.
tags:
- common
responses:
"200":
description: Ready.
"521":
description: Not ready.
/-/healthy:
get:
security:
- Management: []
summary: Get healthy (liveness probe)
operationId: GetHealthyRoute
produces:
- text/plain
description: |-
This endpoint returns 200 when the service is healthy.
Returns an human readable string about the current service status.
In addition to readiness probes, it performs actual write probes.
Note that /-/healthy is private (shielded by the mgmt-secret) as it may expose sensitive information about your service.
tags:
- common
responses:
"200":
description: Ready.
"521":
description: Not ready.
/-/version:
get:
security:
- Management: []
summary: Get version
operationId: GetVersionRoute
produces:
- text/plain
description: |-
This endpoint returns the module name, commit and build-date baked into the app binary.
tags:
- common
responses:
"200":
description: "ModuleName @ Commit (BuildDate)"

33
api/paths/push.yml Normal file
View File

@@ -0,0 +1,33 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths:
/api/v1/push/token:
put:
security:
- Bearer: []
description: |-
Adds a push token for the given provider to the current user.
If the oldToken is present it will be deleted.
Currently only the provider 'fcm' is supported.
tags:
- push
summary: Adds a push token to the user
operationId: PutUpdatePushTokenRoute
parameters:
- name: Payload
in: body
schema:
"$ref": "../definitions/push.yml#/definitions/PutUpdatePushTokenPayload"
responses:
"200":
description: OK
"404":
description: PublicHTTPError, type `OLD_PUSH_TOKEN_NOT_FOUND`
schema:
"$ref": "../definitions/errors.yml#/definitions/PublicHTTPError"
"409":
description: PublicHTTPError, type `PUSH_TOKEN_ALREADY_EXISTS`
schema:
"$ref": "../definitions/errors.yml#/definitions/PublicHTTPError"

27
api/paths/well_known.yml Normal file
View File

@@ -0,0 +1,27 @@
swagger: "2.0"
info:
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths:
/.well-known/apple-app-site-association:
get:
summary: Apple App Site Association
description: |-
Returns the Apple App Site Association file.
operationId: GetAppleAppSiteAssociationRoute
tags:
- well-known
responses:
"200":
description: Apple App Site Association
/.well-known/assetlinks.json:
get:
summary: Android Digital Asset Links
description: |-
Returns the Android Digital Asset Links file.
operationId: GetAndroidDigitalAssetLinksRoute
tags:
- well-known
responses:
"200":
description: Android Digital Asset Links

738
api/swagger.yml Normal file
View File

@@ -0,0 +1,738 @@
# // Code generated by "make swagger"; DO NOT EDIT.
consumes:
- application/json
produces:
- application/json
swagger: "2.0"
info:
description: API documentation
title: allaboutapps.dev/aw/go-starter
version: 0.1.0
paths:
/-/healthy:
get:
security:
- Management: []
description: |-
This endpoint returns 200 when the service is healthy.
Returns an human readable string about the current service status.
In addition to readiness probes, it performs actual write probes.
Note that /-/healthy is private (shielded by the mgmt-secret) as it may expose sensitive information about your service.
produces:
- text/plain
tags:
- common
summary: Get healthy (liveness probe)
operationId: GetHealthyRoute
responses:
"200":
description: Ready.
"521":
description: Not ready.
/-/ready:
get:
description: |-
This endpoint returns 200 when the service is ready to serve traffic.
Does read-only probes apart from the general server ready state.
Note that /-/ready is typically public (and not shielded by a mgmt-secret), we thus prevent information leakage here and only return `"Ready."`.
produces:
- text/plain
tags:
- common
summary: Get ready (readiness probe)
operationId: GetReadyRoute
responses:
"200":
description: Ready.
"521":
description: Not ready.
/-/version:
get:
security:
- Management: []
description: This endpoint returns the module name, commit and build-date baked
into the app binary.
produces:
- text/plain
tags:
- common
summary: Get version
operationId: GetVersionRoute
responses:
"200":
description: ModuleName @ Commit (BuildDate)
/.well-known/apple-app-site-association:
get:
description: Returns the Apple App Site Association file.
tags:
- well-known
summary: Apple App Site Association
operationId: GetAppleAppSiteAssociationRoute
responses:
"200":
description: Apple App Site Association
/.well-known/assetlinks.json:
get:
description: Returns the Android Digital Asset Links file.
tags:
- well-known
summary: Android Digital Asset Links
operationId: GetAndroidDigitalAssetLinksRoute
responses:
"200":
description: Android Digital Asset Links
/api/v1/auth/account:
delete:
security:
- Bearer: []
description: Completely delete the user account. This action is irreversible
and therefore requires additional password authentication.
tags:
- auth
summary: Delete user account
operationId: DeleteUserAccountRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/deleteUserAccountPayload'
responses:
"204":
description: NoContent
"400":
description: PublicHTTPValidationError, type `INVALID_PASSWORD`
schema:
$ref: '#/definitions/publicHttpValidationError'
"401":
description: PublicHTTPError
schema:
$ref: '#/definitions/publicHttpError'
"403":
description: PublicHTTPError, type `USER_DEACTIVATED`/`NOT_LOCAL_USER`
schema:
$ref: '#/definitions/publicHttpError'
/api/v1/auth/change-password:
post:
security:
- Bearer: []
description: |-
After successful password change, all current access and refresh tokens are
invalidated and a new set of auth tokens is returned
tags:
- auth
summary: Change local user's password
operationId: PostChangePasswordRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/postChangePasswordPayload'
responses:
"200":
description: PostLoginResponse
schema:
$ref: '#/definitions/postLoginResponse'
"400":
description: PublicHTTPValidationError, type `INVALID_PASSWORD`
schema:
$ref: '#/definitions/publicHttpValidationError'
"401":
description: PublicHTTPError
schema:
$ref: '#/definitions/publicHttpError'
"403":
description: PublicHTTPError, type `USER_DEACTIVATED`/`NOT_LOCAL_USER`
schema:
$ref: '#/definitions/publicHttpError'
/api/v1/auth/forgot-password:
post:
description: |-
Initiates a password reset for a local user, sending an email with a password
reset link to the provided email address if a user account exists. Will always
succeed, even if no user was found in order to prevent user enumeration
tags:
- auth
summary: Initiate password reset for local user
operationId: PostForgotPasswordRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/postForgotPasswordPayload'
responses:
"204":
description: Success
"400":
description: PublicHTTPValidationError
schema:
$ref: '#/definitions/publicHttpValidationError'
/api/v1/auth/forgot-password/complete:
post:
description: |-
Completes a password reset for a local user, using the password reset token sent via email
to confirm user access, setting the new password if successful. All current access and refresh
tokens are invalidated and a new set of auth tokens is returned
tags:
- auth
summary: Completes password reset for local user
operationId: PostForgotPasswordCompleteRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/postForgotPasswordCompletePayload'
responses:
"200":
description: PostLoginResponse
schema:
$ref: '#/definitions/postLoginResponse'
"400":
description: PublicHTTPValidationError, type `INVALID_PASSWORD`
schema:
$ref: '#/definitions/publicHttpValidationError'
"403":
description: PublicHTTPError, type `USER_DEACTIVATED`/`NOT_LOCAL_USER`
schema:
$ref: '#/definitions/publicHttpError'
"404":
description: PublicHTTPError, type `TOKEN_NOT_FOUND`
schema:
$ref: '#/definitions/publicHttpError'
"409":
description: PublicHTTPError, type `TOKEN_EXPIRED`
schema:
$ref: '#/definitions/publicHttpError'
/api/v1/auth/login:
post:
description: Returns an access and refresh token on successful authentication
tags:
- auth
summary: Login with local user
operationId: PostLoginRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/postLoginPayload'
responses:
"200":
description: PostLoginResponse
schema:
$ref: '#/definitions/postLoginResponse'
"400":
description: PublicHTTPValidationError
schema:
$ref: '#/definitions/publicHttpValidationError'
"401":
description: PublicHTTPError
schema:
$ref: '#/definitions/publicHttpError'
"403":
description: PublicHTTPError, type `USER_DEACTIVATED`
schema:
$ref: '#/definitions/publicHttpError'
/api/v1/auth/logout:
post:
security:
- Bearer: []
description: |-
Logs the local user out, destroying the provided access token.
A refresh token can optionally be provided, destroying it as well if found.
tags:
- auth
summary: Log out local user
operationId: PostLogoutRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/postLogoutPayload'
responses:
"204":
description: Success
"400":
description: PublicHTTPValidationError
schema:
$ref: '#/definitions/publicHttpValidationError'
"401":
description: PublicHTTPError
schema:
$ref: '#/definitions/publicHttpError'
/api/v1/auth/refresh:
post:
description: |-
Returns a fresh set of access and refresh tokens if a valid refresh token was provided.
The old refresh token used to authenticate the request will be invalidated.
tags:
- auth
summary: Refresh tokens
operationId: PostRefreshRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/postRefreshPayload'
responses:
"200":
description: PostLoginResponse
schema:
$ref: '#/definitions/postLoginResponse'
"400":
description: PublicHTTPValidationError
schema:
$ref: '#/definitions/publicHttpValidationError'
"401":
description: PublicHTTPError
schema:
$ref: '#/definitions/publicHttpError'
"403":
description: PublicHTTPError, type `USER_DEACTIVATED`
schema:
$ref: '#/definitions/publicHttpError'
/api/v1/auth/register:
get:
produces:
- text/html
tags:
- auth
summary: Page to complete registration
operationId: GetCompleteRegisterRoute
parameters:
- type: string
format: uuid4
description: Registration token to complete the registration process
name: token
in: query
required: true
responses:
"200":
description: Page to complete registration
post:
description: |
Registers a local user. If the registration process requires a confirmation
the status code `202` is returned without auth tokens. Afterwards the registration needs to be confirmed
using the `POST /api/v1/auth/register/{registrationToken}` endpoint.
tags:
- auth
summary: Registers a local user
operationId: PostRegisterRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/postRegisterPayload'
responses:
"200":
description: PostLoginResponse
schema:
$ref: '#/definitions/postLoginResponse'
"202":
description: RegisterResponse
schema:
$ref: '#/definitions/registerResponse'
"400":
description: PublicHTTPValidationError, type `INVALID_PASSWORD`
schema:
$ref: '#/definitions/publicHttpValidationError'
"409":
description: PublicHTTPError, type `USER_ALREADY_EXISTS`
schema:
$ref: '#/definitions/publicHttpError'
/api/v1/auth/register/{registrationToken}:
post:
description: Completes registration for a local user
tags:
- auth
summary: Complete registration
operationId: PostCompleteRegisterRoute
parameters:
- type: string
format: uuid4
description: Registration token to complete the registration process
name: registrationToken
in: path
required: true
responses:
"200":
description: PostLoginResponse
schema:
$ref: '#/definitions/postLoginResponse'
"401":
description: Unauthorized
/api/v1/auth/userinfo:
get:
security:
- Bearer: []
description: |-
Returns user information compatible with the OpenID Connect Core 1.0 specification.
Information returned depends on the requesting user as some data is only available if an app user profile exists.
tags:
- auth
summary: Get user info
operationId: GetUserInfoRoute
responses:
"200":
description: GetUserInfoResponse
schema:
$ref: '#/definitions/getUserInfoResponse'
/api/v1/push/token:
put:
security:
- Bearer: []
description: |-
Adds a push token for the given provider to the current user.
If the oldToken is present it will be deleted.
Currently only the provider 'fcm' is supported.
tags:
- push
summary: Adds a push token to the user
operationId: PutUpdatePushTokenRoute
parameters:
- name: Payload
in: body
schema:
$ref: '#/definitions/putUpdatePushTokenPayload'
responses:
"200":
description: OK
"404":
description: PublicHTTPError, type `OLD_PUSH_TOKEN_NOT_FOUND`
schema:
$ref: '#/definitions/publicHttpError'
"409":
description: PublicHTTPError, type `PUSH_TOKEN_ALREADY_EXISTS`
schema:
$ref: '#/definitions/publicHttpError'
/swagger.yml:
get:
description: |-
OpenAPI Specification ver. 2 (fka Swagger)
Returns our handcrafted and validated `swagger.yml`.
produces:
- text/plain
tags:
- common
summary: Get swagger.yml
operationId: GetSwaggerRoute
responses:
"200":
description: OK
definitions:
deleteUserAccountPayload:
type: object
required:
- currentPassword
properties:
currentPassword:
description: Current password of user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
getUserInfoResponse:
type: object
required:
- sub
- updated_at
properties:
email:
description: Email address of user, if available
type: string
format: email
maxLength: 255
example: user@example.com
scopes:
description: Auth-Scopes of the user, if available
type: array
items:
type: string
enum:
- app
- cms
example:
- app
sub:
description: ID of user
type: string
example: 82ebdfad-c586-4407-a873-4cc1c33d56fc
updated_at:
description: Unix timestamp the user's info was last updated at
type: integer
example: 1591960808
httpValidationErrorDetail:
type: object
required:
- key
- in
- error
properties:
error:
description: Error describing field validation failure
type: string
in:
description: Indicates how the invalid field was provided
type: string
key:
description: Key of field failing validation
type: string
orderDir:
type: string
enum:
- asc
- desc
postChangePasswordPayload:
type: object
required:
- currentPassword
- newPassword
properties:
currentPassword:
description: Current password of user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
newPassword:
description: New password to set for user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
postForgotPasswordCompletePayload:
type: object
required:
- token
- password
properties:
password:
description: New password to set for user
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
token:
description: Password reset token sent via email
type: string
format: uuid4
example: ec16f032-3c44-4148-bbcc-45557466fa74
postForgotPasswordPayload:
type: object
required:
- username
properties:
username:
description: Username to initiate password reset for
type: string
format: email
maxLength: 255
minLength: 1
example: user@example.com
postLoginPayload:
type: object
required:
- username
- password
properties:
password:
description: Password of user to authenticate as
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
username:
description: Username of user to authenticate as
type: string
format: email
maxLength: 255
minLength: 1
example: user@example.com
postLoginResponse:
type: object
required:
- access_token
- token_type
- expires_in
- refresh_token
properties:
access_token:
description: Access token required for accessing protected API endpoints
type: string
format: uuid4
example: c1247d8d-0d65-41c4-bc86-ec041d2ac437
expires_in:
description: Access token expiry in seconds
type: integer
format: int64
example: 86400
refresh_token:
description: Refresh token for refreshing the access token once it expires
type: string
format: uuid4
example: 1dadb3bd-50d8-485d-83a3-6111392568f0
token_type:
description: Type of access token, will always be `bearer`
type: string
example: bearer
postLogoutPayload:
type: object
properties:
refresh_token:
description: Optional refresh token to delete while logging out
type: string
format: uuid4
example: 700ebed3-40f7-4211-bc83-a89b22b9875e
postRefreshPayload:
type: object
required:
- refresh_token
properties:
refresh_token:
description: Refresh token to use for retrieving new token set
type: string
format: uuid4
example: 7503cd8a-c921-4368-a32d-6c1d01d86da9
postRegisterPayload:
type: object
required:
- username
- password
properties:
password:
description: Password to register with
type: string
maxLength: 500
minLength: 1
example: correct horse battery staple
username:
description: Username to register with
type: string
format: email
maxLength: 255
minLength: 1
example: user@example.com
publicHttpError:
type: object
required:
- status
- type
- title
properties:
detail:
description: More detailed, human-readable, optional explanation of the error
type: string
example: User is lacking permission to access this resource
status:
description: HTTP status code returned for the error
type: integer
format: int64
maximum: 599
minimum: 100
x-go-name: Code
example: 403
title:
description: Short, human-readable description of the error
type: string
example: Forbidden
type:
$ref: '#/definitions/publicHttpErrorType'
publicHttpErrorType:
description: Type of error returned, should be used for client-side error handling
type: string
enum:
- generic
- PUSH_TOKEN_ALREADY_EXISTS
- OLD_PUSH_TOKEN_NOT_FOUND
- ZERO_FILE_SIZE
- USER_DEACTIVATED
- INVALID_PASSWORD
- NOT_LOCAL_USER
- TOKEN_NOT_FOUND
- TOKEN_EXPIRED
- USER_ALREADY_EXISTS
- MALFORMED_TOKEN
- LAST_AUTHENTICATED_AT_EXCEEDED
- MISSING_SCOPES
publicHttpValidationError:
type: object
required:
- validationErrors
allOf:
- $ref: '#/definitions/publicHttpError'
properties:
validationErrors:
description: List of errors received while validating payload against schema
type: array
items:
$ref: '#/definitions/httpValidationErrorDetail'
putUpdatePushTokenPayload:
type: object
required:
- newToken
- provider
properties:
newToken:
description: New push token for given provider.
type: string
maxLength: 500
example: 1c91e550-8167-439c-8021-dee7de2f7e96
oldToken:
description: Old token that can be deleted if present.
type: string
maxLength: 500
x-nullable: true
example: 495179de-b771-48f0-aab2-8d23701b0f02
provider:
description: Identifier of the provider the token is for (eg. "fcm", "apn").
Currently only "fcm" is supported.
type: string
maxLength: 500
example: fcm
registerResponse:
type: object
required:
- requiresConfirmation
properties:
requiresConfirmation:
description: Indicates whether the registration process requires email confirmation
type: boolean
example: true
parameters:
registrationTokenParam:
type: string
format: uuid4
description: Registration token to complete the registration process
name: registrationToken
in: path
required: true
responses:
AuthForbiddenResponse:
description: PublicHTTPError, type `USER_DEACTIVATED`/`NOT_LOCAL_USER`
schema:
$ref: '#/definitions/publicHttpError'
AuthUnauthorizedResponse:
description: PublicHTTPError
schema:
$ref: '#/definitions/publicHttpError'
InvalidPasswordResponse:
description: PublicHTTPValidationError, type `INVALID_PASSWORD`
schema:
$ref: '#/definitions/publicHttpValidationError'
ValidationError:
description: PublicHTTPValidationError
schema:
$ref: '#/definitions/publicHttpValidationError'
securityDefinitions:
Bearer:
description: |-
Access token for application access, **must** include "Bearer " prefix.
Example: `Bearer b4a94a42-3ea2-4af3-9699-8bcbfee6e6d2`
type: apiKey
name: Authorization
in: header
x-keyPrefix: 'Bearer '
Management:
description: Management secret, used for monitoring and infrastructure related
calls
type: apiKey
name: mgmt-secret
in: query

View File

@@ -0,0 +1,5 @@
https://raw.githubusercontent.com/go-swagger/go-swagger/master/generator/templates/server/builder.gotmpl
Based on commit in 78f49e7e72c09ff0e39251f002bba729b861e756
Feb 24, 2020
Note that our `builder.gotmpl` is minimal and only used for validating if any routes without a swagger spec exist.

View File

@@ -0,0 +1,49 @@
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package types
{{ $package := "types" }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// NewSwaggerSpec creates a new SwaggerSpec instance
func NewSwaggerSpec() *SwaggerSpec {
spec := &SwaggerSpec{
Handlers: make(map[string]map[string]bool),
}
if len(spec.Handlers) == 0 {
spec.initHandlerCache()
}
return spec
}
/*SwaggerSpec {{ if .Info }}{{ if .Info.Description }}{{.Info.Description}}{{ else }}the {{ humanize .Name }} API{{ end }}{{ end }} */
type SwaggerSpec struct {
Handlers map[string]map[string]bool
}
func ({{.ReceiverName}} *SwaggerSpec) initHandlerCache() {
{{- if .Operations }}
{{.ReceiverName}}.Handlers = make(map[string]map[string]bool)
// https://swagger.io/specification/v2/ fixed fields: GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH
{{.ReceiverName}}.Handlers["GET"] = make(map[string]bool)
{{.ReceiverName}}.Handlers["PUT"] = make(map[string]bool)
{{.ReceiverName}}.Handlers["POST"] = make(map[string]bool)
{{.ReceiverName}}.Handlers["DELETE"] = make(map[string]bool)
{{.ReceiverName}}.Handlers["OPTIONS"] = make(map[string]bool)
{{.ReceiverName}}.Handlers["HEAD"] = make(map[string]bool)
{{.ReceiverName}}.Handlers["PATCH"] = make(map[string]bool)
{{ range .Operations }}
{{.ReceiverName}}.Handlers[{{ printf "%q" (upper .Method) }}][{{ if eq .Path "/" }}""{{ else }}{{ printf "%q" (cleanPath .Path) }}{{ end }}] = true
{{- end }}
{{- end }}
}

View File

@@ -0,0 +1,3 @@
https://github.com/go-swagger/go-swagger/blob/master/generator/templates/server/parameter.gotmpl
Based on commit in b1ca570cfcd2e281d445e0049506f18fa1c2c2cc
Jun 13, 2020

View File

@@ -0,0 +1,759 @@
{{ define "bindprimitiveparam" }}{{/* an empty test definition to test template repo dependencies resolution - DO NOT CHANGE THIS */}}
{{ end }}
{{ define "bodyvalidator" }}
{{- if .HasModelBodyParams }}
// validate body object{{/* delegate validation to model object */}}
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
}
{{- else if and .HasSimpleBodyParams .HasModelBodyItems }}
{{- if or .Schema.HasSliceValidations .Schema.Items.HasValidations }}
// validate array of body objects
{{- end }}
{{- if .Schema.HasSliceValidations }}
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{ template "sliceparamvalidator" . }}
{{- end }}
{{- if and .Schema.Items.HasValidations (not (or .Schema.Items.IsInterface .Schema.Items.IsStream)) }}
for {{ .IndexVar }} := range body {
{{- if .Schema.Items.IsNullable }}
if body[{{ .IndexVar }}] == nil {
{{- if .Schema.Items.Required }}
res = append(res, errors.Required({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, body[{{ .IndexVar }}]))
break
{{- else }}
continue
{{- end }}
}
{{- end }}
if err := body[{{ .IndexVar }}].Validate(route.Formats); err != nil {
res = append(res, err)
break
}
}
{{- if not .Schema.HasSliceValidations }}
if len(res) == 0 {
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
}
{{- end }}
{{- else }}
// no validation for items in this slice
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end }}
{{- else if and .HasSimpleBodyParams .HasModelBodyMap }}
{{- if and .Schema.HasValidations (not (or .Schema.AdditionalProperties.IsInterface .Schema.AdditionalProperties.IsStream)) }}
// validate map of body objects
for {{ .KeyVar }} := range body {
{{- if .Schema.AdditionalProperties.Required }}
if err := validate.Required({{ if .Child.Path }}{{ .Child.Path }}{{ else }}""{{ end }}, {{ printf "%q" .Child.Location }}, {{ if not .IsAnonymous }}{{ .Schema.GoType }}({{ end }}body[{{ .KeyVar }}]{{ if not .IsAnonymous }}){{ end }}); err != nil {
return err
}
{{- end }}
{{- if and .Schema.AdditionalProperties.IsNullable (not .IsMapNullOverride) }}
if body[{{ .KeyVar }}] == nil {
{{- if .Schema.AdditionalProperties.Required }}
res = append(res, errors.Required({{ .Path }}, {{ printf "%q" .Location }}, body[{{ .KeyVar }}]))
break
{{- else }}
continue
{{- end }}
}
{{- end }}
if val , ok :=body[{{ .KeyVar }}]; ok {
{{- if and .IsNullable (not .IsMapNullOverride) }}
if val != nil {
{{- end }}
if err := val.Validate(route.Formats); err != nil {
res = append(res, err)
break
}
{{- if and .IsNullable (not .IsMapNullOverride) }}
}
{{- end }}
}
}
if len(res) == 0 {
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
}
{{- else }}
// no validation for this map
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end }}
{{- else if .HasSimpleBodyParams }}
{{- if and (not .IsArray) (not .IsMap) .Schema.HasValidations }}
// validate inline body
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}Body(route.Formats); err != nil {
res = append(res, err)
}
{{- else if and (or .IsArray .IsMap) .Schema.HasValidations }}
// validate inline body {{ if .IsArray }}array{{ else }}map{{ end }}
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}Body(route.Formats); err != nil {
res = append(res, err)
}
{{- else }}
// no validation required on inline body
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end}}
{{- else }}
{{- if .IsInterface }}
// no validation on generic interface
{{ .ReceiverName }}.{{ pascalize .Name }} = {{ if and (not .Schema.IsBaseType) .IsNullable }}&{{ end }}body
{{- end }}
{{- end }}
{{- end }}
{{ define "sliceparamvalidator"}}
{{ if or .MinItems .MaxItems }}
{{ camelize .Name }}Size := int64(len({{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{ if and .Child (not (hasPrefix .ValueExpression "o.")) }}{{ .Child.ValueExpression }}C{{ else }}{{ .ValueExpression }}{{ end }}))
{{ end }}
{{ if .MinItems }}
// {{ .ItemsDepth }}minItems: {{ .MinItems }}
if err := validate.MinItems({{ .Path }}, {{ printf "%q" .Location }}, {{ camelize .Name }}Size, {{ .MinItems }}); err != nil {
return err
}
{{ end }}
{{ if .MaxItems }}
// {{ .ItemsDepth }}maxItems: {{ .MaxItems }}
if err := validate.MaxItems({{ .Path }}, {{ printf "%q" .Location }}, {{ camelize .Name }}Size, {{.MaxItems}}); err != nil {
return err
}
{{ end }}
{{ if .UniqueItems }}
// {{ .ItemsDepth }}uniqueItems: true
if err := validate.UniqueItems({{ .Path }}, {{ printf "%q" .Location }}, {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{ if and .Child (not ( hasPrefix .ValueExpression "o." )) }}{{ .Child.ValueExpression }}C{{ else }}{{ .ValueExpression }}{{ end }}); err != nil {
return err
}
{{ end }}
{{ if .Enum }}
// {{ .ItemsDepth }}Enum: {{ .Enum }}
if err := validate.EnumCase(
{{- .Path }}, {{ printf "%q" .Location }},
{{- if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end -}}
{{- if .Child -}}
{{- if not ( hasPrefix .ValueExpression "o." ) -}}
{{- .Child.ValueExpression }}C{{- if .IsCustomFormatter }}.String(){{ end -}}
{{- else -}}
{{- .ValueExpression -}}{{- if .Child.IsCustomFormatter }}.String(){{ end -}}
{{- end -}}
{{- end -}},
{{- printf "%#v" .Enum -}}, {{ if .IsEnumCI }}false{{ else }}true{{ end }}); err != nil {
return err
}
{{ end }}
{{/* BEGIN CUSTOM BY AAA */}}
{{- if .IsArray }}{{/* slice validations */}}
for {{ if .Child.NeedsIndex }}{{ .IndexVar }}{{ else }}_{{ end }}, {{ varname .Child.ValueExpression }}V := range {{ varname .Child.ValueExpression }}C {
{{- if .Child.IsArray }}{{/* recursive resolution of arrays in params */}}
{{- if not .Child.SkipParse }}
// {{ .Child.ItemsDepth }}CollectionFormat: {{ .Child.CollectionFormat }}
{{- end }}
{{ .Child.Child.ValueExpression }}C := {{ if .Child.SkipParse }}{{ varname .Child.ValueExpression }}V{{ else }}swag.SplitByFormat({{ varname .Child.ValueExpression }}V, {{ printf "%q" .Child.CollectionFormat }}){{ end }}
{{- if .Child.HasSliceValidations }}
{{- template "sliceparamvalidator" .Child }}
{{- end }}
{{- else if .Child.IsMap }}{{/* simple map in items (possible with body params)*/}}
{{ .Child.Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{- template "mapparamvalidator" .Child }}
{{- else }}{{/* non-array && non-map type in items */}}
{{- if and .Child.IsNullable (not .IsMapNullOverride) }}
if {{ varname .Child.ValueExpression }}V == nil {
{{- if .Child.Required }}
return errors.Required({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, {{ varname .Child.ValueExpression }}V)
{{- else }}
continue
{{- end }}
}
{{- end }}
{{- template "childvalidator" .Child }}
{{- end }}
}
{{ end }}
{{/* END CUSTOM BY AAA */}}
{{ end }}
{{- define "childvalidator" }}
{{- if .Converter }}
{{- if ne .SwaggerFormat "" }}
// {{ .ItemsDepth }}Format: {{ printf "%q" .SwaggerFormat }}
{{- end }}
{{ varname .ValueExpression }}, err := {{ .Converter }}({{ varname .ValueExpression }}V)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, "{{ .GoType }}", {{ varname .ValueExpression }})
}
{{- else if and .IsCustomFormatter (not .SkipParse) }}{{/* parsing is skipped for simple body items */}}
// {{ .ItemsDepth }}Format: {{ printf "%q" .SwaggerFormat }}
value, err := formats.Parse({{ printf "%q" .SwaggerFormat }},{{ varname .ValueExpression }}V)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, "{{ .GoType }}", value)
}
{{ varname .ValueExpression }} := *(value.(*{{.GoType}}))
{{- else if and .IsComplexObject .HasValidations }}{{/* dedicated to nested body params */}}
{{ varname .ValueExpression }} := {{ varname .ValueExpression }}V
if err := {{ .ValueExpression }}.Validate(formats) ; err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName({{ .Path }})
}
return err
}
{{- else }}
{{ varname .ValueExpression }} := {{ varname .ValueExpression }}V
{{- end }}
{{ template "propertyparamvalidator" . }}
{{- end }}
{{- define "mapparamvalidator" }}
{{- if and .Child.HasValidations (not (or .Child.IsInterface .Child.IsStream)) }}
// validations for map
{{- else }}
// map has no validations: copying all elements
{{- end }}
{{ varname .Child.ValueExpression }}R := make({{ .GoType }},len({{ .Child.ValueExpression }}C))
for {{ .KeyVar }}, {{ .Child.ValueExpression }}V := range {{ .Child.ValueExpression}}C {
{{- if .Child.IsArray }}
{{ .Child.Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{- if .Child.HasSliceValidations }}
{{- template "sliceparamvalidator" .Child }}
{{- end }}
{{- template "sliceparambinder" .Child }}
{{- else if .Child.IsMap }}
{{ .Child.Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{ template "mapparamvalidator" .Child }}
{{- else }}
{{- if and .Child.IsNullable }}
if {{ varname .Child.ValueExpression }}V == nil {
{{- if .Child.Required }}
return errors.Required({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, {{ varname .Child.ValueExpression }}V)
{{- else }}
continue
{{- end }}
}
{{- end }}
{{- template "childvalidator" .Child }}
{{- end }}
{{ varname .Child.ValueExpression }}R[{{.KeyVar}}] = {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap}}IR{{end}}
}
{{- end }}
{{- define "propertyparamvalidator" }}
{{/* BEGIN CUSTOM BY AAA */}}
{{- if .IsNullable }}
// Required: {{ if .Required }}true{{ else }}false{{ end }}
if {{.ValueExpression}} == nil {
{{- if .Required }}
return errors.Required({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, "")
{{- else }}
return nil
{{- end }}
}
{{- end }}
{{- if .IsPrimitive }}
{{ template "validationPrimitive" . }}
{{- end }}
{{- if and .IsCustomFormatter (not .IsStream) (not .IsBase64) }}
if err := validate.FormatOf({{.Path}}, "{{.Location}}", "{{.SwaggerFormat}}", {{ if .IsNullable }}(*{{ end }}{{.ValueExpression}}{{ if .IsNullable }}){{ end }}{{ if .IsCustomFormatter }}.String(){{ end }}, formats); err != nil {
return err
}
{{- end }}
{{- if .IsArray }}{{/* slice validations */}}
{{ varname .Child.ValueExpression }}C := {{ .ValueExpression }}
{{/* END CUSTOM BY AAA */}}
{{ template "sliceparamvalidator" . }}
{{- else if .IsMap }}
{{ .Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{ template "mapparamvalidator" . }}
{{- end }}
{{- end }}
{{ define "sliceparambinder" }}
var {{ varname .Child.ValueExpression }}R {{ .GoType }}
for {{ if .Child.NeedsIndex }}{{ .IndexVar }}{{ else }}_{{ end }}, {{ varname .Child.ValueExpression }}V := range {{ varname .Child.ValueExpression }}C {
{{- if .Child.IsArray }}{{/* recursive resolution of arrays in params */}}
{{- if not .Child.SkipParse }}
// {{ .Child.ItemsDepth }}CollectionFormat: {{ .Child.CollectionFormat }}
{{- end }}
{{ .Child.Child.ValueExpression }}C := {{ if .Child.SkipParse }}{{ varname .Child.ValueExpression }}V{{ else }}swag.SplitByFormat({{ varname .Child.ValueExpression }}V, {{ printf "%q" .Child.CollectionFormat }}){{ end }}
{{- if .Child.HasSliceValidations }}
{{- template "sliceparamvalidator" .Child }}
{{- end }}
if len({{ varname .Child.Child.ValueExpression }}C) > 0 {
{{ template "sliceparambinder" .Child }}
{{ varname .Child.ValueExpression }}R = append({{ varname .Child.ValueExpression }}R, {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap }}IR{{end}})
}
{{- else if .Child.IsMap }}{{/* simple map in items (possible with body params)*/}}
{{ .Child.Child.ValueExpression }}C := {{ varname .Child.ValueExpression }}V
{{- template "mapparamvalidator" .Child }}
{{ varname .Child.ValueExpression }}R = append({{ varname .Child.ValueExpression }}R, {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap }}IR{{end}})
{{- else }}{{/* non-array && non-map type in items */}}
{{- if and .Child.IsNullable (not .IsMapNullOverride) }}
if {{ varname .Child.ValueExpression }}V == nil {
{{- if .Child.Required }}
return errors.Required({{ .Child.Path }}, {{ printf "%q" .Child.Location }}, {{ varname .Child.ValueExpression }}V)
{{- else }}
continue
{{- end }}
}
{{- end }}
{{- template "childvalidator" .Child }}
{{ varname .Child.ValueExpression }}R = append({{ varname .Child.ValueExpression }}R, {{ varname .Child.ValueExpression }}{{ if or .Child.IsArray .Child.IsMap }}IR{{end}})
{{- end }}
}
{{ end }}
{{/* BEGIN CUSTOM BY AAA */}}
{{- define "customsliceparamvalidator" }}
{{- template "sliceparamvalidator" . -}}
for _, {{ varname .Child.ValueExpression }}V := range {{ varname .Child.ValueExpression }}C {
{{- if .Child.IsArray }}{{/* recursive resolution of arrays in params */}}
{{ varname .Child.ValueExpression }}IC := {{ varname .Child.ValueExpression }}V
{{- template "customsliceparamvalidator" .Child }}
{{- else if .Child.IsCustomFormatter }}
// Format: {{ .Child.SwaggerFormat }}
if err := validate.FormatOf({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .SwaggerFormat }}, {{ varname .Child.ValueExpression }}V.String(), formats); err != nil {
res = append(res, errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, {{ varname .Child.ValueExpression }}V))
}
{{- end -}}
}
{{- end }}
{{/* END CUSTOM BY AAA */}}
// Code generated by go-swagger; DO NOT EDIT.
{{ if .Copyright -}}// {{ comment .Copyright -}}{{ end }}
package {{ .Package }}
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/security"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
{{ imports .DefaultImports }}
{{ imports .Imports }}
)
// New{{ pascalize .Name }}Params creates a new {{ pascalize .Name }}Params object
{{ if .Params.HasSomeDefaults -}}
// with the default values initialized.{{ else -}}
// no default values defined in spec.
{{- end }}
func New{{ pascalize .Name }}Params() {{ pascalize .Name }}Params {
{{ if .Params.HasSomeDefaults }}
var (
// initialize parameters with default values
{{ range .Params }}
{{ if .HasDefault -}}
{{ if not .IsFileParam }}{{ varname .ID}}Default =
{{- if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"" )) }}{{ .Zero }}{{/* strfmt type initializer requires UnmarshalText(), e.g. Date, Datetime, Duration */}}
{{- else if and .IsPrimitive .IsCustomFormatter (stringContains .Zero "(\"" ) }}{{.GoType}}({{- printf "%#v" .Default }}){{/* strfmt type initializer takes string */}}
{{- else if and .IsPrimitive (not .IsCustomFormatter) -}}{{.GoType}}({{- printf "%#v" .Default }}){{/* regular go primitive type initializer */}}
{{- else if .IsArray -}}{{- /* Do not initialize from possible defaults in nested arrays */ -}}
{{- if and .Child.IsPrimitive .Child.IsCustomFormatter }}{{ .Zero }}{{/* initialization strategy with UnmarshalText() */}}
{{- else if .Child.IsArray -}}{{ .Zero }}{{/* initialization strategy with json.Unmarshal() */}}
{{- else if and .Child.IsPrimitive (not .Child.IsCustomFormatter) -}}{{.GoType}}{{- arrayInitializer .Default }}{{/* regular go primitive type initializer: simple slice initializer */}}
{{- else }}{{ printf "%#v" .Default }}{{/* all other cases (e.g. schema) [should not occur] */}}
{{- end }}
{{- else }}{{ printf "%#v" .Default }}{{/* case .Schema */}}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
)
{{ range .Params }}{{ if .HasDefault -}}{{- /* carry on UnmarshalText initialization strategy */ -}}
{{ if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"")) }}{{ varname .ID}}Default.UnmarshalText([]byte({{ printf "%q" .Default }}))
{{ else if .IsArray -}}
{{ if or ( and .Child.IsPrimitive .Child.IsCustomFormatter ) .Child.IsArray -}}
if err := json.Unmarshal([]byte(`{{printf "%s" (json .Default)}}`), &{{ varname .ID }}Default); err != nil {
// panics if specification is invalid
msg := fmt.Sprintf("invalid default value for parameter {{ varname .ID }}: %v",err)
panic(msg)
}
{{ end -}}
{{- end }}
{{ end -}}
{{- end }}
{{ end }}
return {{ pascalize .Name }}Params{ {{ range .Params }}{{ if .HasDefault }}
{{ pascalize .ID}}: {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}&{{ end }}{{ varname .ID }}Default,
{{ end }}{{ end }} }
}
// {{ pascalize .Name }}Params contains all the bound params for the {{ humanize .Name }} operation
// typically these are obtained from a http.Request
//
// swagger:parameters {{ .Name }}
type {{ pascalize .Name }}Params struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
{{ range .Params }}/*{{ if .Description }}{{ blockcomment .Description }}{{ end }}{{ if .Required }}
Required: true{{ end }}{{ if .Maximum }}
Maximum: {{ if .ExclusiveMaximum }}< {{ end }}{{ .Maximum }}{{ end }}{{ if .Minimum }}
Minimum: {{ if .ExclusiveMinimum }}> {{ end }}{{ .Minimum }}{{ end }}{{ if .MultipleOf }}
Multiple Of: {{ .MultipleOf }}{{ end }}{{ if .MaxLength }}
Max Length: {{ .MaxLength }}{{ end }}{{ if .MinLength }}
Min Length: {{ .MinLength }}{{ end }}{{ if .Pattern }}
Pattern: {{ .Pattern }}{{ end }}{{ if .MaxItems }}
Max Items: {{ .MaxItems }}{{ end }}{{ if .MinItems }}
Min Items: {{ .MinItems }}{{ end }}{{ if .UniqueItems }}
Unique: true{{ end }}{{ if .Location }}
In: {{ .Location }}{{ end }}{{ if .CollectionFormat }}
Collection Format: {{ .CollectionFormat }}{{ end }}{{ if .HasDefault }}
Default: {{ printf "%#v" .Default }}{{ end }}
*/
{{ if not .Schema }}{{ pascalize .ID }} {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}*{{ end }}{{.GoType}}{{ else }}{{ pascalize .Name }} {{ if and (not .Schema.IsBaseType) .IsNullable (not .Schema.IsStream) }}*{{ end }}{{.GoType}}{{ end }}{{/* BEGIN CUSTOM BY AAA */}}{{ if .IsQueryParam }}`query:"{{ .Name }}"`{{ else if .IsPathParam }}`param:"{{ .Name }}"`{{ else if .IsFormParam }}`form:"{{ .Name }}"`{{ end }}{{/* END CUSTOM BY AAA */}}
{{ end}}
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with New{{ pascalize .Name }}Params() beforehand.
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
{{ .ReceiverName }}.HTTPRequest = r
{{ if .HasQueryParams }}qs := runtime.Values(r.URL.Query()){{ end }}
{{ if .HasFormParams }}if err := r.ParseMultipartForm(32 << 20); err != nil {
if err != http.ErrNotMultipart {
return errors.New(400,"%v",err)
} else if err := r.ParseForm(); err != nil {
return errors.New(400,"%v",err)
}
}{{ if .HasFormValueParams }}
fds := runtime.Values(r.Form)
{{ end }}{{ end }}
{{ range .Params }}
{{ if not .IsArray -}}
{{ if .IsQueryParam }}q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, _ := qs.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{ else if .IsPathParam }}r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, _ := route.Params.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{ else if .IsHeaderParam }}if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r.Header[http.CanonicalHeaderKey({{ .Path }})], true, route.Formats); err != nil {
res = append(res, err)
}
{{ else if .IsFormParam -}}
{{if .IsFileParam }}{{ camelize .Name }}, {{ camelize .Name }}Header, err := r.FormFile({{ .Path }})
if err != nil {{ if .IsNullable }}&& err != http.ErrMissingFile{{ end }}{
res = append(res, errors.New(400, "reading file %q failed: %v", {{ printf "%q" (camelize .Name) }}, err))
{{ if .IsNullable }}} else if err == http.ErrMissingFile {
// no-op for missing but optional file parameter
{{ end }}} else if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}({{ camelize .Name }}, {{ camelize .Name }}Header); err != nil {
{{ if .Required }}// Required: true{{ printf "\n" }}{{end }}res = append(res, err)
} else {
{{ .ReceiverName }}.{{ pascalize .Name }} = &runtime.File{Data: {{ camelize .Name }}, Header: {{ camelize .Name }}Header}
}
{{ else }}fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, _ := fds.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{ end }}{{/* end .FileParam */}}
{{ end }}{{/* end not .Array */}}
{{ else if .IsArray -}}
{{ if .IsQueryParam }}q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, _ := qs.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(q{{ pascalize .Name }}, qhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{ else if .IsPathParam }}r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, _ := route.Params.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r{{ pascalize .Name }}, rhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{ else if .IsHeaderParam }}if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(r.Header[http.CanonicalHeaderKey({{ .Path }})], true, route.Formats); err != nil {
res = append(res, err)
}
{{ else if and .IsFormParam }}fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, _ := fds.GetOK({{ .Path }})
if err := {{ .ReceiverName }}.bind{{ pascalize .ID }}(fd{{ pascalize .Name }}, fdhk{{ pascalize .Name }}, route.Formats); err != nil {
res = append(res, err)
}
{{ end }}{{ end }}
{{ if and .IsBodyParam .Schema -}}
if runtime.HasBody(r) {
{{- if .Schema.IsStream }}
{{ .ReceiverName }}.{{ pascalize .Name }} = r.Body
{{- else }}
defer r.Body.Close()
{{- if and .Schema.IsBaseType .Schema.IsExported }}
body, err := {{ toPackageName .ModelsPackage }}.Unmarshal{{ dropPackage .GoType }}{{ if .IsArray }}Slice{{ end }}(r.Body, route.Consumer)
if err != nil {
{{- if .Required }}
if err == io.EOF {
err = errors.Required({{ .Path }}, {{ printf "%q" .Location }}, "")
}
{{- end }}
res = append(res, err)
{{- else }}
var body {{ .GoType }}
if err := route.Consumer.Consume(r.Body, &body); err != nil {
{{- if .Required }}
if err == io.EOF {
res = append(res, errors.Required({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, ""))
} else {
{{- end }}
res = append(res, errors.NewParseError({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, "", err))
{{- if .Required }}
}
{{- end -}}
{{- end }}
} else {
{{- template "bodyvalidator" . }}
}
{{- end }}
}
{{- if .Required }} else {
res = append(res, errors.Required({{ printf "%q" (camelize .Name) }}, {{ printf "%q" .Location }}, ""))
}
{{- end }}
{{- end }}
{{- end }}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
{{/* BEGIN CUSTOM BY AAA */}}
func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) Validate(formats strfmt.Registry) error {
var res []error
{{ range .Params }}
// {{ .Name }}
// Required: {{ .Required }}
{{ if .IsQueryParam }}// AllowEmptyValue: {{ .AllowEmptyValue }}{{ end -}}{{ if .IsPathParam }}// Parameter is provided by construction from the route{{ end -}}
{{/* file validations */}}
{{- if .IsFileParam }}
{{- if or .MinLength .MaxLength }}
size, _ := {{ .ReceiverName }}.{{ .ID }}.Seek(0, io.SeekEnd)
file.Seek(0, io.SeekStart)
{{- end }}
{{- if .MinLength}}
if size < {{.MinLength}} {
res = append(res, errors.ExceedsMinimum({{ .Path }}, {{ printf "%q" .Location }}, {{ .MinLength }}, false))
}
{{- end }}
{{- if .MaxLength}}
if size > {{.MaxLength}} {
res = append(res, errors.ExceedsMaximum({{ .Path }}, {{ printf "%q" .Location }}, {{ .MaxLength }}, false))
}
{{- end }}
{{/* body validations, currently not used */}}
{{- else if .IsBodyParam }}
// body is validated in endpoint
//if err := {{ .ReceiverName }}.{{ .ID }}.Validate(formats); err != nil {
// res = append(res, err)
//}
{{/* validate for arrays */}}
{{- else if .IsArray }}
{{- if .HasSliceValidations }}
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil {
res = append(res, err)
}
{{- else if or .Child.IsCustomFormatter .Child.IsArray }}
{{ varname .Child.ValueExpression }}C := {{ .ValueExpression }}
{{- template "customsliceparamvalidator" . }}
{{- end }}
{{/* path and query validaionts */}}
{{- else if not .IsBodyParam }}
{{/* if required check if exists */}}
{{- if and (not .IsPathParam) .Required (not .AllowEmptyValue) -}}
if err := validate.Required({{ .Path }}, {{ printf "%q" .Location }}, {{ .ReceiverName }}.{{ .ID }}); err != nil {
res = append(res, err)
}
{{- end }}
{{/* if a validation method exists for the property exists, call it */}}
{{- if .HasValidations }}
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil {
res = append(res, err)
}
{{- else if .IsCustomFormatter }}// Format: {{ .SwaggerFormat }}
if err := validate.FormatOf({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .SwaggerFormat }}, {{ .ReceiverName }}.{{ .ID }}, formats); err != nil {
res = append(res, errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, {{ .ReceiverName }}.{{ .ID }}))
}
{{- end }}
{{/* in case of missing validation a TODO comment is added */}}
{{- else }}
// TODO add case in template
{{- end }}
{{- end }}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
{{/* END CUSTOM BY AAA */}}
{{- $className := (pascalize .Name) }}
{{ range .Params }}
{{- if .IsFileParam }}
// bind{{ pascalize .ID }} binds file parameter {{ .ID }}.
//
// The only supported validations on files are MinLength and MaxLength
func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(file multipart.File, header *multipart.FileHeader) error {
{{- if or .MinLength .MaxLength }}
size, _ := file.Seek(0, io.SeekEnd)
file.Seek(0, io.SeekStart)
{{- end }}
{{- if .MinLength}}
if size < {{.MinLength}} {
return errors.ExceedsMinimum({{ .Path }}, {{ printf "%q" .Location }}, {{ .MinLength }}, false)
}
{{- end }}
{{- if .MaxLength}}
if size > {{.MaxLength}} {
return errors.ExceedsMaximum({{ .Path }}, {{ printf "%q" .Location }}, {{ .MaxLength }}, false)
}
{{- end }}
return nil
}
{{- else if not .IsBodyParam }}
{{- if or .IsPrimitive .IsCustomFormatter }}
// bind{{ pascalize .ID }} binds and validates parameter {{ .ID }} from {{ .Location }}.
func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(rawData []string, hasKey bool, formats strfmt.Registry) error {
{{- if and (not .IsPathParam) .Required }}
if !hasKey {
return errors.Required({{ .Path }}, {{ printf "%q" .Location }}, rawData)
}
{{- end }}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: {{ .Required }}
{{ if .IsQueryParam }}// AllowEmptyValue: {{ .AllowEmptyValue }}{{ end }}{{ if .IsPathParam }}// Parameter is provided by construction from the route{{ end }}
{{ if and (not .IsPathParam) .Required (not .AllowEmptyValue) -}}
if err := validate.RequiredString({{ .Path }}, {{ printf "%q" .Location }}, raw); err != nil {
return err
}
{{ else if and ( not .IsPathParam ) (or (not .Required) .AllowEmptyValue) -}}
if raw == "" { // empty values pass all other validations{{ if .HasDefault }}
// Default values have been previously initialized by New{{ $className }}Params(){{ end }}
return nil
}
{{ end }}
{{ if .Converter }}value, err := {{ .Converter }}(raw)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, raw)
}
{{ .ValueExpression }} = {{ if .IsNullable }}&{{ end }}value
{{ else if .IsCustomFormatter }}// Format: {{ .SwaggerFormat }}
value, err := formats.Parse({{ printf "%q" .SwaggerFormat }}, raw)
if err != nil {
return errors.InvalidType({{ .Path }}, {{ printf "%q" .Location }}, {{ printf "%q" .GoType }}, raw)
}
{{ .ValueExpression }} = {{ if or .IsArray .HasDiscriminator .IsFileParam .IsStream (not .IsNullable) }}*{{ end }}(value.(*{{ .GoType }}))
{{else}}{{ .ValueExpression }} = {{ if .IsNullable }}&{{ end }}raw
{{ end }}
{{if .HasValidations }}if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil {
return err
}
{{ end }}
return nil
}
{{- else if .IsArray }}
// bind{{ pascalize .ID }} binds and validates array parameter {{ .ID }} from {{ .Location }}.
//
// Arrays are parsed according to CollectionFormat: "{{ .CollectionFormat }}" (defaults to "csv" when empty).
func ({{ .ReceiverName }} *{{ $className }}Params) bind{{ pascalize .ID }}(rawData []string, hasKey bool, formats strfmt.Registry) error {
{{if .Required }}if !hasKey {
return errors.Required({{ .Path }}, {{ printf "%q" .Location }}, rawData)
}
{{ end }}
{{ if eq .CollectionFormat "multi" -}}
// CollectionFormat: {{ .CollectionFormat }}
{{ varname .Child.ValueExpression }}C := rawData
{{ else -}}
var qv{{ pascalize .Name }} string
if len(rawData) > 0 {
qv{{ pascalize .Name }} = rawData[len(rawData) - 1]
}
// CollectionFormat: {{ .CollectionFormat }}
{{ varname .Child.ValueExpression }}C := swag.SplitByFormat(qv{{ pascalize .Name }}, {{ printf "%q" .CollectionFormat }}){{ end }}
{{if and .Required (not .AllowEmptyValue) }}
if len({{ varname .Child.ValueExpression }}C) == 0 {
return errors.Required({{ .Path }}, {{ printf "%q" .Location }}, {{ varname .Child.ValueExpression }}C)
}
{{ else }}if len({{ varname .Child.ValueExpression }}C) == 0 { {{ if .HasDefault }}
// Default values have been previously initialized by New{{ $className }}Params(){{ end }}
return nil
} {{- end }}
{{ template "sliceparambinder" . }}
{{ .ValueExpression }} = {{ varname .Child.ValueExpression }}R
{{- if .HasSliceValidations }}
if err := {{ .ReceiverName }}.validate{{ pascalize .ID }}(formats); err != nil {
return err
}
{{- end }}
return nil
}
{{ end }}
{{ if or (and (not .IsArray) .HasValidations) (and .IsArray .HasSliceValidations) }}
// validate{{ pascalize .ID }} carries on validations for parameter {{ .ID }}
func ({{ .ReceiverName }} *{{ $className }}Params) validate{{ pascalize .ID }}(formats strfmt.Registry) error {
{{ template "propertyparamvalidator" . }}
return nil
}
{{ end }}
{{- else if .IsBodyParam }}{{/* validation method for inline body parameters with validations */}}
{{- if and .HasSimpleBodyParams (not .HasModelBodyItems) (not .HasModelBodyMap) }}
{{- if .Schema.HasValidations }}
// validate{{ pascalize .ID }}Body validates an inline body parameter
func ({{ .ReceiverName }} *{{ $className }}Params) validate{{ pascalize .ID }}Body(formats strfmt.Registry) error {
{{- if .IsArray }}
{{- if .HasSliceValidations }}
{{- template "sliceparamvalidator" . }}
{{- end }}
{{- if .Child.HasValidations }}
{{ varname .Child.ValueExpression }}C := {{ .ValueExpression }}
{{ template "sliceparambinder" . }}
{{ .ValueExpression }} = {{ varname .Child.ValueExpression }}R
{{- end }}
{{- else if .IsMap }}
{{ varname .Child.ValueExpression }}C := {{ .ValueExpression }}
{{ template "mapparamvalidator" . }}
{{ .ValueExpression }} = {{ varname .Child.ValueExpression }}R
{{- else }}
{{ template "propertyparamvalidator" . }}
{{- end }}
return nil
}
{{- end }}
{{- end }}
{{- end }}
{{ end }}