feat: refactor api spec to use OpenAPI and add missing spec

Convert the existing swagger2.0 file to a moden OpenAPI file.
Add missing endpoitns and model definitions.

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
This commit is contained in:
Lucas Roesler 2023-06-04 13:16:20 +02:00 committed by Alex Ellis
parent 910b8dae1b
commit 06ade37420
54 changed files with 5306 additions and 631 deletions

View File

@ -5,6 +5,12 @@ NS?=openfaas
build-gateway:
(cd gateway; docker buildx build --platform linux/amd64 -t ${NS}/gateway:latest-dev .)
# generate Go models from the OpenAPI spec using https://github.com/contiamo/openapi-generator-go
generate:
rm gateway/models/model_*.go || true
openapi-generator-go generate models -s api-docs/spec.openapi.yml -o gateway/models --package-name models
# .PHONY: test-ci
# test-ci:
# ./contrib/ci.sh

968
api-docs/spec.openapi.yml Normal file
View File

@ -0,0 +1,968 @@
openapi: 3.0.1
info:
title: OpenFaaS API Gateway
description: OpenFaaS API documentation
license:
name: MIT
version: 0.8.12
contact:
name: OpenFaaS Ltd
url: https://www.openfaas.com/support/
servers:
- url: "http://localhost:8080"
description: Local server
tags:
- name: internal
description: Internal use only
- name: system
description: System endpoints for managing functions and related objects
- name: function
description: Endpoints for invoking functions
paths:
"/healthz":
get:
summary: Healthcheck
operationId: healthcheck
description: Healthcheck for the gateway, indicates if the gateway is running and available
tags:
- internal
responses:
'200':
description: Healthy
'500':
description: Not healthy
"/metrics":
get:
summary: Prometheus metrics
operationId: metrics
description: Prometheus metrics for the gateway
tags:
- internal
responses:
'200':
description: Prometheus metrics in text format
"/system/info":
get:
operationId: GetSystemInfo
description: Get system provider information
summary: Get info such as provider version number and provider orchestrator
tags:
- system
responses:
'200':
description: Info result
content:
application/json:
schema:
"$ref": "#/components/schemas/GatewayInfo"
'500':
description: Internal Server Error
"/system/alert":
post:
operationId: ScaleAlert
description: Scale a function based on an alert
summary: |
Event-sink for AlertManager, for auto-scaling
Internal use for AlertManager, requires valid AlertManager alert
JSON
tags:
- internal
requestBody:
description: Incoming alert
content:
application/json:
schema:
$ref: '#/components/schemas/PrometheusAlert'
required: false
responses:
'200':
description: Alert handled successfully
'500':
description: Internal error with swarm or request JSON invalid
"/system/functions":
get:
operationId: GetFunctions
description: Get a list of deployed functions
summary: 'Get a list of deployed functions with: stats and image digest'
tags:
- system
responses:
'200':
description: List of deployed functions.
content:
application/json:
schema:
type: array
items:
"$ref": "#/components/schemas/FunctionStatus"
put:
operationId: UpdateFunction
description: update a function spec
summary: Update a function.
tags:
- system
requestBody:
description: Function to update
content:
application/json:
schema:
"$ref": "#/components/schemas/FunctionDeployment"
required: true
responses:
'200':
description: Accepted
'400':
description: Bad Request
'404':
description: Not Found
'500':
description: Internal Server Error
post:
operationId: DeployFunction
description: Deploy a new function.
summary: Deploy a new function.
tags:
- system
requestBody:
description: Function to deploy
content:
application/json:
schema:
"$ref": "#/components/schemas/FunctionDeployment"
required: true
responses:
'202':
description: Accepted
'400':
description: Bad Request
'500':
description: Internal Server Error
delete:
operationId: DeleteFunction
description: Remove a deployed function.
summary: Remove a deployed function.
tags:
- system
requestBody:
description: Function to delete
content:
application/json:
schema:
"$ref": "#/components/schemas/DeleteFunctionRequest"
required: true
responses:
'200':
description: OK
'400':
description: Bad Request
'404':
description: Not Found
'500':
description: Internal Server Error
"/system/scale-function/{functionName}":
post:
operationId: ScaleFunction
description: Scale a function
summary: Scale a function to a specific replica count
tags:
- system
parameters:
- name: functionName
in: path
description: Function name
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ScaleServiceRequest'
responses:
'200':
description: Scaling OK
'202':
description: Scaling OK
'404':
description: Function not found
'500':
description: Error scaling function
"/system/function/{functionName}":
get:
operationId: GetFunctionStatus
description: Get the status of a function by name
tags:
- system
parameters:
- name: functionName
in: path
description: Function name
required: true
schema:
type: string
- name: namespace
in: query
description: Namespace of the function
required: false
schema:
type: string
responses:
'200':
description: Function Summary
content:
"*/*":
schema:
"$ref": "#/components/schemas/FunctionStatus"
'404':
description: Not Found
'500':
description: Internal Server Error
"/system/secrets":
get:
operationId: ListSecrets
description: Get a list of secret names and metadata from the provider
summary: Get a list of secret names and metadata from the provider
tags:
- system
responses:
'200':
description: List of submitted secrets.
content:
application/json:
schema:
"$ref": "#/components/schemas/SecretDescription"
put:
operationId: UpdateSecret
description: Update a secret.
summary: Update a secret, the value is replaced.
tags:
- system
requestBody:
description: Secret to update
content:
application/json:
schema:
"$ref": "#/components/schemas/Secret"
required: true
responses:
'200':
description: Ok
'400':
description: Bad Request
'404':
description: Not Found
'405':
description: Method Not Allowed. Secret update is not allowed in faas-swarm.
'500':
description: Internal Server Error
post:
operationId: CreateSecret
description: Create a new secret.
tags:
- system
requestBody:
description: A new secret to create
content:
application/json:
schema:
"$ref": "#/components/schemas/Secret"
required: true
responses:
'201':
description: Created
'400':
description: Bad Request
'500':
description: Internal Server Error
delete:
operationId: DeleteSecret
description: Remove a secret.
tags:
- system
requestBody:
description: Secret to delete
content:
application/json:
schema:
"$ref": "#/components/schemas/SecretDescription"
required: true
responses:
'204':
description: OK
'400':
description: Bad Request
'404':
description: Not Found
'500':
description: Internal Server Error
"/system/logs":
get:
operationId: GetFunctionLogs
description: Get a stream of the logs for a specific function
tags:
- system
parameters:
- name: name
in: query
description: Function name
required: true
schema:
type: string
- name: namespace
in: query
description: Namespace of the function
required: false
schema:
type: string
- name: instance
in: query
description: Instance of the function
required: false
schema:
type: string
- name: tail
in: query
description: Sets the maximum number of log messages to return, <=0 means
unlimited
schema:
type: integer
- name: follow
in: query
description: When true, the request will stream logs until the request timeout
schema:
type: boolean
- name: since
in: query
description: Only return logs after a specific date (RFC3339)
schema:
type: string
format: date-time
responses:
'200':
description: Newline delimited stream of log messages
content:
application/x-ndjson:
schema:
"$ref": "#/components/schemas/LogEntry"
'404':
description: Not Found
'500':
description: Internal Server Error
"/async-function/{functionName}":
post:
operationId: InvokeAsync
description: Invoke a function asynchronously
summary: |
Invoke a function asynchronously in the default OpenFaaS namespace
Any additional path segments and query parameters will be passed to the function as is.
See https://docs.openfaas.com/reference/async/.
tags:
- function
parameters:
- name: functionName
in: path
description: Function name
required: true
schema:
type: string
requestBody:
description: "(Optional) data to pass to function"
content:
"*/*":
schema:
type: string
format: binary
example: '{"hello": "world"}'
required: false
responses:
'202':
description: Request accepted and queued
'404':
description: Not Found
'500':
description: Internal Server Error
"/async-function/{functionName}.{namespace}":
post:
operationId: InvokeAsyncNamespaced
description: Invoke a function asynchronously in an OpenFaaS namespace.
summary: |
Invoke a function asynchronously in an OpenFaaS namespace.
Any additional path segments and query parameters will be passed to the function as is.
See https://docs.openfaas.com/reference/async/.
tags:
- function
parameters:
- name: functionName
in: path
description: Function name
required: true
schema:
type: string
- name: namespace
in: path
description: Namespace of the function
required: true
schema:
type: string
requestBody:
description: "(Optional) data to pass to function"
content:
"*/*":
schema:
type: string
format: binary
example: '{"hello": "world"}'
required: false
responses:
'202':
description: Request accepted and queued
'404':
description: Not Found
'500':
description: Internal Server Error
"/function/{functionName}":
post:
operationId: InvokeFunction
description: Invoke a function in the default OpenFaaS namespace.
summary: |
Synchronously invoke a function defined in te default OpenFaaS namespace.
Any additional path segments and query parameters will be passed to the function as is.
tags:
- function
parameters:
- name: functionName
in: path
description: Function name
required: true
schema:
type: string
requestBody:
description: "(Optional) data to pass to function"
content:
"*/*":
schema:
type: string
format: binary
example: '{"hello": "world"}'
required: false
responses:
'200':
description: Value returned from function
'404':
description: Not Found
'500':
description: Internal server error
"/function/{functionName}.{namespace}":
post:
operationId: InvokeFunctionNamespaced
description: Invoke a function in an OpenFaaS namespace.
summary: |
Synchronously invoke a function defined in the specified namespace.
Any additional path segments and query parameters will be passed to the function as is.
tags:
- function
parameters:
- name: functionName
in: path
description: Function name
required: true
schema:
type: string
- name: namespace
in: path
description: Namespace of the function
required: true
schema:
type: string
requestBody:
description: "(Optional) data to pass to function"
content:
"*/*":
schema:
type: string
format: binary
example: '{"hello": "world"}'
required: false
responses:
'200':
description: Value returned from function
'404':
description: Not Found
'500':
description: Internal server error
components:
securitySchemes:
basicAuth:
type: http
scheme: basic
schemas:
GatewayInfo:
required:
- provider
- version
- arch
type: object
properties:
provider:
nullable: true
allOf:
- $ref: "#/components/schemas/ProviderInfo"
version:
nullable: true
description: version of the gateway
allOf:
- $ref: "#/components/schemas/VersionInfo"
arch:
type: string
description: Platform architecture
example: x86_64
VersionInfo:
type: object
required:
- sha
- release
properties:
commit_message:
type: string
example: Sample Message
sha:
type: string
example: 7108418d9dd6b329ddff40e7393b3166f8160a88
release:
type: string
format: semver
example: 0.8.9
ProviderInfo:
type: object
required:
- provider
- orchestration
- version
properties:
provider:
type: string
description: The orchestration provider / implementation
example: faas-netes
orchestration:
type: string
example: kubernetes
version:
description: The version of the provider
nullable: true
allOf:
- $ref: "#/components/schemas/VersionInfo"
PrometheusAlert:
type: object
description: Prometheus alert produced by AlertManager. This is only a subset of the full alert payload.
required:
- status
- receiver
- alerts
properties:
status:
type: string
description: The status of the alert
example: resolved
receiver:
type: string
description: The name of the receiver
example: webhook
alerts:
type: array
description: The list of alerts
items:
$ref: "#/components/schemas/PrometheusInnerAlert"
example:
{
"receiver": "scale-up",
"status": "firing",
"alerts": [{
"status": "firing",
"labels": {
"alertname": "APIHighInvocationRate",
"code": "200",
"function_name": "func_nodeinfo",
"instance": "gateway:8080",
"job": "gateway",
"monitor": "faas-monitor",
"service": "gateway",
"severity": "major",
"value": "8.998200359928017"
},
"annotations": {
"description": "High invocation total on gateway:8080",
"summary": "High invocation total on gateway:8080"
},
"startsAt": "2017-03-15T15:52:57.805Z",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://4156cb797423:9090/graph?g0.expr=rate%28gateway_function_invocation_total%5B10s%5D%29+%3E+5\u0026g0.tab=0"
}],
"groupLabels": {
"alertname": "APIHighInvocationRate",
"service": "gateway"
},
"commonLabels": {
"alertname": "APIHighInvocationRate",
"code": "200",
"function_name": "func_nodeinfo",
"instance": "gateway:8080",
"job": "gateway",
"monitor": "faas-monitor",
"service": "gateway",
"severity": "major",
"value": "8.998200359928017"
},
"commonAnnotations": {
"description": "High invocation total on gateway:8080",
"summary": "High invocation total on gateway:8080"
},
"externalURL": "http://f054879d97db:9093",
"version": "3",
"groupKey": 18195285354214864953
}
PrometheusInnerAlert:
type: object
description: A single alert produced by Prometheus
required:
- status
- labels
properties:
status:
type: string
description: The status of the alert
example: resolved
labels:
$ref: "#/components/schemas/PrometheusInnerAlertLabel"
PrometheusInnerAlertLabel:
type: object
description: A single label of a Prometheus alert
required:
- alertname
- function_name
properties:
alertname:
type: string
description: The name of the alert
function_name:
type: string
description: The name of the function
example: nodeinfo
FunctionDeployment:
required:
- service
- image
type: object
properties:
service:
type: string
description: Name of deployed function
example: nodeinfo
image:
type: string
description: Docker image in accessible registry
example: functions/nodeinfo:latest
namespace:
type: string
description: Namespace to deploy function to. When omitted, the default namespace
is used, typically this is `openfaas-fn` but is configured by the provider.
example: openfaas-fn
envProcess:
type: string
description: |
Process for watchdog to fork, i.e. the command to start the function process.
This value configures the `fprocess` env variable.
example: node main.js
constraints:
type: array
items:
type: string
description: Constraints are specific to OpenFaaS Provider
example: node.platform.os == linux
envVars:
type: object
additionalProperties:
type: string
description: Overrides to environmental variables
secrets:
type: array
items:
type: string
description: An array of names of secrets that are required to be loaded
from the Docker Swarm.
example: secret-name-1
labels:
type: object
nullable: true
additionalProperties:
type: string
description: A map of labels for making scheduling or routing decisions
example:
foo: bar
annotations:
type: object
nullable: true
additionalProperties:
type: string
description: A map of annotations for management, orchestration, events
and build tasks
example:
topics: awesome-kafka-topic
foo: bar
limits:
nullable: true
allOf:
- $ref: "#/components/schemas/FunctionResources"
requests:
nullable: true
allOf:
- $ref: "#/components/schemas/FunctionResources"
readOnlyRootFilesystem:
type: boolean
description: Make the root filesystem of the function read-only
# DEPRECATED FIELDS, these fields are ignored in all current providers
registryAuth:
type: string
description: |
Deprecated: Private registry base64-encoded basic auth (as present in ~/.docker/config.json)
Use a Kubernetes Secret with registry-auth secret type to provide this value instead.
This value is completely ignored.
example: dXNlcjpwYXNzd29yZA==
deprecated: true
network:
type: string
description: |
Deprecated: Network, usually func_functions for Swarm.
This value is completely ignored.
deprecated: true
example: func_functions
FunctionStatus:
type: object
required:
- name
- image
properties:
name:
type: string
description: The name of the function
example: nodeinfo
image:
type: string
description: The fully qualified docker image name of the function
example: functions/nodeinfo:latest
namespace:
type: string
description: The namespace of the function
example: openfaas-fn
envProcess:
type: string
description: Process for watchdog to fork
example: node main.js
envVars:
type: object
additionalProperties:
type: string
description: environment variables for the function runtime
constraints:
type: array
items:
type: string
description: Constraints are specific to OpenFaaS Provider
example: node.platform.os == linux
secrets:
type: array
items:
type: string
description: An array of names of secrets that are made available to the function
labels:
type: object
nullable: true
additionalProperties:
type: string
description: A map of labels for making scheduling or routing decisions
example:
foo: bar
annotations:
type: object
nullable: true
additionalProperties:
type: string
description: A map of annotations for management, orchestration, events
and build tasks
example:
topics: awesome-kafka-topic
foo: bar
limits:
nullable: true
allOf:
- $ref: "#/components/schemas/FunctionResources"
requests:
nullable: true
allOf:
- $ref: "#/components/schemas/FunctionResources"
readOnlyRootFilesystem:
type: boolean
description: removes write-access from the root filesystem mount-point.
invocationCount:
type: number
description: The amount of invocations for the specified function
format: integer
example: 1337
replicas:
type: number
description: The current minimal ammount of replicas
format: integer
example: 2
availableReplicas:
type: number
description: The current available amount of replicas
format: integer
example: 2
createdAt:
type: string
description: |
is the time read back from the faas backend's
data store for when the function or its container was created.
format: date-time
usage:
nullable: true
allOf:
- $ref: "#/components/schemas/FunctionUsage"
FunctionResources:
type: object
properties:
memory:
type: string
description: The amount of memory that is allocated for the function
example: 128M
cpu:
type: string
description: The amount of cpu that is allocated for the function
example: '0.01'
FunctionUsage:
type: object
properties:
cpu:
type: number
description: |
is the increase in CPU usage since the last measurement
equivalent to Kubernetes' concept of millicores.
format: double
example: 0.01
totalMemoryBytes:
type: number
description: is the total memory usage in bytes.
format: double
example: 1337
DeleteFunctionRequest:
required:
- functionName
type: object
properties:
functionName:
type: string
description: Name of deployed function
example: nodeinfo
ScaleServiceRequest:
required:
- serviceName
- replicas
type: object
properties:
serviceName:
type: string
description: Name of deployed function
example: nodeinfo
replicas:
type: integer
format: int64
minimum: 0
description: Number of replicas to scale to
example: 2
SecretDescription:
required:
- name
type: object
properties:
name:
type: string
description: Name of secret
example: aws-key
namespace:
type: string
description: Namespace of secret
example: openfaas-fn
SecretValues:
type: object
properties:
value:
type: string
description: Value of secret in plain-text
example: changeme
rawValue:
type: string
format: byte
description: |
Value of secret in base64.
This can be used to provide raw binary data when the `value` field is omitted.
example: Y2hhbmdlbWU=
Secret:
type: object
allOf:
- $ref: "#/components/schemas/SecretDescription"
- $ref: "#/components/schemas/SecretValues"
LogEntry:
type: object
required:
- name
- namespace
- instance
- timestamp
- text
properties:
name:
type: string
description: the function name
namespace:
type: string
description: the namespace of the function
instance:
type: string
description: the name/id of the specific function instance
timestamp:
type: string
description: the timestamp of when the log message was recorded
format: date-time
text:
type: string
description: raw log message content

View File

@ -1,628 +0,0 @@
swagger: '2.0'
info:
description: OpenFaaS API documentation
version: 0.8.12
title: OpenFaaS API Gateway
license:
name: MIT
basePath: /
schemes:
- http
paths:
'/system/functions':
get:
summary: 'Get a list of deployed functions with: stats and image digest'
consumes:
- application/json
produces:
- application/json
responses:
'200':
description: List of deployed functions.
schema:
type: array
items:
$ref: '#/definitions/FunctionListEntry'
post:
summary: Deploy a new function.
description: ''
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: Function to deploy
required: true
schema:
$ref: '#/definitions/FunctionDefinition'
responses:
'202':
description: Accepted
'400':
description: Bad Request
'500':
description: Internal Server Error
put:
summary: Update a function.
description: ''
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: Function to update
required: true
schema:
$ref: '#/definitions/FunctionDefinition'
responses:
'200':
description: Accepted
'400':
description: Bad Request
'404':
description: Not Found
'500':
description: Internal Server Error
delete:
summary: Remove a deployed function.
description: ''
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: Function to delete
required: true
schema:
$ref: '#/definitions/DeleteFunctionRequest'
responses:
'200':
description: OK
'400':
description: Bad Request
'404':
description: Not Found
'500':
description: Internal Server Error
'/system/alert':
post:
summary: 'Event-sink for AlertManager, for auto-scaling'
description: 'Internal use for AlertManager, requires valid AlertManager alert JSON'
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: Incoming alert
schema:
type: object
example: |-
{"receiver": "scale-up",
"status": "firing",
"alerts": [{
"status": "firing",
"labels": {
"alertname": "APIHighInvocationRate",
"code": "200",
"function_name": "func_nodeinfo",
"instance": "gateway:8080",
"job": "gateway",
"monitor": "faas-monitor",
"service": "gateway",
"severity": "major",
"value": "8.998200359928017"
},
"annotations": {
"description": "High invocation total on gateway:8080",
"summary": "High invocation total on gateway:8080"
},
"startsAt": "2017-03-15T15:52:57.805Z",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://4156cb797423:9090/graph?g0.expr=rate%28gateway_function_invocation_total%5B10s%5D%29+%3E+5\u0026g0.tab=0"
}],
"groupLabels": {
"alertname": "APIHighInvocationRate",
"service": "gateway"
},
"commonLabels": {
"alertname": "APIHighInvocationRate",
"code": "200",
"function_name": "func_nodeinfo",
"instance": "gateway:8080",
"job": "gateway",
"monitor": "faas-monitor",
"service": "gateway",
"severity": "major",
"value": "8.998200359928017"
},
"commonAnnotations": {
"description": "High invocation total on gateway:8080",
"summary": "High invocation total on gateway:8080"
},
"externalURL": "http://f054879d97db:9093",
"version": "3",
"groupKey": 18195285354214864953
}
responses:
'200':
description: Alert handled successfully
'500':
description: Internal error with swarm or request JSON invalid
'/async-function/{functionName}':
post:
summary: 'Invoke a function asynchronously in OpenFaaS'
description: >-
See https://docs.openfaas.com/reference/async/.
parameters:
- in: path
name: functionName
description: Function name
type: string
required: true
- in: body
name: input
description: (Optional) data to pass to function
schema:
type: string
format: binary
example:
'{"hello": "world"}'
required: false
responses:
'202':
description: Request accepted and queued
'404':
description: Not Found
'500':
description: Internal Server Error
'/function/{functionName}':
post:
summary: Invoke a function defined in OpenFaaS
parameters:
- in: path
name: functionName
description: Function name
type: string
required: true
- in: body
name: input
description: (Optional) data to pass to function
schema:
type: string
format: binary
example:
'{"hello": "world"}'
required: false
responses:
'200':
description: Value returned from function
'404':
description: Not Found
'500':
description: Internal server error
'/system/scale-function/{functionName}':
post:
summary: Scale a function
parameters:
- in: path
name: functionName
description: Function name
type: string
required: true
- in: body
name: input
description: Function to scale plus replica count
schema:
type: string
format: binary
example:
'{"service": "hello-world", "replicas": 10}'
required: false
responses:
'200':
description: Scaling OK
'202':
description: Scaling OK
'404':
description: Function not found
'500':
description: Error scaling function
'/system/function/{functionName}':
get:
summary: Get a summary of an OpenFaaS function
parameters:
- in: path
name: functionName
description: Function name
type: string
required: true
responses:
'200':
description: Function Summary
schema:
$ref: '#/definitions/FunctionListEntry'
'404':
description: Not Found
'500':
description: Internal Server Error
'/system/secrets':
get:
summary: 'Get a list of secret names and metadata from the provider'
consumes:
- application/json
produces:
- application/json
responses:
'200':
description: List of submitted secrets.
schema:
$ref: '#/definitions/SecretName'
post:
summary: Create a new secret.
description: ''
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: A new secret to create
required: true
schema:
$ref: '#/definitions/Secret'
responses:
'201':
description: Created
'400':
description: Bad Request
'500':
description: Internal Server Error
put:
summary: Update a secret.
description: ''
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: Secret to update
required: true
schema:
$ref: '#/definitions/Secret'
responses:
'200':
description: Ok
'400':
description: Bad Request
'404':
description: Not Found
'405':
description: Method Not Allowed. Secret update is not allowed in faas-swarm.
'500':
description: Internal Server Error
delete:
summary: Remove a secret.
description: ''
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
description: Secret to delete
required: true
schema:
$ref: '#/definitions/SecretName'
responses:
'204':
description: OK
'400':
description: Bad Request
'404':
description: Not Found
'500':
description: Internal Server Error
'/system/logs':
get:
summary: Get a stream of the logs for a specific function
produces:
- application/x-ndjson
parameters:
- in: query
name: name
description: Function name
type: string
required: true
- in: query
name: since
description: Only return logs after a specific date (RFC3339)
type: string
required: false
- in: query
name: tail
description: Sets the maximum number of log messages to return, <=0 means unlimited
type: integer
required: false
- in: query
name: follow
description: When true, the request will stream logs until the request timeout
type: boolean
required: false
responses:
'200':
description: Newline delimited stream of log messages
schema:
$ref: '#/definitions/LogEntry'
'404':
description: Not Found
'500':
description: Internal Server Error
'/system/info':
get:
summary: Get info such as provider version number and provider orchestrator
produces:
- application/json
responses:
'200':
description: Info result
schema:
$ref: '#/definitions/Info'
'404':
description: Provider does not support info endpoint
'500':
description: Internal Server Error
'/healthz':
get:
summary: Healthcheck
responses:
'200':
description: Healthy
'500':
description: Not healthy
securityDefinitions:
basicAuth:
type: basic
definitions:
Info:
type: object
properties:
provider:
type: object
description: The OpenFaaS Provider
properties:
provider:
type: string
example: faas-netes
orchestration:
type: string
example: kubernetes
version:
type: object
description: Version of the OpenFaaS Provider
properties:
commit_message:
type: string
example: Sample Message
sha:
type: string
example: 7108418d9dd6b329ddff40e7393b3166f8160a88
release:
type: string
format: semver
example: 0.2.6
version:
type: object
description: Version of the Gateway
properties:
commit_message:
type: string
example: Sample Message
sha:
type: string
example: 7108418d9dd6b329ddff40e7393b3166f8160a88
release:
type: string
format: semver
example: 0.8.9
arch:
type: string
description: "Platform architecture"
example: "x86_64"
required:
- provider
- version
DeleteFunctionRequest:
type: object
properties:
functionName:
type: string
description: Name of deployed function
example: nodeinfo
required:
- functionName
FunctionDefinition:
type: object
properties:
service:
type: string
description: Name of deployed function
example: nodeinfo
network:
type: string
description: Network, usually func_functions for Swarm (deprecated)
example: func_functions
image:
type: string
description: Docker image in accessible registry
example: functions/nodeinfo:latest
envProcess:
type: string
description: Process for watchdog to fork
example: node main.js
envVars:
type: object
additionalProperties:
type: string
description: Overrides to environmental variables
constraints:
type: array
items:
type: string
description: Constraints are specific to OpenFaaS Provider
example: "node.platform.os == linux"
labels:
description: A map of labels for making scheduling or routing decisions
type: object
additionalProperties:
type: string
example:
foo: bar
annotations:
description: A map of annotations for management, orchestration, events and build tasks
type: object
additionalProperties:
type: string
example:
topics: awesome-kafka-topic
foo: bar
secrets:
type: array
items:
type: string
description: An array of names of secrets that are required to be loaded from the Docker Swarm.
example: "secret-name-1"
registryAuth:
type: string
description: >-
Private registry base64-encoded basic auth (as present in
~/.docker/config.json)
example: dXNlcjpwYXNzd29yZA==
limits:
type: object
properties:
memory:
type: string
example: "128M"
cpu:
type: string
example: "0.01"
requests:
type: object
properties:
memory:
type: string
example: "128M"
cpu:
type: string
example: "0.01"
readOnlyRootFilesystem:
type: boolean
description: Make the root filesystem of the function read-only
required:
- service
- image
- envProcess
FunctionListEntry:
type: object
properties:
name:
description: The name of the function
type: string
example: nodeinfo
image:
description: The fully qualified docker image name of the function
type: string
example: functions/nodeinfo:latest
invocationCount:
description: The amount of invocations for the specified function
type: number
format: integer
example: 1337
replicas:
description: The current minimal ammount of replicas
type: number
format: integer
example: 2
availableReplicas:
description: The current available amount of replicas
type: number
format: integer
example: 2
envProcess:
description: Process for watchdog to fork
type: string
example: node main.js
labels:
description: A map of labels for making scheduling or routing decisions
type: object
additionalProperties:
type: string
example:
foo: bar
annotations:
description: A map of annotations for management, orchestration, events and build tasks
type: object
additionalProperties:
type: string
example:
topics: awesome-kafka-topic
foo: bar
required:
- name
- image
- invocationCount
- replicas
- availableReplicas
- envProcess
- labels
Secret:
type: object
properties:
name:
type: string
description: Name of secret
example: aws-key
value:
type: string
description: Value of secret in plain-text
example: changeme
required:
- name
LogEntry:
type: object
properties:
name:
type: string
description: the function name
instance:
type: string
description: the name/id of the specific function instance
timestamp:
type: string
format: date-time
description: the timestamp of when the log message was recorded
text:
type: string
description: raw log message content
SecretName:
type: object
properties:
name:
type: string
description: Name of secret
example: aws-key
externalDocs:
description: More documentation available on Github
url: 'https://github.com/openfaas/faas'

View File

@ -4,6 +4,7 @@ go 1.19
require (
github.com/docker/distribution v2.8.1+incompatible
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/gorilla/mux v1.8.0
github.com/openfaas/faas-provider v0.19.1
github.com/openfaas/nats-queue-worker v0.0.0-20230117214128-3615ccb286cc

View File

@ -41,6 +41,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -78,6 +80,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=

View File

@ -44,7 +44,7 @@ func MakeAlertHandler(service scaling.ServiceQuery, defaultNamespace string) htt
return
}
errors := handleAlerts(&req, service, defaultNamespace)
errors := handleAlerts(req, service, defaultNamespace)
if len(errors) > 0 {
log.Println(errors)
var errorOutput string
@ -60,7 +60,7 @@ func MakeAlertHandler(service scaling.ServiceQuery, defaultNamespace string) htt
}
}
func handleAlerts(req *requests.PrometheusAlert, service scaling.ServiceQuery, defaultNamespace string) []error {
func handleAlerts(req requests.PrometheusAlert, service scaling.ServiceQuery, defaultNamespace string) []error {
var errors []error
for _, alert := range req.Alerts {
if err := scaleService(alert, service, defaultNamespace); err != nil {

View File

@ -0,0 +1,32 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// DeleteFunctionRequest is an object.
type DeleteFunctionRequest struct {
// FunctionName: Name of deployed function
FunctionName string `json:"functionName" mapstructure:"functionName"`
}
// Validate implements basic validation for this model
func (m DeleteFunctionRequest) Validate() error {
return validation.Errors{}.Filter()
}
// GetFunctionName returns the FunctionName property
func (m DeleteFunctionRequest) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *DeleteFunctionRequest) SetFunctionName(val string) {
m.FunctionName = val
}

View File

@ -0,0 +1,218 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// FunctionDeployment is an object.
type FunctionDeployment struct {
// Annotations: A map of annotations for management, orchestration, events and build tasks
Annotations map[string]string `json:"annotations,omitempty" mapstructure:"annotations,omitempty"`
// Constraints:
Constraints []string `json:"constraints,omitempty" mapstructure:"constraints,omitempty"`
// EnvProcess: Process for watchdog to fork, i.e. the command to start the function process.
//
// This value configures the `fprocess` env variable.
EnvProcess string `json:"envProcess,omitempty" mapstructure:"envProcess,omitempty"`
// EnvVars: Overrides to environmental variables
EnvVars map[string]string `json:"envVars,omitempty" mapstructure:"envVars,omitempty"`
// Image: Docker image in accessible registry
Image string `json:"image" mapstructure:"image"`
// Labels: A map of labels for making scheduling or routing decisions
Labels map[string]string `json:"labels,omitempty" mapstructure:"labels,omitempty"`
// Limits:
Limits *FunctionResources `json:"limits,omitempty" mapstructure:"limits,omitempty"`
// Namespace: Namespace to deploy function to. When omitted, the default namespace is used, typically this is `openfaas-fn` but is configured by the provider.
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
// Network: Deprecated: Network, usually func_functions for Swarm.
//
// This value is completely ignored.
Network string `json:"network,omitempty" mapstructure:"network,omitempty"`
// ReadOnlyRootFilesystem: Make the root filesystem of the function read-only
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty" mapstructure:"readOnlyRootFilesystem,omitempty"`
// RegistryAuth: Deprecated: Private registry base64-encoded basic auth (as present in ~/.docker/config.json)
//
// Use a Kubernetes Secret with registry-auth secret type to provide this value instead.
//
// This value is completely ignored.
RegistryAuth string `json:"registryAuth,omitempty" mapstructure:"registryAuth,omitempty"`
// Requests:
Requests *FunctionResources `json:"requests,omitempty" mapstructure:"requests,omitempty"`
// Secrets:
Secrets []string `json:"secrets,omitempty" mapstructure:"secrets,omitempty"`
// Service: Name of deployed function
Service string `json:"service" mapstructure:"service"`
}
// Validate implements basic validation for this model
func (m FunctionDeployment) Validate() error {
return validation.Errors{
"annotations": validation.Validate(
m.Annotations,
),
"constraints": validation.Validate(
m.Constraints,
),
"envVars": validation.Validate(
m.EnvVars,
),
"labels": validation.Validate(
m.Labels,
),
"limits": validation.Validate(
m.Limits,
),
"requests": validation.Validate(
m.Requests,
),
"secrets": validation.Validate(
m.Secrets,
),
}.Filter()
}
// GetAnnotations returns the Annotations property
func (m FunctionDeployment) GetAnnotations() map[string]string {
return m.Annotations
}
// SetAnnotations sets the Annotations property
func (m *FunctionDeployment) SetAnnotations(val map[string]string) {
m.Annotations = val
}
// GetConstraints returns the Constraints property
func (m FunctionDeployment) GetConstraints() []string {
return m.Constraints
}
// SetConstraints sets the Constraints property
func (m *FunctionDeployment) SetConstraints(val []string) {
m.Constraints = val
}
// GetEnvProcess returns the EnvProcess property
func (m FunctionDeployment) GetEnvProcess() string {
return m.EnvProcess
}
// SetEnvProcess sets the EnvProcess property
func (m *FunctionDeployment) SetEnvProcess(val string) {
m.EnvProcess = val
}
// GetEnvVars returns the EnvVars property
func (m FunctionDeployment) GetEnvVars() map[string]string {
return m.EnvVars
}
// SetEnvVars sets the EnvVars property
func (m *FunctionDeployment) SetEnvVars(val map[string]string) {
m.EnvVars = val
}
// GetImage returns the Image property
func (m FunctionDeployment) GetImage() string {
return m.Image
}
// SetImage sets the Image property
func (m *FunctionDeployment) SetImage(val string) {
m.Image = val
}
// GetLabels returns the Labels property
func (m FunctionDeployment) GetLabels() map[string]string {
return m.Labels
}
// SetLabels sets the Labels property
func (m *FunctionDeployment) SetLabels(val map[string]string) {
m.Labels = val
}
// GetLimits returns the Limits property
func (m FunctionDeployment) GetLimits() *FunctionResources {
return m.Limits
}
// SetLimits sets the Limits property
func (m *FunctionDeployment) SetLimits(val *FunctionResources) {
m.Limits = val
}
// GetNamespace returns the Namespace property
func (m FunctionDeployment) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *FunctionDeployment) SetNamespace(val string) {
m.Namespace = val
}
// GetNetwork returns the Network property
func (m FunctionDeployment) GetNetwork() string {
return m.Network
}
// SetNetwork sets the Network property
func (m *FunctionDeployment) SetNetwork(val string) {
m.Network = val
}
// GetReadOnlyRootFilesystem returns the ReadOnlyRootFilesystem property
func (m FunctionDeployment) GetReadOnlyRootFilesystem() bool {
return m.ReadOnlyRootFilesystem
}
// SetReadOnlyRootFilesystem sets the ReadOnlyRootFilesystem property
func (m *FunctionDeployment) SetReadOnlyRootFilesystem(val bool) {
m.ReadOnlyRootFilesystem = val
}
// GetRegistryAuth returns the RegistryAuth property
func (m FunctionDeployment) GetRegistryAuth() string {
return m.RegistryAuth
}
// SetRegistryAuth sets the RegistryAuth property
func (m *FunctionDeployment) SetRegistryAuth(val string) {
m.RegistryAuth = val
}
// GetRequests returns the Requests property
func (m FunctionDeployment) GetRequests() *FunctionResources {
return m.Requests
}
// SetRequests sets the Requests property
func (m *FunctionDeployment) SetRequests(val *FunctionResources) {
m.Requests = val
}
// GetSecrets returns the Secrets property
func (m FunctionDeployment) GetSecrets() []string {
return m.Secrets
}
// SetSecrets sets the Secrets property
func (m *FunctionDeployment) SetSecrets(val []string) {
m.Secrets = val
}
// GetService returns the Service property
func (m FunctionDeployment) GetService() string {
return m.Service
}
// SetService sets the Service property
func (m *FunctionDeployment) SetService(val string) {
m.Service = val
}

View File

@ -0,0 +1,44 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// FunctionResources is an object.
type FunctionResources struct {
// Cpu: The amount of cpu that is allocated for the function
Cpu string `json:"cpu,omitempty" mapstructure:"cpu,omitempty"`
// Memory: The amount of memory that is allocated for the function
Memory string `json:"memory,omitempty" mapstructure:"memory,omitempty"`
}
// Validate implements basic validation for this model
func (m FunctionResources) Validate() error {
return validation.Errors{}.Filter()
}
// GetCpu returns the Cpu property
func (m FunctionResources) GetCpu() string {
return m.Cpu
}
// SetCpu sets the Cpu property
func (m *FunctionResources) SetCpu(val string) {
m.Cpu = val
}
// GetMemory returns the Memory property
func (m FunctionResources) GetMemory() string {
return m.Memory
}
// SetMemory sets the Memory property
func (m *FunctionResources) SetMemory(val string) {
m.Memory = val
}

View File

@ -0,0 +1,251 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
"time"
)
// FunctionStatus is an object.
type FunctionStatus struct {
// Annotations: A map of annotations for management, orchestration, events and build tasks
Annotations map[string]string `json:"annotations,omitempty" mapstructure:"annotations,omitempty"`
// AvailableReplicas: The current available amount of replicas
AvailableReplicas float32 `json:"availableReplicas,omitempty" mapstructure:"availableReplicas,omitempty"`
// Constraints:
Constraints []string `json:"constraints,omitempty" mapstructure:"constraints,omitempty"`
// CreatedAt: is the time read back from the faas backend's
// data store for when the function or its container was created.
CreatedAt time.Time `json:"createdAt,omitempty" mapstructure:"createdAt,omitempty"`
// EnvProcess: Process for watchdog to fork
EnvProcess string `json:"envProcess,omitempty" mapstructure:"envProcess,omitempty"`
// EnvVars: environment variables for the function runtime
EnvVars map[string]string `json:"envVars,omitempty" mapstructure:"envVars,omitempty"`
// Image: The fully qualified docker image name of the function
Image string `json:"image" mapstructure:"image"`
// InvocationCount: The amount of invocations for the specified function
InvocationCount float32 `json:"invocationCount,omitempty" mapstructure:"invocationCount,omitempty"`
// Labels: A map of labels for making scheduling or routing decisions
Labels map[string]string `json:"labels,omitempty" mapstructure:"labels,omitempty"`
// Limits:
Limits *FunctionResources `json:"limits,omitempty" mapstructure:"limits,omitempty"`
// Name: The name of the function
Name string `json:"name" mapstructure:"name"`
// Namespace: The namespace of the function
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
// ReadOnlyRootFilesystem: removes write-access from the root filesystem mount-point.
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty" mapstructure:"readOnlyRootFilesystem,omitempty"`
// Replicas: The current minimal ammount of replicas
Replicas float32 `json:"replicas,omitempty" mapstructure:"replicas,omitempty"`
// Requests:
Requests *FunctionResources `json:"requests,omitempty" mapstructure:"requests,omitempty"`
// Secrets:
Secrets []string `json:"secrets,omitempty" mapstructure:"secrets,omitempty"`
// Usage:
Usage *FunctionUsage `json:"usage,omitempty" mapstructure:"usage,omitempty"`
}
// Validate implements basic validation for this model
func (m FunctionStatus) Validate() error {
return validation.Errors{
"annotations": validation.Validate(
m.Annotations,
),
"constraints": validation.Validate(
m.Constraints,
),
"envVars": validation.Validate(
m.EnvVars,
),
"labels": validation.Validate(
m.Labels,
),
"limits": validation.Validate(
m.Limits,
),
"requests": validation.Validate(
m.Requests,
),
"secrets": validation.Validate(
m.Secrets,
),
"usage": validation.Validate(
m.Usage,
),
}.Filter()
}
// GetAnnotations returns the Annotations property
func (m FunctionStatus) GetAnnotations() map[string]string {
return m.Annotations
}
// SetAnnotations sets the Annotations property
func (m *FunctionStatus) SetAnnotations(val map[string]string) {
m.Annotations = val
}
// GetAvailableReplicas returns the AvailableReplicas property
func (m FunctionStatus) GetAvailableReplicas() float32 {
return m.AvailableReplicas
}
// SetAvailableReplicas sets the AvailableReplicas property
func (m *FunctionStatus) SetAvailableReplicas(val float32) {
m.AvailableReplicas = val
}
// GetConstraints returns the Constraints property
func (m FunctionStatus) GetConstraints() []string {
return m.Constraints
}
// SetConstraints sets the Constraints property
func (m *FunctionStatus) SetConstraints(val []string) {
m.Constraints = val
}
// GetCreatedAt returns the CreatedAt property
func (m FunctionStatus) GetCreatedAt() time.Time {
return m.CreatedAt
}
// SetCreatedAt sets the CreatedAt property
func (m *FunctionStatus) SetCreatedAt(val time.Time) {
m.CreatedAt = val
}
// GetEnvProcess returns the EnvProcess property
func (m FunctionStatus) GetEnvProcess() string {
return m.EnvProcess
}
// SetEnvProcess sets the EnvProcess property
func (m *FunctionStatus) SetEnvProcess(val string) {
m.EnvProcess = val
}
// GetEnvVars returns the EnvVars property
func (m FunctionStatus) GetEnvVars() map[string]string {
return m.EnvVars
}
// SetEnvVars sets the EnvVars property
func (m *FunctionStatus) SetEnvVars(val map[string]string) {
m.EnvVars = val
}
// GetImage returns the Image property
func (m FunctionStatus) GetImage() string {
return m.Image
}
// SetImage sets the Image property
func (m *FunctionStatus) SetImage(val string) {
m.Image = val
}
// GetInvocationCount returns the InvocationCount property
func (m FunctionStatus) GetInvocationCount() float32 {
return m.InvocationCount
}
// SetInvocationCount sets the InvocationCount property
func (m *FunctionStatus) SetInvocationCount(val float32) {
m.InvocationCount = val
}
// GetLabels returns the Labels property
func (m FunctionStatus) GetLabels() map[string]string {
return m.Labels
}
// SetLabels sets the Labels property
func (m *FunctionStatus) SetLabels(val map[string]string) {
m.Labels = val
}
// GetLimits returns the Limits property
func (m FunctionStatus) GetLimits() *FunctionResources {
return m.Limits
}
// SetLimits sets the Limits property
func (m *FunctionStatus) SetLimits(val *FunctionResources) {
m.Limits = val
}
// GetName returns the Name property
func (m FunctionStatus) GetName() string {
return m.Name
}
// SetName sets the Name property
func (m *FunctionStatus) SetName(val string) {
m.Name = val
}
// GetNamespace returns the Namespace property
func (m FunctionStatus) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *FunctionStatus) SetNamespace(val string) {
m.Namespace = val
}
// GetReadOnlyRootFilesystem returns the ReadOnlyRootFilesystem property
func (m FunctionStatus) GetReadOnlyRootFilesystem() bool {
return m.ReadOnlyRootFilesystem
}
// SetReadOnlyRootFilesystem sets the ReadOnlyRootFilesystem property
func (m *FunctionStatus) SetReadOnlyRootFilesystem(val bool) {
m.ReadOnlyRootFilesystem = val
}
// GetReplicas returns the Replicas property
func (m FunctionStatus) GetReplicas() float32 {
return m.Replicas
}
// SetReplicas sets the Replicas property
func (m *FunctionStatus) SetReplicas(val float32) {
m.Replicas = val
}
// GetRequests returns the Requests property
func (m FunctionStatus) GetRequests() *FunctionResources {
return m.Requests
}
// SetRequests sets the Requests property
func (m *FunctionStatus) SetRequests(val *FunctionResources) {
m.Requests = val
}
// GetSecrets returns the Secrets property
func (m FunctionStatus) GetSecrets() []string {
return m.Secrets
}
// SetSecrets sets the Secrets property
func (m *FunctionStatus) SetSecrets(val []string) {
m.Secrets = val
}
// GetUsage returns the Usage property
func (m FunctionStatus) GetUsage() *FunctionUsage {
return m.Usage
}
// SetUsage sets the Usage property
func (m *FunctionStatus) SetUsage(val *FunctionUsage) {
m.Usage = val
}

View File

@ -0,0 +1,45 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// FunctionUsage is an object.
type FunctionUsage struct {
// Cpu: is the increase in CPU usage since the last measurement
// equivalent to Kubernetes' concept of millicores.
Cpu float64 `json:"cpu,omitempty" mapstructure:"cpu,omitempty"`
// TotalMemoryBytes: is the total memory usage in bytes.
TotalMemoryBytes float64 `json:"totalMemoryBytes,omitempty" mapstructure:"totalMemoryBytes,omitempty"`
}
// Validate implements basic validation for this model
func (m FunctionUsage) Validate() error {
return validation.Errors{}.Filter()
}
// GetCpu returns the Cpu property
func (m FunctionUsage) GetCpu() float64 {
return m.Cpu
}
// SetCpu sets the Cpu property
func (m *FunctionUsage) SetCpu(val float64) {
m.Cpu = val
}
// GetTotalMemoryBytes returns the TotalMemoryBytes property
func (m FunctionUsage) GetTotalMemoryBytes() float64 {
return m.TotalMemoryBytes
}
// SetTotalMemoryBytes sets the TotalMemoryBytes property
func (m *FunctionUsage) SetTotalMemoryBytes(val float64) {
m.TotalMemoryBytes = val
}

View File

@ -0,0 +1,63 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// GatewayInfo is an object.
type GatewayInfo struct {
// Arch: Platform architecture
Arch string `json:"arch" mapstructure:"arch"`
// Provider:
Provider *ProviderInfo `json:"provider" mapstructure:"provider"`
// Version: version of the gateway
Version *VersionInfo `json:"version" mapstructure:"version"`
}
// Validate implements basic validation for this model
func (m GatewayInfo) Validate() error {
return validation.Errors{
"provider": validation.Validate(
m.Provider,
),
"version": validation.Validate(
m.Version,
),
}.Filter()
}
// GetArch returns the Arch property
func (m GatewayInfo) GetArch() string {
return m.Arch
}
// SetArch sets the Arch property
func (m *GatewayInfo) SetArch(val string) {
m.Arch = val
}
// GetProvider returns the Provider property
func (m GatewayInfo) GetProvider() *ProviderInfo {
return m.Provider
}
// SetProvider sets the Provider property
func (m *GatewayInfo) SetProvider(val *ProviderInfo) {
m.Provider = val
}
// GetVersion returns the Version property
func (m GatewayInfo) GetVersion() *VersionInfo {
return m.Version
}
// SetVersion sets the Version property
func (m *GatewayInfo) SetVersion(val *VersionInfo) {
m.Version = val
}

View File

@ -0,0 +1,93 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
"time"
)
// GetFunctionLogsQueryParameters is an object.
type GetFunctionLogsQueryParameters struct {
// Name: Function name
Name string `json:"name" mapstructure:"name"`
// Namespace: Namespace of the function
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
// Instance: Instance of the function
Instance string `json:"instance,omitempty" mapstructure:"instance,omitempty"`
// Tail: Sets the maximum number of log messages to return, <=0 means unlimited
Tail int32 `json:"tail,omitempty" mapstructure:"tail,omitempty"`
// Follow: When true, the request will stream logs until the request timeout
Follow bool `json:"follow,omitempty" mapstructure:"follow,omitempty"`
// Since: Only return logs after a specific date (RFC3339)
Since time.Time `json:"since" mapstructure:"since"`
}
// Validate implements basic validation for this model
func (m GetFunctionLogsQueryParameters) Validate() error {
return validation.Errors{}.Filter()
}
// GetName returns the Name property
func (m GetFunctionLogsQueryParameters) GetName() string {
return m.Name
}
// SetName sets the Name property
func (m *GetFunctionLogsQueryParameters) SetName(val string) {
m.Name = val
}
// GetNamespace returns the Namespace property
func (m GetFunctionLogsQueryParameters) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *GetFunctionLogsQueryParameters) SetNamespace(val string) {
m.Namespace = val
}
// GetInstance returns the Instance property
func (m GetFunctionLogsQueryParameters) GetInstance() string {
return m.Instance
}
// SetInstance sets the Instance property
func (m *GetFunctionLogsQueryParameters) SetInstance(val string) {
m.Instance = val
}
// GetTail returns the Tail property
func (m GetFunctionLogsQueryParameters) GetTail() int32 {
return m.Tail
}
// SetTail sets the Tail property
func (m *GetFunctionLogsQueryParameters) SetTail(val int32) {
m.Tail = val
}
// GetFollow returns the Follow property
func (m GetFunctionLogsQueryParameters) GetFollow() bool {
return m.Follow
}
// SetFollow sets the Follow property
func (m *GetFunctionLogsQueryParameters) SetFollow(val bool) {
m.Follow = val
}
// GetSince returns the Since property
func (m GetFunctionLogsQueryParameters) GetSince() time.Time {
return m.Since
}
// SetSince sets the Since property
func (m *GetFunctionLogsQueryParameters) SetSince(val time.Time) {
m.Since = val
}

View File

@ -0,0 +1,44 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// GetFunctionStatusQueryParameters is an object.
type GetFunctionStatusQueryParameters struct {
// FunctionName: Function name
FunctionName string `json:"functionName" mapstructure:"functionName"`
// Namespace: Namespace of the function
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
}
// Validate implements basic validation for this model
func (m GetFunctionStatusQueryParameters) Validate() error {
return validation.Errors{}.Filter()
}
// GetFunctionName returns the FunctionName property
func (m GetFunctionStatusQueryParameters) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *GetFunctionStatusQueryParameters) SetFunctionName(val string) {
m.FunctionName = val
}
// GetNamespace returns the Namespace property
func (m GetFunctionStatusQueryParameters) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *GetFunctionStatusQueryParameters) SetNamespace(val string) {
m.Namespace = val
}

View File

@ -0,0 +1,44 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// InvokeAsyncNamespacedQueryParameters is an object.
type InvokeAsyncNamespacedQueryParameters struct {
// FunctionName: Function name
FunctionName string `json:"functionName" mapstructure:"functionName"`
// Namespace: Namespace of the function
Namespace string `json:"namespace" mapstructure:"namespace"`
}
// Validate implements basic validation for this model
func (m InvokeAsyncNamespacedQueryParameters) Validate() error {
return validation.Errors{}.Filter()
}
// GetFunctionName returns the FunctionName property
func (m InvokeAsyncNamespacedQueryParameters) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *InvokeAsyncNamespacedQueryParameters) SetFunctionName(val string) {
m.FunctionName = val
}
// GetNamespace returns the Namespace property
func (m InvokeAsyncNamespacedQueryParameters) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *InvokeAsyncNamespacedQueryParameters) SetNamespace(val string) {
m.Namespace = val
}

View File

@ -0,0 +1,32 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// InvokeAsyncQueryParameters is an object.
type InvokeAsyncQueryParameters struct {
// FunctionName: Function name
FunctionName string `json:"functionName" mapstructure:"functionName"`
}
// Validate implements basic validation for this model
func (m InvokeAsyncQueryParameters) Validate() error {
return validation.Errors{}.Filter()
}
// GetFunctionName returns the FunctionName property
func (m InvokeAsyncQueryParameters) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *InvokeAsyncQueryParameters) SetFunctionName(val string) {
m.FunctionName = val
}

View File

@ -0,0 +1,44 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// InvokeFunctionNamespacedQueryParameters is an object.
type InvokeFunctionNamespacedQueryParameters struct {
// FunctionName: Function name
FunctionName string `json:"functionName" mapstructure:"functionName"`
// Namespace: Namespace of the function
Namespace string `json:"namespace" mapstructure:"namespace"`
}
// Validate implements basic validation for this model
func (m InvokeFunctionNamespacedQueryParameters) Validate() error {
return validation.Errors{}.Filter()
}
// GetFunctionName returns the FunctionName property
func (m InvokeFunctionNamespacedQueryParameters) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *InvokeFunctionNamespacedQueryParameters) SetFunctionName(val string) {
m.FunctionName = val
}
// GetNamespace returns the Namespace property
func (m InvokeFunctionNamespacedQueryParameters) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *InvokeFunctionNamespacedQueryParameters) SetNamespace(val string) {
m.Namespace = val
}

View File

@ -0,0 +1,32 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// InvokeFunctionQueryParameters is an object.
type InvokeFunctionQueryParameters struct {
// FunctionName: Function name
FunctionName string `json:"functionName" mapstructure:"functionName"`
}
// Validate implements basic validation for this model
func (m InvokeFunctionQueryParameters) Validate() error {
return validation.Errors{}.Filter()
}
// GetFunctionName returns the FunctionName property
func (m InvokeFunctionQueryParameters) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *InvokeFunctionQueryParameters) SetFunctionName(val string) {
m.FunctionName = val
}

View File

@ -0,0 +1,81 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
"time"
)
// LogEntry is an object.
type LogEntry struct {
// Instance: the name/id of the specific function instance
Instance string `json:"instance" mapstructure:"instance"`
// Name: the function name
Name string `json:"name" mapstructure:"name"`
// Namespace: the namespace of the function
Namespace string `json:"namespace" mapstructure:"namespace"`
// Text: raw log message content
Text string `json:"text" mapstructure:"text"`
// Timestamp: the timestamp of when the log message was recorded
Timestamp time.Time `json:"timestamp" mapstructure:"timestamp"`
}
// Validate implements basic validation for this model
func (m LogEntry) Validate() error {
return validation.Errors{}.Filter()
}
// GetInstance returns the Instance property
func (m LogEntry) GetInstance() string {
return m.Instance
}
// SetInstance sets the Instance property
func (m *LogEntry) SetInstance(val string) {
m.Instance = val
}
// GetName returns the Name property
func (m LogEntry) GetName() string {
return m.Name
}
// SetName sets the Name property
func (m *LogEntry) SetName(val string) {
m.Name = val
}
// GetNamespace returns the Namespace property
func (m LogEntry) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *LogEntry) SetNamespace(val string) {
m.Namespace = val
}
// GetText returns the Text property
func (m LogEntry) GetText() string {
return m.Text
}
// SetText sets the Text property
func (m *LogEntry) SetText(val string) {
m.Text = val
}
// GetTimestamp returns the Timestamp property
func (m LogEntry) GetTimestamp() time.Time {
return m.Timestamp
}
// SetTimestamp sets the Timestamp property
func (m *LogEntry) SetTimestamp(val time.Time) {
m.Timestamp = val
}

View File

@ -0,0 +1,60 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// PrometheusAlert is an object. Prometheus alert produced by AlertManager. This is only a subset of the full alert payload.
type PrometheusAlert struct {
// Alerts: The list of alerts
Alerts []PrometheusInnerAlert `json:"alerts" mapstructure:"alerts"`
// Receiver: The name of the receiver
Receiver string `json:"receiver" mapstructure:"receiver"`
// Status: The status of the alert
Status string `json:"status" mapstructure:"status"`
}
// Validate implements basic validation for this model
func (m PrometheusAlert) Validate() error {
return validation.Errors{
"alerts": validation.Validate(
m.Alerts, validation.NotNil,
),
}.Filter()
}
// GetAlerts returns the Alerts property
func (m PrometheusAlert) GetAlerts() []PrometheusInnerAlert {
return m.Alerts
}
// SetAlerts sets the Alerts property
func (m *PrometheusAlert) SetAlerts(val []PrometheusInnerAlert) {
m.Alerts = val
}
// GetReceiver returns the Receiver property
func (m PrometheusAlert) GetReceiver() string {
return m.Receiver
}
// SetReceiver sets the Receiver property
func (m *PrometheusAlert) SetReceiver(val string) {
m.Receiver = val
}
// GetStatus returns the Status property
func (m PrometheusAlert) GetStatus() string {
return m.Status
}
// SetStatus sets the Status property
func (m *PrometheusAlert) SetStatus(val string) {
m.Status = val
}

View File

@ -0,0 +1,48 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// PrometheusInnerAlert is an object. A single alert produced by Prometheus
type PrometheusInnerAlert struct {
// Labels: A single label of a Prometheus alert
Labels PrometheusInnerAlertLabel `json:"labels" mapstructure:"labels"`
// Status: The status of the alert
Status string `json:"status" mapstructure:"status"`
}
// Validate implements basic validation for this model
func (m PrometheusInnerAlert) Validate() error {
return validation.Errors{
"labels": validation.Validate(
m.Labels, validation.NotNil,
),
}.Filter()
}
// GetLabels returns the Labels property
func (m PrometheusInnerAlert) GetLabels() PrometheusInnerAlertLabel {
return m.Labels
}
// SetLabels sets the Labels property
func (m *PrometheusInnerAlert) SetLabels(val PrometheusInnerAlertLabel) {
m.Labels = val
}
// GetStatus returns the Status property
func (m PrometheusInnerAlert) GetStatus() string {
return m.Status
}
// SetStatus sets the Status property
func (m *PrometheusInnerAlert) SetStatus(val string) {
m.Status = val
}

View File

@ -0,0 +1,44 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// PrometheusInnerAlertLabel is an object. A single label of a Prometheus alert
type PrometheusInnerAlertLabel struct {
// Alertname: The name of the alert
Alertname string `json:"alertname" mapstructure:"alertname"`
// FunctionName: The name of the function
FunctionName string `json:"function_name" mapstructure:"function_name"`
}
// Validate implements basic validation for this model
func (m PrometheusInnerAlertLabel) Validate() error {
return validation.Errors{}.Filter()
}
// GetAlertname returns the Alertname property
func (m PrometheusInnerAlertLabel) GetAlertname() string {
return m.Alertname
}
// SetAlertname sets the Alertname property
func (m *PrometheusInnerAlertLabel) SetAlertname(val string) {
m.Alertname = val
}
// GetFunctionName returns the FunctionName property
func (m PrometheusInnerAlertLabel) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *PrometheusInnerAlertLabel) SetFunctionName(val string) {
m.FunctionName = val
}

View File

@ -0,0 +1,60 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// ProviderInfo is an object.
type ProviderInfo struct {
// Orchestration:
Orchestration string `json:"orchestration" mapstructure:"orchestration"`
// Provider: The orchestration provider / implementation
Provider string `json:"provider" mapstructure:"provider"`
// Version: The version of the provider
Version *VersionInfo `json:"version" mapstructure:"version"`
}
// Validate implements basic validation for this model
func (m ProviderInfo) Validate() error {
return validation.Errors{
"version": validation.Validate(
m.Version,
),
}.Filter()
}
// GetOrchestration returns the Orchestration property
func (m ProviderInfo) GetOrchestration() string {
return m.Orchestration
}
// SetOrchestration sets the Orchestration property
func (m *ProviderInfo) SetOrchestration(val string) {
m.Orchestration = val
}
// GetProvider returns the Provider property
func (m ProviderInfo) GetProvider() string {
return m.Provider
}
// SetProvider sets the Provider property
func (m *ProviderInfo) SetProvider(val string) {
m.Provider = val
}
// GetVersion returns the Version property
func (m ProviderInfo) GetVersion() *VersionInfo {
return m.Version
}
// SetVersion sets the Version property
func (m *ProviderInfo) SetVersion(val *VersionInfo) {
m.Version = val
}

View File

@ -0,0 +1,32 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// ScaleFunctionQueryParameters is an object.
type ScaleFunctionQueryParameters struct {
// FunctionName: Function name
FunctionName string `json:"functionName" mapstructure:"functionName"`
}
// Validate implements basic validation for this model
func (m ScaleFunctionQueryParameters) Validate() error {
return validation.Errors{}.Filter()
}
// GetFunctionName returns the FunctionName property
func (m ScaleFunctionQueryParameters) GetFunctionName() string {
return m.FunctionName
}
// SetFunctionName sets the FunctionName property
func (m *ScaleFunctionQueryParameters) SetFunctionName(val string) {
m.FunctionName = val
}

View File

@ -0,0 +1,48 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// ScaleServiceRequest is an object.
type ScaleServiceRequest struct {
// Replicas: Number of replicas to scale to
Replicas int64 `json:"replicas" mapstructure:"replicas"`
// ServiceName: Name of deployed function
ServiceName string `json:"serviceName" mapstructure:"serviceName"`
}
// Validate implements basic validation for this model
func (m ScaleServiceRequest) Validate() error {
return validation.Errors{
"replicas": validation.Validate(
m.Replicas, validation.Min(int64(0)),
),
}.Filter()
}
// GetReplicas returns the Replicas property
func (m ScaleServiceRequest) GetReplicas() int64 {
return m.Replicas
}
// SetReplicas sets the Replicas property
func (m *ScaleServiceRequest) SetReplicas(val int64) {
m.Replicas = val
}
// GetServiceName returns the ServiceName property
func (m ScaleServiceRequest) GetServiceName() string {
return m.ServiceName
}
// SetServiceName sets the ServiceName property
func (m *ScaleServiceRequest) SetServiceName(val string) {
m.ServiceName = val
}

View File

@ -0,0 +1,75 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
)
// Secret is an object.
type Secret struct {
// Name: Name of secret
Name string `json:"name" mapstructure:"name"`
// Namespace: Namespace of secret
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
// RawValue: Value of secret in base64.
//
// This can be used to provide raw binary data when the `value` field is omitted.
RawValue string `json:"rawValue,omitempty" mapstructure:"rawValue,omitempty"`
// Value: Value of secret in plain-text
Value string `json:"value,omitempty" mapstructure:"value,omitempty"`
}
// Validate implements basic validation for this model
func (m Secret) Validate() error {
return validation.Errors{
"rawValue": validation.Validate(
m.RawValue, is.Base64,
),
}.Filter()
}
// GetName returns the Name property
func (m Secret) GetName() string {
return m.Name
}
// SetName sets the Name property
func (m *Secret) SetName(val string) {
m.Name = val
}
// GetNamespace returns the Namespace property
func (m Secret) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *Secret) SetNamespace(val string) {
m.Namespace = val
}
// GetRawValue returns the RawValue property
func (m Secret) GetRawValue() string {
return m.RawValue
}
// SetRawValue sets the RawValue property
func (m *Secret) SetRawValue(val string) {
m.RawValue = val
}
// GetValue returns the Value property
func (m Secret) GetValue() string {
return m.Value
}
// SetValue sets the Value property
func (m *Secret) SetValue(val string) {
m.Value = val
}

View File

@ -0,0 +1,44 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// SecretDescription is an object.
type SecretDescription struct {
// Name: Name of secret
Name string `json:"name" mapstructure:"name"`
// Namespace: Namespace of secret
Namespace string `json:"namespace,omitempty" mapstructure:"namespace,omitempty"`
}
// Validate implements basic validation for this model
func (m SecretDescription) Validate() error {
return validation.Errors{}.Filter()
}
// GetName returns the Name property
func (m SecretDescription) GetName() string {
return m.Name
}
// SetName sets the Name property
func (m *SecretDescription) SetName(val string) {
m.Name = val
}
// GetNamespace returns the Namespace property
func (m SecretDescription) GetNamespace() string {
return m.Namespace
}
// SetNamespace sets the Namespace property
func (m *SecretDescription) SetNamespace(val string) {
m.Namespace = val
}

View File

@ -0,0 +1,51 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
)
// SecretValues is an object.
type SecretValues struct {
// RawValue: Value of secret in base64.
//
// This can be used to provide raw binary data when the `value` field is omitted.
RawValue string `json:"rawValue,omitempty" mapstructure:"rawValue,omitempty"`
// Value: Value of secret in plain-text
Value string `json:"value,omitempty" mapstructure:"value,omitempty"`
}
// Validate implements basic validation for this model
func (m SecretValues) Validate() error {
return validation.Errors{
"rawValue": validation.Validate(
m.RawValue, is.Base64,
),
}.Filter()
}
// GetRawValue returns the RawValue property
func (m SecretValues) GetRawValue() string {
return m.RawValue
}
// SetRawValue sets the RawValue property
func (m *SecretValues) SetRawValue(val string) {
m.RawValue = val
}
// GetValue returns the Value property
func (m SecretValues) GetValue() string {
return m.Value
}
// SetValue sets the Value property
func (m *SecretValues) SetValue(val string) {
m.Value = val
}

View File

@ -0,0 +1,56 @@
// This file is auto-generated, DO NOT EDIT.
//
// Source:
//
// Title: OpenFaaS API Gateway
// Version: 0.8.12
package models
import (
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// VersionInfo is an object.
type VersionInfo struct {
// CommitMessage:
CommitMessage string `json:"commit_message,omitempty" mapstructure:"commit_message,omitempty"`
// Release:
Release string `json:"release" mapstructure:"release"`
// Sha:
Sha string `json:"sha" mapstructure:"sha"`
}
// Validate implements basic validation for this model
func (m VersionInfo) Validate() error {
return validation.Errors{}.Filter()
}
// GetCommitMessage returns the CommitMessage property
func (m VersionInfo) GetCommitMessage() string {
return m.CommitMessage
}
// SetCommitMessage sets the CommitMessage property
func (m *VersionInfo) SetCommitMessage(val string) {
m.CommitMessage = val
}
// GetRelease returns the Release property
func (m VersionInfo) GetRelease() string {
return m.Release
}
// SetRelease sets the Release property
func (m *VersionInfo) SetRelease(val string) {
m.Release = val
}
// GetSha returns the Sha property
func (m VersionInfo) GetSha() string {
return m.Sha
}
// SetSha sets the Sha property
func (m *VersionInfo) SetSha(val string) {
m.Sha = val
}

View File

@ -0,0 +1,28 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Idea Directory
.idea
# 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
.DS_Store

View File

@ -0,0 +1,17 @@
dist: bionic
language: go
go:
- 1.13.x
install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- go get golang.org/x/lint/golint
script:
- test -z "`gofmt -l -d .`"
- test -z "`golint ./...`"
- go test -v -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci

View File

@ -0,0 +1,17 @@
The MIT License (MIT)
Copyright (c) 2016, Qiang Xue
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,703 @@
# ozzo-validation
[![GoDoc](https://godoc.org/github.com/go-ozzo/ozzo-validation?status.png)](http://godoc.org/github.com/go-ozzo/ozzo-validation)
[![Build Status](https://travis-ci.org/go-ozzo/ozzo-validation.svg?branch=master)](https://travis-ci.org/go-ozzo/ozzo-validation)
[![Coverage Status](https://coveralls.io/repos/github/go-ozzo/ozzo-validation/badge.svg?branch=master)](https://coveralls.io/github/go-ozzo/ozzo-validation?branch=master)
[![Go Report](https://goreportcard.com/badge/github.com/go-ozzo/ozzo-validation)](https://goreportcard.com/report/github.com/go-ozzo/ozzo-validation)
## Description
ozzo-validation is a Go package that provides configurable and extensible data validation capabilities.
It has the following features:
* use normal programming constructs rather than error-prone struct tags to specify how data should be validated.
* can validate data of different types, e.g., structs, strings, byte slices, slices, maps, arrays.
* can validate custom data types as long as they implement the `Validatable` interface.
* can validate data types that implement the `sql.Valuer` interface (e.g. `sql.NullString`).
* customizable and well-formatted validation errors.
* error code and message translation support.
* provide a rich set of validation rules right out of box.
* extremely easy to create and use custom validation rules.
For an example on how this library is used in an application, please refer to [go-rest-api](https://github.com/qiangxue/go-rest-api) which is a starter kit for building RESTful APIs in Go.
## Requirements
Go 1.13 or above.
## Getting Started
The ozzo-validation package mainly includes a set of validation rules and two validation methods. You use
validation rules to describe how a value should be considered valid, and you call either `validation.Validate()`
or `validation.ValidateStruct()` to validate the value.
### Installation
Run the following command to install the package:
```
go get github.com/go-ozzo/ozzo-validation
```
### Validating a Simple Value
For a simple value, such as a string or an integer, you may use `validation.Validate()` to validate it. For example,
```go
package main
import (
"fmt"
"github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
)
func main() {
data := "example"
err := validation.Validate(data,
validation.Required, // not empty
validation.Length(5, 100), // length between 5 and 100
is.URL, // is a valid URL
)
fmt.Println(err)
// Output:
// must be a valid URL
}
```
The method `validation.Validate()` will run through the rules in the order that they are listed. If a rule fails
the validation, the method will return the corresponding error and skip the rest of the rules. The method will
return nil if the value passes all validation rules.
### Validating a Struct
For a struct value, you usually want to check if its fields are valid. For example, in a RESTful application, you
may unmarshal the request payload into a struct and then validate the struct fields. If one or multiple fields
are invalid, you may want to get an error describing which fields are invalid. You can use `validation.ValidateStruct()`
to achieve this purpose. A single struct can have rules for multiple fields, and a field can be associated with multiple
rules. For example,
```go
type Address struct {
Street string
City string
State string
Zip string
}
func (a Address) Validate() error {
return validation.ValidateStruct(&a,
// Street cannot be empty, and the length must between 5 and 50
validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
// City cannot be empty, and the length must between 5 and 50
validation.Field(&a.City, validation.Required, validation.Length(5, 50)),
// State cannot be empty, and must be a string consisting of two letters in upper case
validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
// State cannot be empty, and must be a string consisting of five digits
validation.Field(&a.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
)
}
a := Address{
Street: "123",
City: "Unknown",
State: "Virginia",
Zip: "12345",
}
err := a.Validate()
fmt.Println(err)
// Output:
// Street: the length must be between 5 and 50; State: must be in a valid format.
```
Note that when calling `validation.ValidateStruct` to validate a struct, you should pass to the method a pointer
to the struct instead of the struct itself. Similarly, when calling `validation.Field` to specify the rules
for a struct field, you should use a pointer to the struct field.
When the struct validation is performed, the fields are validated in the order they are specified in `ValidateStruct`.
And when each field is validated, its rules are also evaluated in the order they are associated with the field.
If a rule fails, an error is recorded for that field, and the validation will continue with the next field.
### Validating a Map
Sometimes you might need to work with dynamic data stored in maps rather than a typed model. You can use `validation.Map()`
in this situation. A single map can have rules for multiple keys, and a key can be associated with multiple
rules. For example,
```go
c := map[string]interface{}{
"Name": "Qiang Xue",
"Email": "q",
"Address": map[string]interface{}{
"Street": "123",
"City": "Unknown",
"State": "Virginia",
"Zip": "12345",
},
}
err := validation.Validate(c,
validation.Map(
// Name cannot be empty, and the length must be between 5 and 20.
validation.Key("Name", validation.Required, validation.Length(5, 20)),
// Email cannot be empty and should be in a valid email format.
validation.Key("Email", validation.Required, is.Email),
// Validate Address using its own validation rules
validation.Key("Address", validation.Map(
// Street cannot be empty, and the length must between 5 and 50
validation.Key("Street", validation.Required, validation.Length(5, 50)),
// City cannot be empty, and the length must between 5 and 50
validation.Key("City", validation.Required, validation.Length(5, 50)),
// State cannot be empty, and must be a string consisting of two letters in upper case
validation.Key("State", validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
// State cannot be empty, and must be a string consisting of five digits
validation.Key("Zip", validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
)),
),
)
fmt.Println(err)
// Output:
// Address: (State: must be in a valid format; Street: the length must be between 5 and 50.); Email: must be a valid email address.
```
When the map validation is performed, the keys are validated in the order they are specified in `Map`.
And when each key is validated, its rules are also evaluated in the order they are associated with the key.
If a rule fails, an error is recorded for that key, and the validation will continue with the next key.
### Validation Errors
The `validation.ValidateStruct` method returns validation errors found in struct fields in terms of `validation.Errors`
which is a map of fields and their corresponding errors. Nil is returned if validation passes.
By default, `validation.Errors` uses the struct tags named `json` to determine what names should be used to
represent the invalid fields. The type also implements the `json.Marshaler` interface so that it can be marshaled
into a proper JSON object. For example,
```go
type Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
Zip string `json:"zip"`
}
// ...perform validation here...
err := a.Validate()
b, _ := json.Marshal(err)
fmt.Println(string(b))
// Output:
// {"street":"the length must be between 5 and 50","state":"must be in a valid format"}
```
You may modify `validation.ErrorTag` to use a different struct tag name.
If you do not like the magic that `ValidateStruct` determines error keys based on struct field names or corresponding
tag values, you may use the following alternative approach:
```go
c := Customer{
Name: "Qiang Xue",
Email: "q",
Address: Address{
State: "Virginia",
},
}
err := validation.Errors{
"name": validation.Validate(c.Name, validation.Required, validation.Length(5, 20)),
"email": validation.Validate(c.Name, validation.Required, is.Email),
"zip": validation.Validate(c.Address.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
}.Filter()
fmt.Println(err)
// Output:
// email: must be a valid email address; zip: cannot be blank.
```
In the above example, we build a `validation.Errors` by a list of names and the corresponding validation results.
At the end we call `Errors.Filter()` to remove from `Errors` all nils which correspond to those successful validation
results. The method will return nil if `Errors` is empty.
The above approach is very flexible as it allows you to freely build up your validation error structure. You can use
it to validate both struct and non-struct values. Compared to using `ValidateStruct` to validate a struct,
it has the drawback that you have to redundantly specify the error keys while `ValidateStruct` can automatically
find them out.
### Internal Errors
Internal errors are different from validation errors in that internal errors are caused by malfunctioning code (e.g.
a validator making a remote call to validate some data when the remote service is down) rather
than the data being validated. When an internal error happens during data validation, you may allow the user to resubmit
the same data to perform validation again, hoping the program resumes functioning. On the other hand, if data validation
fails due to data error, the user should generally not resubmit the same data again.
To differentiate internal errors from validation errors, when an internal error occurs in a validator, wrap it
into `validation.InternalError` by calling `validation.NewInternalError()`. The user of the validator can then check
if a returned error is an internal error or not. For example,
```go
if err := a.Validate(); err != nil {
if e, ok := err.(validation.InternalError); ok {
// an internal error happened
fmt.Println(e.InternalError())
}
}
```
## Validatable Types
A type is validatable if it implements the `validation.Validatable` interface.
When `validation.Validate` is used to validate a validatable value, if it does not find any error with the
given validation rules, it will further call the value's `Validate()` method.
Similarly, when `validation.ValidateStruct` is validating a struct field whose type is validatable, it will call
the field's `Validate` method after it passes the listed rules.
> Note: When implementing `validation.Validatable`, do not call `validation.Validate()` to validate the value in its
> original type because this will cause infinite loops. For example, if you define a new type `MyString` as `string`
> and implement `validation.Validatable` for `MyString`, within the `Validate()` function you should cast the value
> to `string` first before calling `validation.Validate()` to validate it.
In the following example, the `Address` field of `Customer` is validatable because `Address` implements
`validation.Validatable`. Therefore, when validating a `Customer` struct with `validation.ValidateStruct`,
validation will "dive" into the `Address` field.
```go
type Customer struct {
Name string
Gender string
Email string
Address Address
}
func (c Customer) Validate() error {
return validation.ValidateStruct(&c,
// Name cannot be empty, and the length must be between 5 and 20.
validation.Field(&c.Name, validation.Required, validation.Length(5, 20)),
// Gender is optional, and should be either "Female" or "Male".
validation.Field(&c.Gender, validation.In("Female", "Male")),
// Email cannot be empty and should be in a valid email format.
validation.Field(&c.Email, validation.Required, is.Email),
// Validate Address using its own validation rules
validation.Field(&c.Address),
)
}
c := Customer{
Name: "Qiang Xue",
Email: "q",
Address: Address{
Street: "123 Main Street",
City: "Unknown",
State: "Virginia",
Zip: "12345",
},
}
err := c.Validate()
fmt.Println(err)
// Output:
// Address: (State: must be in a valid format.); Email: must be a valid email address.
```
Sometimes, you may want to skip the invocation of a type's `Validate` method. To do so, simply associate
a `validation.Skip` rule with the value being validated.
### Maps/Slices/Arrays of Validatables
When validating an iterable (map, slice, or array), whose element type implements the `validation.Validatable` interface,
the `validation.Validate` method will call the `Validate` method of every non-nil element.
The validation errors of the elements will be returned as `validation.Errors` which maps the keys of the
invalid elements to their corresponding validation errors. For example,
```go
addresses := []Address{
Address{State: "MD", Zip: "12345"},
Address{Street: "123 Main St", City: "Vienna", State: "VA", Zip: "12345"},
Address{City: "Unknown", State: "NC", Zip: "123"},
}
err := validation.Validate(addresses)
fmt.Println(err)
// Output:
// 0: (City: cannot be blank; Street: cannot be blank.); 2: (Street: cannot be blank; Zip: must be in a valid format.).
```
When using `validation.ValidateStruct` to validate a struct, the above validation procedure also applies to those struct
fields which are map/slices/arrays of validatables.
#### Each
The `Each` validation rule allows you to apply a set of rules to each element of an array, slice, or map.
```go
type Customer struct {
Name string
Emails []string
}
func (c Customer) Validate() error {
return validation.ValidateStruct(&c,
// Name cannot be empty, and the length must be between 5 and 20.
validation.Field(&c.Name, validation.Required, validation.Length(5, 20)),
// Emails are optional, but if given must be valid.
validation.Field(&c.Emails, validation.Each(is.Email)),
)
}
c := Customer{
Name: "Qiang Xue",
Emails: []Email{
"valid@example.com",
"invalid",
},
}
err := c.Validate()
fmt.Println(err)
// Output:
// Emails: (1: must be a valid email address.).
```
### Pointers
When a value being validated is a pointer, most validation rules will validate the actual value pointed to by the pointer.
If the pointer is nil, these rules will skip the validation.
An exception is the `validation.Required` and `validation.NotNil` rules. When a pointer is nil, they
will report a validation error.
### Types Implementing `sql.Valuer`
If a data type implements the `sql.Valuer` interface (e.g. `sql.NullString`), the built-in validation rules will handle
it properly. In particular, when a rule is validating such data, it will call the `Value()` method and validate
the returned value instead.
### Required vs. Not Nil
When validating input values, there are two different scenarios about checking if input values are provided or not.
In the first scenario, an input value is considered missing if it is not entered or it is entered as a zero value
(e.g. an empty string, a zero integer). You can use the `validation.Required` rule in this case.
In the second scenario, an input value is considered missing only if it is not entered. A pointer field is usually
used in this case so that you can detect if a value is entered or not by checking if the pointer is nil or not.
You can use the `validation.NotNil` rule to ensure a value is entered (even if it is a zero value).
### Embedded Structs
The `validation.ValidateStruct` method will properly validate a struct that contains embedded structs. In particular,
the fields of an embedded struct are treated as if they belong directly to the containing struct. For example,
```go
type Employee struct {
Name string
}
type Manager struct {
Employee
Level int
}
m := Manager{}
err := validation.ValidateStruct(&m,
validation.Field(&m.Name, validation.Required),
validation.Field(&m.Level, validation.Required),
)
fmt.Println(err)
// Output:
// Level: cannot be blank; Name: cannot be blank.
```
In the above code, we use `&m.Name` to specify the validation of the `Name` field of the embedded struct `Employee`.
And the validation error uses `Name` as the key for the error associated with the `Name` field as if `Name` a field
directly belonging to `Manager`.
If `Employee` implements the `validation.Validatable` interface, we can also use the following code to validate
`Manager`, which generates the same validation result:
```go
func (e Employee) Validate() error {
return validation.ValidateStruct(&e,
validation.Field(&e.Name, validation.Required),
)
}
err := validation.ValidateStruct(&m,
validation.Field(&m.Employee),
validation.Field(&m.Level, validation.Required),
)
fmt.Println(err)
// Output:
// Level: cannot be blank; Name: cannot be blank.
```
### Conditional Validation
Sometimes, we may want to validate a value only when certain condition is met. For example, we want to ensure the
`unit` struct field is not empty only when the `quantity` field is not empty; or we may want to ensure either `email`
or `phone` is provided. The so-called conditional validation can be achieved with the help of `validation.When`.
The following code implements the aforementioned examples:
```go
result := validation.ValidateStruct(&a,
validation.Field(&a.Unit, validation.When(a.Quantity != "", validation.Required).Else(validation.Nil)),
validation.Field(&a.Phone, validation.When(a.Email == "", validation.Required.Error('Either phone or Email is required.')),
validation.Field(&a.Email, validation.When(a.Phone == "", validation.Required.Error('Either phone or Email is required.')),
)
```
Note that `validation.When` and `validation.When.Else` can take a list of validation rules. These rules will be executed only when the condition is true (When) or false (Else).
The above code can also be simplified using the shortcut `validation.Required.When`:
```go
result := validation.ValidateStruct(&a,
validation.Field(&a.Unit, validation.Required.When(a.Quantity != ""), validation.Nil.When(a.Quantity == "")),
validation.Field(&a.Phone, validation.Required.When(a.Email == "").Error('Either phone or Email is required.')),
validation.Field(&a.Email, validation.Required.When(a.Phone == "").Error('Either phone or Email is required.')),
)
```
### Customizing Error Messages
All built-in validation rules allow you to customize their error messages. To do so, simply call the `Error()` method
of the rules. For example,
```go
data := "2123"
err := validation.Validate(data,
validation.Required.Error("is required"),
validation.Match(regexp.MustCompile("^[0-9]{5}$")).Error("must be a string with five digits"),
)
fmt.Println(err)
// Output:
// must be a string with five digits
```
You can also customize the pre-defined error(s) of a built-in rule such that the customization applies to *every*
instance of the rule. For example, the `Required` rule uses the pre-defined error `ErrRequired`. You can customize it
during the application initialization:
```go
validation.ErrRequired = validation.ErrRequired.SetMessage("the value is required")
```
### Error Code and Message Translation
The errors returned by the validation rules implement the `Error` interface which contains the `Code()` method
to provide the error code information. While the message of a validation error is often customized, the code is immutable.
You can use error code to programmatically check a validation error or look for the translation of the corresponding message.
If you are developing your own validation rules, you can use `validation.NewError()` to create a validation error which
implements the aforementioned `Error` interface.
## Creating Custom Rules
Creating a custom rule is as simple as implementing the `validation.Rule` interface. The interface contains a single
method as shown below, which should validate the value and return the validation error, if any:
```go
// Validate validates a value and returns an error if validation fails.
Validate(value interface{}) error
```
If you already have a function with the same signature as shown above, you can call `validation.By()` to turn
it into a validation rule. For example,
```go
func checkAbc(value interface{}) error {
s, _ := value.(string)
if s != "abc" {
return errors.New("must be abc")
}
return nil
}
err := validation.Validate("xyz", validation.By(checkAbc))
fmt.Println(err)
// Output: must be abc
```
If your validation function takes additional parameters, you can use the following closure trick:
```go
func stringEquals(str string) validation.RuleFunc {
return func(value interface{}) error {
s, _ := value.(string)
if s != str {
return errors.New("unexpected string")
}
return nil
}
}
err := validation.Validate("xyz", validation.By(stringEquals("abc")))
fmt.Println(err)
// Output: unexpected string
```
### Rule Groups
When a combination of several rules are used in multiple places, you may use the following trick to create a
rule group so that your code is more maintainable.
```go
var NameRule = []validation.Rule{
validation.Required,
validation.Length(5, 20),
}
type User struct {
FirstName string
LastName string
}
func (u User) Validate() error {
return validation.ValidateStruct(&u,
validation.Field(&u.FirstName, NameRule...),
validation.Field(&u.LastName, NameRule...),
)
}
```
In the above example, we create a rule group `NameRule` which consists of two validation rules. We then use this rule
group to validate both `FirstName` and `LastName`.
## Context-aware Validation
While most validation rules are self-contained, some rules may depend dynamically on a context. A rule may implement the
`validation.RuleWithContext` interface to support the so-called context-aware validation.
To validate an arbitrary value with a context, call `validation.ValidateWithContext()`. The `context.Conext` parameter
will be passed along to those rules that implement `validation.RuleWithContext`.
To validate the fields of a struct with a context, call `validation.ValidateStructWithContext()`.
You can define a context-aware rule from scratch by implementing both `validation.Rule` and `validation.RuleWithContext`.
You can also use `validation.WithContext()` to turn a function into a context-aware rule. For example,
```go
rule := validation.WithContext(func(ctx context.Context, value interface{}) error {
if ctx.Value("secret") == value.(string) {
return nil
}
return errors.New("value incorrect")
})
value := "xyz"
ctx := context.WithValue(context.Background(), "secret", "example")
err := validation.ValidateWithContext(ctx, value, rule)
fmt.Println(err)
// Output: value incorrect
```
When performing context-aware validation, if a rule does not implement `validation.RuleWithContext`, its
`validation.Rule` will be used instead.
## Built-in Validation Rules
The following rules are provided in the `validation` package:
* `In(...interface{})`: checks if a value can be found in the given list of values.
* `NotIn(...interface{})`: checks if a value is NOT among the given list of values.
* `Length(min, max int)`: checks if the length of a value is within the specified range.
This rule should only be used for validating strings, slices, maps, and arrays.
* `RuneLength(min, max int)`: checks if the length of a string is within the specified range.
This rule is similar as `Length` except that when the value being validated is a string, it checks
its rune length instead of byte length.
* `Min(min interface{})` and `Max(max interface{})`: checks if a value is within the specified range.
These two rules should only be used for validating int, uint, float and time.Time types.
* `Match(*regexp.Regexp)`: checks if a value matches the specified regular expression.
This rule should only be used for strings and byte slices.
* `Date(layout string)`: checks if a string value is a date whose format is specified by the layout.
By calling `Min()` and/or `Max()`, you can check additionally if the date is within the specified range.
* `Required`: checks if a value is not empty (neither nil nor zero).
* `NotNil`: checks if a pointer value is not nil. Non-pointer values are considered valid.
* `NilOrNotEmpty`: checks if a value is a nil pointer or a non-empty value. This differs from `Required` in that it treats a nil pointer as valid.
* `Nil`: checks if a value is a nil pointer.
* `Empty`: checks if a value is empty. nil pointers are considered valid.
* `Skip`: this is a special rule used to indicate that all rules following it should be skipped (including the nested ones).
* `MultipleOf`: checks if the value is a multiple of the specified range.
* `Each(rules ...Rule)`: checks the elements within an iterable (map/slice/array) with other rules.
* `When(condition, rules ...Rule)`: validates with the specified rules only when the condition is true.
* `Else(rules ...Rule)`: must be used with `When(condition, rules ...Rule)`, validates with the specified rules only when the condition is false.
The `is` sub-package provides a list of commonly used string validation rules that can be used to check if the format
of a value satisfies certain requirements. Note that these rules only handle strings and byte slices and if a string
or byte slice is empty, it is considered valid. You may use a `Required` rule to ensure a value is not empty.
Below is the whole list of the rules provided by the `is` package:
* `Email`: validates if a string is an email or not. It also checks if the MX record exists for the email domain.
* `EmailFormat`: validates if a string is an email or not. It does NOT check the existence of the MX record.
* `URL`: validates if a string is a valid URL
* `RequestURL`: validates if a string is a valid request URL
* `RequestURI`: validates if a string is a valid request URI
* `Alpha`: validates if a string contains English letters only (a-zA-Z)
* `Digit`: validates if a string contains digits only (0-9)
* `Alphanumeric`: validates if a string contains English letters and digits only (a-zA-Z0-9)
* `UTFLetter`: validates if a string contains unicode letters only
* `UTFDigit`: validates if a string contains unicode decimal digits only
* `UTFLetterNumeric`: validates if a string contains unicode letters and numbers only
* `UTFNumeric`: validates if a string contains unicode number characters (category N) only
* `LowerCase`: validates if a string contains lower case unicode letters only
* `UpperCase`: validates if a string contains upper case unicode letters only
* `Hexadecimal`: validates if a string is a valid hexadecimal number
* `HexColor`: validates if a string is a valid hexadecimal color code
* `RGBColor`: validates if a string is a valid RGB color in the form of rgb(R, G, B)
* `Int`: validates if a string is a valid integer number
* `Float`: validates if a string is a floating point number
* `UUIDv3`: validates if a string is a valid version 3 UUID
* `UUIDv4`: validates if a string is a valid version 4 UUID
* `UUIDv5`: validates if a string is a valid version 5 UUID
* `UUID`: validates if a string is a valid UUID
* `CreditCard`: validates if a string is a valid credit card number
* `ISBN10`: validates if a string is an ISBN version 10
* `ISBN13`: validates if a string is an ISBN version 13
* `ISBN`: validates if a string is an ISBN (either version 10 or 13)
* `JSON`: validates if a string is in valid JSON format
* `ASCII`: validates if a string contains ASCII characters only
* `PrintableASCII`: validates if a string contains printable ASCII characters only
* `Multibyte`: validates if a string contains multibyte characters
* `FullWidth`: validates if a string contains full-width characters
* `HalfWidth`: validates if a string contains half-width characters
* `VariableWidth`: validates if a string contains both full-width and half-width characters
* `Base64`: validates if a string is encoded in Base64
* `DataURI`: validates if a string is a valid base64-encoded data URI
* `E164`: validates if a string is a valid E164 phone number (+19251232233)
* `CountryCode2`: validates if a string is a valid ISO3166 Alpha 2 country code
* `CountryCode3`: validates if a string is a valid ISO3166 Alpha 3 country code
* `DialString`: validates if a string is a valid dial string that can be passed to Dial()
* `MAC`: validates if a string is a MAC address
* `IP`: validates if a string is a valid IP address (either version 4 or 6)
* `IPv4`: validates if a string is a valid version 4 IP address
* `IPv6`: validates if a string is a valid version 6 IP address
* `Subdomain`: validates if a string is valid subdomain
* `Domain`: validates if a string is valid domain
* `DNSName`: validates if a string is valid DNS name
* `Host`: validates if a string is a valid IP (both v4 and v6) or a valid DNS name
* `Port`: validates if a string is a valid port number
* `MongoID`: validates if a string is a valid Mongo ID
* `Latitude`: validates if a string is a valid latitude
* `Longitude`: validates if a string is a valid longitude
* `SSN`: validates if a string is a social security number (SSN)
* `Semver`: validates if a string is a valid semantic version
## Credits
The `is` sub-package wraps the excellent validators provided by the [govalidator](https://github.com/asaskevich/govalidator) package.

View File

@ -0,0 +1,64 @@
# Upgrade Instructions
## Upgrade from 3.x to 4.x
If you are customizing the error messages of the following built-in validation rules, you may need to
change the parameter placeholders from `%v` to the Go template variable placeholders.
* `Length`: use `{{.min}}` and `{{.max}}` in the error message to refer to the minimum and maximum lengths.
* `Min`: use `{{.threshold}}` in the error message to refer to the lower bound.
* `Max`: use `{{.threshold}}` in the error message to refer to the upper bound
* `MultipleOf`: use `{{.base}}` in the error message to refer to the base of the multiples.
For example,
```go
// 3.x
lengthRule := validation.Length(2,10).Error("the length must be between %v and %v")
// 4.x
lengthRule := validation.Length(2,10).Error("the length must be between {{.min}} and {{.max}}")
```
## Upgrade from 2.x to 3.x
* Instead of using `StructRules` to define struct validation rules, use `ValidateStruct()` to declare and perform
struct validation. The following code snippet shows how to modify your code:
```go
// 2.x usage
err := validation.StructRules{}.
Add("Street", validation.Required, validation.Length(5, 50)).
Add("City", validation.Required, validation.Length(5, 50)).
Add("State", validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))).
Add("Zip", validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))).
Validate(a)
// 3.x usage
err := validation.ValidateStruct(&a,
validation.Field(&a.Street, validation.Required, validation.Length(5, 50)),
validation.Field(&a.City, validation.Required, validation.Length(5, 50)),
validation.Field(&a.State, validation.Required, validation.Match(regexp.MustCompile("^[A-Z]{2}$"))),
validation.Field(&a.Zip, validation.Required, validation.Match(regexp.MustCompile("^[0-9]{5}$"))),
)
```
* Instead of using `Rules` to declare a rule list and use it to validate a value, call `Validate()` with the rules directly.
```go
data := "example"
// 2.x usage
rules := validation.Rules{
validation.Required,
validation.Length(5, 100),
is.URL,
}
err := rules.Validate(data)
// 3.x usage
err := validation.Validate(data,
validation.Required,
validation.Length(5, 100),
is.URL,
)
```
* The default struct tags used for determining error keys is changed from `validation` to `json`. You may modify
`validation.ErrorTag` to change it back.

View File

@ -0,0 +1,67 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
var (
// ErrNil is the error that returns when a value is not nil.
ErrNil = NewError("validation_nil", "must be blank")
// ErrEmpty is the error that returns when a not nil value is not empty.
ErrEmpty = NewError("validation_empty", "must be blank")
)
// Nil is a validation rule that checks if a value is nil.
// It is the opposite of NotNil rule
var Nil = absentRule{condition: true, skipNil: false}
// Empty checks if a not nil value is empty.
var Empty = absentRule{condition: true, skipNil: true}
type absentRule struct {
condition bool
err Error
skipNil bool
}
// Validate checks if the given value is valid or not.
func (r absentRule) Validate(value interface{}) error {
if r.condition {
value, isNil := Indirect(value)
if !r.skipNil && !isNil || r.skipNil && !isNil && !IsEmpty(value) {
if r.err != nil {
return r.err
}
if r.skipNil {
return ErrEmpty
}
return ErrNil
}
}
return nil
}
// When sets the condition that determines if the validation should be performed.
func (r absentRule) When(condition bool) absentRule {
r.condition = condition
return r
}
// Error sets the error message for the rule.
func (r absentRule) Error(message string) absentRule {
if r.err == nil {
if r.skipNil {
r.err = ErrEmpty
} else {
r.err = ErrNil
}
}
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r absentRule) ErrorObject(err Error) absentRule {
r.err = err
return r
}

View File

@ -0,0 +1,102 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"time"
)
var (
// ErrDateInvalid is the error that returns in case of an invalid date.
ErrDateInvalid = NewError("validation_date_invalid", "must be a valid date")
// ErrDateOutOfRange is the error that returns in case of an invalid date.
ErrDateOutOfRange = NewError("validation_date_out_of_range", "the date is out of range")
)
// DateRule is a validation rule that validates date/time string values.
type DateRule struct {
layout string
min, max time.Time
err, rangeErr Error
}
// Date returns a validation rule that checks if a string value is in a format that can be parsed into a date.
// The format of the date should be specified as the layout parameter which accepts the same value as that for time.Parse.
// For example,
// validation.Date(time.ANSIC)
// validation.Date("02 Jan 06 15:04 MST")
// validation.Date("2006-01-02")
//
// By calling Min() and/or Max(), you can let the Date rule to check if a parsed date value is within
// the specified date range.
//
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func Date(layout string) DateRule {
return DateRule{
layout: layout,
err: ErrDateInvalid,
rangeErr: ErrDateOutOfRange,
}
}
// Error sets the error message that is used when the value being validated is not a valid date.
func (r DateRule) Error(message string) DateRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct that is used when the value being validated is not a valid date..
func (r DateRule) ErrorObject(err Error) DateRule {
r.err = err
return r
}
// RangeError sets the error message that is used when the value being validated is out of the specified Min/Max date range.
func (r DateRule) RangeError(message string) DateRule {
r.rangeErr = r.rangeErr.SetMessage(message)
return r
}
// RangeErrorObject sets the error struct that is used when the value being validated is out of the specified Min/Max date range.
func (r DateRule) RangeErrorObject(err Error) DateRule {
r.rangeErr = err
return r
}
// Min sets the minimum date range. A zero value means skipping the minimum range validation.
func (r DateRule) Min(min time.Time) DateRule {
r.min = min
return r
}
// Max sets the maximum date range. A zero value means skipping the maximum range validation.
func (r DateRule) Max(max time.Time) DateRule {
r.max = max
return r
}
// Validate checks if the given value is a valid date.
func (r DateRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
str, err := EnsureString(value)
if err != nil {
return err
}
date, err := time.Parse(r.layout, str)
if err != nil {
return r.err
}
if !r.min.IsZero() && r.min.After(date) || !r.max.IsZero() && date.After(r.max) {
return r.rangeErr
}
return nil
}

View File

@ -0,0 +1,97 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"context"
"errors"
"reflect"
"strconv"
)
// Each returns a validation rule that loops through an iterable (map, slice or array)
// and validates each value inside with the provided rules.
// An empty iterable is considered valid. Use the Required rule to make sure the iterable is not empty.
func Each(rules ...Rule) EachRule {
return EachRule{
rules: rules,
}
}
// EachRule is a validation rule that validates elements in a map/slice/array using the specified list of rules.
type EachRule struct {
rules []Rule
}
// Validate loops through the given iterable and calls the Ozzo Validate() method for each value.
func (r EachRule) Validate(value interface{}) error {
return r.ValidateWithContext(nil, value)
}
// ValidateWithContext loops through the given iterable and calls the Ozzo ValidateWithContext() method for each value.
func (r EachRule) ValidateWithContext(ctx context.Context, value interface{}) error {
errs := Errors{}
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Map:
for _, k := range v.MapKeys() {
val := r.getInterface(v.MapIndex(k))
var err error
if ctx == nil {
err = Validate(val, r.rules...)
} else {
err = ValidateWithContext(ctx, val, r.rules...)
}
if err != nil {
errs[r.getString(k)] = err
}
}
case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ {
val := r.getInterface(v.Index(i))
var err error
if ctx == nil {
err = Validate(val, r.rules...)
} else {
err = ValidateWithContext(ctx, val, r.rules...)
}
if err != nil {
errs[strconv.Itoa(i)] = err
}
}
default:
return errors.New("must be an iterable (map, slice or array)")
}
if len(errs) > 0 {
return errs
}
return nil
}
func (r EachRule) getInterface(value reflect.Value) interface{} {
switch value.Kind() {
case reflect.Ptr, reflect.Interface:
if value.IsNil() {
return nil
}
return value.Elem().Interface()
default:
return value.Interface()
}
}
func (r EachRule) getString(value reflect.Value) string {
switch value.Kind() {
case reflect.Ptr, reflect.Interface:
if value.IsNil() {
return ""
}
return value.Elem().String()
default:
return value.String()
}
}

View File

@ -0,0 +1,180 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"bytes"
"encoding/json"
"fmt"
"sort"
"strings"
"text/template"
)
type (
// Error interface represents an validation error
Error interface {
Error() string
Code() string
Message() string
SetMessage(string) Error
Params() map[string]interface{}
SetParams(map[string]interface{}) Error
}
// ErrorObject is the default validation error
// that implements the Error interface.
ErrorObject struct {
code string
message string
params map[string]interface{}
}
// Errors represents the validation errors that are indexed by struct field names, map or slice keys.
// values are Error or Errors (for map, slice and array error value is Errors).
Errors map[string]error
// InternalError represents an error that should NOT be treated as a validation error.
InternalError interface {
error
InternalError() error
}
internalError struct {
error
}
)
// NewInternalError wraps a given error into an InternalError.
func NewInternalError(err error) InternalError {
return internalError{error: err}
}
// InternalError returns the actual error that it wraps around.
func (e internalError) InternalError() error {
return e.error
}
// SetCode set the error's translation code.
func (e ErrorObject) SetCode(code string) Error {
e.code = code
return e
}
// Code get the error's translation code.
func (e ErrorObject) Code() string {
return e.code
}
// SetParams set the error's params.
func (e ErrorObject) SetParams(params map[string]interface{}) Error {
e.params = params
return e
}
// AddParam add parameter to the error's parameters.
func (e ErrorObject) AddParam(name string, value interface{}) Error {
if e.params == nil {
e.params = make(map[string]interface{})
}
e.params[name] = value
return e
}
// Params returns the error's params.
func (e ErrorObject) Params() map[string]interface{} {
return e.params
}
// SetMessage set the error's message.
func (e ErrorObject) SetMessage(message string) Error {
e.message = message
return e
}
// Message return the error's message.
func (e ErrorObject) Message() string {
return e.message
}
// Error returns the error message.
func (e ErrorObject) Error() string {
if len(e.params) == 0 {
return e.message
}
res := bytes.Buffer{}
_ = template.Must(template.New("err").Parse(e.message)).Execute(&res, e.params)
return res.String()
}
// Error returns the error string of Errors.
func (es Errors) Error() string {
if len(es) == 0 {
return ""
}
keys := make([]string, len(es))
i := 0
for key := range es {
keys[i] = key
i++
}
sort.Strings(keys)
var s strings.Builder
for i, key := range keys {
if i > 0 {
s.WriteString("; ")
}
if errs, ok := es[key].(Errors); ok {
_, _ = fmt.Fprintf(&s, "%v: (%v)", key, errs)
} else {
_, _ = fmt.Fprintf(&s, "%v: %v", key, es[key].Error())
}
}
s.WriteString(".")
return s.String()
}
// MarshalJSON converts the Errors into a valid JSON.
func (es Errors) MarshalJSON() ([]byte, error) {
errs := map[string]interface{}{}
for key, err := range es {
if ms, ok := err.(json.Marshaler); ok {
errs[key] = ms
} else {
errs[key] = err.Error()
}
}
return json.Marshal(errs)
}
// Filter removes all nils from Errors and returns back the updated Errors as an error.
// If the length of Errors becomes 0, it will return nil.
func (es Errors) Filter() error {
for key, value := range es {
if value == nil {
delete(es, key)
}
}
if len(es) == 0 {
return nil
}
return es
}
// NewError create new validation error.
func NewError(code, message string) Error {
return ErrorObject{
code: code,
message: message,
}
}
// Assert that our ErrorObject implements the Error interface.
var _ Error = ErrorObject{}

View File

@ -0,0 +1,57 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"reflect"
)
// ErrInInvalid is the error that returns in case of an invalid value for "in" rule.
var ErrInInvalid = NewError("validation_in_invalid", "must be a valid value")
// In returns a validation rule that checks if a value can be found in the given list of values.
// reflect.DeepEqual() will be used to determine if two values are equal.
// For more details please refer to https://golang.org/pkg/reflect/#DeepEqual
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func In(values ...interface{}) InRule {
return InRule{
elements: values,
err: ErrInInvalid,
}
}
// InRule is a validation rule that validates if a value can be found in the given list of values.
type InRule struct {
elements []interface{}
err Error
}
// Validate checks if the given value is valid or not.
func (r InRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
for _, e := range r.elements {
if reflect.DeepEqual(e, value) {
return nil
}
}
return r.err
}
// Error sets the error message for the rule.
func (r InRule) Error(message string) InRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r InRule) ErrorObject(err Error) InRule {
r.err = err
return r
}

View File

@ -0,0 +1,104 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"unicode/utf8"
)
var (
// ErrLengthTooLong is the error that returns in case of too long length.
ErrLengthTooLong = NewError("validation_length_too_long", "the length must be no more than {{.max}}")
// ErrLengthTooShort is the error that returns in case of too short length.
ErrLengthTooShort = NewError("validation_length_too_short", "the length must be no less than {{.min}}")
// ErrLengthInvalid is the error that returns in case of an invalid length.
ErrLengthInvalid = NewError("validation_length_invalid", "the length must be exactly {{.min}}")
// ErrLengthOutOfRange is the error that returns in case of out of range length.
ErrLengthOutOfRange = NewError("validation_length_out_of_range", "the length must be between {{.min}} and {{.max}}")
// ErrLengthEmptyRequired is the error that returns in case of non-empty value.
ErrLengthEmptyRequired = NewError("validation_length_empty_required", "the value must be empty")
)
// Length returns a validation rule that checks if a value's length is within the specified range.
// If max is 0, it means there is no upper bound for the length.
// This rule should only be used for validating strings, slices, maps, and arrays.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func Length(min, max int) LengthRule {
return LengthRule{min: min, max: max, err: buildLengthRuleError(min, max)}
}
// RuneLength returns a validation rule that checks if a string's rune length is within the specified range.
// If max is 0, it means there is no upper bound for the length.
// This rule should only be used for validating strings, slices, maps, and arrays.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
// If the value being validated is not a string, the rule works the same as Length.
func RuneLength(min, max int) LengthRule {
r := Length(min, max)
r.rune = true
return r
}
// LengthRule is a validation rule that checks if a value's length is within the specified range.
type LengthRule struct {
err Error
min, max int
rune bool
}
// Validate checks if the given value is valid or not.
func (r LengthRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
var (
l int
err error
)
if s, ok := value.(string); ok && r.rune {
l = utf8.RuneCountInString(s)
} else if l, err = LengthOfValue(value); err != nil {
return err
}
if r.min > 0 && l < r.min || r.max > 0 && l > r.max || r.min == 0 && r.max == 0 && l > 0 {
return r.err
}
return nil
}
// Error sets the error message for the rule.
func (r LengthRule) Error(message string) LengthRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r LengthRule) ErrorObject(err Error) LengthRule {
r.err = err
return r
}
func buildLengthRuleError(min, max int) (err Error) {
if min == 0 && max > 0 {
err = ErrLengthTooLong
} else if min > 0 && max == 0 {
err = ErrLengthTooShort
} else if min > 0 && max > 0 {
if min == max {
err = ErrLengthInvalid
} else {
err = ErrLengthOutOfRange
}
} else {
err = ErrLengthEmptyRequired
}
return err.SetParams(map[string]interface{}{"min": min, "max": max})
}

View File

@ -0,0 +1,144 @@
package validation
import (
"context"
"errors"
"fmt"
"reflect"
)
var (
// ErrNotMap is the error that the value being validated is not a map.
ErrNotMap = errors.New("only a map can be validated")
// ErrKeyWrongType is the error returned in case of an incorrect key type.
ErrKeyWrongType = NewError("validation_key_wrong_type", "key not the correct type")
// ErrKeyMissing is the error returned in case of a missing key.
ErrKeyMissing = NewError("validation_key_missing", "required key is missing")
// ErrKeyUnexpected is the error returned in case of an unexpected key.
ErrKeyUnexpected = NewError("validation_key_unexpected", "key not expected")
)
type (
// MapRule represents a rule set associated with a map.
MapRule struct {
keys []*KeyRules
allowExtraKeys bool
}
// KeyRules represents a rule set associated with a map key.
KeyRules struct {
key interface{}
optional bool
rules []Rule
}
)
// Map returns a validation rule that checks the keys and values of a map.
// This rule should only be used for validating maps, or a validation error will be reported.
// Use Key() to specify map keys that need to be validated. Each Key() call specifies a single key which can
// be associated with multiple rules.
// For example,
// validation.Map(
// validation.Key("Name", validation.Required),
// validation.Key("Value", validation.Required, validation.Length(5, 10)),
// )
//
// A nil value is considered valid. Use the Required rule to make sure a map value is present.
func Map(keys ...*KeyRules) MapRule {
return MapRule{keys: keys}
}
// AllowExtraKeys configures the rule to ignore extra keys.
func (r MapRule) AllowExtraKeys() MapRule {
r.allowExtraKeys = true
return r
}
// Validate checks if the given value is valid or not.
func (r MapRule) Validate(m interface{}) error {
return r.ValidateWithContext(nil, m)
}
// ValidateWithContext checks if the given value is valid or not.
func (r MapRule) ValidateWithContext(ctx context.Context, m interface{}) error {
value := reflect.ValueOf(m)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
if value.Kind() != reflect.Map {
// must be a map
return NewInternalError(ErrNotMap)
}
if value.IsNil() {
// treat a nil map as valid
return nil
}
errs := Errors{}
kt := value.Type().Key()
var extraKeys map[interface{}]bool
if !r.allowExtraKeys {
extraKeys = make(map[interface{}]bool, value.Len())
for _, k := range value.MapKeys() {
extraKeys[k.Interface()] = true
}
}
for _, kr := range r.keys {
var err error
if kv := reflect.ValueOf(kr.key); !kt.AssignableTo(kv.Type()) {
err = ErrKeyWrongType
} else if vv := value.MapIndex(kv); !vv.IsValid() {
if !kr.optional {
err = ErrKeyMissing
}
} else if ctx == nil {
err = Validate(vv.Interface(), kr.rules...)
} else {
err = ValidateWithContext(ctx, vv.Interface(), kr.rules...)
}
if err != nil {
if ie, ok := err.(InternalError); ok && ie.InternalError() != nil {
return err
}
errs[getErrorKeyName(kr.key)] = err
}
if !r.allowExtraKeys {
delete(extraKeys, kr.key)
}
}
if !r.allowExtraKeys {
for key := range extraKeys {
errs[getErrorKeyName(key)] = ErrKeyUnexpected
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// Key specifies a map key and the corresponding validation rules.
func Key(key interface{}, rules ...Rule) *KeyRules {
return &KeyRules{
key: key,
rules: rules,
}
}
// Optional configures the rule to ignore the key if missing.
func (r *KeyRules) Optional() *KeyRules {
r.optional = true
return r
}
// getErrorKeyName returns the name that should be used to represent the validation error of a map key.
func getErrorKeyName(key interface{}) string {
return fmt.Sprintf("%v", key)
}

View File

@ -0,0 +1,56 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"regexp"
)
// ErrMatchInvalid is the error that returns in case of invalid format.
var ErrMatchInvalid = NewError("validation_match_invalid", "must be in a valid format")
// Match returns a validation rule that checks if a value matches the specified regular expression.
// This rule should only be used for validating strings and byte slices, or a validation error will be reported.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func Match(re *regexp.Regexp) MatchRule {
return MatchRule{
re: re,
err: ErrMatchInvalid,
}
}
// MatchRule is a validation rule that checks if a value matches the specified regular expression.
type MatchRule struct {
re *regexp.Regexp
err Error
}
// Validate checks if the given value is valid or not.
func (r MatchRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil {
return nil
}
isString, str, isBytes, bs := StringOrBytes(value)
if isString && (str == "" || r.re.MatchString(str)) {
return nil
} else if isBytes && (len(bs) == 0 || r.re.Match(bs)) {
return nil
}
return r.err
}
// Error sets the error message for the rule.
func (r MatchRule) Error(message string) MatchRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r MatchRule) ErrorObject(err Error) MatchRule {
r.err = err
return r
}

View File

@ -0,0 +1,195 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"fmt"
"reflect"
"time"
)
var (
// ErrMinGreaterEqualThanRequired is the error that returns when a value is less than a specified threshold.
ErrMinGreaterEqualThanRequired = NewError("validation_min_greater_equal_than_required", "must be no less than {{.threshold}}")
// ErrMaxLessEqualThanRequired is the error that returns when a value is greater than a specified threshold.
ErrMaxLessEqualThanRequired = NewError("validation_max_less_equal_than_required", "must be no greater than {{.threshold}}")
// ErrMinGreaterThanRequired is the error that returns when a value is less than or equal to a specified threshold.
ErrMinGreaterThanRequired = NewError("validation_min_greater_than_required", "must be greater than {{.threshold}}")
// ErrMaxLessThanRequired is the error that returns when a value is greater than or equal to a specified threshold.
ErrMaxLessThanRequired = NewError("validation_max_less_than_required", "must be less than {{.threshold}}")
)
// ThresholdRule is a validation rule that checks if a value satisfies the specified threshold requirement.
type ThresholdRule struct {
threshold interface{}
operator int
err Error
}
const (
greaterThan = iota
greaterEqualThan
lessThan
lessEqualThan
)
// Min returns a validation rule that checks if a value is greater or equal than the specified value.
// By calling Exclusive, the rule will check if the value is strictly greater than the specified value.
// Note that the value being checked and the threshold value must be of the same type.
// Only int, uint, float and time.Time types are supported.
// An empty value is considered valid. Please use the Required rule to make sure a value is not empty.
func Min(min interface{}) ThresholdRule {
return ThresholdRule{
threshold: min,
operator: greaterEqualThan,
err: ErrMinGreaterEqualThanRequired,
}
}
// Max returns a validation rule that checks if a value is less or equal than the specified value.
// By calling Exclusive, the rule will check if the value is strictly less than the specified value.
// Note that the value being checked and the threshold value must be of the same type.
// Only int, uint, float and time.Time types are supported.
// An empty value is considered valid. Please use the Required rule to make sure a value is not empty.
func Max(max interface{}) ThresholdRule {
return ThresholdRule{
threshold: max,
operator: lessEqualThan,
err: ErrMaxLessEqualThanRequired,
}
}
// Exclusive sets the comparison to exclude the boundary value.
func (r ThresholdRule) Exclusive() ThresholdRule {
if r.operator == greaterEqualThan {
r.operator = greaterThan
r.err = ErrMinGreaterThanRequired
} else if r.operator == lessEqualThan {
r.operator = lessThan
r.err = ErrMaxLessThanRequired
}
return r
}
// Validate checks if the given value is valid or not.
func (r ThresholdRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
rv := reflect.ValueOf(r.threshold)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v, err := ToInt(value)
if err != nil {
return err
}
if r.compareInt(rv.Int(), v) {
return nil
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
v, err := ToUint(value)
if err != nil {
return err
}
if r.compareUint(rv.Uint(), v) {
return nil
}
case reflect.Float32, reflect.Float64:
v, err := ToFloat(value)
if err != nil {
return err
}
if r.compareFloat(rv.Float(), v) {
return nil
}
case reflect.Struct:
t, ok := r.threshold.(time.Time)
if !ok {
return fmt.Errorf("type not supported: %v", rv.Type())
}
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("cannot convert %v to time.Time", reflect.TypeOf(value))
}
if v.IsZero() || r.compareTime(t, v) {
return nil
}
default:
return fmt.Errorf("type not supported: %v", rv.Type())
}
return r.err.SetParams(map[string]interface{}{"threshold": r.threshold})
}
// Error sets the error message for the rule.
func (r ThresholdRule) Error(message string) ThresholdRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r ThresholdRule) ErrorObject(err Error) ThresholdRule {
r.err = err
return r
}
func (r ThresholdRule) compareInt(threshold, value int64) bool {
switch r.operator {
case greaterThan:
return value > threshold
case greaterEqualThan:
return value >= threshold
case lessThan:
return value < threshold
default:
return value <= threshold
}
}
func (r ThresholdRule) compareUint(threshold, value uint64) bool {
switch r.operator {
case greaterThan:
return value > threshold
case greaterEqualThan:
return value >= threshold
case lessThan:
return value < threshold
default:
return value <= threshold
}
}
func (r ThresholdRule) compareFloat(threshold, value float64) bool {
switch r.operator {
case greaterThan:
return value > threshold
case greaterEqualThan:
return value >= threshold
case lessThan:
return value < threshold
default:
return value <= threshold
}
}
func (r ThresholdRule) compareTime(threshold, value time.Time) bool {
switch r.operator {
case greaterThan:
return value.After(threshold)
case greaterEqualThan:
return value.After(threshold) || value.Equal(threshold)
case lessThan:
return value.Before(threshold)
default:
return value.Before(threshold) || value.Equal(threshold)
}
}

View File

@ -0,0 +1,65 @@
package validation
import (
"fmt"
"reflect"
)
// ErrMultipleOfInvalid is the error that returns when a value is not multiple of a base.
var ErrMultipleOfInvalid = NewError("validation_multiple_of_invalid", "must be multiple of {{.base}}")
// MultipleOf returns a validation rule that checks if a value is a multiple of the "base" value.
// Note that "base" should be of integer type.
func MultipleOf(base interface{}) MultipleOfRule {
return MultipleOfRule{
base: base,
err: ErrMultipleOfInvalid,
}
}
// MultipleOfRule is a validation rule that checks if a value is a multiple of the "base" value.
type MultipleOfRule struct {
base interface{}
err Error
}
// Error sets the error message for the rule.
func (r MultipleOfRule) Error(message string) MultipleOfRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r MultipleOfRule) ErrorObject(err Error) MultipleOfRule {
r.err = err
return r
}
// Validate checks if the value is a multiple of the "base" value.
func (r MultipleOfRule) Validate(value interface{}) error {
rv := reflect.ValueOf(r.base)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
v, err := ToInt(value)
if err != nil {
return err
}
if v%rv.Int() == 0 {
return nil
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
v, err := ToUint(value)
if err != nil {
return err
}
if v%rv.Uint() == 0 {
return nil
}
default:
return fmt.Errorf("type not supported: %v", rv.Type())
}
return r.err.SetParams(map[string]interface{}{"base": r.base})
}

View File

@ -0,0 +1,51 @@
// Copyright 2018 Qiang Xue, Google LLC. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
// ErrNotInInvalid is the error that returns when a value is in a list.
var ErrNotInInvalid = NewError("validation_not_in_invalid", "must not be in list")
// NotIn returns a validation rule that checks if a value is absent from the given list of values.
// Note that the value being checked and the possible range of values must be of the same type.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func NotIn(values ...interface{}) NotInRule {
return NotInRule{
elements: values,
err: ErrNotInInvalid,
}
}
// NotInRule is a validation rule that checks if a value is absent from the given list of values.
type NotInRule struct {
elements []interface{}
err Error
}
// Validate checks if the given value is valid or not.
func (r NotInRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
for _, e := range r.elements {
if e == value {
return r.err
}
}
return nil
}
// Error sets the error message for the rule.
func (r NotInRule) Error(message string) NotInRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r NotInRule) ErrorObject(err Error) NotInRule {
r.err = err
return r
}

View File

@ -0,0 +1,44 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
// ErrNotNilRequired is the error that returns when a value is Nil.
var ErrNotNilRequired = NewError("validation_not_nil_required", "is required")
// NotNil is a validation rule that checks if a value is not nil.
// NotNil only handles types including interface, pointer, slice, and map.
// All other types are considered valid.
var NotNil = notNilRule{}
type notNilRule struct {
err Error
}
// Validate checks if the given value is valid or not.
func (r notNilRule) Validate(value interface{}) error {
_, isNil := Indirect(value)
if isNil {
if r.err != nil {
return r.err
}
return ErrNotNilRequired
}
return nil
}
// Error sets the error message for the rule.
func (r notNilRule) Error(message string) notNilRule {
if r.err == nil {
r.err = ErrNotNilRequired
}
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r notNilRule) ErrorObject(err Error) notNilRule {
r.err = err
return r
}

View File

@ -0,0 +1,74 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
var (
// ErrRequired is the error that returns when a value is required.
ErrRequired = NewError("validation_required", "cannot be blank")
// ErrNilOrNotEmpty is the error that returns when a value is not nil and is empty.
ErrNilOrNotEmpty = NewError("validation_nil_or_not_empty_required", "cannot be blank")
)
// Required is a validation rule that checks if a value is not empty.
// A value is considered not empty if
// - integer, float: not zero
// - bool: true
// - string, array, slice, map: len() > 0
// - interface, pointer: not nil and the referenced value is not empty
// - any other types
var Required = RequiredRule{skipNil: false, condition: true}
// NilOrNotEmpty checks if a value is a nil pointer or a value that is not empty.
// NilOrNotEmpty differs from Required in that it treats a nil pointer as valid.
var NilOrNotEmpty = RequiredRule{skipNil: true, condition: true}
// RequiredRule is a rule that checks if a value is not empty.
type RequiredRule struct {
condition bool
skipNil bool
err Error
}
// Validate checks if the given value is valid or not.
func (r RequiredRule) Validate(value interface{}) error {
if r.condition {
value, isNil := Indirect(value)
if r.skipNil && !isNil && IsEmpty(value) || !r.skipNil && (isNil || IsEmpty(value)) {
if r.err != nil {
return r.err
}
if r.skipNil {
return ErrNilOrNotEmpty
}
return ErrRequired
}
}
return nil
}
// When sets the condition that determines if the validation should be performed.
func (r RequiredRule) When(condition bool) RequiredRule {
r.condition = condition
return r
}
// Error sets the error message for the rule.
func (r RequiredRule) Error(message string) RequiredRule {
if r.err == nil {
if r.skipNil {
r.err = ErrNilOrNotEmpty
} else {
r.err = ErrRequired
}
}
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r RequiredRule) ErrorObject(err Error) RequiredRule {
r.err = err
return r
}

View File

@ -0,0 +1,64 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
type stringValidator func(string) bool
// StringRule is a rule that checks a string variable using a specified stringValidator.
type StringRule struct {
validate stringValidator
err Error
}
// NewStringRule creates a new validation rule using a function that takes a string value and returns a bool.
// The rule returned will use the function to check if a given string or byte slice is valid or not.
// An empty value is considered to be valid. Please use the Required rule to make sure a value is not empty.
func NewStringRule(validator stringValidator, message string) StringRule {
return StringRule{
validate: validator,
err: NewError("", message),
}
}
// NewStringRuleWithError creates a new validation rule using a function that takes a string value and returns a bool.
// The rule returned will use the function to check if a given string or byte slice is valid or not.
// An empty value is considered to be valid. Please use the Required rule to make sure a value is not empty.
func NewStringRuleWithError(validator stringValidator, err Error) StringRule {
return StringRule{
validate: validator,
err: err,
}
}
// Error sets the error message for the rule.
func (r StringRule) Error(message string) StringRule {
r.err = r.err.SetMessage(message)
return r
}
// ErrorObject sets the error struct for the rule.
func (r StringRule) ErrorObject(err Error) StringRule {
r.err = err
return r
}
// Validate checks if the given value is valid or not.
func (r StringRule) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}
str, err := EnsureString(value)
if err != nil {
return err
}
if r.validate(str) {
return nil
}
return r.err
}

View File

@ -0,0 +1,169 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"context"
"errors"
"fmt"
"reflect"
"strings"
)
var (
// ErrStructPointer is the error that a struct being validated is not specified as a pointer.
ErrStructPointer = errors.New("only a pointer to a struct can be validated")
)
type (
// ErrFieldPointer is the error that a field is not specified as a pointer.
ErrFieldPointer int
// ErrFieldNotFound is the error that a field cannot be found in the struct.
ErrFieldNotFound int
// FieldRules represents a rule set associated with a struct field.
FieldRules struct {
fieldPtr interface{}
rules []Rule
}
)
// Error returns the error string of ErrFieldPointer.
func (e ErrFieldPointer) Error() string {
return fmt.Sprintf("field #%v must be specified as a pointer", int(e))
}
// Error returns the error string of ErrFieldNotFound.
func (e ErrFieldNotFound) Error() string {
return fmt.Sprintf("field #%v cannot be found in the struct", int(e))
}
// ValidateStruct validates a struct by checking the specified struct fields against the corresponding validation rules.
// Note that the struct being validated must be specified as a pointer to it. If the pointer is nil, it is considered valid.
// Use Field() to specify struct fields that need to be validated. Each Field() call specifies a single field which
// should be specified as a pointer to the field. A field can be associated with multiple rules.
// For example,
//
// value := struct {
// Name string
// Value string
// }{"name", "demo"}
// err := validation.ValidateStruct(&value,
// validation.Field(&a.Name, validation.Required),
// validation.Field(&a.Value, validation.Required, validation.Length(5, 10)),
// )
// fmt.Println(err)
// // Value: the length must be between 5 and 10.
//
// An error will be returned if validation fails.
func ValidateStruct(structPtr interface{}, fields ...*FieldRules) error {
return ValidateStructWithContext(nil, structPtr, fields...)
}
// ValidateStructWithContext validates a struct with the given context.
// The only difference between ValidateStructWithContext and ValidateStruct is that the former will
// validate struct fields with the provided context.
// Please refer to ValidateStruct for the detailed instructions on how to use this function.
func ValidateStructWithContext(ctx context.Context, structPtr interface{}, fields ...*FieldRules) error {
value := reflect.ValueOf(structPtr)
if value.Kind() != reflect.Ptr || !value.IsNil() && value.Elem().Kind() != reflect.Struct {
// must be a pointer to a struct
return NewInternalError(ErrStructPointer)
}
if value.IsNil() {
// treat a nil struct pointer as valid
return nil
}
value = value.Elem()
errs := Errors{}
for i, fr := range fields {
fv := reflect.ValueOf(fr.fieldPtr)
if fv.Kind() != reflect.Ptr {
return NewInternalError(ErrFieldPointer(i))
}
ft := findStructField(value, fv)
if ft == nil {
return NewInternalError(ErrFieldNotFound(i))
}
var err error
if ctx == nil {
err = Validate(fv.Elem().Interface(), fr.rules...)
} else {
err = ValidateWithContext(ctx, fv.Elem().Interface(), fr.rules...)
}
if err != nil {
if ie, ok := err.(InternalError); ok && ie.InternalError() != nil {
return err
}
if ft.Anonymous {
// merge errors from anonymous struct field
if es, ok := err.(Errors); ok {
for name, value := range es {
errs[name] = value
}
continue
}
}
errs[getErrorFieldName(ft)] = err
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// Field specifies a struct field and the corresponding validation rules.
// The struct field must be specified as a pointer to it.
func Field(fieldPtr interface{}, rules ...Rule) *FieldRules {
return &FieldRules{
fieldPtr: fieldPtr,
rules: rules,
}
}
// findStructField looks for a field in the given struct.
// The field being looked for should be a pointer to the actual struct field.
// If found, the field info will be returned. Otherwise, nil will be returned.
func findStructField(structValue reflect.Value, fieldValue reflect.Value) *reflect.StructField {
ptr := fieldValue.Pointer()
for i := structValue.NumField() - 1; i >= 0; i-- {
sf := structValue.Type().Field(i)
if ptr == structValue.Field(i).UnsafeAddr() {
// do additional type comparison because it's possible that the address of
// an embedded struct is the same as the first field of the embedded struct
if sf.Type == fieldValue.Elem().Type() {
return &sf
}
}
if sf.Anonymous {
// delve into anonymous struct to look for the field
fi := structValue.Field(i)
if sf.Type.Kind() == reflect.Ptr {
fi = fi.Elem()
}
if fi.Kind() == reflect.Struct {
if f := findStructField(fi, fieldValue); f != nil {
return f
}
}
}
}
return nil
}
// getErrorFieldName returns the name that should be used to represent the validation error of a struct field.
func getErrorFieldName(f *reflect.StructField) string {
if tag := f.Tag.Get(ErrorTag); tag != "" && tag != "-" {
if cps := strings.SplitN(tag, ",", 2); cps[0] != "" {
return cps[0]
}
}
return f.Name
}

View File

@ -0,0 +1,163 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package validation
import (
"database/sql/driver"
"errors"
"fmt"
"reflect"
"time"
)
var (
bytesType = reflect.TypeOf([]byte(nil))
valuerType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
)
// EnsureString ensures the given value is a string.
// If the value is a byte slice, it will be typecast into a string.
// An error is returned otherwise.
func EnsureString(value interface{}) (string, error) {
v := reflect.ValueOf(value)
if v.Kind() == reflect.String {
return v.String(), nil
}
if v.Type() == bytesType {
return string(v.Interface().([]byte)), nil
}
return "", errors.New("must be either a string or byte slice")
}
// StringOrBytes typecasts a value into a string or byte slice.
// Boolean flags are returned to indicate if the typecasting succeeds or not.
func StringOrBytes(value interface{}) (isString bool, str string, isBytes bool, bs []byte) {
v := reflect.ValueOf(value)
if v.Kind() == reflect.String {
str = v.String()
isString = true
} else if v.Kind() == reflect.Slice && v.Type() == bytesType {
bs = v.Interface().([]byte)
isBytes = true
}
return
}
// LengthOfValue returns the length of a value that is a string, slice, map, or array.
// An error is returned for all other types.
func LengthOfValue(value interface{}) (int, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.String, reflect.Slice, reflect.Map, reflect.Array:
return v.Len(), nil
}
return 0, fmt.Errorf("cannot get the length of %v", v.Kind())
}
// ToInt converts the given value to an int64.
// An error is returned for all incompatible types.
func ToInt(value interface{}) (int64, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int(), nil
}
return 0, fmt.Errorf("cannot convert %v to int64", v.Kind())
}
// ToUint converts the given value to an uint64.
// An error is returned for all incompatible types.
func ToUint(value interface{}) (uint64, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint(), nil
}
return 0, fmt.Errorf("cannot convert %v to uint64", v.Kind())
}
// ToFloat converts the given value to a float64.
// An error is returned for all incompatible types.
func ToFloat(value interface{}) (float64, error) {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Float32, reflect.Float64:
return v.Float(), nil
}
return 0, fmt.Errorf("cannot convert %v to float64", v.Kind())
}
// IsEmpty checks if a value is empty or not.
// A value is considered empty if
// - integer, float: zero
// - bool: false
// - string, array: len() == 0
// - slice, map: nil or len() == 0
// - interface, pointer: nil or the referenced value is empty
func IsEmpty(value interface{}) bool {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.String, reflect.Array, reflect.Map, reflect.Slice:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Invalid:
return true
case reflect.Interface, reflect.Ptr:
if v.IsNil() {
return true
}
return IsEmpty(v.Elem().Interface())
case reflect.Struct:
v, ok := value.(time.Time)
if ok && v.IsZero() {
return true
}
}
return false
}
// Indirect returns the value that the given interface or pointer references to.
// If the value implements driver.Valuer, it will deal with the value returned by
// the Value() method instead. A boolean value is also returned to indicate if
// the value is nil or not (only applicable to interface, pointer, map, and slice).
// If the value is neither an interface nor a pointer, it will be returned back.
func Indirect(value interface{}) (interface{}, bool) {
rv := reflect.ValueOf(value)
kind := rv.Kind()
switch kind {
case reflect.Invalid:
return nil, true
case reflect.Ptr, reflect.Interface:
if rv.IsNil() {
return nil, true
}
return Indirect(rv.Elem().Interface())
case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
if rv.IsNil() {
return nil, true
}
}
if rv.Type().Implements(valuerType) {
return indirectValuer(value.(driver.Valuer))
}
return value, false
}
func indirectValuer(valuer driver.Valuer) (interface{}, bool) {
if value, err := valuer.Value(); value != nil && err == nil {
return Indirect(value)
}
return nil, true
}

View File

@ -0,0 +1,272 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Package validation provides configurable and extensible rules for validating data of various types.
package validation
import (
"context"
"fmt"
"reflect"
"strconv"
)
type (
// Validatable is the interface indicating the type implementing it supports data validation.
Validatable interface {
// Validate validates the data and returns an error if validation fails.
Validate() error
}
// ValidatableWithContext is the interface indicating the type implementing it supports context-aware data validation.
ValidatableWithContext interface {
// ValidateWithContext validates the data with the given context and returns an error if validation fails.
ValidateWithContext(ctx context.Context) error
}
// Rule represents a validation rule.
Rule interface {
// Validate validates a value and returns a value if validation fails.
Validate(value interface{}) error
}
// RuleWithContext represents a context-aware validation rule.
RuleWithContext interface {
// ValidateWithContext validates a value and returns a value if validation fails.
ValidateWithContext(ctx context.Context, value interface{}) error
}
// RuleFunc represents a validator function.
// You may wrap it as a Rule by calling By().
RuleFunc func(value interface{}) error
// RuleWithContextFunc represents a validator function that is context-aware.
// You may wrap it as a Rule by calling WithContext().
RuleWithContextFunc func(ctx context.Context, value interface{}) error
)
var (
// ErrorTag is the struct tag name used to customize the error field name for a struct field.
ErrorTag = "json"
// Skip is a special validation rule that indicates all rules following it should be skipped.
Skip = skipRule{skip: true}
validatableType = reflect.TypeOf((*Validatable)(nil)).Elem()
validatableWithContextType = reflect.TypeOf((*ValidatableWithContext)(nil)).Elem()
)
// Validate validates the given value and returns the validation error, if any.
//
// Validate performs validation using the following steps:
// 1. For each rule, call its `Validate()` to validate the value. Return if any error is found.
// 2. If the value being validated implements `Validatable`, call the value's `Validate()`.
// Return with the validation result.
// 3. If the value being validated is a map/slice/array, and the element type implements `Validatable`,
// for each element call the element value's `Validate()`. Return with the validation result.
func Validate(value interface{}, rules ...Rule) error {
for _, rule := range rules {
if s, ok := rule.(skipRule); ok && s.skip {
return nil
}
if err := rule.Validate(value); err != nil {
return err
}
}
rv := reflect.ValueOf(value)
if (rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface) && rv.IsNil() {
return nil
}
if v, ok := value.(Validatable); ok {
return v.Validate()
}
switch rv.Kind() {
case reflect.Map:
if rv.Type().Elem().Implements(validatableType) {
return validateMap(rv)
}
case reflect.Slice, reflect.Array:
if rv.Type().Elem().Implements(validatableType) {
return validateSlice(rv)
}
case reflect.Ptr, reflect.Interface:
return Validate(rv.Elem().Interface())
}
return nil
}
// ValidateWithContext validates the given value with the given context and returns the validation error, if any.
//
// ValidateWithContext performs validation using the following steps:
// 1. For each rule, call its `ValidateWithContext()` to validate the value if the rule implements `RuleWithContext`.
// Otherwise call `Validate()` of the rule. Return if any error is found.
// 2. If the value being validated implements `ValidatableWithContext`, call the value's `ValidateWithContext()`
// and return with the validation result.
// 3. If the value being validated implements `Validatable`, call the value's `Validate()`
// and return with the validation result.
// 4. If the value being validated is a map/slice/array, and the element type implements `ValidatableWithContext`,
// for each element call the element value's `ValidateWithContext()`. Return with the validation result.
// 5. If the value being validated is a map/slice/array, and the element type implements `Validatable`,
// for each element call the element value's `Validate()`. Return with the validation result.
func ValidateWithContext(ctx context.Context, value interface{}, rules ...Rule) error {
for _, rule := range rules {
if s, ok := rule.(skipRule); ok && s.skip {
return nil
}
if rc, ok := rule.(RuleWithContext); ok {
if err := rc.ValidateWithContext(ctx, value); err != nil {
return err
}
} else if err := rule.Validate(value); err != nil {
return err
}
}
rv := reflect.ValueOf(value)
if (rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface) && rv.IsNil() {
return nil
}
if v, ok := value.(ValidatableWithContext); ok {
return v.ValidateWithContext(ctx)
}
if v, ok := value.(Validatable); ok {
return v.Validate()
}
switch rv.Kind() {
case reflect.Map:
if rv.Type().Elem().Implements(validatableWithContextType) {
return validateMapWithContext(ctx, rv)
}
if rv.Type().Elem().Implements(validatableType) {
return validateMap(rv)
}
case reflect.Slice, reflect.Array:
if rv.Type().Elem().Implements(validatableWithContextType) {
return validateSliceWithContext(ctx, rv)
}
if rv.Type().Elem().Implements(validatableType) {
return validateSlice(rv)
}
case reflect.Ptr, reflect.Interface:
return ValidateWithContext(ctx, rv.Elem().Interface())
}
return nil
}
// validateMap validates a map of validatable elements
func validateMap(rv reflect.Value) error {
errs := Errors{}
for _, key := range rv.MapKeys() {
if mv := rv.MapIndex(key).Interface(); mv != nil {
if err := mv.(Validatable).Validate(); err != nil {
errs[fmt.Sprintf("%v", key.Interface())] = err
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// validateMapWithContext validates a map of validatable elements with the given context.
func validateMapWithContext(ctx context.Context, rv reflect.Value) error {
errs := Errors{}
for _, key := range rv.MapKeys() {
if mv := rv.MapIndex(key).Interface(); mv != nil {
if err := mv.(ValidatableWithContext).ValidateWithContext(ctx); err != nil {
errs[fmt.Sprintf("%v", key.Interface())] = err
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// validateSlice validates a slice/array of validatable elements
func validateSlice(rv reflect.Value) error {
errs := Errors{}
l := rv.Len()
for i := 0; i < l; i++ {
if ev := rv.Index(i).Interface(); ev != nil {
if err := ev.(Validatable).Validate(); err != nil {
errs[strconv.Itoa(i)] = err
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// validateSliceWithContext validates a slice/array of validatable elements with the given context.
func validateSliceWithContext(ctx context.Context, rv reflect.Value) error {
errs := Errors{}
l := rv.Len()
for i := 0; i < l; i++ {
if ev := rv.Index(i).Interface(); ev != nil {
if err := ev.(ValidatableWithContext).ValidateWithContext(ctx); err != nil {
errs[strconv.Itoa(i)] = err
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}
type skipRule struct {
skip bool
}
func (r skipRule) Validate(interface{}) error {
return nil
}
// When determines if all rules following it should be skipped.
func (r skipRule) When(condition bool) skipRule {
r.skip = condition
return r
}
type inlineRule struct {
f RuleFunc
fc RuleWithContextFunc
}
func (r *inlineRule) Validate(value interface{}) error {
if r.f == nil {
return r.fc(context.Background(), value)
}
return r.f(value)
}
func (r *inlineRule) ValidateWithContext(ctx context.Context, value interface{}) error {
if r.fc == nil {
return r.f(value)
}
return r.fc(ctx, value)
}
// By wraps a RuleFunc into a Rule.
func By(f RuleFunc) Rule {
return &inlineRule{f: f}
}
// WithContext wraps a RuleWithContextFunc into a context-aware Rule.
func WithContext(f RuleWithContextFunc) Rule {
return &inlineRule{fc: f}
}

View File

@ -0,0 +1,47 @@
package validation
import "context"
// When returns a validation rule that executes the given list of rules when the condition is true.
func When(condition bool, rules ...Rule) WhenRule {
return WhenRule{
condition: condition,
rules: rules,
elseRules: []Rule{},
}
}
// WhenRule is a validation rule that executes the given list of rules when the condition is true.
type WhenRule struct {
condition bool
rules []Rule
elseRules []Rule
}
// Validate checks if the condition is true and if so, it validates the value using the specified rules.
func (r WhenRule) Validate(value interface{}) error {
return r.ValidateWithContext(nil, value)
}
// ValidateWithContext checks if the condition is true and if so, it validates the value using the specified rules.
func (r WhenRule) ValidateWithContext(ctx context.Context, value interface{}) error {
if r.condition {
if ctx == nil {
return Validate(value, r.rules...)
} else {
return ValidateWithContext(ctx, value, r.rules...)
}
}
if ctx == nil {
return Validate(value, r.elseRules...)
} else {
return ValidateWithContext(ctx, value, r.elseRules...)
}
}
// Else returns a validation rule that executes the given list of rules when the condition is false.
func (r WhenRule) Else(rules ...Rule) WhenRule {
r.elseRules = rules
return r
}

View File

@ -7,6 +7,9 @@ github.com/cespare/xxhash/v2
# github.com/docker/distribution v2.8.1+incompatible
## explicit
github.com/docker/distribution/uuid
# github.com/go-ozzo/ozzo-validation/v4 v4.3.0
## explicit; go 1.13
github.com/go-ozzo/ozzo-validation/v4
# github.com/gogo/protobuf v1.3.2
## explicit; go 1.15
github.com/gogo/protobuf/gogoproto