diff --git a/pkg/handler/crud.go b/pkg/handler/crud.go index cbd5cf97..7b806b3c 100644 --- a/pkg/handler/crud.go +++ b/pkg/handler/crud.go @@ -10,6 +10,7 @@ import ( "github.com/checkr/flagr/swagger_gen/restapi/operations/distribution" "github.com/checkr/flagr/swagger_gen/restapi/operations/flag" "github.com/checkr/flagr/swagger_gen/restapi/operations/segment" + "github.com/checkr/flagr/swagger_gen/restapi/operations/variant" "github.com/go-openapi/runtime/middleware" ) @@ -33,6 +34,10 @@ type CRUD interface { // Distributions FindDistributions(distribution.FindDistributionsParams) middleware.Responder PutDistributions(distribution.PutDistributionsParams) middleware.Responder + + // Variants + CreateVariant(variant.CreateVariantParams) middleware.Responder + FindVariants(variant.FindVariantsParams) middleware.Responder } // NewCRUD creates a new CRUD instance @@ -121,7 +126,7 @@ func (c *crud) CreateSegment(params segment.CreateSegmentParams) middleware.Resp err := s.Create(repo.GetDB()) if err != nil { - return segment.NewFindSegmentsDefault(500) + return segment.NewCreateSegmentDefault(500) } resp := segment.NewCreateSegmentOK() @@ -218,3 +223,32 @@ func (c *crud) FindDistributions(params distribution.FindDistributionsParams) mi resp.SetPayload(e2r.MapDistributions(ds)) return resp } + +func (c *crud) CreateVariant(params variant.CreateVariantParams) middleware.Responder { + v := &entity.Variant{} + v.FlagID = uint(params.FlagID) + v.Key = util.SafeString(params.Body.Key) + + err := v.Create(repo.GetDB()) + if err != nil { + return variant.NewCreateVariantDefault(500) + } + + resp := variant.NewCreateVariantOK() + resp.SetPayload(e2r.MapVariant(v)) + return resp +} + +func (c *crud) FindVariants(params variant.FindVariantsParams) middleware.Responder { + vs := []entity.Variant{} + + q := entity.NewVariantQuerySet(repo.GetDB()) + err := q.FlagIDEq(uint(params.FlagID)).OrderAscByID().All(&vs) + if err != nil { + return variant.NewFindVariantsDefault(500) + } + + resp := variant.NewFindVariantsOK() + resp.SetPayload(e2r.MapVariants(vs)) + return resp +} diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 0d217a4c..0a4eb91a 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -7,6 +7,7 @@ import ( "github.com/checkr/flagr/swagger_gen/restapi/operations/evaluation" "github.com/checkr/flagr/swagger_gen/restapi/operations/flag" "github.com/checkr/flagr/swagger_gen/restapi/operations/segment" + "github.com/checkr/flagr/swagger_gen/restapi/operations/variant" ) // Setup initialize all the hanlder functions @@ -31,6 +32,10 @@ func Setup(api *operations.FlagrAPI) { api.DistributionFindDistributionsHandler = distribution.FindDistributionsHandlerFunc(c.FindDistributions) api.DistributionPutDistributionsHandler = distribution.PutDistributionsHandlerFunc(c.PutDistributions) + // variants + api.VariantCreateVariantHandler = variant.CreateVariantHandlerFunc(c.CreateVariant) + api.VariantFindVariantsHandler = variant.FindVariantsHandlerFunc(c.FindVariants) + // evaluation e := NewEval() api.EvaluationPostEvaluationHandler = evaluation.PostEvaluationHandlerFunc(e.PostEvaluation) diff --git a/pkg/mapper/entity_restapi/e2r/e2r.go b/pkg/mapper/entity_restapi/e2r/e2r.go index fe296e05..f55ab230 100644 --- a/pkg/mapper/entity_restapi/e2r/e2r.go +++ b/pkg/mapper/entity_restapi/e2r/e2r.go @@ -81,3 +81,21 @@ func MapDistributions(e []entity.Distribution) []*models.Distribution { } return ret } + +// MapVariant maps variant +func MapVariant(e *entity.Variant) *models.Variant { + r := &models.Variant{ + ID: int64(e.ID), + Key: util.StringPtr(e.Key), + } + return r +} + +// MapVariants maps variant +func MapVariants(e []entity.Variant) []*models.Variant { + ret := make([]*models.Variant, len(e), len(e)) + for i, v := range e { + ret[i] = MapVariant(&v) + } + return ret +} diff --git a/swagger.yml b/swagger.yml index ebb81563..8326e974 100644 --- a/swagger.yml +++ b/swagger.yml @@ -140,6 +140,33 @@ paths: description: generic error response schema: $ref: "#/definitions/error" + post: + tags: + - variant + operationId: createVariant + parameters: + - in: path + name: flagID + description: numeric ID of the flag + required: true + type: integer + format: int32 + minimum: 1 + - in: body + name: body + description: create a variant + required: true + schema: + $ref: "#/definitions/createVariantRequest" + responses: + 200: + description: variant just created + schema: + $ref: "#/definitions/variant" + default: + description: generic error response + schema: + $ref: "#/definitions/error" /flags/{flagID}/segments: get: tags: @@ -376,6 +403,14 @@ definitions: format: int32 minimum: 0 maximum: 100 + createVariantRequest: + type: object + required: + - key + properties: + key: + type: string + minLength: 1 createConstraintRequest: type: object required: diff --git a/swagger_gen/models/create_variant_request.go b/swagger_gen/models/create_variant_request.go new file mode 100644 index 00000000..c1f5a2e5 --- /dev/null +++ b/swagger_gen/models/create_variant_request.go @@ -0,0 +1,73 @@ +// 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" +) + +// CreateVariantRequest create variant request +// swagger:model createVariantRequest + +type CreateVariantRequest struct { + + // key + // Required: true + // Min Length: 1 + Key *string `json:"key"` +} + +/* polymorph createVariantRequest key false */ + +// Validate validates this create variant request +func (m *CreateVariantRequest) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateKey(formats); err != nil { + // prop + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *CreateVariantRequest) validateKey(formats strfmt.Registry) error { + + if err := validate.Required("key", "body", m.Key); err != nil { + return err + } + + if err := validate.MinLength("key", "body", string(*m.Key), 1); err != nil { + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *CreateVariantRequest) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *CreateVariantRequest) UnmarshalBinary(b []byte) error { + var res CreateVariantRequest + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/swagger_gen/restapi/embedded_spec.go b/swagger_gen/restapi/embedded_spec.go index e8abe413..35097e24 100644 --- a/swagger_gen/restapi/embedded_spec.go +++ b/swagger_gen/restapi/embedded_spec.go @@ -505,6 +505,46 @@ func init() { } } } + }, + "post": { + "tags": [ + "variant" + ], + "operationId": "createVariant", + "parameters": [ + { + "minimum": 1, + "type": "integer", + "format": "int32", + "description": "numeric ID of the flag", + "name": "flagID", + "in": "path", + "required": true + }, + { + "description": "create a variant", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/createVariantRequest" + } + } + ], + "responses": { + "200": { + "description": "variant just created", + "schema": { + "$ref": "#/definitions/variant" + } + }, + "default": { + "description": "generic error response", + "schema": { + "$ref": "#/definitions/error" + } + } + } } } }, @@ -603,6 +643,18 @@ func init() { } } }, + "createVariantRequest": { + "type": "object", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "string", + "minLength": 1 + } + } + }, "distribution": { "type": "object", "required": [ diff --git a/swagger_gen/restapi/operations/flagr_api.go b/swagger_gen/restapi/operations/flagr_api.go index e3511de6..29329fa9 100644 --- a/swagger_gen/restapi/operations/flagr_api.go +++ b/swagger_gen/restapi/operations/flagr_api.go @@ -51,6 +51,9 @@ func NewFlagrAPI(spec *loads.Document) *FlagrAPI { SegmentCreateSegmentHandler: segment.CreateSegmentHandlerFunc(func(params segment.CreateSegmentParams) middleware.Responder { return middleware.NotImplemented("operation SegmentCreateSegment has not yet been implemented") }), + VariantCreateVariantHandler: variant.CreateVariantHandlerFunc(func(params variant.CreateVariantParams) middleware.Responder { + return middleware.NotImplemented("operation VariantCreateVariant has not yet been implemented") + }), FlagDeleteFlagHandler: flag.DeleteFlagHandlerFunc(func(params flag.DeleteFlagParams) middleware.Responder { return middleware.NotImplemented("operation FlagDeleteFlag has not yet been implemented") }), @@ -116,6 +119,8 @@ type FlagrAPI struct { FlagCreateFlagHandler flag.CreateFlagHandler // SegmentCreateSegmentHandler sets the operation handler for the create segment operation SegmentCreateSegmentHandler segment.CreateSegmentHandler + // VariantCreateVariantHandler sets the operation handler for the create variant operation + VariantCreateVariantHandler variant.CreateVariantHandler // FlagDeleteFlagHandler sets the operation handler for the delete flag operation FlagDeleteFlagHandler flag.DeleteFlagHandler // ConstraintFindConstraintsHandler sets the operation handler for the find constraints operation @@ -211,6 +216,10 @@ func (o *FlagrAPI) Validate() error { unregistered = append(unregistered, "segment.CreateSegmentHandler") } + if o.VariantCreateVariantHandler == nil { + unregistered = append(unregistered, "variant.CreateVariantHandler") + } + if o.FlagDeleteFlagHandler == nil { unregistered = append(unregistered, "flag.DeleteFlagHandler") } @@ -356,6 +365,11 @@ func (o *FlagrAPI) initHandlerCache() { } o.handlers["POST"]["/flags/{flagID}/segments"] = segment.NewCreateSegment(o.context, o.SegmentCreateSegmentHandler) + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"]["/flags/{flagID}/variants"] = variant.NewCreateVariant(o.context, o.VariantCreateVariantHandler) + if o.handlers["DELETE"] == nil { o.handlers["DELETE"] = make(map[string]http.Handler) } diff --git a/swagger_gen/restapi/operations/variant/create_variant.go b/swagger_gen/restapi/operations/variant/create_variant.go new file mode 100644 index 00000000..15e32bde --- /dev/null +++ b/swagger_gen/restapi/operations/variant/create_variant.go @@ -0,0 +1,58 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package variant + +// 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" +) + +// CreateVariantHandlerFunc turns a function with the right signature into a create variant handler +type CreateVariantHandlerFunc func(CreateVariantParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn CreateVariantHandlerFunc) Handle(params CreateVariantParams) middleware.Responder { + return fn(params) +} + +// CreateVariantHandler interface for that can handle valid create variant params +type CreateVariantHandler interface { + Handle(CreateVariantParams) middleware.Responder +} + +// NewCreateVariant creates a new http.Handler for the create variant operation +func NewCreateVariant(ctx *middleware.Context, handler CreateVariantHandler) *CreateVariant { + return &CreateVariant{Context: ctx, Handler: handler} +} + +/*CreateVariant swagger:route POST /flags/{flagID}/variants variant createVariant + +CreateVariant create variant API + +*/ +type CreateVariant struct { + Context *middleware.Context + Handler CreateVariantHandler +} + +func (o *CreateVariant) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + var Params = NewCreateVariantParams() + + 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/swagger_gen/restapi/operations/variant/create_variant_parameters.go b/swagger_gen/restapi/operations/variant/create_variant_parameters.go new file mode 100644 index 00000000..08f3f8f0 --- /dev/null +++ b/swagger_gen/restapi/operations/variant/create_variant_parameters.go @@ -0,0 +1,119 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package variant + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" + + strfmt "github.com/go-openapi/strfmt" + + "github.com/checkr/flagr/swagger_gen/models" +) + +// NewCreateVariantParams creates a new CreateVariantParams object +// with the default values initialized. +func NewCreateVariantParams() CreateVariantParams { + var () + return CreateVariantParams{} +} + +// CreateVariantParams contains all the bound params for the create variant operation +// typically these are obtained from a http.Request +// +// swagger:parameters createVariant +type CreateVariantParams struct { + + // HTTP Request Object + HTTPRequest *http.Request + + /*create a variant + Required: true + In: body + */ + Body *models.CreateVariantRequest + /*numeric ID of the flag + Required: true + Minimum: 1 + In: path + */ + FlagID int32 +} + +// 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 +func (o *CreateVariantParams) 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.CreateVariantRequest + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("body", "body")) + } else { + res = append(res, errors.NewParseError("body", "body", "", err)) + } + + } else { + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Body = &body + } + } + + } else { + res = append(res, errors.Required("body", "body")) + } + + rFlagID, rhkFlagID, _ := route.Params.GetOK("flagID") + if err := o.bindFlagID(rFlagID, rhkFlagID, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (o *CreateVariantParams) bindFlagID(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + value, err := swag.ConvertInt32(raw) + if err != nil { + return errors.InvalidType("flagID", "path", "int32", raw) + } + o.FlagID = value + + if err := o.validateFlagID(formats); err != nil { + return err + } + + return nil +} + +func (o *CreateVariantParams) validateFlagID(formats strfmt.Registry) error { + + if err := validate.MinimumInt("flagID", "path", int64(o.FlagID), 1, false); err != nil { + return err + } + + return nil +} diff --git a/swagger_gen/restapi/operations/variant/create_variant_responses.go b/swagger_gen/restapi/operations/variant/create_variant_responses.go new file mode 100644 index 00000000..9ffb3a00 --- /dev/null +++ b/swagger_gen/restapi/operations/variant/create_variant_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package variant + +// 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" + + "github.com/checkr/flagr/swagger_gen/models" +) + +// CreateVariantOKCode is the HTTP code returned for type CreateVariantOK +const CreateVariantOKCode int = 200 + +/*CreateVariantOK variant just created + +swagger:response createVariantOK +*/ +type CreateVariantOK struct { + + /* + In: Body + */ + Payload *models.Variant `json:"body,omitempty"` +} + +// NewCreateVariantOK creates CreateVariantOK with default headers values +func NewCreateVariantOK() *CreateVariantOK { + return &CreateVariantOK{} +} + +// WithPayload adds the payload to the create variant o k response +func (o *CreateVariantOK) WithPayload(payload *models.Variant) *CreateVariantOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the create variant o k response +func (o *CreateVariantOK) SetPayload(payload *models.Variant) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *CreateVariantOK) 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 + } + } +} + +/*CreateVariantDefault generic error response + +swagger:response createVariantDefault +*/ +type CreateVariantDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewCreateVariantDefault creates CreateVariantDefault with default headers values +func NewCreateVariantDefault(code int) *CreateVariantDefault { + if code <= 0 { + code = 500 + } + + return &CreateVariantDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the create variant default response +func (o *CreateVariantDefault) WithStatusCode(code int) *CreateVariantDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the create variant default response +func (o *CreateVariantDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the create variant default response +func (o *CreateVariantDefault) WithPayload(payload *models.Error) *CreateVariantDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the create variant default response +func (o *CreateVariantDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *CreateVariantDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + 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/swagger_gen/restapi/operations/variant/create_variant_urlbuilder.go b/swagger_gen/restapi/operations/variant/create_variant_urlbuilder.go new file mode 100644 index 00000000..9152098e --- /dev/null +++ b/swagger_gen/restapi/operations/variant/create_variant_urlbuilder.go @@ -0,0 +1,100 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package variant + +// 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" + "strings" + + "github.com/go-openapi/swag" +) + +// CreateVariantURL generates an URL for the create variant operation +type CreateVariantURL struct { + FlagID int32 + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// 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 *CreateVariantURL) WithBasePath(bp string) *CreateVariantURL { + 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 *CreateVariantURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *CreateVariantURL) Build() (*url.URL, error) { + var result url.URL + + var _path = "/flags/{flagID}/variants" + + flagID := swag.FormatInt32(o.FlagID) + if flagID != "" { + _path = strings.Replace(_path, "{flagID}", flagID, -1) + } else { + return nil, errors.New("FlagID is required on CreateVariantURL") + } + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api" + } + 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 *CreateVariantURL) 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 *CreateVariantURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *CreateVariantURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on CreateVariantURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on CreateVariantURL") + } + + 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 *CreateVariantURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +}