diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8009d4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/timeofday-server +/vendor + diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..38efbf9 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,176 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/PuerkitoBio/purell" + packages = ["."] + revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/PuerkitoBio/urlesc" + packages = ["."] + revision = "de5bf2ad457846296e2031421a34e2568e304e35" + +[[projects]] + name = "github.com/asaskevich/govalidator" + packages = ["."] + revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f" + version = "v9" + +[[projects]] + name = "github.com/docker/go-units" + packages = ["."] + revision = "0dadbb0345b35ec7ef35e228dabb8de89a65bf52" + version = "v0.3.2" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/analysis" + packages = ["."] + revision = "f59a71f0ece6f9dfb438be7f45148f006cbad88e" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/errors" + packages = ["."] + revision = "7bcb96a367bac6b76e6e42fa84155bb5581dcff8" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/jsonpointer" + packages = ["."] + revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/jsonreference" + packages = ["."] + revision = "3fb327e6747da3043567ee86abd02bb6376b6be2" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/loads" + packages = ["."] + revision = "2a2b323bab96e6b1fdee110e57d959322446e9c9" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/runtime" + packages = [ + ".", + "flagext", + "logger", + "middleware", + "middleware/denco", + "middleware/header", + "middleware/untyped", + "security" + ] + revision = "62281b694b396a17fe3e4313ee8b0ca2c3cca719" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/spec" + packages = ["."] + revision = "9acd88844bc186c3ec7f318cd3d56f1114b4ab99" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/strfmt" + packages = ["."] + revision = "6ba31556a6c60db8615afb9d8eddae7aae15eb48" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/swag" + packages = ["."] + revision = "ceb469cb0fdf2d792f28d771bc05da6c606f55e5" + +[[projects]] + branch = "master" + name = "github.com/go-openapi/validate" + packages = ["."] + revision = "180bba53b98899f743a112e568bed9e2ef31aa20" + +[[projects]] + name = "github.com/jessevdk/go-flags" + packages = ["."] + revision = "96dc06278ce32a0e9d957d590bb987c81ee66407" + version = "v1.3.0" + +[[projects]] + branch = "master" + name = "github.com/mailru/easyjson" + packages = [ + "buffer", + "jlexer", + "jwriter" + ] + revision = "8b799c424f57fa123fc63a99d6383bc6e4c02578" + +[[projects]] + branch = "master" + name = "github.com/mitchellh/mapstructure" + packages = ["."] + revision = "00c29f56e2386353d58c599509e8dc3801b0d716" + +[[projects]] + name = "github.com/tylerb/graceful" + packages = ["."] + revision = "4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb" + version = "v1.2.15" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = [ + "context", + "idna" + ] + revision = "6078986fec03a1dcc236c34816c71b0e05018fda" + +[[projects]] + name = "golang.org/x/text" + packages = [ + "collate", + "collate/build", + "internal/colltab", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "language", + "secure/bidirule", + "transform", + "unicode/bidi", + "unicode/cldr", + "unicode/norm", + "unicode/rangetable", + "width" + ] + revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" + version = "v0.3.0" + +[[projects]] + branch = "v2" + name = "gopkg.in/mgo.v2" + packages = [ + "bson", + "internal/json" + ] + revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655" + +[[projects]] + name = "gopkg.in/yaml.v2" + packages = ["."] + revision = "86f5ed62f8a0ee96bd888d2efdfd6d4fb100a4eb" + version = "v2.2.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "d28759715da1bd6ffbf46a4622a8b6f9757e7d2a6e1568aadf0be496c42cc19c" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..d32fc5f --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,66 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "master" + name = "github.com/go-openapi/errors" + +[[constraint]] + branch = "master" + name = "github.com/go-openapi/loads" + +[[constraint]] + branch = "master" + name = "github.com/go-openapi/runtime" + +[[constraint]] + branch = "master" + name = "github.com/go-openapi/spec" + +[[constraint]] + branch = "master" + name = "github.com/go-openapi/strfmt" + +[[constraint]] + branch = "master" + name = "github.com/go-openapi/swag" + +[[constraint]] + branch = "master" + name = "github.com/go-openapi/validate" + +[[constraint]] + name = "github.com/jessevdk/go-flags" + version = "1.3.0" + +[[constraint]] + name = "github.com/tylerb/graceful" + version = "1.2.15" + +[prune] + go-tests = true + unused-packages = true diff --git a/cmd/timeofday-server/main.go b/cmd/timeofday-server/main.go new file mode 100644 index 0000000..f389d5b --- /dev/null +++ b/cmd/timeofday-server/main.go @@ -0,0 +1,58 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package main + +import ( + "log" + "os" + + loads "github.com/go-openapi/loads" + flags "github.com/jessevdk/go-flags" + + "github.com/elfsternberg/timeofday/restapi" + "github.com/elfsternberg/timeofday/restapi/operations" +) + +// This file was generated by the swagger tool. +// Make sure not to overwrite this file after you generated it because all your edits would be lost! + +func main() { + + swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON) + if err != nil { + log.Fatalln(err) + } + + api := operations.NewTimeofdayAPI(swaggerSpec) + server := restapi.NewServer(api) + defer server.Shutdown() + + parser := flags.NewParser(server, flags.Default) + parser.ShortDescription = "timeofday" + parser.LongDescription = "Return the time of day. Timezone optional." + + server.ConfigureFlags() + for _, optsGroup := range api.CommandLineOptionsGroups { + _, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options) + if err != nil { + log.Fatalln(err) + } + } + + if _, err := parser.Parse(); err != nil { + code := 1 + if fe, ok := err.(*flags.Error); ok { + if fe.Type == flags.ErrHelp { + code = 0 + } + } + os.Exit(code) + } + + server.ConfigureAPI() + + if err := server.Serve(); err != nil { + log.Fatalln(err) + } + +} diff --git a/models/error_response.go b/models/error_response.go new file mode 100644 index 0000000..a9e99ae --- /dev/null +++ b/models/error_response.go @@ -0,0 +1,68 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// ErrorResponse error response +// swagger:model errorResponse +type ErrorResponse struct { + + // code + Code int64 `json:"code,omitempty"` + + // message + // Required: true + Message *string `json:"message"` +} + +// Validate validates this error response +func (m *ErrorResponse) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateMessage(formats); err != nil { + // prop + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *ErrorResponse) validateMessage(formats strfmt.Registry) error { + + if err := validate.Required("message", "body", m.Message); err != nil { + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *ErrorResponse) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *ErrorResponse) UnmarshalBinary(b []byte) error { + var res ErrorResponse + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/timeofday.go b/models/timeofday.go new file mode 100644 index 0000000..8410b7b --- /dev/null +++ b/models/timeofday.go @@ -0,0 +1,49 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" +) + +// Timeofday timeofday +// swagger:model timeofday +type Timeofday struct { + + // timeofday + Timeofday string `json:"timeofday,omitempty"` +} + +// Validate validates this timeofday +func (m *Timeofday) Validate(formats strfmt.Registry) error { + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// MarshalBinary interface implementation +func (m *Timeofday) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Timeofday) UnmarshalBinary(b []byte) error { + var res Timeofday + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/timezone.go b/models/timezone.go new file mode 100644 index 0000000..16a2ce4 --- /dev/null +++ b/models/timezone.go @@ -0,0 +1,70 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// Timezone timezone +// swagger:model timezone +type Timezone struct { + + // timezone + // Required: true + // Min Length: 3 + Timezone *string `json:"timezone"` +} + +// Validate validates this timezone +func (m *Timezone) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateTimezone(formats); err != nil { + // prop + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Timezone) validateTimezone(formats strfmt.Registry) error { + + if err := validate.Required("timezone", "body", m.Timezone); err != nil { + return err + } + + if err := validate.MinLength("timezone", "body", string(*m.Timezone), 3); err != nil { + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *Timezone) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Timezone) UnmarshalBinary(b []byte) error { + var res Timezone + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/restapi/configure_timeofday.go b/restapi/configure_timeofday.go new file mode 100644 index 0000000..225c28c --- /dev/null +++ b/restapi/configure_timeofday.go @@ -0,0 +1,71 @@ +// This file is safe to edit. Once it exists it will not be overwritten + +package restapi + +import ( + "crypto/tls" + "net/http" + + errors "github.com/go-openapi/errors" + runtime "github.com/go-openapi/runtime" + middleware "github.com/go-openapi/runtime/middleware" + graceful "github.com/tylerb/graceful" + + "github.com/elfsternberg/timeofday/restapi/operations" +) + +//go:generate swagger generate server --target .. --name --spec ../swagger.yml + +func configureFlags(api *operations.TimeofdayAPI) { + // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... } +} + +func configureAPI(api *operations.TimeofdayAPI) http.Handler { + // configure the api here + api.ServeError = errors.ServeError + + // Set your custom logger if needed. Default one is log.Printf + // Expected interface func(string, ...interface{}) + // + // Example: + // api.Logger = log.Printf + + api.JSONConsumer = runtime.JSONConsumer() + + api.JSONProducer = runtime.JSONProducer() + + api.ClockGetHandler = operations.ClockGetHandlerFunc(func(params operations.ClockGetParams) middleware.Responder { + return middleware.NotImplemented("operation .ClockGet has not yet been implemented") + }) + api.ClockPostHandler = operations.ClockPostHandlerFunc(func(params operations.ClockPostParams) middleware.Responder { + return middleware.NotImplemented("operation .ClockPost has not yet been implemented") + }) + + api.ServerShutdown = func() {} + + return setupGlobalMiddleware(api.Serve(setupMiddlewares)) +} + +// The TLS configuration before HTTPS server starts. +func configureTLS(tlsConfig *tls.Config) { + // Make all necessary changes to the TLS configuration here. +} + +// As soon as server is initialized but not run yet, this function will be called. +// If you need to modify a config, store server instance to stop it individually later, this is the place. +// This function can be called multiple times, depending on the number of serving schemes. +// scheme value will be set accordingly: "http", "https" or "unix" +func configureServer(s *graceful.Server, scheme, addr string) { +} + +// The middleware configuration is for the handler executors. These do not apply to the swagger.json document. +// The middleware executes after routing but before authentication, binding and validation +func setupMiddlewares(handler http.Handler) http.Handler { + return handler +} + +// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document. +// So this is a good place to plug in a panic handling middleware, logging and metrics +func setupGlobalMiddleware(handler http.Handler) http.Handler { + return handler +} diff --git a/restapi/doc.go b/restapi/doc.go new file mode 100644 index 0000000..dc5a12d --- /dev/null +++ b/restapi/doc.go @@ -0,0 +1,24 @@ +// Code generated by go-swagger; DO NOT EDIT. + +/* +Package restapi timeofday +Return the time of day. Timezone optional. + + + Schemes: + http + Host: localhost + BasePath: /timeofday/v1 + Version: 0.1.0 + License: apache-2.0 + Contact: Elf Sternberg https://github.com/elfsternberg/go-timeofday + + Consumes: + - application/json + + Produces: + - application/json + +swagger:meta +*/ +package restapi diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go new file mode 100644 index 0000000..8ba1f64 --- /dev/null +++ b/restapi/embedded_spec.go @@ -0,0 +1,292 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package restapi + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" +) + +var ( + // SwaggerJSON embedded version of the swagger document used at generation time + SwaggerJSON json.RawMessage + // FlatSwaggerJSON embedded flattened version of the swagger document used at generation time + FlatSwaggerJSON json.RawMessage +) + +func init() { + SwaggerJSON = json.RawMessage([]byte(`{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "Return the time of day. Timezone optional.", + "title": "timeofday", + "contact": { + "name": "Elf Sternberg", + "url": "https://github.com/elfsternberg/go-timeofday", + "email": "elf.sternberg@gmail.com" + }, + "license": { + "name": "apache-2.0" + }, + "version": "0.1.0" + }, + "basePath": "/timeofday/v1", + "paths": { + "/time": { + "get": { + "description": "Returns time of day.", + "operationId": "ClockGet", + "parameters": [ + { + "description": "Timezone to return", + "name": "timezone", + "in": "body", + "schema": { + "$ref": "#/definitions/timezone" + } + } + ], + "responses": { + "200": { + "description": "Returns the time of day.", + "schema": { + "$ref": "#/definitions/timeofday" + } + }, + "404": { + "description": "Time zone not found", + "schema": { + "$ref": "#/definitions/errorResponse" + } + }, + "500": { + "description": "Something has gone horribly wrong", + "schema": { + "$ref": "#/definitions/errorResponse" + } + } + } + }, + "post": { + "description": "Returns time of day.", + "operationId": "ClockPost", + "parameters": [ + { + "description": "Timezone to return", + "name": "timezone", + "in": "body", + "schema": { + "$ref": "#/definitions/timezone" + } + } + ], + "responses": { + "200": { + "description": "Returns the time of day.", + "schema": { + "$ref": "#/definitions/timeofday" + } + }, + "404": { + "description": "Time zone not found", + "schema": { + "$ref": "#/definitions/errorResponse" + } + }, + "500": { + "description": "Something has gone horribly wrong", + "schema": { + "$ref": "#/definitions/errorResponse" + } + } + } + } + } + }, + "definitions": { + "errorResponse": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "message": { + "type": "string" + } + } + }, + "timeofday": { + "type": "object", + "properties": { + "timeofday": { + "type": "string" + } + } + }, + "timezone": { + "type": "object", + "required": [ + "timezone" + ], + "properties": { + "timezone": { + "type": "string", + "default": "UTC", + "minLength": 3 + } + } + } + } +}`)) + FlatSwaggerJSON = json.RawMessage([]byte(`{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "Return the time of day. Timezone optional.", + "title": "timeofday", + "contact": { + "name": "Elf Sternberg", + "url": "https://github.com/elfsternberg/go-timeofday", + "email": "elf.sternberg@gmail.com" + }, + "license": { + "name": "apache-2.0" + }, + "version": "0.1.0" + }, + "basePath": "/timeofday/v1", + "paths": { + "/time": { + "get": { + "description": "Returns time of day.", + "operationId": "ClockGet", + "parameters": [ + { + "description": "Timezone to return", + "name": "timezone", + "in": "body", + "schema": { + "$ref": "#/definitions/timezone" + } + } + ], + "responses": { + "200": { + "description": "Returns the time of day.", + "schema": { + "$ref": "#/definitions/timeofday" + } + }, + "404": { + "description": "Time zone not found", + "schema": { + "$ref": "#/definitions/errorResponse" + } + }, + "500": { + "description": "Something has gone horribly wrong", + "schema": { + "$ref": "#/definitions/errorResponse" + } + } + } + }, + "post": { + "description": "Returns time of day.", + "operationId": "ClockPost", + "parameters": [ + { + "description": "Timezone to return", + "name": "timezone", + "in": "body", + "schema": { + "$ref": "#/definitions/timezone" + } + } + ], + "responses": { + "200": { + "description": "Returns the time of day.", + "schema": { + "$ref": "#/definitions/timeofday" + } + }, + "404": { + "description": "Time zone not found", + "schema": { + "$ref": "#/definitions/errorResponse" + } + }, + "500": { + "description": "Something has gone horribly wrong", + "schema": { + "$ref": "#/definitions/errorResponse" + } + } + } + } + } + }, + "definitions": { + "errorResponse": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "message": { + "type": "string" + } + } + }, + "timeofday": { + "type": "object", + "properties": { + "timeofday": { + "type": "string" + } + } + }, + "timezone": { + "type": "object", + "required": [ + "timezone" + ], + "properties": { + "timezone": { + "type": "string", + "default": "UTC", + "minLength": 3 + } + } + } + } +}`)) +} diff --git a/restapi/operations/clock_get.go b/restapi/operations/clock_get.go new file mode 100644 index 0000000..b2dd2a3 --- /dev/null +++ b/restapi/operations/clock_get.go @@ -0,0 +1,58 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + middleware "github.com/go-openapi/runtime/middleware" +) + +// ClockGetHandlerFunc turns a function with the right signature into a clock get handler +type ClockGetHandlerFunc func(ClockGetParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn ClockGetHandlerFunc) Handle(params ClockGetParams) middleware.Responder { + return fn(params) +} + +// ClockGetHandler interface for that can handle valid clock get params +type ClockGetHandler interface { + Handle(ClockGetParams) middleware.Responder +} + +// NewClockGet creates a new http.Handler for the clock get operation +func NewClockGet(ctx *middleware.Context, handler ClockGetHandler) *ClockGet { + return &ClockGet{Context: ctx, Handler: handler} +} + +/*ClockGet swagger:route GET /time clockGet + +Returns time of day. + +*/ +type ClockGet struct { + Context *middleware.Context + Handler ClockGetHandler +} + +func (o *ClockGet) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewClockGetParams() + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/clock_get_parameters.go b/restapi/operations/clock_get_parameters.go new file mode 100644 index 0000000..f1438bc --- /dev/null +++ b/restapi/operations/clock_get_parameters.go @@ -0,0 +1,70 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + + models "github.com/elfsternberg/timeofday/models" +) + +// NewClockGetParams creates a new ClockGetParams object +// no default values defined in spec. +func NewClockGetParams() ClockGetParams { + + return ClockGetParams{} +} + +// ClockGetParams contains all the bound params for the clock get operation +// typically these are obtained from a http.Request +// +// swagger:parameters ClockGet +type ClockGetParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /*Timezone to return + In: body + */ + Timezone *models.Timezone +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewClockGetParams() beforehand. +func (o *ClockGetParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.Timezone + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("timezone", "body", "", err)) + } else { + + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Timezone = &body + } + } + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/restapi/operations/clock_get_responses.go b/restapi/operations/clock_get_responses.go new file mode 100644 index 0000000..d8cfa36 --- /dev/null +++ b/restapi/operations/clock_get_responses.go @@ -0,0 +1,146 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + models "github.com/elfsternberg/timeofday/models" +) + +// ClockGetOKCode is the HTTP code returned for type ClockGetOK +const ClockGetOKCode int = 200 + +/*ClockGetOK Returns the time of day. + +swagger:response clockGetOK +*/ +type ClockGetOK struct { + + /* + In: Body + */ + Payload *models.Timeofday `json:"body,omitempty"` +} + +// NewClockGetOK creates ClockGetOK with default headers values +func NewClockGetOK() *ClockGetOK { + + return &ClockGetOK{} +} + +// WithPayload adds the payload to the clock get o k response +func (o *ClockGetOK) WithPayload(payload *models.Timeofday) *ClockGetOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the clock get o k response +func (o *ClockGetOK) SetPayload(payload *models.Timeofday) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ClockGetOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// ClockGetNotFoundCode is the HTTP code returned for type ClockGetNotFound +const ClockGetNotFoundCode int = 404 + +/*ClockGetNotFound Time zone not found + +swagger:response clockGetNotFound +*/ +type ClockGetNotFound struct { + + /* + In: Body + */ + Payload *models.ErrorResponse `json:"body,omitempty"` +} + +// NewClockGetNotFound creates ClockGetNotFound with default headers values +func NewClockGetNotFound() *ClockGetNotFound { + + return &ClockGetNotFound{} +} + +// WithPayload adds the payload to the clock get not found response +func (o *ClockGetNotFound) WithPayload(payload *models.ErrorResponse) *ClockGetNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the clock get not found response +func (o *ClockGetNotFound) SetPayload(payload *models.ErrorResponse) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ClockGetNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// ClockGetInternalServerErrorCode is the HTTP code returned for type ClockGetInternalServerError +const ClockGetInternalServerErrorCode int = 500 + +/*ClockGetInternalServerError Something has gone horribly wrong + +swagger:response clockGetInternalServerError +*/ +type ClockGetInternalServerError struct { + + /* + In: Body + */ + Payload *models.ErrorResponse `json:"body,omitempty"` +} + +// NewClockGetInternalServerError creates ClockGetInternalServerError with default headers values +func NewClockGetInternalServerError() *ClockGetInternalServerError { + + return &ClockGetInternalServerError{} +} + +// WithPayload adds the payload to the clock get internal server error response +func (o *ClockGetInternalServerError) WithPayload(payload *models.ErrorResponse) *ClockGetInternalServerError { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the clock get internal server error response +func (o *ClockGetInternalServerError) SetPayload(payload *models.ErrorResponse) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ClockGetInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/clock_get_urlbuilder.go b/restapi/operations/clock_get_urlbuilder.go new file mode 100644 index 0000000..b1d95be --- /dev/null +++ b/restapi/operations/clock_get_urlbuilder.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// ClockGetURL generates an URL for the clock get operation +type ClockGetURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ClockGetURL) WithBasePath(bp string) *ClockGetURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ClockGetURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *ClockGetURL) Build() (*url.URL, error) { + var result url.URL + + var _path = "/time" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/timeofday/v1" + } + result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *ClockGetURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *ClockGetURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *ClockGetURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on ClockGetURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on ClockGetURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *ClockGetURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/operations/clock_post.go b/restapi/operations/clock_post.go new file mode 100644 index 0000000..a4544f3 --- /dev/null +++ b/restapi/operations/clock_post.go @@ -0,0 +1,58 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + middleware "github.com/go-openapi/runtime/middleware" +) + +// ClockPostHandlerFunc turns a function with the right signature into a clock post handler +type ClockPostHandlerFunc func(ClockPostParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn ClockPostHandlerFunc) Handle(params ClockPostParams) middleware.Responder { + return fn(params) +} + +// ClockPostHandler interface for that can handle valid clock post params +type ClockPostHandler interface { + Handle(ClockPostParams) middleware.Responder +} + +// NewClockPost creates a new http.Handler for the clock post operation +func NewClockPost(ctx *middleware.Context, handler ClockPostHandler) *ClockPost { + return &ClockPost{Context: ctx, Handler: handler} +} + +/*ClockPost swagger:route POST /time clockPost + +Returns time of day. + +*/ +type ClockPost struct { + Context *middleware.Context + Handler ClockPostHandler +} + +func (o *ClockPost) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewClockPostParams() + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/clock_post_parameters.go b/restapi/operations/clock_post_parameters.go new file mode 100644 index 0000000..41e2c65 --- /dev/null +++ b/restapi/operations/clock_post_parameters.go @@ -0,0 +1,70 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + + models "github.com/elfsternberg/timeofday/models" +) + +// NewClockPostParams creates a new ClockPostParams object +// no default values defined in spec. +func NewClockPostParams() ClockPostParams { + + return ClockPostParams{} +} + +// ClockPostParams contains all the bound params for the clock post operation +// typically these are obtained from a http.Request +// +// swagger:parameters ClockPost +type ClockPostParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /*Timezone to return + In: body + */ + Timezone *models.Timezone +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewClockPostParams() beforehand. +func (o *ClockPostParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.Timezone + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("timezone", "body", "", err)) + } else { + + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Timezone = &body + } + } + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/restapi/operations/clock_post_responses.go b/restapi/operations/clock_post_responses.go new file mode 100644 index 0000000..39b9e1e --- /dev/null +++ b/restapi/operations/clock_post_responses.go @@ -0,0 +1,146 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + models "github.com/elfsternberg/timeofday/models" +) + +// ClockPostOKCode is the HTTP code returned for type ClockPostOK +const ClockPostOKCode int = 200 + +/*ClockPostOK Returns the time of day. + +swagger:response clockPostOK +*/ +type ClockPostOK struct { + + /* + In: Body + */ + Payload *models.Timeofday `json:"body,omitempty"` +} + +// NewClockPostOK creates ClockPostOK with default headers values +func NewClockPostOK() *ClockPostOK { + + return &ClockPostOK{} +} + +// WithPayload adds the payload to the clock post o k response +func (o *ClockPostOK) WithPayload(payload *models.Timeofday) *ClockPostOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the clock post o k response +func (o *ClockPostOK) SetPayload(payload *models.Timeofday) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ClockPostOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// ClockPostNotFoundCode is the HTTP code returned for type ClockPostNotFound +const ClockPostNotFoundCode int = 404 + +/*ClockPostNotFound Time zone not found + +swagger:response clockPostNotFound +*/ +type ClockPostNotFound struct { + + /* + In: Body + */ + Payload *models.ErrorResponse `json:"body,omitempty"` +} + +// NewClockPostNotFound creates ClockPostNotFound with default headers values +func NewClockPostNotFound() *ClockPostNotFound { + + return &ClockPostNotFound{} +} + +// WithPayload adds the payload to the clock post not found response +func (o *ClockPostNotFound) WithPayload(payload *models.ErrorResponse) *ClockPostNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the clock post not found response +func (o *ClockPostNotFound) SetPayload(payload *models.ErrorResponse) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ClockPostNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// ClockPostInternalServerErrorCode is the HTTP code returned for type ClockPostInternalServerError +const ClockPostInternalServerErrorCode int = 500 + +/*ClockPostInternalServerError Something has gone horribly wrong + +swagger:response clockPostInternalServerError +*/ +type ClockPostInternalServerError struct { + + /* + In: Body + */ + Payload *models.ErrorResponse `json:"body,omitempty"` +} + +// NewClockPostInternalServerError creates ClockPostInternalServerError with default headers values +func NewClockPostInternalServerError() *ClockPostInternalServerError { + + return &ClockPostInternalServerError{} +} + +// WithPayload adds the payload to the clock post internal server error response +func (o *ClockPostInternalServerError) WithPayload(payload *models.ErrorResponse) *ClockPostInternalServerError { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the clock post internal server error response +func (o *ClockPostInternalServerError) SetPayload(payload *models.ErrorResponse) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *ClockPostInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/clock_post_urlbuilder.go b/restapi/operations/clock_post_urlbuilder.go new file mode 100644 index 0000000..157da55 --- /dev/null +++ b/restapi/operations/clock_post_urlbuilder.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// ClockPostURL generates an URL for the clock post operation +type ClockPostURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ClockPostURL) WithBasePath(bp string) *ClockPostURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *ClockPostURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *ClockPostURL) Build() (*url.URL, error) { + var result url.URL + + var _path = "/time" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/timeofday/v1" + } + result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *ClockPostURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *ClockPostURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *ClockPostURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on ClockPostURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on ClockPostURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *ClockPostURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/operations/timeofday_api.go b/restapi/operations/timeofday_api.go new file mode 100644 index 0000000..8b0ce67 --- /dev/null +++ b/restapi/operations/timeofday_api.go @@ -0,0 +1,288 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package operations + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "net/http" + "strings" + + errors "github.com/go-openapi/errors" + loads "github.com/go-openapi/loads" + runtime "github.com/go-openapi/runtime" + middleware "github.com/go-openapi/runtime/middleware" + security "github.com/go-openapi/runtime/security" + spec "github.com/go-openapi/spec" + strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// NewTimeofdayAPI creates a new Timeofday instance +func NewTimeofdayAPI(spec *loads.Document) *TimeofdayAPI { + return &TimeofdayAPI{ + handlers: make(map[string]map[string]http.Handler), + formats: strfmt.Default, + defaultConsumes: "application/json", + defaultProduces: "application/json", + customConsumers: make(map[string]runtime.Consumer), + customProducers: make(map[string]runtime.Producer), + ServerShutdown: func() {}, + spec: spec, + ServeError: errors.ServeError, + BasicAuthenticator: security.BasicAuth, + APIKeyAuthenticator: security.APIKeyAuth, + BearerAuthenticator: security.BearerAuth, + JSONConsumer: runtime.JSONConsumer(), + JSONProducer: runtime.JSONProducer(), + ClockGetHandler: ClockGetHandlerFunc(func(params ClockGetParams) middleware.Responder { + return middleware.NotImplemented("operation ClockGet has not yet been implemented") + }), + ClockPostHandler: ClockPostHandlerFunc(func(params ClockPostParams) middleware.Responder { + return middleware.NotImplemented("operation ClockPost has not yet been implemented") + }), + } +} + +/*TimeofdayAPI Return the time of day. Timezone optional. */ +type TimeofdayAPI struct { + spec *loads.Document + context *middleware.Context + handlers map[string]map[string]http.Handler + formats strfmt.Registry + customConsumers map[string]runtime.Consumer + customProducers map[string]runtime.Producer + defaultConsumes string + defaultProduces string + Middleware func(middleware.Builder) http.Handler + + // BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function. + // It has a default implemention in the security package, however you can replace it for your particular usage. + BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator + // APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function. + // It has a default implemention in the security package, however you can replace it for your particular usage. + APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator + // BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function. + // It has a default implemention in the security package, however you can replace it for your particular usage. + BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator + + // JSONConsumer registers a consumer for a "application/json" mime type + JSONConsumer runtime.Consumer + + // JSONProducer registers a producer for a "application/json" mime type + JSONProducer runtime.Producer + + // ClockGetHandler sets the operation handler for the clock get operation + ClockGetHandler ClockGetHandler + // ClockPostHandler sets the operation handler for the clock post operation + ClockPostHandler ClockPostHandler + + // ServeError is called when an error is received, there is a default handler + // but you can set your own with this + ServeError func(http.ResponseWriter, *http.Request, error) + + // ServerShutdown is called when the HTTP(S) server is shut down and done + // handling all active connections and does not accept connections any more + ServerShutdown func() + + // Custom command line argument groups with their descriptions + CommandLineOptionsGroups []swag.CommandLineOptionsGroup + + // User defined logger function. + Logger func(string, ...interface{}) +} + +// SetDefaultProduces sets the default produces media type +func (o *TimeofdayAPI) SetDefaultProduces(mediaType string) { + o.defaultProduces = mediaType +} + +// SetDefaultConsumes returns the default consumes media type +func (o *TimeofdayAPI) SetDefaultConsumes(mediaType string) { + o.defaultConsumes = mediaType +} + +// SetSpec sets a spec that will be served for the clients. +func (o *TimeofdayAPI) SetSpec(spec *loads.Document) { + o.spec = spec +} + +// DefaultProduces returns the default produces media type +func (o *TimeofdayAPI) DefaultProduces() string { + return o.defaultProduces +} + +// DefaultConsumes returns the default consumes media type +func (o *TimeofdayAPI) DefaultConsumes() string { + return o.defaultConsumes +} + +// Formats returns the registered string formats +func (o *TimeofdayAPI) Formats() strfmt.Registry { + return o.formats +} + +// RegisterFormat registers a custom format validator +func (o *TimeofdayAPI) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) { + o.formats.Add(name, format, validator) +} + +// Validate validates the registrations in the TimeofdayAPI +func (o *TimeofdayAPI) Validate() error { + var unregistered []string + + if o.JSONConsumer == nil { + unregistered = append(unregistered, "JSONConsumer") + } + + if o.JSONProducer == nil { + unregistered = append(unregistered, "JSONProducer") + } + + if o.ClockGetHandler == nil { + unregistered = append(unregistered, "ClockGetHandler") + } + + if o.ClockPostHandler == nil { + unregistered = append(unregistered, "ClockPostHandler") + } + + if len(unregistered) > 0 { + return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", ")) + } + + return nil +} + +// ServeErrorFor gets a error handler for a given operation id +func (o *TimeofdayAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) { + return o.ServeError +} + +// AuthenticatorsFor gets the authenticators for the specified security schemes +func (o *TimeofdayAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator { + + return nil + +} + +// Authorizer returns the registered authorizer +func (o *TimeofdayAPI) Authorizer() runtime.Authorizer { + + return nil + +} + +// ConsumersFor gets the consumers for the specified media types +func (o *TimeofdayAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer { + + result := make(map[string]runtime.Consumer) + for _, mt := range mediaTypes { + switch mt { + + case "application/json": + result["application/json"] = o.JSONConsumer + + } + + if c, ok := o.customConsumers[mt]; ok { + result[mt] = c + } + } + return result + +} + +// ProducersFor gets the producers for the specified media types +func (o *TimeofdayAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer { + + result := make(map[string]runtime.Producer) + for _, mt := range mediaTypes { + switch mt { + + case "application/json": + result["application/json"] = o.JSONProducer + + } + + if p, ok := o.customProducers[mt]; ok { + result[mt] = p + } + } + return result + +} + +// HandlerFor gets a http.Handler for the provided operation method and path +func (o *TimeofdayAPI) HandlerFor(method, path string) (http.Handler, bool) { + if o.handlers == nil { + return nil, false + } + um := strings.ToUpper(method) + if _, ok := o.handlers[um]; !ok { + return nil, false + } + if path == "/" { + path = "" + } + h, ok := o.handlers[um][path] + return h, ok +} + +// Context returns the middleware context for the timeofday API +func (o *TimeofdayAPI) Context() *middleware.Context { + if o.context == nil { + o.context = middleware.NewRoutableContext(o.spec, o, nil) + } + + return o.context +} + +func (o *TimeofdayAPI) initHandlerCache() { + o.Context() // don't care about the result, just that the initialization happened + + if o.handlers == nil { + o.handlers = make(map[string]map[string]http.Handler) + } + + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } + o.handlers["GET"]["/time"] = NewClockGet(o.context, o.ClockGetHandler) + + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"]["/time"] = NewClockPost(o.context, o.ClockPostHandler) + +} + +// Serve creates a http handler to serve the API over HTTP +// can be used directly in http.ListenAndServe(":8000", api.Serve(nil)) +func (o *TimeofdayAPI) Serve(builder middleware.Builder) http.Handler { + o.Init() + + if o.Middleware != nil { + return o.Middleware(builder) + } + return o.context.APIHandler(builder) +} + +// Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit +func (o *TimeofdayAPI) Init() { + if len(o.handlers) == 0 { + o.initHandlerCache() + } +} + +// RegisterConsumer allows you to add (or override) a consumer for a media type. +func (o *TimeofdayAPI) RegisterConsumer(mediaType string, consumer runtime.Consumer) { + o.customConsumers[mediaType] = consumer +} + +// RegisterProducer allows you to add (or override) a producer for a media type. +func (o *TimeofdayAPI) RegisterProducer(mediaType string, producer runtime.Producer) { + o.customProducers[mediaType] = producer +} diff --git a/restapi/server.go b/restapi/server.go new file mode 100644 index 0000000..c368247 --- /dev/null +++ b/restapi/server.go @@ -0,0 +1,447 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package restapi + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "io/ioutil" + "log" + "net" + "net/http" + "os" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/go-openapi/runtime/flagext" + "github.com/go-openapi/swag" + flags "github.com/jessevdk/go-flags" + graceful "github.com/tylerb/graceful" + + "github.com/elfsternberg/timeofday/restapi/operations" +) + +const ( + schemeHTTP = "http" + schemeHTTPS = "https" + schemeUnix = "unix" +) + +var defaultSchemes []string + +func init() { + defaultSchemes = []string{ + schemeHTTP, + } +} + +// NewServer creates a new api timeofday server but does not configure it +func NewServer(api *operations.TimeofdayAPI) *Server { + s := new(Server) + + s.shutdown = make(chan struct{}) + s.api = api + return s +} + +// ConfigureAPI configures the API and handlers. +func (s *Server) ConfigureAPI() { + if s.api != nil { + s.handler = configureAPI(s.api) + } +} + +// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse +func (s *Server) ConfigureFlags() { + if s.api != nil { + configureFlags(s.api) + } +} + +// Server for the timeofday API +type Server struct { + EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"` + CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before shutting down the server" default:"10s"` + MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"` + + SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/timeofday.sock"` + domainSocketL net.Listener + + Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"` + Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"` + ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"` + KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"` + ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"` + WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"` + httpServerL net.Listener + + TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"` + TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"` + TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"` + TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure conections" env:"TLS_PRIVATE_KEY"` + TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"` + TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"` + TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"` + TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"` + TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"` + httpsServerL net.Listener + + api *operations.TimeofdayAPI + handler http.Handler + hasListeners bool + shutdown chan struct{} + shuttingDown int32 +} + +// Logf logs message either via defined user logger or via system one if no user logger is defined. +func (s *Server) Logf(f string, args ...interface{}) { + if s.api != nil && s.api.Logger != nil { + s.api.Logger(f, args...) + } else { + log.Printf(f, args...) + } +} + +// Fatalf logs message either via defined user logger or via system one if no user logger is defined. +// Exits with non-zero status after printing +func (s *Server) Fatalf(f string, args ...interface{}) { + if s.api != nil && s.api.Logger != nil { + s.api.Logger(f, args...) + os.Exit(1) + } else { + log.Fatalf(f, args...) + } +} + +// SetAPI configures the server with the specified API. Needs to be called before Serve +func (s *Server) SetAPI(api *operations.TimeofdayAPI) { + if api == nil { + s.api = nil + s.handler = nil + return + } + + s.api = api + s.api.Logger = log.Printf + s.handler = configureAPI(api) +} + +func (s *Server) hasScheme(scheme string) bool { + schemes := s.EnabledListeners + if len(schemes) == 0 { + schemes = defaultSchemes + } + + for _, v := range schemes { + if v == scheme { + return true + } + } + return false +} + +// Serve the api +func (s *Server) Serve() (err error) { + if !s.hasListeners { + if err = s.Listen(); err != nil { + return err + } + } + + // set default handler, if none is set + if s.handler == nil { + if s.api == nil { + return errors.New("can't create the default handler, as no api is set") + } + + s.SetHandler(s.api.Serve(nil)) + } + + var wg sync.WaitGroup + + if s.hasScheme(schemeUnix) { + domainSocket := &graceful.Server{Server: new(http.Server)} + domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize) + domainSocket.Handler = s.handler + domainSocket.LogFunc = s.Logf + if int64(s.CleanupTimeout) > 0 { + domainSocket.Timeout = s.CleanupTimeout + } + + configureServer(domainSocket, "unix", string(s.SocketPath)) + + wg.Add(2) + s.Logf("Serving timeofday at unix://%s", s.SocketPath) + go func(l net.Listener) { + defer wg.Done() + if err := domainSocket.Serve(l); err != nil { + s.Fatalf("%v", err) + } + s.Logf("Stopped serving timeofday at unix://%s", s.SocketPath) + }(s.domainSocketL) + go s.handleShutdown(&wg, domainSocket) + } + + if s.hasScheme(schemeHTTP) { + httpServer := &graceful.Server{Server: new(http.Server)} + httpServer.MaxHeaderBytes = int(s.MaxHeaderSize) + httpServer.ReadTimeout = s.ReadTimeout + httpServer.WriteTimeout = s.WriteTimeout + httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) + httpServer.TCPKeepAlive = s.KeepAlive + if s.ListenLimit > 0 { + httpServer.ListenLimit = s.ListenLimit + } + + if int64(s.CleanupTimeout) > 0 { + httpServer.Timeout = s.CleanupTimeout + } + + httpServer.Handler = s.handler + httpServer.LogFunc = s.Logf + + configureServer(httpServer, "http", s.httpServerL.Addr().String()) + + wg.Add(2) + s.Logf("Serving timeofday at http://%s", s.httpServerL.Addr()) + go func(l net.Listener) { + defer wg.Done() + if err := httpServer.Serve(l); err != nil { + s.Fatalf("%v", err) + } + s.Logf("Stopped serving timeofday at http://%s", l.Addr()) + }(s.httpServerL) + go s.handleShutdown(&wg, httpServer) + } + + if s.hasScheme(schemeHTTPS) { + httpsServer := &graceful.Server{Server: new(http.Server)} + httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize) + httpsServer.ReadTimeout = s.TLSReadTimeout + httpsServer.WriteTimeout = s.TLSWriteTimeout + httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) + httpsServer.TCPKeepAlive = s.TLSKeepAlive + if s.TLSListenLimit > 0 { + httpsServer.ListenLimit = s.TLSListenLimit + } + if int64(s.CleanupTimeout) > 0 { + httpsServer.Timeout = s.CleanupTimeout + } + httpsServer.Handler = s.handler + httpsServer.LogFunc = s.Logf + + // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go + httpsServer.TLSConfig = &tls.Config{ + // Causes servers to use Go's default ciphersuite preferences, + // which are tuned to avoid attacks. Does nothing on clients. + PreferServerCipherSuites: true, + // Only use curves which have assembly implementations + // https://github.com/golang/go/tree/master/src/crypto/elliptic + CurvePreferences: []tls.CurveID{tls.CurveP256}, + // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility + NextProtos: []string{"http/1.1", "h2"}, + // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols + MinVersion: tls.VersionTLS12, + // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + } + + if s.TLSCertificate != "" && s.TLSCertificateKey != "" { + httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) + httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey)) + } + + if s.TLSCACertificate != "" { + caCert, caCertErr := ioutil.ReadFile(string(s.TLSCACertificate)) + if caCertErr != nil { + log.Fatal(caCertErr) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + httpsServer.TLSConfig.ClientCAs = caCertPool + httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert + } + + configureTLS(httpsServer.TLSConfig) + httpsServer.TLSConfig.BuildNameToCertificate() + + if err != nil { + return err + } + + if len(httpsServer.TLSConfig.Certificates) == 0 { + if s.TLSCertificate == "" { + if s.TLSCertificateKey == "" { + s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") + } + s.Fatalf("the required flag `--tls-certificate` was not specified") + } + if s.TLSCertificateKey == "" { + s.Fatalf("the required flag `--tls-key` was not specified") + } + } + + configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) + + wg.Add(2) + s.Logf("Serving timeofday at https://%s", s.httpsServerL.Addr()) + go func(l net.Listener) { + defer wg.Done() + if err := httpsServer.Serve(l); err != nil { + s.Fatalf("%v", err) + } + s.Logf("Stopped serving timeofday at https://%s", l.Addr()) + }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) + go s.handleShutdown(&wg, httpsServer) + } + + wg.Wait() + return nil +} + +// Listen creates the listeners for the server +func (s *Server) Listen() error { + if s.hasListeners { // already done this + return nil + } + + if s.hasScheme(schemeHTTPS) { + // Use http host if https host wasn't defined + if s.TLSHost == "" { + s.TLSHost = s.Host + } + // Use http listen limit if https listen limit wasn't defined + if s.TLSListenLimit == 0 { + s.TLSListenLimit = s.ListenLimit + } + // Use http tcp keep alive if https tcp keep alive wasn't defined + if int64(s.TLSKeepAlive) == 0 { + s.TLSKeepAlive = s.KeepAlive + } + // Use http read timeout if https read timeout wasn't defined + if int64(s.TLSReadTimeout) == 0 { + s.TLSReadTimeout = s.ReadTimeout + } + // Use http write timeout if https write timeout wasn't defined + if int64(s.TLSWriteTimeout) == 0 { + s.TLSWriteTimeout = s.WriteTimeout + } + } + + if s.hasScheme(schemeUnix) { + domSockListener, err := net.Listen("unix", string(s.SocketPath)) + if err != nil { + return err + } + s.domainSocketL = domSockListener + } + + if s.hasScheme(schemeHTTP) { + listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) + if err != nil { + return err + } + + h, p, err := swag.SplitHostPort(listener.Addr().String()) + if err != nil { + return err + } + s.Host = h + s.Port = p + s.httpServerL = listener + } + + if s.hasScheme(schemeHTTPS) { + tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) + if err != nil { + return err + } + + sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) + if err != nil { + return err + } + s.TLSHost = sh + s.TLSPort = sp + s.httpsServerL = tlsListener + } + + s.hasListeners = true + return nil +} + +// Shutdown server and clean up resources +func (s *Server) Shutdown() error { + if atomic.LoadInt32(&s.shuttingDown) != 0 { + s.Logf("already shutting down") + return nil + } + s.shutdown <- struct{}{} + return nil +} + +func (s *Server) handleShutdown(wg *sync.WaitGroup, server *graceful.Server) { + defer wg.Done() + for { + select { + case <-s.shutdown: + atomic.AddInt32(&s.shuttingDown, 1) + server.Stop(s.CleanupTimeout) + <-server.StopChan() + s.api.ServerShutdown() + return + case <-server.StopChan(): + atomic.AddInt32(&s.shuttingDown, 1) + s.api.ServerShutdown() + return + } + } +} + +// GetHandler returns a handler useful for testing +func (s *Server) GetHandler() http.Handler { + return s.handler +} + +// SetHandler allows for setting a http handler on this server +func (s *Server) SetHandler(handler http.Handler) { + s.handler = handler +} + +// UnixListener returns the domain socket listener +func (s *Server) UnixListener() (net.Listener, error) { + if !s.hasListeners { + if err := s.Listen(); err != nil { + return nil, err + } + } + return s.domainSocketL, nil +} + +// HTTPListener returns the http listener +func (s *Server) HTTPListener() (net.Listener, error) { + if !s.hasListeners { + if err := s.Listen(); err != nil { + return nil, err + } + } + return s.httpServerL, nil +} + +// TLSListener returns the https listener +func (s *Server) TLSListener() (net.Listener, error) { + if !s.hasListeners { + if err := s.Listen(); err != nil { + return nil, err + } + } + return s.httpsServerL, nil +}