diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 58249a82..32215ac4 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -275,6 +275,7 @@ var ( PermsColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, {Name: "repo_perm", Type: field.TypeEnum, Enums: []string{"read", "write", "admin"}, Default: "read"}, + {Name: "synced_at", Type: field.TypeTime, Nullable: true}, {Name: "created_at", Type: field.TypeTime}, {Name: "updated_at", Type: field.TypeTime}, {Name: "repo_id", Type: field.TypeInt64, Nullable: true}, @@ -288,13 +289,13 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "perms_repos_perms", - Columns: []*schema.Column{PermsColumns[4]}, + Columns: []*schema.Column{PermsColumns[5]}, RefColumns: []*schema.Column{ReposColumns[0]}, OnDelete: schema.Cascade, }, { Symbol: "perms_users_perms", - Columns: []*schema.Column{PermsColumns[5]}, + Columns: []*schema.Column{PermsColumns[6]}, RefColumns: []*schema.Column{UsersColumns[0]}, OnDelete: schema.Cascade, }, @@ -303,12 +304,12 @@ var ( { Name: "perm_repo_id_user_id", Unique: false, - Columns: []*schema.Column{PermsColumns[4], PermsColumns[5]}, + Columns: []*schema.Column{PermsColumns[5], PermsColumns[6]}, }, { - Name: "perm_user_id_updated_at", + Name: "perm_user_id_synced_at", Unique: false, - Columns: []*schema.Column{PermsColumns[5], PermsColumns[3]}, + Columns: []*schema.Column{PermsColumns[6], PermsColumns[2]}, }, }, } diff --git a/ent/mutation.go b/ent/mutation.go index b6f57598..f5d06da3 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -5939,6 +5939,7 @@ type PermMutation struct { typ string id *int repo_perm *perm.RepoPerm + synced_at *time.Time created_at *time.Time updated_at *time.Time clearedFields map[string]struct{} @@ -6066,6 +6067,55 @@ func (m *PermMutation) ResetRepoPerm() { m.repo_perm = nil } +// SetSyncedAt sets the "synced_at" field. +func (m *PermMutation) SetSyncedAt(t time.Time) { + m.synced_at = &t +} + +// SyncedAt returns the value of the "synced_at" field in the mutation. +func (m *PermMutation) SyncedAt() (r time.Time, exists bool) { + v := m.synced_at + if v == nil { + return + } + return *v, true +} + +// OldSyncedAt returns the old "synced_at" field's value of the Perm entity. +// If the Perm object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PermMutation) OldSyncedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldSyncedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldSyncedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSyncedAt: %w", err) + } + return oldValue.SyncedAt, nil +} + +// ClearSyncedAt clears the value of the "synced_at" field. +func (m *PermMutation) ClearSyncedAt() { + m.synced_at = nil + m.clearedFields[perm.FieldSyncedAt] = struct{}{} +} + +// SyncedAtCleared returns if the "synced_at" field was cleared in this mutation. +func (m *PermMutation) SyncedAtCleared() bool { + _, ok := m.clearedFields[perm.FieldSyncedAt] + return ok +} + +// ResetSyncedAt resets all changes to the "synced_at" field. +func (m *PermMutation) ResetSyncedAt() { + m.synced_at = nil + delete(m.clearedFields, perm.FieldSyncedAt) +} + // SetCreatedAt sets the "created_at" field. func (m *PermMutation) SetCreatedAt(t time.Time) { m.created_at = &t @@ -6281,10 +6331,13 @@ func (m *PermMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *PermMutation) Fields() []string { - fields := make([]string, 0, 5) + fields := make([]string, 0, 6) if m.repo_perm != nil { fields = append(fields, perm.FieldRepoPerm) } + if m.synced_at != nil { + fields = append(fields, perm.FieldSyncedAt) + } if m.created_at != nil { fields = append(fields, perm.FieldCreatedAt) } @@ -6307,6 +6360,8 @@ func (m *PermMutation) Field(name string) (ent.Value, bool) { switch name { case perm.FieldRepoPerm: return m.RepoPerm() + case perm.FieldSyncedAt: + return m.SyncedAt() case perm.FieldCreatedAt: return m.CreatedAt() case perm.FieldUpdatedAt: @@ -6326,6 +6381,8 @@ func (m *PermMutation) OldField(ctx context.Context, name string) (ent.Value, er switch name { case perm.FieldRepoPerm: return m.OldRepoPerm(ctx) + case perm.FieldSyncedAt: + return m.OldSyncedAt(ctx) case perm.FieldCreatedAt: return m.OldCreatedAt(ctx) case perm.FieldUpdatedAt: @@ -6350,6 +6407,13 @@ func (m *PermMutation) SetField(name string, value ent.Value) error { } m.SetRepoPerm(v) return nil + case perm.FieldSyncedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSyncedAt(v) + return nil case perm.FieldCreatedAt: v, ok := value.(time.Time) if !ok { @@ -6410,7 +6474,11 @@ func (m *PermMutation) AddField(name string, value ent.Value) error { // ClearedFields returns all nullable fields that were cleared during this // mutation. func (m *PermMutation) ClearedFields() []string { - return nil + var fields []string + if m.FieldCleared(perm.FieldSyncedAt) { + fields = append(fields, perm.FieldSyncedAt) + } + return fields } // FieldCleared returns a boolean indicating if a field with the given name was @@ -6423,6 +6491,11 @@ func (m *PermMutation) FieldCleared(name string) bool { // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. func (m *PermMutation) ClearField(name string) error { + switch name { + case perm.FieldSyncedAt: + m.ClearSyncedAt() + return nil + } return fmt.Errorf("unknown Perm nullable field %s", name) } @@ -6433,6 +6506,9 @@ func (m *PermMutation) ResetField(name string) error { case perm.FieldRepoPerm: m.ResetRepoPerm() return nil + case perm.FieldSyncedAt: + m.ResetSyncedAt() + return nil case perm.FieldCreatedAt: m.ResetCreatedAt() return nil diff --git a/ent/perm.go b/ent/perm.go index f3b7aaeb..00918d0f 100644 --- a/ent/perm.go +++ b/ent/perm.go @@ -20,6 +20,8 @@ type Perm struct { ID int `json:"id,omitempty"` // RepoPerm holds the value of the "repo_perm" field. RepoPerm perm.RepoPerm `json:"repo_perm"` + // SyncedAt holds the value of the "synced_at" field. + SyncedAt time.Time `json:"synced_at,omitemtpy"` // CreatedAt holds the value of the "created_at" field. CreatedAt time.Time `json:"created_at"` // UpdatedAt holds the value of the "updated_at" field. @@ -81,7 +83,7 @@ func (*Perm) scanValues(columns []string) ([]interface{}, error) { values[i] = new(sql.NullInt64) case perm.FieldRepoPerm: values[i] = new(sql.NullString) - case perm.FieldCreatedAt, perm.FieldUpdatedAt: + case perm.FieldSyncedAt, perm.FieldCreatedAt, perm.FieldUpdatedAt: values[i] = new(sql.NullTime) default: return nil, fmt.Errorf("unexpected column %q for type Perm", columns[i]) @@ -110,6 +112,12 @@ func (pe *Perm) assignValues(columns []string, values []interface{}) error { } else if value.Valid { pe.RepoPerm = perm.RepoPerm(value.String) } + case perm.FieldSyncedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field synced_at", values[i]) + } else if value.Valid { + pe.SyncedAt = value.Time + } case perm.FieldCreatedAt: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field created_at", values[i]) @@ -174,6 +182,8 @@ func (pe *Perm) String() string { builder.WriteString(fmt.Sprintf("id=%v", pe.ID)) builder.WriteString(", repo_perm=") builder.WriteString(fmt.Sprintf("%v", pe.RepoPerm)) + builder.WriteString(", synced_at=") + builder.WriteString(pe.SyncedAt.Format(time.ANSIC)) builder.WriteString(", created_at=") builder.WriteString(pe.CreatedAt.Format(time.ANSIC)) builder.WriteString(", updated_at=") diff --git a/ent/perm/perm.go b/ent/perm/perm.go index 22a2a963..f84008d9 100644 --- a/ent/perm/perm.go +++ b/ent/perm/perm.go @@ -14,6 +14,8 @@ const ( FieldID = "id" // FieldRepoPerm holds the string denoting the repo_perm field in the database. FieldRepoPerm = "repo_perm" + // FieldSyncedAt holds the string denoting the synced_at field in the database. + FieldSyncedAt = "synced_at" // FieldCreatedAt holds the string denoting the created_at field in the database. FieldCreatedAt = "created_at" // FieldUpdatedAt holds the string denoting the updated_at field in the database. @@ -48,6 +50,7 @@ const ( var Columns = []string{ FieldID, FieldRepoPerm, + FieldSyncedAt, FieldCreatedAt, FieldUpdatedAt, FieldUserID, diff --git a/ent/perm/where.go b/ent/perm/where.go index 70a82645..d2ce2575 100644 --- a/ent/perm/where.go +++ b/ent/perm/where.go @@ -93,6 +93,13 @@ func IDLTE(id int) predicate.Perm { }) } +// SyncedAt applies equality check predicate on the "synced_at" field. It's identical to SyncedAtEQ. +func SyncedAt(v time.Time) predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldSyncedAt), v)) + }) +} + // CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. func CreatedAt(v time.Time) predicate.Perm { return predicate.Perm(func(s *sql.Selector) { @@ -169,6 +176,96 @@ func RepoPermNotIn(vs ...RepoPerm) predicate.Perm { }) } +// SyncedAtEQ applies the EQ predicate on the "synced_at" field. +func SyncedAtEQ(v time.Time) predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldSyncedAt), v)) + }) +} + +// SyncedAtNEQ applies the NEQ predicate on the "synced_at" field. +func SyncedAtNEQ(v time.Time) predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldSyncedAt), v)) + }) +} + +// SyncedAtIn applies the In predicate on the "synced_at" field. +func SyncedAtIn(vs ...time.Time) predicate.Perm { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Perm(func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.In(s.C(FieldSyncedAt), v...)) + }) +} + +// SyncedAtNotIn applies the NotIn predicate on the "synced_at" field. +func SyncedAtNotIn(vs ...time.Time) predicate.Perm { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Perm(func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.NotIn(s.C(FieldSyncedAt), v...)) + }) +} + +// SyncedAtGT applies the GT predicate on the "synced_at" field. +func SyncedAtGT(v time.Time) predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldSyncedAt), v)) + }) +} + +// SyncedAtGTE applies the GTE predicate on the "synced_at" field. +func SyncedAtGTE(v time.Time) predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldSyncedAt), v)) + }) +} + +// SyncedAtLT applies the LT predicate on the "synced_at" field. +func SyncedAtLT(v time.Time) predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldSyncedAt), v)) + }) +} + +// SyncedAtLTE applies the LTE predicate on the "synced_at" field. +func SyncedAtLTE(v time.Time) predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldSyncedAt), v)) + }) +} + +// SyncedAtIsNil applies the IsNil predicate on the "synced_at" field. +func SyncedAtIsNil() predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.IsNull(s.C(FieldSyncedAt))) + }) +} + +// SyncedAtNotNil applies the NotNil predicate on the "synced_at" field. +func SyncedAtNotNil() predicate.Perm { + return predicate.Perm(func(s *sql.Selector) { + s.Where(sql.NotNull(s.C(FieldSyncedAt))) + }) +} + // CreatedAtEQ applies the EQ predicate on the "created_at" field. func CreatedAtEQ(v time.Time) predicate.Perm { return predicate.Perm(func(s *sql.Selector) { diff --git a/ent/perm_create.go b/ent/perm_create.go index f9bd0c52..2796a17e 100644 --- a/ent/perm_create.go +++ b/ent/perm_create.go @@ -36,6 +36,20 @@ func (pc *PermCreate) SetNillableRepoPerm(pp *perm.RepoPerm) *PermCreate { return pc } +// SetSyncedAt sets the "synced_at" field. +func (pc *PermCreate) SetSyncedAt(t time.Time) *PermCreate { + pc.mutation.SetSyncedAt(t) + return pc +} + +// SetNillableSyncedAt sets the "synced_at" field if the given value is not nil. +func (pc *PermCreate) SetNillableSyncedAt(t *time.Time) *PermCreate { + if t != nil { + pc.SetSyncedAt(*t) + } + return pc +} + // SetCreatedAt sets the "created_at" field. func (pc *PermCreate) SetCreatedAt(t time.Time) *PermCreate { pc.mutation.SetCreatedAt(t) @@ -234,6 +248,14 @@ func (pc *PermCreate) createSpec() (*Perm, *sqlgraph.CreateSpec) { }) _node.RepoPerm = value } + if value, ok := pc.mutation.SyncedAt(); ok { + _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: perm.FieldSyncedAt, + }) + _node.SyncedAt = value + } if value, ok := pc.mutation.CreatedAt(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ Type: field.TypeTime, diff --git a/ent/perm_update.go b/ent/perm_update.go index 5807d5ee..d7bf6984 100644 --- a/ent/perm_update.go +++ b/ent/perm_update.go @@ -44,6 +44,26 @@ func (pu *PermUpdate) SetNillableRepoPerm(pp *perm.RepoPerm) *PermUpdate { return pu } +// SetSyncedAt sets the "synced_at" field. +func (pu *PermUpdate) SetSyncedAt(t time.Time) *PermUpdate { + pu.mutation.SetSyncedAt(t) + return pu +} + +// SetNillableSyncedAt sets the "synced_at" field if the given value is not nil. +func (pu *PermUpdate) SetNillableSyncedAt(t *time.Time) *PermUpdate { + if t != nil { + pu.SetSyncedAt(*t) + } + return pu +} + +// ClearSyncedAt clears the value of the "synced_at" field. +func (pu *PermUpdate) ClearSyncedAt() *PermUpdate { + pu.mutation.ClearSyncedAt() + return pu +} + // SetCreatedAt sets the "created_at" field. func (pu *PermUpdate) SetCreatedAt(t time.Time) *PermUpdate { pu.mutation.SetCreatedAt(t) @@ -213,6 +233,19 @@ func (pu *PermUpdate) sqlSave(ctx context.Context) (n int, err error) { Column: perm.FieldRepoPerm, }) } + if value, ok := pu.mutation.SyncedAt(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: perm.FieldSyncedAt, + }) + } + if pu.mutation.SyncedAtCleared() { + _spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Column: perm.FieldSyncedAt, + }) + } if value, ok := pu.mutation.CreatedAt(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeTime, @@ -330,6 +363,26 @@ func (puo *PermUpdateOne) SetNillableRepoPerm(pp *perm.RepoPerm) *PermUpdateOne return puo } +// SetSyncedAt sets the "synced_at" field. +func (puo *PermUpdateOne) SetSyncedAt(t time.Time) *PermUpdateOne { + puo.mutation.SetSyncedAt(t) + return puo +} + +// SetNillableSyncedAt sets the "synced_at" field if the given value is not nil. +func (puo *PermUpdateOne) SetNillableSyncedAt(t *time.Time) *PermUpdateOne { + if t != nil { + puo.SetSyncedAt(*t) + } + return puo +} + +// ClearSyncedAt clears the value of the "synced_at" field. +func (puo *PermUpdateOne) ClearSyncedAt() *PermUpdateOne { + puo.mutation.ClearSyncedAt() + return puo +} + // SetCreatedAt sets the "created_at" field. func (puo *PermUpdateOne) SetCreatedAt(t time.Time) *PermUpdateOne { puo.mutation.SetCreatedAt(t) @@ -523,6 +576,19 @@ func (puo *PermUpdateOne) sqlSave(ctx context.Context) (_node *Perm, err error) Column: perm.FieldRepoPerm, }) } + if value, ok := puo.mutation.SyncedAt(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: perm.FieldSyncedAt, + }) + } + if puo.mutation.SyncedAtCleared() { + _spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Column: perm.FieldSyncedAt, + }) + } if value, ok := puo.mutation.CreatedAt(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeTime, diff --git a/ent/runtime.go b/ent/runtime.go index 4e20eea6..134170d5 100644 --- a/ent/runtime.go +++ b/ent/runtime.go @@ -117,11 +117,11 @@ func init() { permFields := schema.Perm{}.Fields() _ = permFields // permDescCreatedAt is the schema descriptor for created_at field. - permDescCreatedAt := permFields[1].Descriptor() + permDescCreatedAt := permFields[2].Descriptor() // perm.DefaultCreatedAt holds the default value on creation for the created_at field. perm.DefaultCreatedAt = permDescCreatedAt.Default.(func() time.Time) // permDescUpdatedAt is the schema descriptor for updated_at field. - permDescUpdatedAt := permFields[2].Descriptor() + permDescUpdatedAt := permFields[3].Descriptor() // perm.DefaultUpdatedAt holds the default value on creation for the updated_at field. perm.DefaultUpdatedAt = permDescUpdatedAt.Default.(func() time.Time) // perm.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. diff --git a/ent/schema/perm.go b/ent/schema/perm.go index b13b71e4..73f1e014 100644 --- a/ent/schema/perm.go +++ b/ent/schema/perm.go @@ -24,6 +24,8 @@ func (Perm) Fields() []ent.Field { "admin", ). Default("read"), + field.Time("synced_at"). + Optional(), field.Time("created_at"). Default(time.Now), field.Time("updated_at"). @@ -56,6 +58,6 @@ func (Perm) Indexes() []ent.Index { // Find the perm for the repository. index.Fields("repo_id", "user_id"), // Delete staled perms after synchronization - index.Fields("user_id", "updated_at"), + index.Fields("user_id", "synced_at"), } } diff --git a/internal/interactor/interface.go b/internal/interactor/interface.go index 2e0025f2..f61bf8c0 100644 --- a/internal/interactor/interface.go +++ b/internal/interactor/interface.go @@ -41,7 +41,7 @@ type ( FindPermOfRepo(ctx context.Context, r *ent.Repo, u *ent.User) (*ent.Perm, error) CreatePerm(ctx context.Context, p *ent.Perm) (*ent.Perm, error) UpdatePerm(ctx context.Context, p *ent.Perm) (*ent.Perm, error) - DeletePermsOfUserLessThanUpdatedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) + DeletePermsOfUserLessThanSyncedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) SearchDeployments(ctx context.Context, u *ent.User, s []deployment.Status, owned bool, from time.Time, to time.Time, page, perPage int) ([]*ent.Deployment, error) ListInactiveDeploymentsLessThanTime(ctx context.Context, t time.Time, page, perPage int) ([]*ent.Deployment, error) diff --git a/internal/interactor/mock/pkg.go b/internal/interactor/mock/pkg.go index c2dc4b2a..c8ab40b0 100644 --- a/internal/interactor/mock/pkg.go +++ b/internal/interactor/mock/pkg.go @@ -275,19 +275,19 @@ func (mr *MockStoreMockRecorder) DeleteLock(ctx, l interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLock", reflect.TypeOf((*MockStore)(nil).DeleteLock), ctx, l) } -// DeletePermsOfUserLessThanUpdatedAt mocks base method. -func (m *MockStore) DeletePermsOfUserLessThanUpdatedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) { +// DeletePermsOfUserLessThanSyncedAt mocks base method. +func (m *MockStore) DeletePermsOfUserLessThanSyncedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeletePermsOfUserLessThanUpdatedAt", ctx, u, t) + ret := m.ctrl.Call(m, "DeletePermsOfUserLessThanSyncedAt", ctx, u, t) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } -// DeletePermsOfUserLessThanUpdatedAt indicates an expected call of DeletePermsOfUserLessThanUpdatedAt. -func (mr *MockStoreMockRecorder) DeletePermsOfUserLessThanUpdatedAt(ctx, u, t interface{}) *gomock.Call { +// DeletePermsOfUserLessThanSyncedAt indicates an expected call of DeletePermsOfUserLessThanSyncedAt. +func (mr *MockStoreMockRecorder) DeletePermsOfUserLessThanSyncedAt(ctx, u, t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePermsOfUserLessThanUpdatedAt", reflect.TypeOf((*MockStore)(nil).DeletePermsOfUserLessThanUpdatedAt), ctx, u, t) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePermsOfUserLessThanSyncedAt", reflect.TypeOf((*MockStore)(nil).DeletePermsOfUserLessThanSyncedAt), ctx, u, t) } // DeleteUser mocks base method. diff --git a/internal/interactor/sync.go b/internal/interactor/sync.go index 81d20f79..fbfb693c 100644 --- a/internal/interactor/sync.go +++ b/internal/interactor/sync.go @@ -2,6 +2,7 @@ package interactor import ( "context" + "time" "github.com/gitploy-io/gitploy/ent" "github.com/gitploy-io/gitploy/ent/perm" @@ -22,7 +23,7 @@ func (i *Interactor) IsEntryOrg(ctx context.Context, namespace string) bool { return false } -func (i *Interactor) SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.RemoteRepo) error { +func (i *Interactor) SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.RemoteRepo, t time.Time) error { var ( r *ent.Repo p *ent.Perm @@ -42,6 +43,7 @@ func (i *Interactor) SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.Rem RepoPerm: perm.RepoPerm(re.Perm), UserID: u.ID, RepoID: r.ID, + SyncedAt: t, }); err != nil { return err } @@ -49,6 +51,7 @@ func (i *Interactor) SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.Rem return err } else { p.RepoPerm = perm.RepoPerm(re.Perm) + p.SyncedAt = t if _, err = i.Store.UpdatePerm(ctx, p); err != nil { return err diff --git a/internal/interactor/sync_test.go b/internal/interactor/sync_test.go index bb563506..8b8aaf2f 100644 --- a/internal/interactor/sync_test.go +++ b/internal/interactor/sync_test.go @@ -3,6 +3,7 @@ package interactor import ( "context" "testing" + "time" "github.com/gitploy-io/gitploy/ent" "github.com/gitploy-io/gitploy/ent/perm" @@ -18,6 +19,7 @@ func TestInteractor_SyncRemoteRepo(t *testing.T) { input := struct { user *ent.User remote *vo.RemoteRepo + time time.Time }{ user: &ent.User{ ID: 2214, @@ -26,6 +28,7 @@ func TestInteractor_SyncRemoteRepo(t *testing.T) { ID: 2214, Perm: vo.RemoteRepoPermRead, }, + time: time.Now(), } ctrl := gomock.NewController(t) @@ -58,13 +61,62 @@ func TestInteractor_SyncRemoteRepo(t *testing.T) { RepoPerm: perm.RepoPerm(input.remote.Perm), UserID: input.user.ID, RepoID: input.remote.ID, + SyncedAt: input.time, })). DoAndReturn(func(ctx context.Context, p *ent.Perm) (*ent.Perm, error) { return p, nil }) i := &Interactor{Store: store} - if err := i.SyncRemoteRepo(context.Background(), input.user, input.remote); err != nil { + if err := i.SyncRemoteRepo(context.Background(), input.user, input.remote, input.time); err != nil { + t.Fatal("SyncRemoteRepo returns error.") + } + }) + + t.Run("Synchronization updates the perm if it exist.", func(t *testing.T) { + input := struct { + user *ent.User + remote *vo.RemoteRepo + time time.Time + }{ + user: &ent.User{ + ID: 1, + }, + remote: &vo.RemoteRepo{ + ID: 1, + Perm: vo.RemoteRepoPermWrite, + }, + time: time.Now(), + } + + ctrl := gomock.NewController(t) + store := mock.NewMockStore(ctrl) + + t.Log("The repo is found.") + store. + EXPECT(). + FindRepoByID(ctx, input.remote.ID). + Return(&ent.Repo{ID: input.remote.ID}, nil) + + t.Log("The perm is found.") + store. + EXPECT(). + FindPermOfRepo(ctx, gomock.Eq(&ent.Repo{ID: input.remote.ID}), gomock.Eq(input.user)). + Return(&ent.Perm{}, nil) + + t.Log("Update the perm with perm, and synced_at.") + store. + EXPECT(). + UpdatePerm(ctx, gomock.Eq(&ent.Perm{ + RepoPerm: perm.RepoPerm(input.remote.Perm), + SyncedAt: input.time, + })). + DoAndReturn(func(ctx context.Context, p *ent.Perm) (*ent.Perm, error) { + return p, nil + }) + + i := &Interactor{Store: store} + if err := i.SyncRemoteRepo(context.Background(), input.user, input.remote, input.time); err != nil { t.Fatal("SyncRemoteRepo returns error.") } }) diff --git a/internal/pkg/store/perm.go b/internal/pkg/store/perm.go index b331e92a..1807d351 100644 --- a/internal/pkg/store/perm.go +++ b/internal/pkg/store/perm.go @@ -44,6 +44,7 @@ func (s *Store) CreatePerm(ctx context.Context, p *ent.Perm) (*ent.Perm, error) return s.c.Perm. Create(). SetRepoPerm(p.RepoPerm). + SetSyncedAt(p.SyncedAt). SetUserID(p.UserID). SetRepoID(p.RepoID). Save(ctx) @@ -53,10 +54,11 @@ func (s *Store) UpdatePerm(ctx context.Context, p *ent.Perm) (*ent.Perm, error) return s.c.Perm. UpdateOne(p). SetRepoPerm(p.RepoPerm). + SetSyncedAt(p.SyncedAt). Save(ctx) } -func (s *Store) DeletePermsOfUserLessThanUpdatedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) { +func (s *Store) DeletePermsOfUserLessThanSyncedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) { var ( cnt int err error @@ -66,9 +68,15 @@ func (s *Store) DeletePermsOfUserLessThanUpdatedAt(ctx context.Context, u *ent.U cnt, err = tx.Perm. Delete(). Where( - perm.And( - perm.UserIDEQ(u.ID), - perm.UpdatedAtLT(t), + perm.Or( + perm.And( + perm.UserIDEQ(u.ID), + perm.SyncedAtLT(t), + ), + perm.And( + perm.UserIDEQ(u.ID), + perm.SyncedAtIsNil(), + ), ), ). Exec(ctx) diff --git a/internal/pkg/store/perm_test.go b/internal/pkg/store/perm_test.go index e97e570c..30d3cd5f 100644 --- a/internal/pkg/store/perm_test.go +++ b/internal/pkg/store/perm_test.go @@ -11,7 +11,64 @@ import ( "github.com/gitploy-io/gitploy/ent/perm" ) -func TestStore_DeletePermsOfUserLessThanUpdatedAt(t *testing.T) { +func TestStore_CreatePerm(t *testing.T) { + t.Run("Create a new perm", func(t *testing.T) { + client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1", + enttest.WithMigrateOptions(migrate.WithForeignKeys(false)), + ) + defer client.Close() + + s := NewStore(client) + + _, err := s.CreatePerm(context.Background(), &ent.Perm{ + RepoPerm: perm.DefaultRepoPerm, + SyncedAt: time.Now(), + UserID: 1, + RepoID: 1, + }) + if err != nil { + t.Fatalf("CreatePerm returns an error: %s", err) + } + }) +} + +func TestStore_UpdatePerm(t *testing.T) { + t.Run("Update the perm", func(t *testing.T) { + client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1", + enttest.WithMigrateOptions(migrate.WithForeignKeys(false)), + ) + defer client.Close() + + t.Log("Insert a perm") + p := client.Perm. + Create(). + SetRepoPerm(perm.RepoPermRead). + SetSyncedAt(time.Now().Add(-time.Hour)). + SetUserID(1). + SetRepoID(1). + SaveX(context.Background()) + + t.Log("Update the perm") + syncedAt := time.Now() + + p.RepoPerm = perm.RepoPermWrite + p.SyncedAt = syncedAt + + s := NewStore(client) + + n, err := s.UpdatePerm(context.Background(), p) + if err != nil { + t.Fatalf("UpdatePerm returns an error: %s", err) + } + + if !(n.RepoPerm == p.RepoPerm && n.SyncedAt.Equal(p.SyncedAt)) { + t.Log("Values, perm and synced_at, is not equal.") + t.Fatalf("UpdatePerm = %v, wanted %v", n, p) + } + }) +} + +func TestStore_DeletePermsOfUserLessThanSyncedAt(t *testing.T) { client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1", enttest.WithMigrateOptions(migrate.WithForeignKeys(false)), ) @@ -20,48 +77,72 @@ func TestStore_DeletePermsOfUserLessThanUpdatedAt(t *testing.T) { const ( u1 = 1 u2 = 2 - r = 1 + r1 = 1 + r2 = 2 ) nor := time.Now() t.Log("Insert staled perms") + client.Perm. + Create(). + SetRepoPerm(perm.RepoPermWrite). + SetSyncedAt(nor.Add(-1 * time.Hour)). + SetUserID(u1). + SetRepoID(r1). + SaveX(context.Background()) + client.Perm. Create(). SetRepoPerm(perm.RepoPermWrite). SetUserID(u1). - SetRepoID(r). - SetUpdatedAt(nor.Add(-1 * time.Hour)). + SetRepoID(r1). SaveX(context.Background()) client.Perm. Create(). SetRepoPerm(perm.RepoPermWrite). + SetSyncedAt(nor.Add(-1 * time.Hour)). + SetUserID(u1). + SetRepoID(r2). + SaveX(context.Background()) + + client.Perm. + Create(). + SetRepoPerm(perm.RepoPermWrite). + SetSyncedAt(nor.Add(-1 * time.Hour)). SetUserID(u2). - SetRepoID(r). - SetUpdatedAt(nor.Add(-1 * time.Hour)). + SetRepoID(r1). SaveX(context.Background()) t.Log("Insert new perms") client.Perm. Create(). SetRepoPerm(perm.RepoPermWrite). + SetSyncedAt(nor.Add(time.Hour)). SetUserID(u1). - SetRepoID(r). - SetUpdatedAt(nor.Add(time.Hour)). + SetRepoID(r1). + SaveX(context.Background()) + + client.Perm. + Create(). + SetRepoPerm(perm.RepoPermWrite). + SetSyncedAt(nor.Add(time.Hour)). + SetUserID(u2). + SetRepoID(r1). SaveX(context.Background()) t.Run("Delete staled perms.", func(t *testing.T) { s := NewStore(client) - cnt, err := s.DeletePermsOfUserLessThanUpdatedAt(context.Background(), &ent.User{ID: u1}, nor) + cnt, err := s.DeletePermsOfUserLessThanSyncedAt(context.Background(), &ent.User{ID: u1}, nor) if err != nil { - t.Fatalf("DeletePermsOfUserLessThanUpdatedAt returns an error: %s", err) + t.Fatalf("DeletePermsOfUserLessThanSyncedAt returns an error: %s", err) } - expected := 1 + expected := 3 if cnt != expected { - t.Fatalf("DeletePermsOfUserLessThanUpdatedAt = %v: %v", cnt, expected) + t.Fatalf("DeletePermsOfUserLessThanSyncedAt = %v: %v", cnt, expected) } }) } diff --git a/internal/server/api/v1/sync/interface.go b/internal/server/api/v1/sync/interface.go index ba07dade..940f18f6 100644 --- a/internal/server/api/v1/sync/interface.go +++ b/internal/server/api/v1/sync/interface.go @@ -14,7 +14,7 @@ type ( Interactor interface { ListRemoteRepos(ctx context.Context, u *ent.User) ([]*vo.RemoteRepo, error) IsEntryOrg(ctx context.Context, namespace string) bool - SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.RemoteRepo) error - DeletePermsOfUserLessThanUpdatedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) + SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.RemoteRepo, t time.Time) error + DeletePermsOfUserLessThanSyncedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) } ) diff --git a/internal/server/api/v1/sync/mock/interactor.go b/internal/server/api/v1/sync/mock/interactor.go index 6cadd7e7..c2fe3ba5 100644 --- a/internal/server/api/v1/sync/mock/interactor.go +++ b/internal/server/api/v1/sync/mock/interactor.go @@ -37,19 +37,19 @@ func (m *MockInteractor) EXPECT() *MockInteractorMockRecorder { return m.recorder } -// DeletePermsOfUserLessThanUpdatedAt mocks base method. -func (m *MockInteractor) DeletePermsOfUserLessThanUpdatedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) { +// DeletePermsOfUserLessThanSyncedAt mocks base method. +func (m *MockInteractor) DeletePermsOfUserLessThanSyncedAt(ctx context.Context, u *ent.User, t time.Time) (int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeletePermsOfUserLessThanUpdatedAt", ctx, u, t) + ret := m.ctrl.Call(m, "DeletePermsOfUserLessThanSyncedAt", ctx, u, t) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } -// DeletePermsOfUserLessThanUpdatedAt indicates an expected call of DeletePermsOfUserLessThanUpdatedAt. -func (mr *MockInteractorMockRecorder) DeletePermsOfUserLessThanUpdatedAt(ctx, u, t interface{}) *gomock.Call { +// DeletePermsOfUserLessThanSyncedAt indicates an expected call of DeletePermsOfUserLessThanSyncedAt. +func (mr *MockInteractorMockRecorder) DeletePermsOfUserLessThanSyncedAt(ctx, u, t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePermsOfUserLessThanUpdatedAt", reflect.TypeOf((*MockInteractor)(nil).DeletePermsOfUserLessThanUpdatedAt), ctx, u, t) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePermsOfUserLessThanSyncedAt", reflect.TypeOf((*MockInteractor)(nil).DeletePermsOfUserLessThanSyncedAt), ctx, u, t) } // IsEntryOrg mocks base method. @@ -82,15 +82,15 @@ func (mr *MockInteractorMockRecorder) ListRemoteRepos(ctx, u interface{}) *gomoc } // SyncRemoteRepo mocks base method. -func (m *MockInteractor) SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.RemoteRepo) error { +func (m *MockInteractor) SyncRemoteRepo(ctx context.Context, u *ent.User, re *vo.RemoteRepo, t time.Time) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SyncRemoteRepo", ctx, u, re) + ret := m.ctrl.Call(m, "SyncRemoteRepo", ctx, u, re, t) ret0, _ := ret[0].(error) return ret0 } // SyncRemoteRepo indicates an expected call of SyncRemoteRepo. -func (mr *MockInteractorMockRecorder) SyncRemoteRepo(ctx, u, re interface{}) *gomock.Call { +func (mr *MockInteractorMockRecorder) SyncRemoteRepo(ctx, u, re, t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncRemoteRepo", reflect.TypeOf((*MockInteractor)(nil).SyncRemoteRepo), ctx, u, re) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncRemoteRepo", reflect.TypeOf((*MockInteractor)(nil).SyncRemoteRepo), ctx, u, re, t) } diff --git a/internal/server/api/v1/sync/syncher.go b/internal/server/api/v1/sync/syncher.go index 25864bf8..291f8d29 100644 --- a/internal/server/api/v1/sync/syncher.go +++ b/internal/server/api/v1/sync/syncher.go @@ -51,7 +51,7 @@ func (s *Syncher) Sync(c *gin.Context) { continue } - if err := s.i.SyncRemoteRepo(ctx, u, re); err != nil { + if err := s.i.SyncRemoteRepo(ctx, u, re, syncTime); err != nil { s.log.Error("It has failed to sync with the remote repository.", zap.Error(err), zap.Int64("repo_id", re.ID)) continue } @@ -61,7 +61,7 @@ func (s *Syncher) Sync(c *gin.Context) { // Delete staled perms. var cnt int - if cnt, err = s.i.DeletePermsOfUserLessThanUpdatedAt(ctx, u, syncTime); err != nil { + if cnt, err = s.i.DeletePermsOfUserLessThanSyncedAt(ctx, u, syncTime); err != nil { s.log.Error("It has failed to delete staled repositories.", zap.Error(err)) gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to delete staled repositories.") return diff --git a/internal/server/api/v1/sync/syncher_test.go b/internal/server/api/v1/sync/syncher_test.go index f14bc1c4..8417179a 100644 --- a/internal/server/api/v1/sync/syncher_test.go +++ b/internal/server/api/v1/sync/syncher_test.go @@ -5,6 +5,7 @@ import ( "net/http" "net/http/httptest" "testing" + "time" "github.com/gin-gonic/gin" "github.com/gitploy-io/gitploy/ent" @@ -54,13 +55,13 @@ func TestSyncher_Sync(t *testing.T) { ID: 1, Namespace: "octocat", Name: "HelloWorld", - })). + }), gomock.AssignableToTypeOf(time.Time{})). Return(nil) t.Log("Delete staled perms.") m. EXPECT(). - DeletePermsOfUserLessThanUpdatedAt(ctx, gomock.Any(), gomock.Any()). + DeletePermsOfUserLessThanSyncedAt(ctx, gomock.Any(), gomock.Any()). Return(0, nil) gin.SetMode(gin.ReleaseMode)