diff --git a/docs/api_docs/bundle.yaml b/docs/api_docs/bundle.yaml index 5481f31c..ce2d0d2d 100644 --- a/docs/api_docs/bundle.yaml +++ b/docs/api_docs/bundle.yaml @@ -790,6 +790,16 @@ paths: type: integer format: int64 minimum: 1 + - in: query + name: limit + type: integer + format: int64 + description: the number of snapshots to return + - in: query + name: offset + type: integer + format: int64 + description: return snapshots given the offset, it should usually set together with limit responses: '200': description: returns the flag snapshots @@ -1063,6 +1073,7 @@ definitions: type: integer format: int64 minimum: 1 + readOnly: true updatedBy: type: string flag: diff --git a/pkg/handler/crud.go b/pkg/handler/crud.go index 51043227..f24c5f97 100644 --- a/pkg/handler/crud.go +++ b/pkg/handler/crud.go @@ -170,12 +170,20 @@ func (c *crud) GetFlag(params flag.GetFlagParams) middleware.Responder { } func (c *crud) GetFlagSnapshots(params flag.GetFlagSnapshotsParams) middleware.Responder { + tx := getDB() fs := []entity.FlagSnapshot{} - err := getDB(). + + if params.Limit != nil { + tx = tx.Limit(int(*params.Limit)) + } + if params.Offset != nil { + tx = tx.Offset(int(*params.Offset)) + } + + if err := tx. Order("created_at desc"). Where(entity.FlagSnapshot{FlagID: util.SafeUint(params.FlagID)}). - Find(&fs).Error - if err != nil { + Find(&fs).Error; err != nil { return flag.NewGetFlagSnapshotsDefault(500).WithPayload( ErrorMessage("cannot find flag snapshots for %v. %s", params.FlagID, err)) } diff --git a/pkg/handler/crud_test.go b/pkg/handler/crud_test.go index 7c41c560..f6a4f5bc 100644 --- a/pkg/handler/crud_test.go +++ b/pkg/handler/crud_test.go @@ -462,6 +462,53 @@ func TestFindFlags(t *testing.T) { }) } +func TestGetFlagSnapshots(t *testing.T) { + var res middleware.Responder + db := entity.NewTestDB() + c := &crud{} + numOfSnapshots := 10 + + tmpDB, dbErr := db.DB() + if dbErr != nil { + t.Errorf("Failed to get database") + } + + defer tmpDB.Close() + defer gostub.StubFunc(&getDB, db).Reset() + + c.CreateFlag(flag.CreateFlagParams{ + Body: &models.CreateFlagRequest{ + Description: util.StringPtr("funny flag"), + }, + }) + + for i := 0; i < numOfSnapshots; i++ { + entity.SaveFlagSnapshot(db, 1, "flagr-test@example.com") + } + + t.Run("GetFlagSnapshots - got all the results", func(t *testing.T) { + res = c.GetFlagSnapshots(flag.GetFlagSnapshotsParams{}) + assert.Len(t, res.(*flag.GetFlagSnapshotsOK).Payload, numOfSnapshots+1) + }) + + t.Run("GetFlagSnapshots (with limit)", func(t *testing.T) { + res = c.GetFlagSnapshots(flag.GetFlagSnapshotsParams{ + Limit: util.Int64Ptr(int64(2)), + }) + assert.Len(t, res.(*flag.GetFlagSnapshotsOK).Payload, 2) + }) + + t.Run("GetFlagSnapshots (with limit and offset)", func(t *testing.T) { + res = c.GetFlagSnapshots(flag.GetFlagSnapshotsParams{ + Limit: util.Int64Ptr(int64(2)), + Offset: util.Int64Ptr(int64(2)), + }) + assert.Len(t, res.(*flag.GetFlagSnapshotsOK).Payload, 2) + assert.Equal(t, int64(9), res.(*flag.GetFlagSnapshotsOK).Payload[0].ID) + assert.Equal(t, int64(8), res.(*flag.GetFlagSnapshotsOK).Payload[1].ID) + }) +} + func TestCrudSegments(t *testing.T) { var res middleware.Responder db := entity.NewTestDB() diff --git a/pkg/mapper/entity_restapi/e2r/e2r.go b/pkg/mapper/entity_restapi/e2r/e2r.go index 82f10f84..1c771c97 100644 --- a/pkg/mapper/entity_restapi/e2r/e2r.go +++ b/pkg/mapper/entity_restapi/e2r/e2r.go @@ -56,7 +56,7 @@ func MapFlagSnapshot(e *entity.FlagSnapshot) (*models.FlagSnapshot, error) { } r := &models.FlagSnapshot{ Flag: f, - ID: util.Int64Ptr(int64(e.ID)), + ID: int64(e.ID), UpdatedBy: e.UpdatedBy, UpdatedAt: util.StringPtr(e.UpdatedAt.UTC().Format(time.RFC3339)), } diff --git a/swagger/flag_snapshots.yaml b/swagger/flag_snapshots.yaml index 01855c0a..9cae19ef 100644 --- a/swagger/flag_snapshots.yaml +++ b/swagger/flag_snapshots.yaml @@ -10,6 +10,16 @@ get: type: integer format: int64 minimum: 1 + - in: query + name: limit + type: integer + format: int64 + description: the number of snapshots to return + - in: query + name: offset + type: integer + format: int64 + description: return snapshots given the offset, it should usually set together with limit responses: 200: description: returns the flag snapshots diff --git a/swagger/index.yaml b/swagger/index.yaml index d632d495..ee26ab93 100644 --- a/swagger/index.yaml +++ b/swagger/index.yaml @@ -203,6 +203,7 @@ definitions: type: integer format: int64 minimum: 1 + readOnly: true updatedBy: type: string flag: diff --git a/swagger_gen/models/flag_snapshot.go b/swagger_gen/models/flag_snapshot.go index 6c81e87e..7ca500bb 100644 --- a/swagger_gen/models/flag_snapshot.go +++ b/swagger_gen/models/flag_snapshot.go @@ -25,8 +25,9 @@ type FlagSnapshot struct { // id // Required: true + // Read Only: true // Minimum: 1 - ID *int64 `json:"id"` + ID int64 `json:"id"` // updated at // Required: true @@ -81,11 +82,11 @@ func (m *FlagSnapshot) validateFlag(formats strfmt.Registry) error { func (m *FlagSnapshot) validateID(formats strfmt.Registry) error { - if err := validate.Required("id", "body", m.ID); err != nil { + if err := validate.Required("id", "body", int64(m.ID)); err != nil { return err } - if err := validate.MinimumInt("id", "body", *m.ID, 1, false); err != nil { + if err := validate.MinimumInt("id", "body", m.ID, 1, false); err != nil { return err } @@ -113,6 +114,10 @@ func (m *FlagSnapshot) ContextValidate(ctx context.Context, formats strfmt.Regis res = append(res, err) } + if err := m.contextValidateID(ctx, formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -136,6 +141,15 @@ func (m *FlagSnapshot) contextValidateFlag(ctx context.Context, formats strfmt.R return nil } +func (m *FlagSnapshot) contextValidateID(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "id", "body", int64(m.ID)); err != nil { + return err + } + + return nil +} + // MarshalBinary interface implementation func (m *FlagSnapshot) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/swagger_gen/restapi/embedded_spec.go b/swagger_gen/restapi/embedded_spec.go index b3020cfb..6b60238d 100644 --- a/swagger_gen/restapi/embedded_spec.go +++ b/swagger_gen/restapi/embedded_spec.go @@ -993,6 +993,20 @@ func init() { "name": "flagID", "in": "path", "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "the number of snapshots to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "description": "return snapshots given the offset, it should usually set together with limit", + "name": "offset", + "in": "query" } ], "responses": { @@ -1799,7 +1813,8 @@ func init() { "id": { "type": "integer", "format": "int64", - "minimum": 1 + "minimum": 1, + "readOnly": true }, "updatedAt": { "type": "string", @@ -3057,6 +3072,20 @@ func init() { "name": "flagID", "in": "path", "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "the number of snapshots to return", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "description": "return snapshots given the offset, it should usually set together with limit", + "name": "offset", + "in": "query" } ], "responses": { @@ -3865,7 +3894,8 @@ func init() { "id": { "type": "integer", "format": "int64", - "minimum": 1 + "minimum": 1, + "readOnly": true }, "updatedAt": { "type": "string", diff --git a/swagger_gen/restapi/operations/flag/get_flag_snapshots_parameters.go b/swagger_gen/restapi/operations/flag/get_flag_snapshots_parameters.go index ca1c627d..a755affc 100644 --- a/swagger_gen/restapi/operations/flag/get_flag_snapshots_parameters.go +++ b/swagger_gen/restapi/operations/flag/get_flag_snapshots_parameters.go @@ -9,6 +9,7 @@ import ( "net/http" "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -38,6 +39,14 @@ type GetFlagSnapshotsParams struct { In: path */ FlagID int64 + /*the number of snapshots to return + In: query + */ + Limit *int64 + /*return snapshots given the offset, it should usually set together with limit + In: query + */ + Offset *int64 } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -49,10 +58,22 @@ func (o *GetFlagSnapshotsParams) BindRequest(r *http.Request, route *middleware. o.HTTPRequest = r + qs := runtime.Values(r.URL.Query()) + rFlagID, rhkFlagID, _ := route.Params.GetOK("flagID") if err := o.bindFlagID(rFlagID, rhkFlagID, route.Formats); err != nil { res = append(res, err) } + + qLimit, qhkLimit, _ := qs.GetOK("limit") + if err := o.bindLimit(qLimit, qhkLimit, route.Formats); err != nil { + res = append(res, err) + } + + qOffset, qhkOffset, _ := qs.GetOK("offset") + if err := o.bindOffset(qOffset, qhkOffset, route.Formats); err != nil { + res = append(res, err) + } if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -91,3 +112,49 @@ func (o *GetFlagSnapshotsParams) validateFlagID(formats strfmt.Registry) error { return nil } + +// bindLimit binds and validates parameter Limit from query. +func (o *GetFlagSnapshotsParams) bindLimit(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + + value, err := swag.ConvertInt64(raw) + if err != nil { + return errors.InvalidType("limit", "query", "int64", raw) + } + o.Limit = &value + + return nil +} + +// bindOffset binds and validates parameter Offset from query. +func (o *GetFlagSnapshotsParams) bindOffset(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + return nil + } + + value, err := swag.ConvertInt64(raw) + if err != nil { + return errors.InvalidType("offset", "query", "int64", raw) + } + o.Offset = &value + + return nil +} diff --git a/swagger_gen/restapi/operations/flag/get_flag_snapshots_urlbuilder.go b/swagger_gen/restapi/operations/flag/get_flag_snapshots_urlbuilder.go index 67d13ea6..ba1a54f2 100644 --- a/swagger_gen/restapi/operations/flag/get_flag_snapshots_urlbuilder.go +++ b/swagger_gen/restapi/operations/flag/get_flag_snapshots_urlbuilder.go @@ -18,6 +18,9 @@ import ( type GetFlagSnapshotsURL struct { FlagID int64 + Limit *int64 + Offset *int64 + _basePath string // avoid unkeyed usage _ struct{} @@ -57,6 +60,26 @@ func (o *GetFlagSnapshotsURL) Build() (*url.URL, error) { } _result.Path = golangswaggerpaths.Join(_basePath, _path) + qs := make(url.Values) + + var limitQ string + if o.Limit != nil { + limitQ = swag.FormatInt64(*o.Limit) + } + if limitQ != "" { + qs.Set("limit", limitQ) + } + + var offsetQ string + if o.Offset != nil { + offsetQ = swag.FormatInt64(*o.Offset) + } + if offsetQ != "" { + qs.Set("offset", offsetQ) + } + + _result.RawQuery = qs.Encode() + return &_result, nil }