diff --git a/docs/concepts/deployment.md b/docs/concepts/deployment.md index a2a10192..e1ce1a23 100644 --- a/docs/concepts/deployment.md +++ b/docs/concepts/deployment.md @@ -12,6 +12,6 @@ With approval, Gitploy waits until it matches the required approving approvals. Rollback is the best way to recover while you fix the problems. Gitploy supports the rollback, and you can choose one of the successful deployments to rollback. -For best practice, you should lock the repository to block deploying by others until finishing to fix the problems. Gitploy provide the 'lock' feature in UI and Chatops. +For best practice, you should lock the environment to block deploying by others until finishing to fix the problems. Gitploy provide the 'lock' feature in UI and Chatops. *Note that if the ref of the selected deployment is a branch, Gitploy automatically references the commit SHA to prevent deploying the head of the branch.* diff --git a/docs/concepts/permission.md b/docs/concepts/permission.md index 3123b837..29860394 100644 --- a/docs/concepts/permission.md +++ b/docs/concepts/permission.md @@ -11,8 +11,8 @@ Here are capabilities for each permission: * **Read** - Users can read all activities that happened in the repository, such as deployments, approvals. And users are also capable of responding to the approval. -* **Write** - Users can deploy and rollback for the `REF`. +* **Write** - Users can lock, deploy, and rollback for the `REF`. -* **Admin** - Users can configures the repository, such as activating and locking. +* **Admin** - Users can configures the repository, such as activating. Of course, write and admin permission cover the ability of read permission. \ No newline at end of file diff --git a/ent/client.go b/ent/client.go index 7bf7887b..6202aef4 100644 --- a/ent/client.go +++ b/ent/client.go @@ -15,6 +15,7 @@ import ( "github.com/gitploy-io/gitploy/ent/deployment" "github.com/gitploy-io/gitploy/ent/deploymentstatus" "github.com/gitploy-io/gitploy/ent/event" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/notificationrecord" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/repo" @@ -42,6 +43,8 @@ type Client struct { DeploymentStatus *DeploymentStatusClient // Event is the client for interacting with the Event builders. Event *EventClient + // Lock is the client for interacting with the Lock builders. + Lock *LockClient // NotificationRecord is the client for interacting with the NotificationRecord builders. NotificationRecord *NotificationRecordClient // Perm is the client for interacting with the Perm builders. @@ -69,6 +72,7 @@ func (c *Client) init() { c.Deployment = NewDeploymentClient(c.config) c.DeploymentStatus = NewDeploymentStatusClient(c.config) c.Event = NewEventClient(c.config) + c.Lock = NewLockClient(c.config) c.NotificationRecord = NewNotificationRecordClient(c.config) c.Perm = NewPermClient(c.config) c.Repo = NewRepoClient(c.config) @@ -112,6 +116,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Deployment: NewDeploymentClient(cfg), DeploymentStatus: NewDeploymentStatusClient(cfg), Event: NewEventClient(cfg), + Lock: NewLockClient(cfg), NotificationRecord: NewNotificationRecordClient(cfg), Perm: NewPermClient(cfg), Repo: NewRepoClient(cfg), @@ -140,6 +145,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Deployment: NewDeploymentClient(cfg), DeploymentStatus: NewDeploymentStatusClient(cfg), Event: NewEventClient(cfg), + Lock: NewLockClient(cfg), NotificationRecord: NewNotificationRecordClient(cfg), Perm: NewPermClient(cfg), Repo: NewRepoClient(cfg), @@ -179,6 +185,7 @@ func (c *Client) Use(hooks ...Hook) { c.Deployment.Use(hooks...) c.DeploymentStatus.Use(hooks...) c.Event.Use(hooks...) + c.Lock.Use(hooks...) c.NotificationRecord.Use(hooks...) c.Perm.Use(hooks...) c.Repo.Use(hooks...) @@ -949,6 +956,128 @@ func (c *EventClient) Hooks() []Hook { return c.hooks.Event } +// LockClient is a client for the Lock schema. +type LockClient struct { + config +} + +// NewLockClient returns a client for the Lock from the given config. +func NewLockClient(c config) *LockClient { + return &LockClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `lock.Hooks(f(g(h())))`. +func (c *LockClient) Use(hooks ...Hook) { + c.hooks.Lock = append(c.hooks.Lock, hooks...) +} + +// Create returns a create builder for Lock. +func (c *LockClient) Create() *LockCreate { + mutation := newLockMutation(c.config, OpCreate) + return &LockCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Lock entities. +func (c *LockClient) CreateBulk(builders ...*LockCreate) *LockCreateBulk { + return &LockCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Lock. +func (c *LockClient) Update() *LockUpdate { + mutation := newLockMutation(c.config, OpUpdate) + return &LockUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *LockClient) UpdateOne(l *Lock) *LockUpdateOne { + mutation := newLockMutation(c.config, OpUpdateOne, withLock(l)) + return &LockUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *LockClient) UpdateOneID(id int) *LockUpdateOne { + mutation := newLockMutation(c.config, OpUpdateOne, withLockID(id)) + return &LockUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Lock. +func (c *LockClient) Delete() *LockDelete { + mutation := newLockMutation(c.config, OpDelete) + return &LockDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a delete builder for the given entity. +func (c *LockClient) DeleteOne(l *Lock) *LockDeleteOne { + return c.DeleteOneID(l.ID) +} + +// DeleteOneID returns a delete builder for the given id. +func (c *LockClient) DeleteOneID(id int) *LockDeleteOne { + builder := c.Delete().Where(lock.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &LockDeleteOne{builder} +} + +// Query returns a query builder for Lock. +func (c *LockClient) Query() *LockQuery { + return &LockQuery{ + config: c.config, + } +} + +// Get returns a Lock entity by its id. +func (c *LockClient) Get(ctx context.Context, id int) (*Lock, error) { + return c.Query().Where(lock.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *LockClient) GetX(ctx context.Context, id int) *Lock { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryUser queries the user edge of a Lock. +func (c *LockClient) QueryUser(l *Lock) *UserQuery { + query := &UserQuery{config: c.config} + query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) { + id := l.ID + step := sqlgraph.NewStep( + sqlgraph.From(lock.Table, lock.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, lock.UserTable, lock.UserColumn), + ) + fromV = sqlgraph.Neighbors(l.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryRepo queries the repo edge of a Lock. +func (c *LockClient) QueryRepo(l *Lock) *RepoQuery { + query := &RepoQuery{config: c.config} + query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) { + id := l.ID + step := sqlgraph.NewStep( + sqlgraph.From(lock.Table, lock.FieldID, id), + sqlgraph.To(repo.Table, repo.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, lock.RepoTable, lock.RepoColumn), + ) + fromV = sqlgraph.Neighbors(l.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *LockClient) Hooks() []Hook { + return c.hooks.Lock +} + // NotificationRecordClient is a client for the NotificationRecord schema. type NotificationRecordClient struct { config @@ -1310,6 +1439,22 @@ func (c *RepoClient) QueryCallback(r *Repo) *CallbackQuery { return query } +// QueryLocks queries the locks edge of a Repo. +func (c *RepoClient) QueryLocks(r *Repo) *LockQuery { + query := &LockQuery{config: c.config} + query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) { + id := r.ID + step := sqlgraph.NewStep( + sqlgraph.From(repo.Table, repo.FieldID, id), + sqlgraph.To(lock.Table, lock.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, repo.LocksTable, repo.LocksColumn), + ) + fromV = sqlgraph.Neighbors(r.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *RepoClient) Hooks() []Hook { return c.hooks.Repo @@ -1464,6 +1609,22 @@ func (c *UserClient) QueryApprovals(u *User) *ApprovalQuery { return query } +// QueryLocks queries the locks edge of a User. +func (c *UserClient) QueryLocks(u *User) *LockQuery { + query := &LockQuery{config: c.config} + query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) { + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(lock.Table, lock.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.LocksTable, user.LocksColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *UserClient) Hooks() []Hook { return c.hooks.User diff --git a/ent/config.go b/ent/config.go index 4c142d7c..2d771d19 100644 --- a/ent/config.go +++ b/ent/config.go @@ -30,6 +30,7 @@ type hooks struct { Deployment []ent.Hook DeploymentStatus []ent.Hook Event []ent.Hook + Lock []ent.Hook NotificationRecord []ent.Hook Perm []ent.Hook Repo []ent.Hook diff --git a/ent/ent.go b/ent/ent.go index ac189048..ab32e7e7 100644 --- a/ent/ent.go +++ b/ent/ent.go @@ -14,6 +14,7 @@ import ( "github.com/gitploy-io/gitploy/ent/deployment" "github.com/gitploy-io/gitploy/ent/deploymentstatus" "github.com/gitploy-io/gitploy/ent/event" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/notificationrecord" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/repo" @@ -44,6 +45,7 @@ func columnChecker(table string) func(string) error { deployment.Table: deployment.ValidColumn, deploymentstatus.Table: deploymentstatus.ValidColumn, event.Table: event.ValidColumn, + lock.Table: lock.ValidColumn, notificationrecord.Table: notificationrecord.ValidColumn, perm.Table: perm.ValidColumn, repo.Table: repo.ValidColumn, diff --git a/ent/hook/hook.go b/ent/hook/hook.go index 86368bab..edd85988 100644 --- a/ent/hook/hook.go +++ b/ent/hook/hook.go @@ -87,6 +87,19 @@ func (f EventFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error return f(ctx, mv) } +// The LockFunc type is an adapter to allow the use of ordinary +// function as Lock mutator. +type LockFunc func(context.Context, *ent.LockMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f LockFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + mv, ok := m.(*ent.LockMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.LockMutation", m) + } + return f(ctx, mv) +} + // The NotificationRecordFunc type is an adapter to allow the use of ordinary // function as NotificationRecord mutator. type NotificationRecordFunc func(context.Context, *ent.NotificationRecordMutation) (ent.Value, error) diff --git a/ent/lock.go b/ent/lock.go new file mode 100644 index 00000000..6140aab7 --- /dev/null +++ b/ent/lock.go @@ -0,0 +1,186 @@ +// Code generated by entc, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/gitploy-io/gitploy/ent/lock" + "github.com/gitploy-io/gitploy/ent/repo" + "github.com/gitploy-io/gitploy/ent/user" +) + +// Lock is the model entity for the Lock schema. +type Lock struct { + config `json:"-"` + // ID of the ent. + ID int `json:"id,omitempty"` + // Env holds the value of the "env" field. + Env string `json:"env"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at"` + // UserID holds the value of the "user_id" field. + UserID string `json:"user_id"` + // RepoID holds the value of the "repo_id" field. + RepoID string `json:"repo_id"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the LockQuery when eager-loading is set. + Edges LockEdges `json:"edges"` +} + +// LockEdges holds the relations/edges for other nodes in the graph. +type LockEdges struct { + // User holds the value of the user edge. + User *User `json:"user,omitempty"` + // Repo holds the value of the repo edge. + Repo *Repo `json:"repo,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// UserOrErr returns the User value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e LockEdges) UserOrErr() (*User, error) { + if e.loadedTypes[0] { + if e.User == nil { + // The edge user was loaded in eager-loading, + // but was not found. + return nil, &NotFoundError{label: user.Label} + } + return e.User, nil + } + return nil, &NotLoadedError{edge: "user"} +} + +// RepoOrErr returns the Repo value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e LockEdges) RepoOrErr() (*Repo, error) { + if e.loadedTypes[1] { + if e.Repo == nil { + // The edge repo was loaded in eager-loading, + // but was not found. + return nil, &NotFoundError{label: repo.Label} + } + return e.Repo, nil + } + return nil, &NotLoadedError{edge: "repo"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Lock) scanValues(columns []string) ([]interface{}, error) { + values := make([]interface{}, len(columns)) + for i := range columns { + switch columns[i] { + case lock.FieldID: + values[i] = new(sql.NullInt64) + case lock.FieldEnv, lock.FieldUserID, lock.FieldRepoID: + values[i] = new(sql.NullString) + case lock.FieldCreatedAt: + values[i] = new(sql.NullTime) + default: + return nil, fmt.Errorf("unexpected column %q for type Lock", columns[i]) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Lock fields. +func (l *Lock) assignValues(columns []string, values []interface{}) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case lock.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + l.ID = int(value.Int64) + case lock.FieldEnv: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field env", values[i]) + } else if value.Valid { + l.Env = value.String + } + case lock.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + l.CreatedAt = value.Time + } + case lock.FieldUserID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field user_id", values[i]) + } else if value.Valid { + l.UserID = value.String + } + case lock.FieldRepoID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field repo_id", values[i]) + } else if value.Valid { + l.RepoID = value.String + } + } + } + return nil +} + +// QueryUser queries the "user" edge of the Lock entity. +func (l *Lock) QueryUser() *UserQuery { + return (&LockClient{config: l.config}).QueryUser(l) +} + +// QueryRepo queries the "repo" edge of the Lock entity. +func (l *Lock) QueryRepo() *RepoQuery { + return (&LockClient{config: l.config}).QueryRepo(l) +} + +// Update returns a builder for updating this Lock. +// Note that you need to call Lock.Unwrap() before calling this method if this Lock +// was returned from a transaction, and the transaction was committed or rolled back. +func (l *Lock) Update() *LockUpdateOne { + return (&LockClient{config: l.config}).UpdateOne(l) +} + +// Unwrap unwraps the Lock entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (l *Lock) Unwrap() *Lock { + tx, ok := l.config.driver.(*txDriver) + if !ok { + panic("ent: Lock is not a transactional entity") + } + l.config.driver = tx.drv + return l +} + +// String implements the fmt.Stringer. +func (l *Lock) String() string { + var builder strings.Builder + builder.WriteString("Lock(") + builder.WriteString(fmt.Sprintf("id=%v", l.ID)) + builder.WriteString(", env=") + builder.WriteString(l.Env) + builder.WriteString(", created_at=") + builder.WriteString(l.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", user_id=") + builder.WriteString(l.UserID) + builder.WriteString(", repo_id=") + builder.WriteString(l.RepoID) + builder.WriteByte(')') + return builder.String() +} + +// Locks is a parsable slice of Lock. +type Locks []*Lock + +func (l Locks) config(cfg config) { + for _i := range l { + l[_i].config = cfg + } +} diff --git a/ent/lock/lock.go b/ent/lock/lock.go new file mode 100644 index 00000000..8103eadd --- /dev/null +++ b/ent/lock/lock.go @@ -0,0 +1,66 @@ +// Code generated by entc, DO NOT EDIT. + +package lock + +import ( + "time" +) + +const ( + // Label holds the string label denoting the lock type in the database. + Label = "lock" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldEnv holds the string denoting the env field in the database. + FieldEnv = "env" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUserID holds the string denoting the user_id field in the database. + FieldUserID = "user_id" + // FieldRepoID holds the string denoting the repo_id field in the database. + FieldRepoID = "repo_id" + // EdgeUser holds the string denoting the user edge name in mutations. + EdgeUser = "user" + // EdgeRepo holds the string denoting the repo edge name in mutations. + EdgeRepo = "repo" + // Table holds the table name of the lock in the database. + Table = "locks" + // UserTable is the table that holds the user relation/edge. + UserTable = "locks" + // UserInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + UserInverseTable = "users" + // UserColumn is the table column denoting the user relation/edge. + UserColumn = "user_id" + // RepoTable is the table that holds the repo relation/edge. + RepoTable = "locks" + // RepoInverseTable is the table name for the Repo entity. + // It exists in this package in order to avoid circular dependency with the "repo" package. + RepoInverseTable = "repos" + // RepoColumn is the table column denoting the repo relation/edge. + RepoColumn = "repo_id" +) + +// Columns holds all SQL columns for lock fields. +var Columns = []string{ + FieldID, + FieldEnv, + FieldCreatedAt, + FieldUserID, + FieldRepoID, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time +) diff --git a/ent/lock/where.go b/ent/lock/where.go new file mode 100644 index 00000000..84b4012c --- /dev/null +++ b/ent/lock/where.go @@ -0,0 +1,619 @@ +// Code generated by entc, DO NOT EDIT. + +package lock + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/gitploy-io/gitploy/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldID), id)) + }) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldID), id)) + }) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldID), id)) + }) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.Lock { + return predicate.Lock(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(ids) == 0 { + s.Where(sql.False()) + return + } + v := make([]interface{}, len(ids)) + for i := range v { + v[i] = ids[i] + } + s.Where(sql.In(s.C(FieldID), v...)) + }) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.Lock { + return predicate.Lock(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(ids) == 0 { + s.Where(sql.False()) + return + } + v := make([]interface{}, len(ids)) + for i := range v { + v[i] = ids[i] + } + s.Where(sql.NotIn(s.C(FieldID), v...)) + }) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldID), id)) + }) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldID), id)) + }) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldID), id)) + }) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldID), id)) + }) +} + +// Env applies equality check predicate on the "env" field. It's identical to EnvEQ. +func Env(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldEnv), v)) + }) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCreatedAt), v)) + }) +} + +// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ. +func UserID(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldUserID), v)) + }) +} + +// RepoID applies equality check predicate on the "repo_id" field. It's identical to RepoIDEQ. +func RepoID(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldRepoID), v)) + }) +} + +// EnvEQ applies the EQ predicate on the "env" field. +func EnvEQ(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldEnv), v)) + }) +} + +// EnvNEQ applies the NEQ predicate on the "env" field. +func EnvNEQ(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldEnv), v)) + }) +} + +// EnvIn applies the In predicate on the "env" field. +func EnvIn(vs ...string) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldEnv), v...)) + }) +} + +// EnvNotIn applies the NotIn predicate on the "env" field. +func EnvNotIn(vs ...string) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldEnv), v...)) + }) +} + +// EnvGT applies the GT predicate on the "env" field. +func EnvGT(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldEnv), v)) + }) +} + +// EnvGTE applies the GTE predicate on the "env" field. +func EnvGTE(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldEnv), v)) + }) +} + +// EnvLT applies the LT predicate on the "env" field. +func EnvLT(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldEnv), v)) + }) +} + +// EnvLTE applies the LTE predicate on the "env" field. +func EnvLTE(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldEnv), v)) + }) +} + +// EnvContains applies the Contains predicate on the "env" field. +func EnvContains(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.Contains(s.C(FieldEnv), v)) + }) +} + +// EnvHasPrefix applies the HasPrefix predicate on the "env" field. +func EnvHasPrefix(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.HasPrefix(s.C(FieldEnv), v)) + }) +} + +// EnvHasSuffix applies the HasSuffix predicate on the "env" field. +func EnvHasSuffix(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.HasSuffix(s.C(FieldEnv), v)) + }) +} + +// EnvEqualFold applies the EqualFold predicate on the "env" field. +func EnvEqualFold(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EqualFold(s.C(FieldEnv), v)) + }) +} + +// EnvContainsFold applies the ContainsFold predicate on the "env" field. +func EnvContainsFold(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.ContainsFold(s.C(FieldEnv), v)) + }) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldCreatedAt), v...)) + }) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldCreatedAt), v...)) + }) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldCreatedAt), v)) + }) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldCreatedAt), v)) + }) +} + +// UserIDEQ applies the EQ predicate on the "user_id" field. +func UserIDEQ(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldUserID), v)) + }) +} + +// UserIDNEQ applies the NEQ predicate on the "user_id" field. +func UserIDNEQ(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldUserID), v)) + }) +} + +// UserIDIn applies the In predicate on the "user_id" field. +func UserIDIn(vs ...string) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldUserID), v...)) + }) +} + +// UserIDNotIn applies the NotIn predicate on the "user_id" field. +func UserIDNotIn(vs ...string) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldUserID), v...)) + }) +} + +// UserIDGT applies the GT predicate on the "user_id" field. +func UserIDGT(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldUserID), v)) + }) +} + +// UserIDGTE applies the GTE predicate on the "user_id" field. +func UserIDGTE(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldUserID), v)) + }) +} + +// UserIDLT applies the LT predicate on the "user_id" field. +func UserIDLT(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldUserID), v)) + }) +} + +// UserIDLTE applies the LTE predicate on the "user_id" field. +func UserIDLTE(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldUserID), v)) + }) +} + +// UserIDContains applies the Contains predicate on the "user_id" field. +func UserIDContains(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.Contains(s.C(FieldUserID), v)) + }) +} + +// UserIDHasPrefix applies the HasPrefix predicate on the "user_id" field. +func UserIDHasPrefix(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.HasPrefix(s.C(FieldUserID), v)) + }) +} + +// UserIDHasSuffix applies the HasSuffix predicate on the "user_id" field. +func UserIDHasSuffix(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.HasSuffix(s.C(FieldUserID), v)) + }) +} + +// UserIDEqualFold applies the EqualFold predicate on the "user_id" field. +func UserIDEqualFold(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EqualFold(s.C(FieldUserID), v)) + }) +} + +// UserIDContainsFold applies the ContainsFold predicate on the "user_id" field. +func UserIDContainsFold(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.ContainsFold(s.C(FieldUserID), v)) + }) +} + +// RepoIDEQ applies the EQ predicate on the "repo_id" field. +func RepoIDEQ(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldRepoID), v)) + }) +} + +// RepoIDNEQ applies the NEQ predicate on the "repo_id" field. +func RepoIDNEQ(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldRepoID), v)) + }) +} + +// RepoIDIn applies the In predicate on the "repo_id" field. +func RepoIDIn(vs ...string) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldRepoID), v...)) + }) +} + +// RepoIDNotIn applies the NotIn predicate on the "repo_id" field. +func RepoIDNotIn(vs ...string) predicate.Lock { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Lock(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(FieldRepoID), v...)) + }) +} + +// RepoIDGT applies the GT predicate on the "repo_id" field. +func RepoIDGT(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldRepoID), v)) + }) +} + +// RepoIDGTE applies the GTE predicate on the "repo_id" field. +func RepoIDGTE(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldRepoID), v)) + }) +} + +// RepoIDLT applies the LT predicate on the "repo_id" field. +func RepoIDLT(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldRepoID), v)) + }) +} + +// RepoIDLTE applies the LTE predicate on the "repo_id" field. +func RepoIDLTE(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldRepoID), v)) + }) +} + +// RepoIDContains applies the Contains predicate on the "repo_id" field. +func RepoIDContains(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.Contains(s.C(FieldRepoID), v)) + }) +} + +// RepoIDHasPrefix applies the HasPrefix predicate on the "repo_id" field. +func RepoIDHasPrefix(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.HasPrefix(s.C(FieldRepoID), v)) + }) +} + +// RepoIDHasSuffix applies the HasSuffix predicate on the "repo_id" field. +func RepoIDHasSuffix(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.HasSuffix(s.C(FieldRepoID), v)) + }) +} + +// RepoIDEqualFold applies the EqualFold predicate on the "repo_id" field. +func RepoIDEqualFold(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.EqualFold(s.C(FieldRepoID), v)) + }) +} + +// RepoIDContainsFold applies the ContainsFold predicate on the "repo_id" field. +func RepoIDContainsFold(v string) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s.Where(sql.ContainsFold(s.C(FieldRepoID), v)) + }) +} + +// HasUser applies the HasEdge predicate on the "user" edge. +func HasUser() predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates). +func HasUserWith(preds ...predicate.User) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasRepo applies the HasEdge predicate on the "repo" edge. +func HasRepo() predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(RepoTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, RepoTable, RepoColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasRepoWith applies the HasEdge predicate on the "repo" edge with a given conditions (other predicates). +func HasRepoWith(preds ...predicate.Repo) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(RepoInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, RepoTable, RepoColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Lock) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for _, p := range predicates { + p(s1) + } + s.Where(s1.P()) + }) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Lock) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for i, p := range predicates { + if i > 0 { + s1.Or() + } + p(s1) + } + s.Where(s1.P()) + }) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Lock) predicate.Lock { + return predicate.Lock(func(s *sql.Selector) { + p(s.Not()) + }) +} diff --git a/ent/lock_create.go b/ent/lock_create.go new file mode 100644 index 00000000..c03f7c57 --- /dev/null +++ b/ent/lock_create.go @@ -0,0 +1,332 @@ +// Code generated by entc, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/gitploy-io/gitploy/ent/lock" + "github.com/gitploy-io/gitploy/ent/repo" + "github.com/gitploy-io/gitploy/ent/user" +) + +// LockCreate is the builder for creating a Lock entity. +type LockCreate struct { + config + mutation *LockMutation + hooks []Hook +} + +// SetEnv sets the "env" field. +func (lc *LockCreate) SetEnv(s string) *LockCreate { + lc.mutation.SetEnv(s) + return lc +} + +// SetCreatedAt sets the "created_at" field. +func (lc *LockCreate) SetCreatedAt(t time.Time) *LockCreate { + lc.mutation.SetCreatedAt(t) + return lc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (lc *LockCreate) SetNillableCreatedAt(t *time.Time) *LockCreate { + if t != nil { + lc.SetCreatedAt(*t) + } + return lc +} + +// SetUserID sets the "user_id" field. +func (lc *LockCreate) SetUserID(s string) *LockCreate { + lc.mutation.SetUserID(s) + return lc +} + +// SetRepoID sets the "repo_id" field. +func (lc *LockCreate) SetRepoID(s string) *LockCreate { + lc.mutation.SetRepoID(s) + return lc +} + +// SetUser sets the "user" edge to the User entity. +func (lc *LockCreate) SetUser(u *User) *LockCreate { + return lc.SetUserID(u.ID) +} + +// SetRepo sets the "repo" edge to the Repo entity. +func (lc *LockCreate) SetRepo(r *Repo) *LockCreate { + return lc.SetRepoID(r.ID) +} + +// Mutation returns the LockMutation object of the builder. +func (lc *LockCreate) Mutation() *LockMutation { + return lc.mutation +} + +// Save creates the Lock in the database. +func (lc *LockCreate) Save(ctx context.Context) (*Lock, error) { + var ( + err error + node *Lock + ) + lc.defaults() + if len(lc.hooks) == 0 { + if err = lc.check(); err != nil { + return nil, err + } + node, err = lc.sqlSave(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*LockMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err = lc.check(); err != nil { + return nil, err + } + lc.mutation = mutation + if node, err = lc.sqlSave(ctx); err != nil { + return nil, err + } + mutation.id = &node.ID + mutation.done = true + return node, err + }) + for i := len(lc.hooks) - 1; i >= 0; i-- { + if lc.hooks[i] == nil { + return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = lc.hooks[i](mut) + } + if _, err := mut.Mutate(ctx, lc.mutation); err != nil { + return nil, err + } + } + return node, err +} + +// SaveX calls Save and panics if Save returns an error. +func (lc *LockCreate) SaveX(ctx context.Context) *Lock { + v, err := lc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (lc *LockCreate) Exec(ctx context.Context) error { + _, err := lc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (lc *LockCreate) ExecX(ctx context.Context) { + if err := lc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (lc *LockCreate) defaults() { + if _, ok := lc.mutation.CreatedAt(); !ok { + v := lock.DefaultCreatedAt() + lc.mutation.SetCreatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (lc *LockCreate) check() error { + if _, ok := lc.mutation.Env(); !ok { + return &ValidationError{Name: "env", err: errors.New(`ent: missing required field "env"`)} + } + if _, ok := lc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "created_at"`)} + } + if _, ok := lc.mutation.UserID(); !ok { + return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "user_id"`)} + } + if _, ok := lc.mutation.RepoID(); !ok { + return &ValidationError{Name: "repo_id", err: errors.New(`ent: missing required field "repo_id"`)} + } + if _, ok := lc.mutation.UserID(); !ok { + return &ValidationError{Name: "user", err: errors.New("ent: missing required edge \"user\"")} + } + if _, ok := lc.mutation.RepoID(); !ok { + return &ValidationError{Name: "repo", err: errors.New("ent: missing required edge \"repo\"")} + } + return nil +} + +func (lc *LockCreate) sqlSave(ctx context.Context) (*Lock, error) { + _node, _spec := lc.createSpec() + if err := sqlgraph.CreateNode(ctx, lc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{err.Error(), err} + } + return nil, err + } + id := _spec.ID.Value.(int64) + _node.ID = int(id) + return _node, nil +} + +func (lc *LockCreate) createSpec() (*Lock, *sqlgraph.CreateSpec) { + var ( + _node = &Lock{config: lc.config} + _spec = &sqlgraph.CreateSpec{ + Table: lock.Table, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + } + ) + if value, ok := lc.mutation.Env(); ok { + _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ + Type: field.TypeString, + Value: value, + Column: lock.FieldEnv, + }) + _node.Env = value + } + if value, ok := lc.mutation.CreatedAt(); ok { + _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: lock.FieldCreatedAt, + }) + _node.CreatedAt = value + } + if nodes := lc.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.UserTable, + Columns: []string{lock.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: user.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.UserID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := lc.mutation.RepoIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.RepoTable, + Columns: []string{lock.RepoColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: repo.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.RepoID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// LockCreateBulk is the builder for creating many Lock entities in bulk. +type LockCreateBulk struct { + config + builders []*LockCreate +} + +// Save creates the Lock entities in the database. +func (lcb *LockCreateBulk) Save(ctx context.Context) ([]*Lock, error) { + specs := make([]*sqlgraph.CreateSpec, len(lcb.builders)) + nodes := make([]*Lock, len(lcb.builders)) + mutators := make([]Mutator, len(lcb.builders)) + for i := range lcb.builders { + func(i int, root context.Context) { + builder := lcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*LockMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + nodes[i], specs[i] = builder.createSpec() + var err error + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, lcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, lcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{err.Error(), err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + if specs[i].ID.Value != nil { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int(id) + } + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, lcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (lcb *LockCreateBulk) SaveX(ctx context.Context) []*Lock { + v, err := lcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (lcb *LockCreateBulk) Exec(ctx context.Context) error { + _, err := lcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (lcb *LockCreateBulk) ExecX(ctx context.Context) { + if err := lcb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/ent/lock_delete.go b/ent/lock_delete.go new file mode 100644 index 00000000..985ffc5c --- /dev/null +++ b/ent/lock_delete.go @@ -0,0 +1,111 @@ +// Code generated by entc, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/gitploy-io/gitploy/ent/lock" + "github.com/gitploy-io/gitploy/ent/predicate" +) + +// LockDelete is the builder for deleting a Lock entity. +type LockDelete struct { + config + hooks []Hook + mutation *LockMutation +} + +// Where appends a list predicates to the LockDelete builder. +func (ld *LockDelete) Where(ps ...predicate.Lock) *LockDelete { + ld.mutation.Where(ps...) + return ld +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (ld *LockDelete) Exec(ctx context.Context) (int, error) { + var ( + err error + affected int + ) + if len(ld.hooks) == 0 { + affected, err = ld.sqlExec(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*LockMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + ld.mutation = mutation + affected, err = ld.sqlExec(ctx) + mutation.done = true + return affected, err + }) + for i := len(ld.hooks) - 1; i >= 0; i-- { + if ld.hooks[i] == nil { + return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = ld.hooks[i](mut) + } + if _, err := mut.Mutate(ctx, ld.mutation); err != nil { + return 0, err + } + } + return affected, err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ld *LockDelete) ExecX(ctx context.Context) int { + n, err := ld.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (ld *LockDelete) sqlExec(ctx context.Context) (int, error) { + _spec := &sqlgraph.DeleteSpec{ + Node: &sqlgraph.NodeSpec{ + Table: lock.Table, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + if ps := ld.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return sqlgraph.DeleteNodes(ctx, ld.driver, _spec) +} + +// LockDeleteOne is the builder for deleting a single Lock entity. +type LockDeleteOne struct { + ld *LockDelete +} + +// Exec executes the deletion query. +func (ldo *LockDeleteOne) Exec(ctx context.Context) error { + n, err := ldo.ld.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{lock.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (ldo *LockDeleteOne) ExecX(ctx context.Context) { + ldo.ld.ExecX(ctx) +} diff --git a/ent/lock_query.go b/ent/lock_query.go new file mode 100644 index 00000000..1196bb9a --- /dev/null +++ b/ent/lock_query.go @@ -0,0 +1,1083 @@ +// Code generated by entc, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "math" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/gitploy-io/gitploy/ent/lock" + "github.com/gitploy-io/gitploy/ent/predicate" + "github.com/gitploy-io/gitploy/ent/repo" + "github.com/gitploy-io/gitploy/ent/user" +) + +// LockQuery is the builder for querying Lock entities. +type LockQuery struct { + config + limit *int + offset *int + unique *bool + order []OrderFunc + fields []string + predicates []predicate.Lock + // eager-loading edges. + withUser *UserQuery + withRepo *RepoQuery + modifiers []func(s *sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the LockQuery builder. +func (lq *LockQuery) Where(ps ...predicate.Lock) *LockQuery { + lq.predicates = append(lq.predicates, ps...) + return lq +} + +// Limit adds a limit step to the query. +func (lq *LockQuery) Limit(limit int) *LockQuery { + lq.limit = &limit + return lq +} + +// Offset adds an offset step to the query. +func (lq *LockQuery) Offset(offset int) *LockQuery { + lq.offset = &offset + return lq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (lq *LockQuery) Unique(unique bool) *LockQuery { + lq.unique = &unique + return lq +} + +// Order adds an order step to the query. +func (lq *LockQuery) Order(o ...OrderFunc) *LockQuery { + lq.order = append(lq.order, o...) + return lq +} + +// QueryUser chains the current query on the "user" edge. +func (lq *LockQuery) QueryUser() *UserQuery { + query := &UserQuery{config: lq.config} + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := lq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := lq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(lock.Table, lock.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, lock.UserTable, lock.UserColumn), + ) + fromU = sqlgraph.SetNeighbors(lq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryRepo chains the current query on the "repo" edge. +func (lq *LockQuery) QueryRepo() *RepoQuery { + query := &RepoQuery{config: lq.config} + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := lq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := lq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(lock.Table, lock.FieldID, selector), + sqlgraph.To(repo.Table, repo.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, lock.RepoTable, lock.RepoColumn), + ) + fromU = sqlgraph.SetNeighbors(lq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Lock entity from the query. +// Returns a *NotFoundError when no Lock was found. +func (lq *LockQuery) First(ctx context.Context) (*Lock, error) { + nodes, err := lq.Limit(1).All(ctx) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{lock.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (lq *LockQuery) FirstX(ctx context.Context) *Lock { + node, err := lq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Lock ID from the query. +// Returns a *NotFoundError when no Lock ID was found. +func (lq *LockQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = lq.Limit(1).IDs(ctx); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{lock.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (lq *LockQuery) FirstIDX(ctx context.Context) int { + id, err := lq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Lock entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when exactly one Lock entity is not found. +// Returns a *NotFoundError when no Lock entities are found. +func (lq *LockQuery) Only(ctx context.Context) (*Lock, error) { + nodes, err := lq.Limit(2).All(ctx) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{lock.Label} + default: + return nil, &NotSingularError{lock.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (lq *LockQuery) OnlyX(ctx context.Context) *Lock { + node, err := lq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Lock ID in the query. +// Returns a *NotSingularError when exactly one Lock ID is not found. +// Returns a *NotFoundError when no entities are found. +func (lq *LockQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = lq.Limit(2).IDs(ctx); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{lock.Label} + default: + err = &NotSingularError{lock.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (lq *LockQuery) OnlyIDX(ctx context.Context) int { + id, err := lq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Locks. +func (lq *LockQuery) All(ctx context.Context) ([]*Lock, error) { + if err := lq.prepareQuery(ctx); err != nil { + return nil, err + } + return lq.sqlAll(ctx) +} + +// AllX is like All, but panics if an error occurs. +func (lq *LockQuery) AllX(ctx context.Context) []*Lock { + nodes, err := lq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Lock IDs. +func (lq *LockQuery) IDs(ctx context.Context) ([]int, error) { + var ids []int + if err := lq.Select(lock.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (lq *LockQuery) IDsX(ctx context.Context) []int { + ids, err := lq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (lq *LockQuery) Count(ctx context.Context) (int, error) { + if err := lq.prepareQuery(ctx); err != nil { + return 0, err + } + return lq.sqlCount(ctx) +} + +// CountX is like Count, but panics if an error occurs. +func (lq *LockQuery) CountX(ctx context.Context) int { + count, err := lq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (lq *LockQuery) Exist(ctx context.Context) (bool, error) { + if err := lq.prepareQuery(ctx); err != nil { + return false, err + } + return lq.sqlExist(ctx) +} + +// ExistX is like Exist, but panics if an error occurs. +func (lq *LockQuery) ExistX(ctx context.Context) bool { + exist, err := lq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the LockQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (lq *LockQuery) Clone() *LockQuery { + if lq == nil { + return nil + } + return &LockQuery{ + config: lq.config, + limit: lq.limit, + offset: lq.offset, + order: append([]OrderFunc{}, lq.order...), + predicates: append([]predicate.Lock{}, lq.predicates...), + withUser: lq.withUser.Clone(), + withRepo: lq.withRepo.Clone(), + // clone intermediate query. + sql: lq.sql.Clone(), + path: lq.path, + } +} + +// WithUser tells the query-builder to eager-load the nodes that are connected to +// the "user" edge. The optional arguments are used to configure the query builder of the edge. +func (lq *LockQuery) WithUser(opts ...func(*UserQuery)) *LockQuery { + query := &UserQuery{config: lq.config} + for _, opt := range opts { + opt(query) + } + lq.withUser = query + return lq +} + +// WithRepo tells the query-builder to eager-load the nodes that are connected to +// the "repo" edge. The optional arguments are used to configure the query builder of the edge. +func (lq *LockQuery) WithRepo(opts ...func(*RepoQuery)) *LockQuery { + query := &RepoQuery{config: lq.config} + for _, opt := range opts { + opt(query) + } + lq.withRepo = query + return lq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Env string `json:"env"` +// Count int `json:"count,omitempty"` +// } +// +// client.Lock.Query(). +// GroupBy(lock.FieldEnv). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +// +func (lq *LockQuery) GroupBy(field string, fields ...string) *LockGroupBy { + group := &LockGroupBy{config: lq.config} + group.fields = append([]string{field}, fields...) + group.path = func(ctx context.Context) (prev *sql.Selector, err error) { + if err := lq.prepareQuery(ctx); err != nil { + return nil, err + } + return lq.sqlQuery(ctx), nil + } + return group +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Env string `json:"env"` +// } +// +// client.Lock.Query(). +// Select(lock.FieldEnv). +// Scan(ctx, &v) +// +func (lq *LockQuery) Select(fields ...string) *LockSelect { + lq.fields = append(lq.fields, fields...) + return &LockSelect{LockQuery: lq} +} + +func (lq *LockQuery) prepareQuery(ctx context.Context) error { + for _, f := range lq.fields { + if !lock.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if lq.path != nil { + prev, err := lq.path(ctx) + if err != nil { + return err + } + lq.sql = prev + } + return nil +} + +func (lq *LockQuery) sqlAll(ctx context.Context) ([]*Lock, error) { + var ( + nodes = []*Lock{} + _spec = lq.querySpec() + loadedTypes = [2]bool{ + lq.withUser != nil, + lq.withRepo != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]interface{}, error) { + node := &Lock{config: lq.config} + nodes = append(nodes, node) + return node.scanValues(columns) + } + _spec.Assign = func(columns []string, values []interface{}) error { + if len(nodes) == 0 { + return fmt.Errorf("ent: Assign called without calling ScanValues") + } + node := nodes[len(nodes)-1] + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + if len(lq.modifiers) > 0 { + _spec.Modifiers = lq.modifiers + } + if err := sqlgraph.QueryNodes(ctx, lq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + + if query := lq.withUser; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Lock) + for i := range nodes { + fk := nodes[i].UserID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.User = n + } + } + } + + if query := lq.withRepo; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Lock) + for i := range nodes { + fk := nodes[i].RepoID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + query.Where(repo.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "repo_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Repo = n + } + } + } + + return nodes, nil +} + +func (lq *LockQuery) sqlCount(ctx context.Context) (int, error) { + _spec := lq.querySpec() + if len(lq.modifiers) > 0 { + _spec.Modifiers = lq.modifiers + } + return sqlgraph.CountNodes(ctx, lq.driver, _spec) +} + +func (lq *LockQuery) sqlExist(ctx context.Context) (bool, error) { + n, err := lq.sqlCount(ctx) + if err != nil { + return false, fmt.Errorf("ent: check existence: %w", err) + } + return n > 0, nil +} + +func (lq *LockQuery) querySpec() *sqlgraph.QuerySpec { + _spec := &sqlgraph.QuerySpec{ + Node: &sqlgraph.NodeSpec{ + Table: lock.Table, + Columns: lock.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + From: lq.sql, + Unique: true, + } + if unique := lq.unique; unique != nil { + _spec.Unique = *unique + } + if fields := lq.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, lock.FieldID) + for i := range fields { + if fields[i] != lock.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := lq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := lq.limit; limit != nil { + _spec.Limit = *limit + } + if offset := lq.offset; offset != nil { + _spec.Offset = *offset + } + if ps := lq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (lq *LockQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(lq.driver.Dialect()) + t1 := builder.Table(lock.Table) + columns := lq.fields + if len(columns) == 0 { + columns = lock.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if lq.sql != nil { + selector = lq.sql + selector.Select(selector.Columns(columns...)...) + } + for _, m := range lq.modifiers { + m(selector) + } + for _, p := range lq.predicates { + p(selector) + } + for _, p := range lq.order { + p(selector) + } + if offset := lq.offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := lq.limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (lq *LockQuery) ForUpdate(opts ...sql.LockOption) *LockQuery { + if lq.driver.Dialect() == dialect.Postgres { + lq.Unique(false) + } + lq.modifiers = append(lq.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return lq +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (lq *LockQuery) ForShare(opts ...sql.LockOption) *LockQuery { + if lq.driver.Dialect() == dialect.Postgres { + lq.Unique(false) + } + lq.modifiers = append(lq.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return lq +} + +// LockGroupBy is the group-by builder for Lock entities. +type LockGroupBy struct { + config + fields []string + fns []AggregateFunc + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (lgb *LockGroupBy) Aggregate(fns ...AggregateFunc) *LockGroupBy { + lgb.fns = append(lgb.fns, fns...) + return lgb +} + +// Scan applies the group-by query and scans the result into the given value. +func (lgb *LockGroupBy) Scan(ctx context.Context, v interface{}) error { + query, err := lgb.path(ctx) + if err != nil { + return err + } + lgb.sql = query + return lgb.sqlScan(ctx, v) +} + +// ScanX is like Scan, but panics if an error occurs. +func (lgb *LockGroupBy) ScanX(ctx context.Context, v interface{}) { + if err := lgb.Scan(ctx, v); err != nil { + panic(err) + } +} + +// Strings returns list of strings from group-by. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) Strings(ctx context.Context) ([]string, error) { + if len(lgb.fields) > 1 { + return nil, errors.New("ent: LockGroupBy.Strings is not achievable when grouping more than 1 field") + } + var v []string + if err := lgb.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// StringsX is like Strings, but panics if an error occurs. +func (lgb *LockGroupBy) StringsX(ctx context.Context) []string { + v, err := lgb.Strings(ctx) + if err != nil { + panic(err) + } + return v +} + +// String returns a single string from a group-by query. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) String(ctx context.Context) (_ string, err error) { + var v []string + if v, err = lgb.Strings(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockGroupBy.Strings returned %d results when one was expected", len(v)) + } + return +} + +// StringX is like String, but panics if an error occurs. +func (lgb *LockGroupBy) StringX(ctx context.Context) string { + v, err := lgb.String(ctx) + if err != nil { + panic(err) + } + return v +} + +// Ints returns list of ints from group-by. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) Ints(ctx context.Context) ([]int, error) { + if len(lgb.fields) > 1 { + return nil, errors.New("ent: LockGroupBy.Ints is not achievable when grouping more than 1 field") + } + var v []int + if err := lgb.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// IntsX is like Ints, but panics if an error occurs. +func (lgb *LockGroupBy) IntsX(ctx context.Context) []int { + v, err := lgb.Ints(ctx) + if err != nil { + panic(err) + } + return v +} + +// Int returns a single int from a group-by query. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) Int(ctx context.Context) (_ int, err error) { + var v []int + if v, err = lgb.Ints(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockGroupBy.Ints returned %d results when one was expected", len(v)) + } + return +} + +// IntX is like Int, but panics if an error occurs. +func (lgb *LockGroupBy) IntX(ctx context.Context) int { + v, err := lgb.Int(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64s returns list of float64s from group-by. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) Float64s(ctx context.Context) ([]float64, error) { + if len(lgb.fields) > 1 { + return nil, errors.New("ent: LockGroupBy.Float64s is not achievable when grouping more than 1 field") + } + var v []float64 + if err := lgb.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// Float64sX is like Float64s, but panics if an error occurs. +func (lgb *LockGroupBy) Float64sX(ctx context.Context) []float64 { + v, err := lgb.Float64s(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64 returns a single float64 from a group-by query. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) Float64(ctx context.Context) (_ float64, err error) { + var v []float64 + if v, err = lgb.Float64s(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockGroupBy.Float64s returned %d results when one was expected", len(v)) + } + return +} + +// Float64X is like Float64, but panics if an error occurs. +func (lgb *LockGroupBy) Float64X(ctx context.Context) float64 { + v, err := lgb.Float64(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bools returns list of bools from group-by. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) Bools(ctx context.Context) ([]bool, error) { + if len(lgb.fields) > 1 { + return nil, errors.New("ent: LockGroupBy.Bools is not achievable when grouping more than 1 field") + } + var v []bool + if err := lgb.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// BoolsX is like Bools, but panics if an error occurs. +func (lgb *LockGroupBy) BoolsX(ctx context.Context) []bool { + v, err := lgb.Bools(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bool returns a single bool from a group-by query. +// It is only allowed when executing a group-by query with one field. +func (lgb *LockGroupBy) Bool(ctx context.Context) (_ bool, err error) { + var v []bool + if v, err = lgb.Bools(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockGroupBy.Bools returned %d results when one was expected", len(v)) + } + return +} + +// BoolX is like Bool, but panics if an error occurs. +func (lgb *LockGroupBy) BoolX(ctx context.Context) bool { + v, err := lgb.Bool(ctx) + if err != nil { + panic(err) + } + return v +} + +func (lgb *LockGroupBy) sqlScan(ctx context.Context, v interface{}) error { + for _, f := range lgb.fields { + if !lock.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)} + } + } + selector := lgb.sqlQuery() + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := lgb.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +func (lgb *LockGroupBy) sqlQuery() *sql.Selector { + selector := lgb.sql.Select() + aggregation := make([]string, 0, len(lgb.fns)) + for _, fn := range lgb.fns { + aggregation = append(aggregation, fn(selector)) + } + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(lgb.fields)+len(lgb.fns)) + for _, f := range lgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(lgb.fields...)...) +} + +// LockSelect is the builder for selecting fields of Lock entities. +type LockSelect struct { + *LockQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector +} + +// Scan applies the selector query and scans the result into the given value. +func (ls *LockSelect) Scan(ctx context.Context, v interface{}) error { + if err := ls.prepareQuery(ctx); err != nil { + return err + } + ls.sql = ls.LockQuery.sqlQuery(ctx) + return ls.sqlScan(ctx, v) +} + +// ScanX is like Scan, but panics if an error occurs. +func (ls *LockSelect) ScanX(ctx context.Context, v interface{}) { + if err := ls.Scan(ctx, v); err != nil { + panic(err) + } +} + +// Strings returns list of strings from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) Strings(ctx context.Context) ([]string, error) { + if len(ls.fields) > 1 { + return nil, errors.New("ent: LockSelect.Strings is not achievable when selecting more than 1 field") + } + var v []string + if err := ls.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// StringsX is like Strings, but panics if an error occurs. +func (ls *LockSelect) StringsX(ctx context.Context) []string { + v, err := ls.Strings(ctx) + if err != nil { + panic(err) + } + return v +} + +// String returns a single string from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) String(ctx context.Context) (_ string, err error) { + var v []string + if v, err = ls.Strings(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockSelect.Strings returned %d results when one was expected", len(v)) + } + return +} + +// StringX is like String, but panics if an error occurs. +func (ls *LockSelect) StringX(ctx context.Context) string { + v, err := ls.String(ctx) + if err != nil { + panic(err) + } + return v +} + +// Ints returns list of ints from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) Ints(ctx context.Context) ([]int, error) { + if len(ls.fields) > 1 { + return nil, errors.New("ent: LockSelect.Ints is not achievable when selecting more than 1 field") + } + var v []int + if err := ls.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// IntsX is like Ints, but panics if an error occurs. +func (ls *LockSelect) IntsX(ctx context.Context) []int { + v, err := ls.Ints(ctx) + if err != nil { + panic(err) + } + return v +} + +// Int returns a single int from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) Int(ctx context.Context) (_ int, err error) { + var v []int + if v, err = ls.Ints(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockSelect.Ints returned %d results when one was expected", len(v)) + } + return +} + +// IntX is like Int, but panics if an error occurs. +func (ls *LockSelect) IntX(ctx context.Context) int { + v, err := ls.Int(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64s returns list of float64s from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) Float64s(ctx context.Context) ([]float64, error) { + if len(ls.fields) > 1 { + return nil, errors.New("ent: LockSelect.Float64s is not achievable when selecting more than 1 field") + } + var v []float64 + if err := ls.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// Float64sX is like Float64s, but panics if an error occurs. +func (ls *LockSelect) Float64sX(ctx context.Context) []float64 { + v, err := ls.Float64s(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64 returns a single float64 from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) Float64(ctx context.Context) (_ float64, err error) { + var v []float64 + if v, err = ls.Float64s(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockSelect.Float64s returned %d results when one was expected", len(v)) + } + return +} + +// Float64X is like Float64, but panics if an error occurs. +func (ls *LockSelect) Float64X(ctx context.Context) float64 { + v, err := ls.Float64(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bools returns list of bools from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) Bools(ctx context.Context) ([]bool, error) { + if len(ls.fields) > 1 { + return nil, errors.New("ent: LockSelect.Bools is not achievable when selecting more than 1 field") + } + var v []bool + if err := ls.Scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// BoolsX is like Bools, but panics if an error occurs. +func (ls *LockSelect) BoolsX(ctx context.Context) []bool { + v, err := ls.Bools(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bool returns a single bool from a selector. It is only allowed when selecting one field. +func (ls *LockSelect) Bool(ctx context.Context) (_ bool, err error) { + var v []bool + if v, err = ls.Bools(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{lock.Label} + default: + err = fmt.Errorf("ent: LockSelect.Bools returned %d results when one was expected", len(v)) + } + return +} + +// BoolX is like Bool, but panics if an error occurs. +func (ls *LockSelect) BoolX(ctx context.Context) bool { + v, err := ls.Bool(ctx) + if err != nil { + panic(err) + } + return v +} + +func (ls *LockSelect) sqlScan(ctx context.Context, v interface{}) error { + rows := &sql.Rows{} + query, args := ls.sql.Query() + if err := ls.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/ent/lock_update.go b/ent/lock_update.go new file mode 100644 index 00000000..3478de12 --- /dev/null +++ b/ent/lock_update.go @@ -0,0 +1,552 @@ +// Code generated by entc, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/gitploy-io/gitploy/ent/lock" + "github.com/gitploy-io/gitploy/ent/predicate" + "github.com/gitploy-io/gitploy/ent/repo" + "github.com/gitploy-io/gitploy/ent/user" +) + +// LockUpdate is the builder for updating Lock entities. +type LockUpdate struct { + config + hooks []Hook + mutation *LockMutation +} + +// Where appends a list predicates to the LockUpdate builder. +func (lu *LockUpdate) Where(ps ...predicate.Lock) *LockUpdate { + lu.mutation.Where(ps...) + return lu +} + +// SetEnv sets the "env" field. +func (lu *LockUpdate) SetEnv(s string) *LockUpdate { + lu.mutation.SetEnv(s) + return lu +} + +// SetCreatedAt sets the "created_at" field. +func (lu *LockUpdate) SetCreatedAt(t time.Time) *LockUpdate { + lu.mutation.SetCreatedAt(t) + return lu +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (lu *LockUpdate) SetNillableCreatedAt(t *time.Time) *LockUpdate { + if t != nil { + lu.SetCreatedAt(*t) + } + return lu +} + +// SetUserID sets the "user_id" field. +func (lu *LockUpdate) SetUserID(s string) *LockUpdate { + lu.mutation.SetUserID(s) + return lu +} + +// SetRepoID sets the "repo_id" field. +func (lu *LockUpdate) SetRepoID(s string) *LockUpdate { + lu.mutation.SetRepoID(s) + return lu +} + +// SetUser sets the "user" edge to the User entity. +func (lu *LockUpdate) SetUser(u *User) *LockUpdate { + return lu.SetUserID(u.ID) +} + +// SetRepo sets the "repo" edge to the Repo entity. +func (lu *LockUpdate) SetRepo(r *Repo) *LockUpdate { + return lu.SetRepoID(r.ID) +} + +// Mutation returns the LockMutation object of the builder. +func (lu *LockUpdate) Mutation() *LockMutation { + return lu.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (lu *LockUpdate) ClearUser() *LockUpdate { + lu.mutation.ClearUser() + return lu +} + +// ClearRepo clears the "repo" edge to the Repo entity. +func (lu *LockUpdate) ClearRepo() *LockUpdate { + lu.mutation.ClearRepo() + return lu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (lu *LockUpdate) Save(ctx context.Context) (int, error) { + var ( + err error + affected int + ) + if len(lu.hooks) == 0 { + if err = lu.check(); err != nil { + return 0, err + } + affected, err = lu.sqlSave(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*LockMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err = lu.check(); err != nil { + return 0, err + } + lu.mutation = mutation + affected, err = lu.sqlSave(ctx) + mutation.done = true + return affected, err + }) + for i := len(lu.hooks) - 1; i >= 0; i-- { + if lu.hooks[i] == nil { + return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = lu.hooks[i](mut) + } + if _, err := mut.Mutate(ctx, lu.mutation); err != nil { + return 0, err + } + } + return affected, err +} + +// SaveX is like Save, but panics if an error occurs. +func (lu *LockUpdate) SaveX(ctx context.Context) int { + affected, err := lu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (lu *LockUpdate) Exec(ctx context.Context) error { + _, err := lu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (lu *LockUpdate) ExecX(ctx context.Context) { + if err := lu.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (lu *LockUpdate) check() error { + if _, ok := lu.mutation.UserID(); lu.mutation.UserCleared() && !ok { + return errors.New("ent: clearing a required unique edge \"user\"") + } + if _, ok := lu.mutation.RepoID(); lu.mutation.RepoCleared() && !ok { + return errors.New("ent: clearing a required unique edge \"repo\"") + } + return nil +} + +func (lu *LockUpdate) sqlSave(ctx context.Context) (n int, err error) { + _spec := &sqlgraph.UpdateSpec{ + Node: &sqlgraph.NodeSpec{ + Table: lock.Table, + Columns: lock.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + if ps := lu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := lu.mutation.Env(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeString, + Value: value, + Column: lock.FieldEnv, + }) + } + if value, ok := lu.mutation.CreatedAt(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: lock.FieldCreatedAt, + }) + } + if lu.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.UserTable, + Columns: []string{lock.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: user.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := lu.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.UserTable, + Columns: []string{lock.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: user.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if lu.mutation.RepoCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.RepoTable, + Columns: []string{lock.RepoColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: repo.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := lu.mutation.RepoIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.RepoTable, + Columns: []string{lock.RepoColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: repo.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, lu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{lock.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{err.Error(), err} + } + return 0, err + } + return n, nil +} + +// LockUpdateOne is the builder for updating a single Lock entity. +type LockUpdateOne struct { + config + fields []string + hooks []Hook + mutation *LockMutation +} + +// SetEnv sets the "env" field. +func (luo *LockUpdateOne) SetEnv(s string) *LockUpdateOne { + luo.mutation.SetEnv(s) + return luo +} + +// SetCreatedAt sets the "created_at" field. +func (luo *LockUpdateOne) SetCreatedAt(t time.Time) *LockUpdateOne { + luo.mutation.SetCreatedAt(t) + return luo +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (luo *LockUpdateOne) SetNillableCreatedAt(t *time.Time) *LockUpdateOne { + if t != nil { + luo.SetCreatedAt(*t) + } + return luo +} + +// SetUserID sets the "user_id" field. +func (luo *LockUpdateOne) SetUserID(s string) *LockUpdateOne { + luo.mutation.SetUserID(s) + return luo +} + +// SetRepoID sets the "repo_id" field. +func (luo *LockUpdateOne) SetRepoID(s string) *LockUpdateOne { + luo.mutation.SetRepoID(s) + return luo +} + +// SetUser sets the "user" edge to the User entity. +func (luo *LockUpdateOne) SetUser(u *User) *LockUpdateOne { + return luo.SetUserID(u.ID) +} + +// SetRepo sets the "repo" edge to the Repo entity. +func (luo *LockUpdateOne) SetRepo(r *Repo) *LockUpdateOne { + return luo.SetRepoID(r.ID) +} + +// Mutation returns the LockMutation object of the builder. +func (luo *LockUpdateOne) Mutation() *LockMutation { + return luo.mutation +} + +// ClearUser clears the "user" edge to the User entity. +func (luo *LockUpdateOne) ClearUser() *LockUpdateOne { + luo.mutation.ClearUser() + return luo +} + +// ClearRepo clears the "repo" edge to the Repo entity. +func (luo *LockUpdateOne) ClearRepo() *LockUpdateOne { + luo.mutation.ClearRepo() + return luo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (luo *LockUpdateOne) Select(field string, fields ...string) *LockUpdateOne { + luo.fields = append([]string{field}, fields...) + return luo +} + +// Save executes the query and returns the updated Lock entity. +func (luo *LockUpdateOne) Save(ctx context.Context) (*Lock, error) { + var ( + err error + node *Lock + ) + if len(luo.hooks) == 0 { + if err = luo.check(); err != nil { + return nil, err + } + node, err = luo.sqlSave(ctx) + } else { + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*LockMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err = luo.check(); err != nil { + return nil, err + } + luo.mutation = mutation + node, err = luo.sqlSave(ctx) + mutation.done = true + return node, err + }) + for i := len(luo.hooks) - 1; i >= 0; i-- { + if luo.hooks[i] == nil { + return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = luo.hooks[i](mut) + } + if _, err := mut.Mutate(ctx, luo.mutation); err != nil { + return nil, err + } + } + return node, err +} + +// SaveX is like Save, but panics if an error occurs. +func (luo *LockUpdateOne) SaveX(ctx context.Context) *Lock { + node, err := luo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (luo *LockUpdateOne) Exec(ctx context.Context) error { + _, err := luo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (luo *LockUpdateOne) ExecX(ctx context.Context) { + if err := luo.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (luo *LockUpdateOne) check() error { + if _, ok := luo.mutation.UserID(); luo.mutation.UserCleared() && !ok { + return errors.New("ent: clearing a required unique edge \"user\"") + } + if _, ok := luo.mutation.RepoID(); luo.mutation.RepoCleared() && !ok { + return errors.New("ent: clearing a required unique edge \"repo\"") + } + return nil +} + +func (luo *LockUpdateOne) sqlSave(ctx context.Context) (_node *Lock, err error) { + _spec := &sqlgraph.UpdateSpec{ + Node: &sqlgraph.NodeSpec{ + Table: lock.Table, + Columns: lock.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + id, ok := luo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "ID", err: fmt.Errorf("missing Lock.ID for update")} + } + _spec.Node.ID.Value = id + if fields := luo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, lock.FieldID) + for _, f := range fields { + if !lock.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != lock.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := luo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := luo.mutation.Env(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeString, + Value: value, + Column: lock.FieldEnv, + }) + } + if value, ok := luo.mutation.CreatedAt(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeTime, + Value: value, + Column: lock.FieldCreatedAt, + }) + } + if luo.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.UserTable, + Columns: []string{lock.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: user.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := luo.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.UserTable, + Columns: []string{lock.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: user.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if luo.mutation.RepoCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.RepoTable, + Columns: []string{lock.RepoColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: repo.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := luo.mutation.RepoIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: lock.RepoTable, + Columns: []string{lock.RepoColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeString, + Column: repo.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Lock{config: luo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, luo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{lock.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{err.Error(), err} + } + return nil, err + } + return _node, nil +} diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 72e8072f..237856b8 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -217,6 +217,41 @@ var ( }, }, } + // LocksColumns holds the columns for the "locks" table. + LocksColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "env", Type: field.TypeString}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "repo_id", Type: field.TypeString, Nullable: true}, + {Name: "user_id", Type: field.TypeString, Nullable: true}, + } + // LocksTable holds the schema information for the "locks" table. + LocksTable = &schema.Table{ + Name: "locks", + Columns: LocksColumns, + PrimaryKey: []*schema.Column{LocksColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "locks_repos_locks", + Columns: []*schema.Column{LocksColumns[3]}, + RefColumns: []*schema.Column{ReposColumns[0]}, + OnDelete: schema.Cascade, + }, + { + Symbol: "locks_users_locks", + Columns: []*schema.Column{LocksColumns[4]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.SetNull, + }, + }, + Indexes: []*schema.Index{ + { + Name: "lock_repo_id_env", + Unique: true, + Columns: []*schema.Column{LocksColumns[3], LocksColumns[1]}, + }, + }, + } // NotificationRecordsColumns holds the columns for the "notification_records" table. NotificationRecordsColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, @@ -286,7 +321,6 @@ var ( {Name: "config_path", Type: field.TypeString, Default: "deploy.yml"}, {Name: "active", Type: field.TypeBool, Default: false}, {Name: "webhook_id", Type: field.TypeInt64, Nullable: true}, - {Name: "locked", Type: field.TypeBool, Default: false}, {Name: "created_at", Type: field.TypeTime}, {Name: "updated_at", Type: field.TypeTime}, {Name: "latest_deployed_at", Type: field.TypeTime, Nullable: true}, @@ -336,6 +370,7 @@ var ( DeploymentsTable, DeploymentStatusTable, EventsTable, + LocksTable, NotificationRecordsTable, PermsTable, ReposTable, @@ -353,6 +388,8 @@ func init() { DeploymentStatusTable.ForeignKeys[0].RefTable = DeploymentsTable EventsTable.ForeignKeys[0].RefTable = ApprovalsTable EventsTable.ForeignKeys[1].RefTable = DeploymentsTable + LocksTable.ForeignKeys[0].RefTable = ReposTable + LocksTable.ForeignKeys[1].RefTable = UsersTable NotificationRecordsTable.ForeignKeys[0].RefTable = EventsTable PermsTable.ForeignKeys[0].RefTable = ReposTable PermsTable.ForeignKeys[1].RefTable = UsersTable diff --git a/ent/mutation.go b/ent/mutation.go index 208271cc..9a6b3fcf 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -14,6 +14,7 @@ import ( "github.com/gitploy-io/gitploy/ent/deployment" "github.com/gitploy-io/gitploy/ent/deploymentstatus" "github.com/gitploy-io/gitploy/ent/event" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/notificationrecord" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/predicate" @@ -38,6 +39,7 @@ const ( TypeDeployment = "Deployment" TypeDeploymentStatus = "DeploymentStatus" TypeEvent = "Event" + TypeLock = "Lock" TypeNotificationRecord = "NotificationRecord" TypePerm = "Perm" TypeRepo = "Repo" @@ -5017,6 +5019,560 @@ func (m *EventMutation) ResetEdge(name string) error { return fmt.Errorf("unknown Event edge %s", name) } +// LockMutation represents an operation that mutates the Lock nodes in the graph. +type LockMutation struct { + config + op Op + typ string + id *int + env *string + created_at *time.Time + clearedFields map[string]struct{} + user *string + cleareduser bool + repo *string + clearedrepo bool + done bool + oldValue func(context.Context) (*Lock, error) + predicates []predicate.Lock +} + +var _ ent.Mutation = (*LockMutation)(nil) + +// lockOption allows management of the mutation configuration using functional options. +type lockOption func(*LockMutation) + +// newLockMutation creates new mutation for the Lock entity. +func newLockMutation(c config, op Op, opts ...lockOption) *LockMutation { + m := &LockMutation{ + config: c, + op: op, + typ: TypeLock, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withLockID sets the ID field of the mutation. +func withLockID(id int) lockOption { + return func(m *LockMutation) { + var ( + err error + once sync.Once + value *Lock + ) + m.oldValue = func(ctx context.Context) (*Lock, error) { + once.Do(func() { + if m.done { + err = fmt.Errorf("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Lock.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withLock sets the old Lock of the mutation. +func withLock(node *Lock) lockOption { + return func(m *LockMutation) { + m.oldValue = func(context.Context) (*Lock, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m LockMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m LockMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, fmt.Errorf("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *LockMutation) ID() (id int, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// SetEnv sets the "env" field. +func (m *LockMutation) SetEnv(s string) { + m.env = &s +} + +// Env returns the value of the "env" field in the mutation. +func (m *LockMutation) Env() (r string, exists bool) { + v := m.env + if v == nil { + return + } + return *v, true +} + +// OldEnv returns the old "env" field's value of the Lock entity. +// If the Lock 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 *LockMutation) OldEnv(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldEnv is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldEnv requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEnv: %w", err) + } + return oldValue.Env, nil +} + +// ResetEnv resets all changes to the "env" field. +func (m *LockMutation) ResetEnv() { + m.env = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *LockMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *LockMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the Lock entity. +// If the Lock 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 *LockMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *LockMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUserID sets the "user_id" field. +func (m *LockMutation) SetUserID(s string) { + m.user = &s +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *LockMutation) UserID() (r string, exists bool) { + v := m.user + if v == nil { + return + } + return *v, true +} + +// OldUserID returns the old "user_id" field's value of the Lock entity. +// If the Lock 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 *LockMutation) OldUserID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserID: %w", err) + } + return oldValue.UserID, nil +} + +// ResetUserID resets all changes to the "user_id" field. +func (m *LockMutation) ResetUserID() { + m.user = nil +} + +// SetRepoID sets the "repo_id" field. +func (m *LockMutation) SetRepoID(s string) { + m.repo = &s +} + +// RepoID returns the value of the "repo_id" field in the mutation. +func (m *LockMutation) RepoID() (r string, exists bool) { + v := m.repo + if v == nil { + return + } + return *v, true +} + +// OldRepoID returns the old "repo_id" field's value of the Lock entity. +// If the Lock 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 *LockMutation) OldRepoID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldRepoID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldRepoID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRepoID: %w", err) + } + return oldValue.RepoID, nil +} + +// ResetRepoID resets all changes to the "repo_id" field. +func (m *LockMutation) ResetRepoID() { + m.repo = nil +} + +// ClearUser clears the "user" edge to the User entity. +func (m *LockMutation) ClearUser() { + m.cleareduser = true +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *LockMutation) UserCleared() bool { + return m.cleareduser +} + +// UserIDs returns the "user" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UserID instead. It exists only for internal usage by the builders. +func (m *LockMutation) UserIDs() (ids []string) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *LockMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// ClearRepo clears the "repo" edge to the Repo entity. +func (m *LockMutation) ClearRepo() { + m.clearedrepo = true +} + +// RepoCleared reports if the "repo" edge to the Repo entity was cleared. +func (m *LockMutation) RepoCleared() bool { + return m.clearedrepo +} + +// RepoIDs returns the "repo" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// RepoID instead. It exists only for internal usage by the builders. +func (m *LockMutation) RepoIDs() (ids []string) { + if id := m.repo; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetRepo resets all changes to the "repo" edge. +func (m *LockMutation) ResetRepo() { + m.repo = nil + m.clearedrepo = false +} + +// Where appends a list predicates to the LockMutation builder. +func (m *LockMutation) Where(ps ...predicate.Lock) { + m.predicates = append(m.predicates, ps...) +} + +// Op returns the operation name. +func (m *LockMutation) Op() Op { + return m.op +} + +// Type returns the node type of this mutation (Lock). +func (m *LockMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *LockMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.env != nil { + fields = append(fields, lock.FieldEnv) + } + if m.created_at != nil { + fields = append(fields, lock.FieldCreatedAt) + } + if m.user != nil { + fields = append(fields, lock.FieldUserID) + } + if m.repo != nil { + fields = append(fields, lock.FieldRepoID) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *LockMutation) Field(name string) (ent.Value, bool) { + switch name { + case lock.FieldEnv: + return m.Env() + case lock.FieldCreatedAt: + return m.CreatedAt() + case lock.FieldUserID: + return m.UserID() + case lock.FieldRepoID: + return m.RepoID() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *LockMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case lock.FieldEnv: + return m.OldEnv(ctx) + case lock.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case lock.FieldUserID: + return m.OldUserID(ctx) + case lock.FieldRepoID: + return m.OldRepoID(ctx) + } + return nil, fmt.Errorf("unknown Lock field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *LockMutation) SetField(name string, value ent.Value) error { + switch name { + case lock.FieldEnv: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEnv(v) + return nil + case lock.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case lock.FieldUserID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case lock.FieldRepoID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRepoID(v) + return nil + } + return fmt.Errorf("unknown Lock field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *LockMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *LockMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *LockMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Lock numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *LockMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *LockMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// 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 *LockMutation) ClearField(name string) error { + return fmt.Errorf("unknown Lock nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *LockMutation) ResetField(name string) error { + switch name { + case lock.FieldEnv: + m.ResetEnv() + return nil + case lock.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case lock.FieldUserID: + m.ResetUserID() + return nil + case lock.FieldRepoID: + m.ResetRepoID() + return nil + } + return fmt.Errorf("unknown Lock field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *LockMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.user != nil { + edges = append(edges, lock.EdgeUser) + } + if m.repo != nil { + edges = append(edges, lock.EdgeRepo) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *LockMutation) AddedIDs(name string) []ent.Value { + switch name { + case lock.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + case lock.EdgeRepo: + if id := m.repo; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *LockMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *LockMutation) RemovedIDs(name string) []ent.Value { + switch name { + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *LockMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.cleareduser { + edges = append(edges, lock.EdgeUser) + } + if m.clearedrepo { + edges = append(edges, lock.EdgeRepo) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *LockMutation) EdgeCleared(name string) bool { + switch name { + case lock.EdgeUser: + return m.cleareduser + case lock.EdgeRepo: + return m.clearedrepo + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *LockMutation) ClearEdge(name string) error { + switch name { + case lock.EdgeUser: + m.ClearUser() + return nil + case lock.EdgeRepo: + m.ClearRepo() + return nil + } + return fmt.Errorf("unknown Lock unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *LockMutation) ResetEdge(name string) error { + switch name { + case lock.EdgeUser: + m.ResetUser() + return nil + case lock.EdgeRepo: + m.ResetRepo() + return nil + } + return fmt.Errorf("unknown Lock edge %s", name) +} + // NotificationRecordMutation represents an operation that mutates the NotificationRecord nodes in the graph. type NotificationRecordMutation struct { config @@ -5988,7 +6544,6 @@ type RepoMutation struct { active *bool webhook_id *int64 addwebhook_id *int64 - locked *bool created_at *time.Time updated_at *time.Time latest_deployed_at *time.Time @@ -6002,6 +6557,9 @@ type RepoMutation struct { callback map[int]struct{} removedcallback map[int]struct{} clearedcallback bool + locks map[int]struct{} + removedlocks map[int]struct{} + clearedlocks bool done bool oldValue func(context.Context) (*Repo, error) predicates []predicate.Repo @@ -6342,42 +6900,6 @@ func (m *RepoMutation) ResetWebhookID() { delete(m.clearedFields, repo.FieldWebhookID) } -// SetLocked sets the "locked" field. -func (m *RepoMutation) SetLocked(b bool) { - m.locked = &b -} - -// Locked returns the value of the "locked" field in the mutation. -func (m *RepoMutation) Locked() (r bool, exists bool) { - v := m.locked - if v == nil { - return - } - return *v, true -} - -// OldLocked returns the old "locked" field's value of the Repo entity. -// If the Repo 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 *RepoMutation) OldLocked(ctx context.Context) (v bool, err error) { - if !m.op.Is(OpUpdateOne) { - return v, fmt.Errorf("OldLocked is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, fmt.Errorf("OldLocked requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldLocked: %w", err) - } - return oldValue.Locked, nil -} - -// ResetLocked resets all changes to the "locked" field. -func (m *RepoMutation) ResetLocked() { - m.locked = nil -} - // SetCreatedAt sets the "created_at" field. func (m *RepoMutation) SetCreatedAt(t time.Time) { m.created_at = &t @@ -6661,6 +7183,60 @@ func (m *RepoMutation) ResetCallback() { m.removedcallback = nil } +// AddLockIDs adds the "locks" edge to the Lock entity by ids. +func (m *RepoMutation) AddLockIDs(ids ...int) { + if m.locks == nil { + m.locks = make(map[int]struct{}) + } + for i := range ids { + m.locks[ids[i]] = struct{}{} + } +} + +// ClearLocks clears the "locks" edge to the Lock entity. +func (m *RepoMutation) ClearLocks() { + m.clearedlocks = true +} + +// LocksCleared reports if the "locks" edge to the Lock entity was cleared. +func (m *RepoMutation) LocksCleared() bool { + return m.clearedlocks +} + +// RemoveLockIDs removes the "locks" edge to the Lock entity by IDs. +func (m *RepoMutation) RemoveLockIDs(ids ...int) { + if m.removedlocks == nil { + m.removedlocks = make(map[int]struct{}) + } + for i := range ids { + delete(m.locks, ids[i]) + m.removedlocks[ids[i]] = struct{}{} + } +} + +// RemovedLocks returns the removed IDs of the "locks" edge to the Lock entity. +func (m *RepoMutation) RemovedLocksIDs() (ids []int) { + for id := range m.removedlocks { + ids = append(ids, id) + } + return +} + +// LocksIDs returns the "locks" edge IDs in the mutation. +func (m *RepoMutation) LocksIDs() (ids []int) { + for id := range m.locks { + ids = append(ids, id) + } + return +} + +// ResetLocks resets all changes to the "locks" edge. +func (m *RepoMutation) ResetLocks() { + m.locks = nil + m.clearedlocks = false + m.removedlocks = nil +} + // Where appends a list predicates to the RepoMutation builder. func (m *RepoMutation) Where(ps ...predicate.Repo) { m.predicates = append(m.predicates, ps...) @@ -6680,7 +7256,7 @@ func (m *RepoMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *RepoMutation) Fields() []string { - fields := make([]string, 0, 10) + fields := make([]string, 0, 9) if m.namespace != nil { fields = append(fields, repo.FieldNamespace) } @@ -6699,9 +7275,6 @@ func (m *RepoMutation) Fields() []string { if m.webhook_id != nil { fields = append(fields, repo.FieldWebhookID) } - if m.locked != nil { - fields = append(fields, repo.FieldLocked) - } if m.created_at != nil { fields = append(fields, repo.FieldCreatedAt) } @@ -6731,8 +7304,6 @@ func (m *RepoMutation) Field(name string) (ent.Value, bool) { return m.Active() case repo.FieldWebhookID: return m.WebhookID() - case repo.FieldLocked: - return m.Locked() case repo.FieldCreatedAt: return m.CreatedAt() case repo.FieldUpdatedAt: @@ -6760,8 +7331,6 @@ func (m *RepoMutation) OldField(ctx context.Context, name string) (ent.Value, er return m.OldActive(ctx) case repo.FieldWebhookID: return m.OldWebhookID(ctx) - case repo.FieldLocked: - return m.OldLocked(ctx) case repo.FieldCreatedAt: return m.OldCreatedAt(ctx) case repo.FieldUpdatedAt: @@ -6819,13 +7388,6 @@ func (m *RepoMutation) SetField(name string, value ent.Value) error { } m.SetWebhookID(v) return nil - case repo.FieldLocked: - v, ok := value.(bool) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetLocked(v) - return nil case repo.FieldCreatedAt: v, ok := value.(time.Time) if !ok { @@ -6944,9 +7506,6 @@ func (m *RepoMutation) ResetField(name string) error { case repo.FieldWebhookID: m.ResetWebhookID() return nil - case repo.FieldLocked: - m.ResetLocked() - return nil case repo.FieldCreatedAt: m.ResetCreatedAt() return nil @@ -6962,7 +7521,7 @@ func (m *RepoMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *RepoMutation) AddedEdges() []string { - edges := make([]string, 0, 3) + edges := make([]string, 0, 4) if m.perms != nil { edges = append(edges, repo.EdgePerms) } @@ -6972,6 +7531,9 @@ func (m *RepoMutation) AddedEdges() []string { if m.callback != nil { edges = append(edges, repo.EdgeCallback) } + if m.locks != nil { + edges = append(edges, repo.EdgeLocks) + } return edges } @@ -6997,13 +7559,19 @@ func (m *RepoMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case repo.EdgeLocks: + ids := make([]ent.Value, 0, len(m.locks)) + for id := range m.locks { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *RepoMutation) RemovedEdges() []string { - edges := make([]string, 0, 3) + edges := make([]string, 0, 4) if m.removedperms != nil { edges = append(edges, repo.EdgePerms) } @@ -7013,6 +7581,9 @@ func (m *RepoMutation) RemovedEdges() []string { if m.removedcallback != nil { edges = append(edges, repo.EdgeCallback) } + if m.removedlocks != nil { + edges = append(edges, repo.EdgeLocks) + } return edges } @@ -7038,13 +7609,19 @@ func (m *RepoMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case repo.EdgeLocks: + ids := make([]ent.Value, 0, len(m.removedlocks)) + for id := range m.removedlocks { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *RepoMutation) ClearedEdges() []string { - edges := make([]string, 0, 3) + edges := make([]string, 0, 4) if m.clearedperms { edges = append(edges, repo.EdgePerms) } @@ -7054,6 +7631,9 @@ func (m *RepoMutation) ClearedEdges() []string { if m.clearedcallback { edges = append(edges, repo.EdgeCallback) } + if m.clearedlocks { + edges = append(edges, repo.EdgeLocks) + } return edges } @@ -7067,6 +7647,8 @@ func (m *RepoMutation) EdgeCleared(name string) bool { return m.cleareddeployments case repo.EdgeCallback: return m.clearedcallback + case repo.EdgeLocks: + return m.clearedlocks } return false } @@ -7092,6 +7674,9 @@ func (m *RepoMutation) ResetEdge(name string) error { case repo.EdgeCallback: m.ResetCallback() return nil + case repo.EdgeLocks: + m.ResetLocks() + return nil } return fmt.Errorf("unknown Repo edge %s", name) } @@ -7123,6 +7708,9 @@ type UserMutation struct { approvals map[int]struct{} removedapprovals map[int]struct{} clearedapprovals bool + locks map[int]struct{} + removedlocks map[int]struct{} + clearedlocks bool done bool oldValue func(context.Context) (*User, error) predicates []predicate.User @@ -7738,6 +8326,60 @@ func (m *UserMutation) ResetApprovals() { m.removedapprovals = nil } +// AddLockIDs adds the "locks" edge to the Lock entity by ids. +func (m *UserMutation) AddLockIDs(ids ...int) { + if m.locks == nil { + m.locks = make(map[int]struct{}) + } + for i := range ids { + m.locks[ids[i]] = struct{}{} + } +} + +// ClearLocks clears the "locks" edge to the Lock entity. +func (m *UserMutation) ClearLocks() { + m.clearedlocks = true +} + +// LocksCleared reports if the "locks" edge to the Lock entity was cleared. +func (m *UserMutation) LocksCleared() bool { + return m.clearedlocks +} + +// RemoveLockIDs removes the "locks" edge to the Lock entity by IDs. +func (m *UserMutation) RemoveLockIDs(ids ...int) { + if m.removedlocks == nil { + m.removedlocks = make(map[int]struct{}) + } + for i := range ids { + delete(m.locks, ids[i]) + m.removedlocks[ids[i]] = struct{}{} + } +} + +// RemovedLocks returns the removed IDs of the "locks" edge to the Lock entity. +func (m *UserMutation) RemovedLocksIDs() (ids []int) { + for id := range m.removedlocks { + ids = append(ids, id) + } + return +} + +// LocksIDs returns the "locks" edge IDs in the mutation. +func (m *UserMutation) LocksIDs() (ids []int) { + for id := range m.locks { + ids = append(ids, id) + } + return +} + +// ResetLocks resets all changes to the "locks" edge. +func (m *UserMutation) ResetLocks() { + m.locks = nil + m.clearedlocks = false + m.removedlocks = nil +} + // Where appends a list predicates to the UserMutation builder. func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) @@ -7992,7 +8634,7 @@ func (m *UserMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 4) + edges := make([]string, 0, 5) if m.chat_user != nil { edges = append(edges, user.EdgeChatUser) } @@ -8005,6 +8647,9 @@ func (m *UserMutation) AddedEdges() []string { if m.approvals != nil { edges = append(edges, user.EdgeApprovals) } + if m.locks != nil { + edges = append(edges, user.EdgeLocks) + } return edges } @@ -8034,13 +8679,19 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeLocks: + ids := make([]ent.Value, 0, len(m.locks)) + for id := range m.locks { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 4) + edges := make([]string, 0, 5) if m.removedperms != nil { edges = append(edges, user.EdgePerms) } @@ -8050,6 +8701,9 @@ func (m *UserMutation) RemovedEdges() []string { if m.removedapprovals != nil { edges = append(edges, user.EdgeApprovals) } + if m.removedlocks != nil { + edges = append(edges, user.EdgeLocks) + } return edges } @@ -8075,13 +8729,19 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeLocks: + ids := make([]ent.Value, 0, len(m.removedlocks)) + for id := range m.removedlocks { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 4) + edges := make([]string, 0, 5) if m.clearedchat_user { edges = append(edges, user.EdgeChatUser) } @@ -8094,6 +8754,9 @@ func (m *UserMutation) ClearedEdges() []string { if m.clearedapprovals { edges = append(edges, user.EdgeApprovals) } + if m.clearedlocks { + edges = append(edges, user.EdgeLocks) + } return edges } @@ -8109,6 +8772,8 @@ func (m *UserMutation) EdgeCleared(name string) bool { return m.cleareddeployments case user.EdgeApprovals: return m.clearedapprovals + case user.EdgeLocks: + return m.clearedlocks } return false } @@ -8140,6 +8805,9 @@ func (m *UserMutation) ResetEdge(name string) error { case user.EdgeApprovals: m.ResetApprovals() return nil + case user.EdgeLocks: + m.ResetLocks() + return nil } return fmt.Errorf("unknown User edge %s", name) } diff --git a/ent/predicate/predicate.go b/ent/predicate/predicate.go index bc74d266..fb53aeb4 100644 --- a/ent/predicate/predicate.go +++ b/ent/predicate/predicate.go @@ -24,6 +24,9 @@ type DeploymentStatus func(*sql.Selector) // Event is the predicate function for event builders. type Event func(*sql.Selector) +// Lock is the predicate function for lock builders. +type Lock func(*sql.Selector) + // NotificationRecord is the predicate function for notificationrecord builders. type NotificationRecord func(*sql.Selector) diff --git a/ent/repo.go b/ent/repo.go index c9954621..2d6cd256 100644 --- a/ent/repo.go +++ b/ent/repo.go @@ -28,8 +28,6 @@ type Repo struct { Active bool `json:"active"` // WebhookID holds the value of the "webhook_id" field. WebhookID int64 `json:"webhook_id,omitemtpy"` - // Locked holds the value of the "locked" field. - Locked bool `json:"locked"` // CreatedAt holds the value of the "created_at" field. CreatedAt time.Time `json:"created_at"` // UpdatedAt holds the value of the "updated_at" field. @@ -49,9 +47,11 @@ type RepoEdges struct { Deployments []*Deployment `json:"deployments,omitempty"` // Callback holds the value of the callback edge. Callback []*Callback `json:"callback,omitempty"` + // Locks holds the value of the locks edge. + Locks []*Lock `json:"locks,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [3]bool + loadedTypes [4]bool } // PermsOrErr returns the Perms value or an error if the edge @@ -81,12 +81,21 @@ func (e RepoEdges) CallbackOrErr() ([]*Callback, error) { return nil, &NotLoadedError{edge: "callback"} } +// LocksOrErr returns the Locks value or an error if the edge +// was not loaded in eager-loading. +func (e RepoEdges) LocksOrErr() ([]*Lock, error) { + if e.loadedTypes[3] { + return e.Locks, nil + } + return nil, &NotLoadedError{edge: "locks"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*Repo) scanValues(columns []string) ([]interface{}, error) { values := make([]interface{}, len(columns)) for i := range columns { switch columns[i] { - case repo.FieldActive, repo.FieldLocked: + case repo.FieldActive: values[i] = new(sql.NullBool) case repo.FieldWebhookID: values[i] = new(sql.NullInt64) @@ -151,12 +160,6 @@ func (r *Repo) assignValues(columns []string, values []interface{}) error { } else if value.Valid { r.WebhookID = value.Int64 } - case repo.FieldLocked: - if value, ok := values[i].(*sql.NullBool); !ok { - return fmt.Errorf("unexpected type %T for field locked", values[i]) - } else if value.Valid { - r.Locked = value.Bool - } case repo.FieldCreatedAt: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field created_at", values[i]) @@ -195,6 +198,11 @@ func (r *Repo) QueryCallback() *CallbackQuery { return (&RepoClient{config: r.config}).QueryCallback(r) } +// QueryLocks queries the "locks" edge of the Repo entity. +func (r *Repo) QueryLocks() *LockQuery { + return (&RepoClient{config: r.config}).QueryLocks(r) +} + // Update returns a builder for updating this Repo. // Note that you need to call Repo.Unwrap() before calling this method if this Repo // was returned from a transaction, and the transaction was committed or rolled back. @@ -230,8 +238,6 @@ func (r *Repo) String() string { builder.WriteString(fmt.Sprintf("%v", r.Active)) builder.WriteString(", webhook_id=") builder.WriteString(fmt.Sprintf("%v", r.WebhookID)) - builder.WriteString(", locked=") - builder.WriteString(fmt.Sprintf("%v", r.Locked)) builder.WriteString(", created_at=") builder.WriteString(r.CreatedAt.Format(time.ANSIC)) builder.WriteString(", updated_at=") diff --git a/ent/repo/repo.go b/ent/repo/repo.go index bf8e850f..e52a36e5 100644 --- a/ent/repo/repo.go +++ b/ent/repo/repo.go @@ -23,8 +23,6 @@ const ( FieldActive = "active" // FieldWebhookID holds the string denoting the webhook_id field in the database. FieldWebhookID = "webhook_id" - // FieldLocked holds the string denoting the locked field in the database. - FieldLocked = "locked" // 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. @@ -37,6 +35,8 @@ const ( EdgeDeployments = "deployments" // EdgeCallback holds the string denoting the callback edge name in mutations. EdgeCallback = "callback" + // EdgeLocks holds the string denoting the locks edge name in mutations. + EdgeLocks = "locks" // Table holds the table name of the repo in the database. Table = "repos" // PermsTable is the table that holds the perms relation/edge. @@ -60,6 +60,13 @@ const ( CallbackInverseTable = "callbacks" // CallbackColumn is the table column denoting the callback relation/edge. CallbackColumn = "repo_id" + // LocksTable is the table that holds the locks relation/edge. + LocksTable = "locks" + // LocksInverseTable is the table name for the Lock entity. + // It exists in this package in order to avoid circular dependency with the "lock" package. + LocksInverseTable = "locks" + // LocksColumn is the table column denoting the locks relation/edge. + LocksColumn = "repo_id" ) // Columns holds all SQL columns for repo fields. @@ -71,7 +78,6 @@ var Columns = []string{ FieldConfigPath, FieldActive, FieldWebhookID, - FieldLocked, FieldCreatedAt, FieldUpdatedAt, FieldLatestDeployedAt, @@ -92,8 +98,6 @@ var ( DefaultConfigPath string // DefaultActive holds the default value on creation for the "active" field. DefaultActive bool - // DefaultLocked holds the default value on creation for the "locked" field. - DefaultLocked bool // DefaultCreatedAt holds the default value on creation for the "created_at" field. DefaultCreatedAt func() time.Time // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. diff --git a/ent/repo/where.go b/ent/repo/where.go index 91cc7415..a3e3e3b7 100644 --- a/ent/repo/where.go +++ b/ent/repo/where.go @@ -135,13 +135,6 @@ func WebhookID(v int64) predicate.Repo { }) } -// Locked applies equality check predicate on the "locked" field. It's identical to LockedEQ. -func Locked(v bool) predicate.Repo { - return predicate.Repo(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldLocked), v)) - }) -} - // CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. func CreatedAt(v time.Time) predicate.Repo { return predicate.Repo(func(s *sql.Selector) { @@ -711,20 +704,6 @@ func WebhookIDNotNil() predicate.Repo { }) } -// LockedEQ applies the EQ predicate on the "locked" field. -func LockedEQ(v bool) predicate.Repo { - return predicate.Repo(func(s *sql.Selector) { - s.Where(sql.EQ(s.C(FieldLocked), v)) - }) -} - -// LockedNEQ applies the NEQ predicate on the "locked" field. -func LockedNEQ(v bool) predicate.Repo { - return predicate.Repo(func(s *sql.Selector) { - s.Where(sql.NEQ(s.C(FieldLocked), v)) - }) -} - // CreatedAtEQ applies the EQ predicate on the "created_at" field. func CreatedAtEQ(v time.Time) predicate.Repo { return predicate.Repo(func(s *sql.Selector) { @@ -1051,6 +1030,34 @@ func HasCallbackWith(preds ...predicate.Callback) predicate.Repo { }) } +// HasLocks applies the HasEdge predicate on the "locks" edge. +func HasLocks() predicate.Repo { + return predicate.Repo(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LocksTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LocksTable, LocksColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocksWith applies the HasEdge predicate on the "locks" edge with a given conditions (other predicates). +func HasLocksWith(preds ...predicate.Lock) predicate.Repo { + return predicate.Repo(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LocksInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LocksTable, LocksColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Repo) predicate.Repo { return predicate.Repo(func(s *sql.Selector) { diff --git a/ent/repo_create.go b/ent/repo_create.go index 1e70936f..e33f2515 100644 --- a/ent/repo_create.go +++ b/ent/repo_create.go @@ -12,6 +12,7 @@ import ( "entgo.io/ent/schema/field" "github.com/gitploy-io/gitploy/ent/callback" "github.com/gitploy-io/gitploy/ent/deployment" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/repo" ) @@ -83,20 +84,6 @@ func (rc *RepoCreate) SetNillableWebhookID(i *int64) *RepoCreate { return rc } -// SetLocked sets the "locked" field. -func (rc *RepoCreate) SetLocked(b bool) *RepoCreate { - rc.mutation.SetLocked(b) - return rc -} - -// SetNillableLocked sets the "locked" field if the given value is not nil. -func (rc *RepoCreate) SetNillableLocked(b *bool) *RepoCreate { - if b != nil { - rc.SetLocked(*b) - } - return rc -} - // SetCreatedAt sets the "created_at" field. func (rc *RepoCreate) SetCreatedAt(t time.Time) *RepoCreate { rc.mutation.SetCreatedAt(t) @@ -190,6 +177,21 @@ func (rc *RepoCreate) AddCallback(c ...*Callback) *RepoCreate { return rc.AddCallbackIDs(ids...) } +// AddLockIDs adds the "locks" edge to the Lock entity by IDs. +func (rc *RepoCreate) AddLockIDs(ids ...int) *RepoCreate { + rc.mutation.AddLockIDs(ids...) + return rc +} + +// AddLocks adds the "locks" edges to the Lock entity. +func (rc *RepoCreate) AddLocks(l ...*Lock) *RepoCreate { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return rc.AddLockIDs(ids...) +} + // Mutation returns the RepoMutation object of the builder. func (rc *RepoCreate) Mutation() *RepoMutation { return rc.mutation @@ -269,10 +271,6 @@ func (rc *RepoCreate) defaults() { v := repo.DefaultActive rc.mutation.SetActive(v) } - if _, ok := rc.mutation.Locked(); !ok { - v := repo.DefaultLocked - rc.mutation.SetLocked(v) - } if _, ok := rc.mutation.CreatedAt(); !ok { v := repo.DefaultCreatedAt() rc.mutation.SetCreatedAt(v) @@ -300,9 +298,6 @@ func (rc *RepoCreate) check() error { if _, ok := rc.mutation.Active(); !ok { return &ValidationError{Name: "active", err: errors.New(`ent: missing required field "active"`)} } - if _, ok := rc.mutation.Locked(); !ok { - return &ValidationError{Name: "locked", err: errors.New(`ent: missing required field "locked"`)} - } if _, ok := rc.mutation.CreatedAt(); !ok { return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "created_at"`)} } @@ -386,14 +381,6 @@ func (rc *RepoCreate) createSpec() (*Repo, *sqlgraph.CreateSpec) { }) _node.WebhookID = value } - if value, ok := rc.mutation.Locked(); ok { - _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ - Type: field.TypeBool, - Value: value, - Column: repo.FieldLocked, - }) - _node.Locked = value - } if value, ok := rc.mutation.CreatedAt(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ Type: field.TypeTime, @@ -475,6 +462,25 @@ func (rc *RepoCreate) createSpec() (*Repo, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := rc.mutation.LocksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: repo.LocksTable, + Columns: []string{repo.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/ent/repo_query.go b/ent/repo_query.go index 1feebde5..ef7d7662 100644 --- a/ent/repo_query.go +++ b/ent/repo_query.go @@ -15,6 +15,7 @@ import ( "entgo.io/ent/schema/field" "github.com/gitploy-io/gitploy/ent/callback" "github.com/gitploy-io/gitploy/ent/deployment" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/predicate" "github.com/gitploy-io/gitploy/ent/repo" @@ -33,6 +34,7 @@ type RepoQuery struct { withPerms *PermQuery withDeployments *DeploymentQuery withCallback *CallbackQuery + withLocks *LockQuery modifiers []func(s *sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -136,6 +138,28 @@ func (rq *RepoQuery) QueryCallback() *CallbackQuery { return query } +// QueryLocks chains the current query on the "locks" edge. +func (rq *RepoQuery) QueryLocks() *LockQuery { + query := &LockQuery{config: rq.config} + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := rq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := rq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(repo.Table, repo.FieldID, selector), + sqlgraph.To(lock.Table, lock.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, repo.LocksTable, repo.LocksColumn), + ) + fromU = sqlgraph.SetNeighbors(rq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first Repo entity from the query. // Returns a *NotFoundError when no Repo was found. func (rq *RepoQuery) First(ctx context.Context) (*Repo, error) { @@ -320,6 +344,7 @@ func (rq *RepoQuery) Clone() *RepoQuery { withPerms: rq.withPerms.Clone(), withDeployments: rq.withDeployments.Clone(), withCallback: rq.withCallback.Clone(), + withLocks: rq.withLocks.Clone(), // clone intermediate query. sql: rq.sql.Clone(), path: rq.path, @@ -359,6 +384,17 @@ func (rq *RepoQuery) WithCallback(opts ...func(*CallbackQuery)) *RepoQuery { return rq } +// WithLocks tells the query-builder to eager-load the nodes that are connected to +// the "locks" edge. The optional arguments are used to configure the query builder of the edge. +func (rq *RepoQuery) WithLocks(opts ...func(*LockQuery)) *RepoQuery { + query := &LockQuery{config: rq.config} + for _, opt := range opts { + opt(query) + } + rq.withLocks = query + return rq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -424,10 +460,11 @@ func (rq *RepoQuery) sqlAll(ctx context.Context) ([]*Repo, error) { var ( nodes = []*Repo{} _spec = rq.querySpec() - loadedTypes = [3]bool{ + loadedTypes = [4]bool{ rq.withPerms != nil, rq.withDeployments != nil, rq.withCallback != nil, + rq.withLocks != nil, } ) _spec.ScanValues = func(columns []string) ([]interface{}, error) { @@ -528,6 +565,31 @@ func (rq *RepoQuery) sqlAll(ctx context.Context) ([]*Repo, error) { } } + if query := rq.withLocks; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*Repo) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + nodes[i].Edges.Locks = []*Lock{} + } + query.Where(predicate.Lock(func(s *sql.Selector) { + s.Where(sql.InValues(repo.LocksColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.RepoID + node, ok := nodeids[fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "repo_id" returned %v for node %v`, fk, n.ID) + } + node.Edges.Locks = append(node.Edges.Locks, n) + } + } + return nodes, nil } diff --git a/ent/repo_update.go b/ent/repo_update.go index 6e3d0023..6949666a 100644 --- a/ent/repo_update.go +++ b/ent/repo_update.go @@ -12,6 +12,7 @@ import ( "entgo.io/ent/schema/field" "github.com/gitploy-io/gitploy/ent/callback" "github.com/gitploy-io/gitploy/ent/deployment" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/predicate" "github.com/gitploy-io/gitploy/ent/repo" @@ -103,20 +104,6 @@ func (ru *RepoUpdate) ClearWebhookID() *RepoUpdate { return ru } -// SetLocked sets the "locked" field. -func (ru *RepoUpdate) SetLocked(b bool) *RepoUpdate { - ru.mutation.SetLocked(b) - return ru -} - -// SetNillableLocked sets the "locked" field if the given value is not nil. -func (ru *RepoUpdate) SetNillableLocked(b *bool) *RepoUpdate { - if b != nil { - ru.SetLocked(*b) - } - return ru -} - // SetCreatedAt sets the "created_at" field. func (ru *RepoUpdate) SetCreatedAt(t time.Time) *RepoUpdate { ru.mutation.SetCreatedAt(t) @@ -202,6 +189,21 @@ func (ru *RepoUpdate) AddCallback(c ...*Callback) *RepoUpdate { return ru.AddCallbackIDs(ids...) } +// AddLockIDs adds the "locks" edge to the Lock entity by IDs. +func (ru *RepoUpdate) AddLockIDs(ids ...int) *RepoUpdate { + ru.mutation.AddLockIDs(ids...) + return ru +} + +// AddLocks adds the "locks" edges to the Lock entity. +func (ru *RepoUpdate) AddLocks(l ...*Lock) *RepoUpdate { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return ru.AddLockIDs(ids...) +} + // Mutation returns the RepoMutation object of the builder. func (ru *RepoUpdate) Mutation() *RepoMutation { return ru.mutation @@ -270,6 +272,27 @@ func (ru *RepoUpdate) RemoveCallback(c ...*Callback) *RepoUpdate { return ru.RemoveCallbackIDs(ids...) } +// ClearLocks clears all "locks" edges to the Lock entity. +func (ru *RepoUpdate) ClearLocks() *RepoUpdate { + ru.mutation.ClearLocks() + return ru +} + +// RemoveLockIDs removes the "locks" edge to Lock entities by IDs. +func (ru *RepoUpdate) RemoveLockIDs(ids ...int) *RepoUpdate { + ru.mutation.RemoveLockIDs(ids...) + return ru +} + +// RemoveLocks removes "locks" edges to Lock entities. +func (ru *RepoUpdate) RemoveLocks(l ...*Lock) *RepoUpdate { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return ru.RemoveLockIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (ru *RepoUpdate) Save(ctx context.Context) (int, error) { var ( @@ -406,13 +429,6 @@ func (ru *RepoUpdate) sqlSave(ctx context.Context) (n int, err error) { Column: repo.FieldWebhookID, }) } - if value, ok := ru.mutation.Locked(); ok { - _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ - Type: field.TypeBool, - Value: value, - Column: repo.FieldLocked, - }) - } if value, ok := ru.mutation.CreatedAt(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeTime, @@ -602,6 +618,60 @@ func (ru *RepoUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if ru.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: repo.LocksTable, + Columns: []string{repo.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ru.mutation.RemovedLocksIDs(); len(nodes) > 0 && !ru.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: repo.LocksTable, + Columns: []string{repo.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ru.mutation.LocksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: repo.LocksTable, + Columns: []string{repo.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, ru.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{repo.Label} @@ -694,20 +764,6 @@ func (ruo *RepoUpdateOne) ClearWebhookID() *RepoUpdateOne { return ruo } -// SetLocked sets the "locked" field. -func (ruo *RepoUpdateOne) SetLocked(b bool) *RepoUpdateOne { - ruo.mutation.SetLocked(b) - return ruo -} - -// SetNillableLocked sets the "locked" field if the given value is not nil. -func (ruo *RepoUpdateOne) SetNillableLocked(b *bool) *RepoUpdateOne { - if b != nil { - ruo.SetLocked(*b) - } - return ruo -} - // SetCreatedAt sets the "created_at" field. func (ruo *RepoUpdateOne) SetCreatedAt(t time.Time) *RepoUpdateOne { ruo.mutation.SetCreatedAt(t) @@ -793,6 +849,21 @@ func (ruo *RepoUpdateOne) AddCallback(c ...*Callback) *RepoUpdateOne { return ruo.AddCallbackIDs(ids...) } +// AddLockIDs adds the "locks" edge to the Lock entity by IDs. +func (ruo *RepoUpdateOne) AddLockIDs(ids ...int) *RepoUpdateOne { + ruo.mutation.AddLockIDs(ids...) + return ruo +} + +// AddLocks adds the "locks" edges to the Lock entity. +func (ruo *RepoUpdateOne) AddLocks(l ...*Lock) *RepoUpdateOne { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return ruo.AddLockIDs(ids...) +} + // Mutation returns the RepoMutation object of the builder. func (ruo *RepoUpdateOne) Mutation() *RepoMutation { return ruo.mutation @@ -861,6 +932,27 @@ func (ruo *RepoUpdateOne) RemoveCallback(c ...*Callback) *RepoUpdateOne { return ruo.RemoveCallbackIDs(ids...) } +// ClearLocks clears all "locks" edges to the Lock entity. +func (ruo *RepoUpdateOne) ClearLocks() *RepoUpdateOne { + ruo.mutation.ClearLocks() + return ruo +} + +// RemoveLockIDs removes the "locks" edge to Lock entities by IDs. +func (ruo *RepoUpdateOne) RemoveLockIDs(ids ...int) *RepoUpdateOne { + ruo.mutation.RemoveLockIDs(ids...) + return ruo +} + +// RemoveLocks removes "locks" edges to Lock entities. +func (ruo *RepoUpdateOne) RemoveLocks(l ...*Lock) *RepoUpdateOne { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return ruo.RemoveLockIDs(ids...) +} + // Select allows selecting one or more fields (columns) of the returned entity. // The default is selecting all fields defined in the entity schema. func (ruo *RepoUpdateOne) Select(field string, fields ...string) *RepoUpdateOne { @@ -1021,13 +1113,6 @@ func (ruo *RepoUpdateOne) sqlSave(ctx context.Context) (_node *Repo, err error) Column: repo.FieldWebhookID, }) } - if value, ok := ruo.mutation.Locked(); ok { - _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ - Type: field.TypeBool, - Value: value, - Column: repo.FieldLocked, - }) - } if value, ok := ruo.mutation.CreatedAt(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeTime, @@ -1217,6 +1302,60 @@ func (ruo *RepoUpdateOne) sqlSave(ctx context.Context) (_node *Repo, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if ruo.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: repo.LocksTable, + Columns: []string{repo.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ruo.mutation.RemovedLocksIDs(); len(nodes) > 0 && !ruo.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: repo.LocksTable, + Columns: []string{repo.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ruo.mutation.LocksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: repo.LocksTable, + Columns: []string{repo.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &Repo{config: ruo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/ent/runtime.go b/ent/runtime.go index 40fa5e8b..4e20eea6 100644 --- a/ent/runtime.go +++ b/ent/runtime.go @@ -11,6 +11,7 @@ import ( "github.com/gitploy-io/gitploy/ent/deployment" "github.com/gitploy-io/gitploy/ent/deploymentstatus" "github.com/gitploy-io/gitploy/ent/event" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/repo" "github.com/gitploy-io/gitploy/ent/schema" @@ -107,6 +108,12 @@ func init() { eventDescCreatedAt := eventFields[2].Descriptor() // event.DefaultCreatedAt holds the default value on creation for the created_at field. event.DefaultCreatedAt = eventDescCreatedAt.Default.(func() time.Time) + lockFields := schema.Lock{}.Fields() + _ = lockFields + // lockDescCreatedAt is the schema descriptor for created_at field. + lockDescCreatedAt := lockFields[1].Descriptor() + // lock.DefaultCreatedAt holds the default value on creation for the created_at field. + lock.DefaultCreatedAt = lockDescCreatedAt.Default.(func() time.Time) permFields := schema.Perm{}.Fields() _ = permFields // permDescCreatedAt is the schema descriptor for created_at field. @@ -129,16 +136,12 @@ func init() { repoDescActive := repoFields[5].Descriptor() // repo.DefaultActive holds the default value on creation for the active field. repo.DefaultActive = repoDescActive.Default.(bool) - // repoDescLocked is the schema descriptor for locked field. - repoDescLocked := repoFields[7].Descriptor() - // repo.DefaultLocked holds the default value on creation for the locked field. - repo.DefaultLocked = repoDescLocked.Default.(bool) // repoDescCreatedAt is the schema descriptor for created_at field. - repoDescCreatedAt := repoFields[8].Descriptor() + repoDescCreatedAt := repoFields[7].Descriptor() // repo.DefaultCreatedAt holds the default value on creation for the created_at field. repo.DefaultCreatedAt = repoDescCreatedAt.Default.(func() time.Time) // repoDescUpdatedAt is the schema descriptor for updated_at field. - repoDescUpdatedAt := repoFields[9].Descriptor() + repoDescUpdatedAt := repoFields[8].Descriptor() // repo.DefaultUpdatedAt holds the default value on creation for the updated_at field. repo.DefaultUpdatedAt = repoDescUpdatedAt.Default.(func() time.Time) // repo.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. diff --git a/ent/schema/lock.go b/ent/schema/lock.go new file mode 100644 index 00000000..50695a74 --- /dev/null +++ b/ent/schema/lock.go @@ -0,0 +1,50 @@ +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" +) + +// Lock holds the schema definition for the Lock entity. +type Lock struct { + ent.Schema +} + +// Fields of the Lock. +func (Lock) Fields() []ent.Field { + return []ent.Field{ + field.String("env"), + field.Time("created_at"). + Default(time.Now), + // Edges + field.String("user_id"), + field.String("repo_id"), + } +} + +// Edges of the Lock. +func (Lock) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("user", User.Type). + Ref("locks"). + Field("user_id"). + Unique(). + Required(), + edge.From("repo", Repo.Type). + Ref("locks"). + Field("repo_id"). + Unique(). + Required(), + } +} + +func (Lock) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("repo_id", "env"). + Unique(), + } +} diff --git a/ent/schema/repo.go b/ent/schema/repo.go index a10d303d..f411c4cd 100644 --- a/ent/schema/repo.go +++ b/ent/schema/repo.go @@ -29,8 +29,6 @@ func (Repo) Fields() []ent.Field { Default(false), field.Int64("webhook_id"). Optional(), - field.Bool("locked"). - Default(false), field.Time("created_at"). Default(time.Now), field.Time("updated_at"). @@ -57,6 +55,10 @@ func (Repo) Edges() []ent.Edge { Annotations(entsql.Annotation{ OnDelete: entsql.Cascade, }), + edge.To("locks", Lock.Type). + Annotations(entsql.Annotation{ + OnDelete: entsql.Cascade, + }), } } diff --git a/ent/schema/user.go b/ent/schema/user.go index 8a630394..1f337730 100644 --- a/ent/schema/user.go +++ b/ent/schema/user.go @@ -56,6 +56,7 @@ func (User) Edges() []ent.Edge { }), edge.To("deployments", Deployment.Type), edge.To("approvals", Approval.Type), + edge.To("locks", Lock.Type), } } diff --git a/ent/tx.go b/ent/tx.go index 99e41d9e..358158af 100644 --- a/ent/tx.go +++ b/ent/tx.go @@ -24,6 +24,8 @@ type Tx struct { DeploymentStatus *DeploymentStatusClient // Event is the client for interacting with the Event builders. Event *EventClient + // Lock is the client for interacting with the Lock builders. + Lock *LockClient // NotificationRecord is the client for interacting with the NotificationRecord builders. NotificationRecord *NotificationRecordClient // Perm is the client for interacting with the Perm builders. @@ -173,6 +175,7 @@ func (tx *Tx) init() { tx.Deployment = NewDeploymentClient(tx.config) tx.DeploymentStatus = NewDeploymentStatusClient(tx.config) tx.Event = NewEventClient(tx.config) + tx.Lock = NewLockClient(tx.config) tx.NotificationRecord = NewNotificationRecordClient(tx.config) tx.Perm = NewPermClient(tx.config) tx.Repo = NewRepoClient(tx.config) diff --git a/ent/user.go b/ent/user.go index dc6c37b8..50a4accc 100644 --- a/ent/user.go +++ b/ent/user.go @@ -50,9 +50,11 @@ type UserEdges struct { Deployments []*Deployment `json:"deployments,omitempty"` // Approvals holds the value of the approvals edge. Approvals []*Approval `json:"approvals,omitempty"` + // Locks holds the value of the locks edge. + Locks []*Lock `json:"locks,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [4]bool + loadedTypes [5]bool } // ChatUserOrErr returns the ChatUser value or an error if the edge @@ -96,6 +98,15 @@ func (e UserEdges) ApprovalsOrErr() ([]*Approval, error) { return nil, &NotLoadedError{edge: "approvals"} } +// LocksOrErr returns the Locks value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) LocksOrErr() ([]*Lock, error) { + if e.loadedTypes[4] { + return e.Locks, nil + } + return nil, &NotLoadedError{edge: "locks"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues(columns []string) ([]interface{}, error) { values := make([]interface{}, len(columns)) @@ -207,6 +218,11 @@ func (u *User) QueryApprovals() *ApprovalQuery { return (&UserClient{config: u.config}).QueryApprovals(u) } +// QueryLocks queries the "locks" edge of the User entity. +func (u *User) QueryLocks() *LockQuery { + return (&UserClient{config: u.config}).QueryLocks(u) +} + // Update returns a builder for updating this User. // Note that you need to call User.Unwrap() before calling this method if this User // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/ent/user/user.go b/ent/user/user.go index 4f8f1c28..4848ee67 100644 --- a/ent/user/user.go +++ b/ent/user/user.go @@ -37,6 +37,8 @@ const ( EdgeDeployments = "deployments" // EdgeApprovals holds the string denoting the approvals edge name in mutations. EdgeApprovals = "approvals" + // EdgeLocks holds the string denoting the locks edge name in mutations. + EdgeLocks = "locks" // Table holds the table name of the user in the database. Table = "users" // ChatUserTable is the table that holds the chat_user relation/edge. @@ -67,6 +69,13 @@ const ( ApprovalsInverseTable = "approvals" // ApprovalsColumn is the table column denoting the approvals relation/edge. ApprovalsColumn = "user_id" + // LocksTable is the table that holds the locks relation/edge. + LocksTable = "locks" + // LocksInverseTable is the table name for the Lock entity. + // It exists in this package in order to avoid circular dependency with the "lock" package. + LocksInverseTable = "locks" + // LocksColumn is the table column denoting the locks relation/edge. + LocksColumn = "user_id" ) // Columns holds all SQL columns for user fields. diff --git a/ent/user/where.go b/ent/user/where.go index b633ce92..12663d82 100644 --- a/ent/user/where.go +++ b/ent/user/where.go @@ -1065,6 +1065,34 @@ func HasApprovalsWith(preds ...predicate.Approval) predicate.User { }) } +// HasLocks applies the HasEdge predicate on the "locks" edge. +func HasLocks() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LocksTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LocksTable, LocksColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocksWith applies the HasEdge predicate on the "locks" edge with a given conditions (other predicates). +func HasLocksWith(preds ...predicate.Lock) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LocksInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LocksTable, LocksColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User(func(s *sql.Selector) { diff --git a/ent/user_create.go b/ent/user_create.go index ea8fc834..3fbc1d3c 100644 --- a/ent/user_create.go +++ b/ent/user_create.go @@ -13,6 +13,7 @@ import ( "github.com/gitploy-io/gitploy/ent/approval" "github.com/gitploy-io/gitploy/ent/chatuser" "github.com/gitploy-io/gitploy/ent/deployment" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/user" ) @@ -180,6 +181,21 @@ func (uc *UserCreate) AddApprovals(a ...*Approval) *UserCreate { return uc.AddApprovalIDs(ids...) } +// AddLockIDs adds the "locks" edge to the Lock entity by IDs. +func (uc *UserCreate) AddLockIDs(ids ...int) *UserCreate { + uc.mutation.AddLockIDs(ids...) + return uc +} + +// AddLocks adds the "locks" edges to the Lock entity. +func (uc *UserCreate) AddLocks(l ...*Lock) *UserCreate { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return uc.AddLockIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uc *UserCreate) Mutation() *UserMutation { return uc.mutation @@ -475,6 +491,25 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := uc.mutation.LocksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.LocksTable, + Columns: []string{user.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/ent/user_query.go b/ent/user_query.go index d0c863be..0e2e1d58 100644 --- a/ent/user_query.go +++ b/ent/user_query.go @@ -16,6 +16,7 @@ import ( "github.com/gitploy-io/gitploy/ent/approval" "github.com/gitploy-io/gitploy/ent/chatuser" "github.com/gitploy-io/gitploy/ent/deployment" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/predicate" "github.com/gitploy-io/gitploy/ent/user" @@ -35,6 +36,7 @@ type UserQuery struct { withPerms *PermQuery withDeployments *DeploymentQuery withApprovals *ApprovalQuery + withLocks *LockQuery modifiers []func(s *sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -160,6 +162,28 @@ func (uq *UserQuery) QueryApprovals() *ApprovalQuery { return query } +// QueryLocks chains the current query on the "locks" edge. +func (uq *UserQuery) QueryLocks() *LockQuery { + query := &LockQuery{config: uq.config} + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := uq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := uq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, selector), + sqlgraph.To(lock.Table, lock.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.LocksTable, user.LocksColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first User entity from the query. // Returns a *NotFoundError when no User was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { @@ -345,6 +369,7 @@ func (uq *UserQuery) Clone() *UserQuery { withPerms: uq.withPerms.Clone(), withDeployments: uq.withDeployments.Clone(), withApprovals: uq.withApprovals.Clone(), + withLocks: uq.withLocks.Clone(), // clone intermediate query. sql: uq.sql.Clone(), path: uq.path, @@ -395,6 +420,17 @@ func (uq *UserQuery) WithApprovals(opts ...func(*ApprovalQuery)) *UserQuery { return uq } +// WithLocks tells the query-builder to eager-load the nodes that are connected to +// the "locks" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithLocks(opts ...func(*LockQuery)) *UserQuery { + query := &LockQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withLocks = query + return uq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -460,11 +496,12 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { var ( nodes = []*User{} _spec = uq.querySpec() - loadedTypes = [4]bool{ + loadedTypes = [5]bool{ uq.withChatUser != nil, uq.withPerms != nil, uq.withDeployments != nil, uq.withApprovals != nil, + uq.withLocks != nil, } ) _spec.ScanValues = func(columns []string) ([]interface{}, error) { @@ -589,6 +626,31 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { } } + if query := uq.withLocks; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + nodes[i].Edges.Locks = []*Lock{} + } + query.Where(predicate.Lock(func(s *sql.Selector) { + s.Where(sql.InValues(user.LocksColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.UserID + node, ok := nodeids[fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "user_id" returned %v for node %v`, fk, n.ID) + } + node.Edges.Locks = append(node.Edges.Locks, n) + } + } + return nodes, nil } diff --git a/ent/user_update.go b/ent/user_update.go index 2c1ba16f..bae11664 100644 --- a/ent/user_update.go +++ b/ent/user_update.go @@ -13,6 +13,7 @@ import ( "github.com/gitploy-io/gitploy/ent/approval" "github.com/gitploy-io/gitploy/ent/chatuser" "github.com/gitploy-io/gitploy/ent/deployment" + "github.com/gitploy-io/gitploy/ent/lock" "github.com/gitploy-io/gitploy/ent/perm" "github.com/gitploy-io/gitploy/ent/predicate" "github.com/gitploy-io/gitploy/ent/user" @@ -159,6 +160,21 @@ func (uu *UserUpdate) AddApprovals(a ...*Approval) *UserUpdate { return uu.AddApprovalIDs(ids...) } +// AddLockIDs adds the "locks" edge to the Lock entity by IDs. +func (uu *UserUpdate) AddLockIDs(ids ...int) *UserUpdate { + uu.mutation.AddLockIDs(ids...) + return uu +} + +// AddLocks adds the "locks" edges to the Lock entity. +func (uu *UserUpdate) AddLocks(l ...*Lock) *UserUpdate { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return uu.AddLockIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uu *UserUpdate) Mutation() *UserMutation { return uu.mutation @@ -233,6 +249,27 @@ func (uu *UserUpdate) RemoveApprovals(a ...*Approval) *UserUpdate { return uu.RemoveApprovalIDs(ids...) } +// ClearLocks clears all "locks" edges to the Lock entity. +func (uu *UserUpdate) ClearLocks() *UserUpdate { + uu.mutation.ClearLocks() + return uu +} + +// RemoveLockIDs removes the "locks" edge to Lock entities by IDs. +func (uu *UserUpdate) RemoveLockIDs(ids ...int) *UserUpdate { + uu.mutation.RemoveLockIDs(ids...) + return uu +} + +// RemoveLocks removes "locks" edges to Lock entities. +func (uu *UserUpdate) RemoveLocks(l ...*Lock) *UserUpdate { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return uu.RemoveLockIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { var ( @@ -567,6 +604,60 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uu.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.LocksTable, + Columns: []string{user.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedLocksIDs(); len(nodes) > 0 && !uu.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.LocksTable, + Columns: []string{user.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.LocksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.LocksTable, + Columns: []string{user.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{user.Label} @@ -714,6 +805,21 @@ func (uuo *UserUpdateOne) AddApprovals(a ...*Approval) *UserUpdateOne { return uuo.AddApprovalIDs(ids...) } +// AddLockIDs adds the "locks" edge to the Lock entity by IDs. +func (uuo *UserUpdateOne) AddLockIDs(ids ...int) *UserUpdateOne { + uuo.mutation.AddLockIDs(ids...) + return uuo +} + +// AddLocks adds the "locks" edges to the Lock entity. +func (uuo *UserUpdateOne) AddLocks(l ...*Lock) *UserUpdateOne { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return uuo.AddLockIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uuo *UserUpdateOne) Mutation() *UserMutation { return uuo.mutation @@ -788,6 +894,27 @@ func (uuo *UserUpdateOne) RemoveApprovals(a ...*Approval) *UserUpdateOne { return uuo.RemoveApprovalIDs(ids...) } +// ClearLocks clears all "locks" edges to the Lock entity. +func (uuo *UserUpdateOne) ClearLocks() *UserUpdateOne { + uuo.mutation.ClearLocks() + return uuo +} + +// RemoveLockIDs removes the "locks" edge to Lock entities by IDs. +func (uuo *UserUpdateOne) RemoveLockIDs(ids ...int) *UserUpdateOne { + uuo.mutation.RemoveLockIDs(ids...) + return uuo +} + +// RemoveLocks removes "locks" edges to Lock entities. +func (uuo *UserUpdateOne) RemoveLocks(l ...*Lock) *UserUpdateOne { + ids := make([]int, len(l)) + for i := range l { + ids[i] = l[i].ID + } + return uuo.RemoveLockIDs(ids...) +} + // Select allows selecting one or more fields (columns) of the returned entity. // The default is selecting all fields defined in the entity schema. func (uuo *UserUpdateOne) Select(field string, fields ...string) *UserUpdateOne { @@ -1146,6 +1273,60 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uuo.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.LocksTable, + Columns: []string{user.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedLocksIDs(); len(nodes) > 0 && !uuo.mutation.LocksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.LocksTable, + Columns: []string{user.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.LocksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.LocksTable, + Columns: []string{user.LocksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lock.FieldID, + }, + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &User{config: uuo.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/internal/interactor/interface.go b/internal/interactor/interface.go index 9216fcc5..bc79e7bd 100644 --- a/internal/interactor/interface.go +++ b/internal/interactor/interface.go @@ -67,6 +67,12 @@ type ( UpdateApproval(ctx context.Context, a *ent.Approval) (*ent.Approval, error) DeleteApproval(ctx context.Context, a *ent.Approval) error + ListLocksOfRepo(ctx context.Context, r *ent.Repo) ([]*ent.Lock, error) + HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string) (bool, error) + FindLockByID(ctx context.Context, id int) (*ent.Lock, error) + CreateLock(ctx context.Context, l *ent.Lock) (*ent.Lock, error) + DeleteLock(ctx context.Context, l *ent.Lock) error + ListEventsGreaterThanTime(ctx context.Context, t time.Time) ([]*ent.Event, error) CreateEvent(ctx context.Context, e *ent.Event) (*ent.Event, error) CheckNotificationRecordOfEvent(ctx context.Context, e *ent.Event) bool diff --git a/internal/interactor/mock/pkg.go b/internal/interactor/mock/pkg.go index 856514fa..c0dd1726 100644 --- a/internal/interactor/mock/pkg.go +++ b/internal/interactor/mock/pkg.go @@ -173,6 +173,21 @@ func (mr *MockStoreMockRecorder) CreateEvent(ctx, e interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateEvent", reflect.TypeOf((*MockStore)(nil).CreateEvent), ctx, e) } +// CreateLock mocks base method. +func (m *MockStore) CreateLock(ctx context.Context, l *ent.Lock) (*ent.Lock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateLock", ctx, l) + ret0, _ := ret[0].(*ent.Lock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateLock indicates an expected call of CreateLock. +func (mr *MockStoreMockRecorder) CreateLock(ctx, l interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLock", reflect.TypeOf((*MockStore)(nil).CreateLock), ctx, l) +} + // CreatePerm mocks base method. func (m *MockStore) CreatePerm(ctx context.Context, p *ent.Perm) (*ent.Perm, error) { m.ctrl.T.Helper() @@ -246,6 +261,20 @@ func (mr *MockStoreMockRecorder) DeleteChatUser(ctx, cu interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteChatUser", reflect.TypeOf((*MockStore)(nil).DeleteChatUser), ctx, cu) } +// DeleteLock mocks base method. +func (m *MockStore) DeleteLock(ctx context.Context, l *ent.Lock) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteLock", ctx, l) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteLock indicates an expected call of DeleteLock. +func (mr *MockStoreMockRecorder) DeleteLock(ctx, l interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + 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) { m.ctrl.T.Helper() @@ -380,6 +409,21 @@ func (mr *MockStoreMockRecorder) FindDeploymentOfRepoByNumber(ctx, r, number int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDeploymentOfRepoByNumber", reflect.TypeOf((*MockStore)(nil).FindDeploymentOfRepoByNumber), ctx, r, number) } +// FindLockByID mocks base method. +func (m *MockStore) FindLockByID(ctx context.Context, id int) (*ent.Lock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindLockByID", ctx, id) + ret0, _ := ret[0].(*ent.Lock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindLockByID indicates an expected call of FindLockByID. +func (mr *MockStoreMockRecorder) FindLockByID(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindLockByID", reflect.TypeOf((*MockStore)(nil).FindLockByID), ctx, id) +} + // FindPermOfRepo mocks base method. func (m *MockStore) FindPermOfRepo(ctx context.Context, r *ent.Repo, u *ent.User) (*ent.Perm, error) { m.ctrl.T.Helper() @@ -515,6 +559,21 @@ func (mr *MockStoreMockRecorder) GetNextDeploymentNumberOfRepo(ctx, r interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNextDeploymentNumberOfRepo", reflect.TypeOf((*MockStore)(nil).GetNextDeploymentNumberOfRepo), ctx, r) } +// HasLockOfRepoForEnv mocks base method. +func (m *MockStore) HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasLockOfRepoForEnv", ctx, r, env) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HasLockOfRepoForEnv indicates an expected call of HasLockOfRepoForEnv. +func (mr *MockStoreMockRecorder) HasLockOfRepoForEnv(ctx, r, env interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasLockOfRepoForEnv", reflect.TypeOf((*MockStore)(nil).HasLockOfRepoForEnv), ctx, r, env) +} + // ListApprovals mocks base method. func (m *MockStore) ListApprovals(ctx context.Context, d *ent.Deployment) ([]*ent.Approval, error) { m.ctrl.T.Helper() @@ -575,6 +634,21 @@ func (mr *MockStoreMockRecorder) ListInactiveDeploymentsLessThanTime(ctx, t, pag return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListInactiveDeploymentsLessThanTime", reflect.TypeOf((*MockStore)(nil).ListInactiveDeploymentsLessThanTime), ctx, t, page, perPage) } +// ListLocksOfRepo mocks base method. +func (m *MockStore) ListLocksOfRepo(ctx context.Context, r *ent.Repo) ([]*ent.Lock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListLocksOfRepo", ctx, r) + ret0, _ := ret[0].([]*ent.Lock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListLocksOfRepo indicates an expected call of ListLocksOfRepo. +func (mr *MockStoreMockRecorder) ListLocksOfRepo(ctx, r interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListLocksOfRepo", reflect.TypeOf((*MockStore)(nil).ListLocksOfRepo), ctx, r) +} + // ListPermsOfRepo mocks base method. func (m *MockStore) ListPermsOfRepo(ctx context.Context, r *ent.Repo, q string, page, perPage int) ([]*ent.Perm, error) { m.ctrl.T.Helper() diff --git a/internal/pkg/store/lock.go b/internal/pkg/store/lock.go new file mode 100644 index 00000000..9ec85efe --- /dev/null +++ b/internal/pkg/store/lock.go @@ -0,0 +1,62 @@ +package store + +import ( + "context" + + "github.com/gitploy-io/gitploy/ent" + "github.com/gitploy-io/gitploy/ent/lock" +) + +func (s *Store) ListLocksOfRepo(ctx context.Context, r *ent.Repo) ([]*ent.Lock, error) { + return s.c.Lock. + Query(). + Where(lock.RepoID(r.ID)). + WithUser(). + WithRepo(). + All(ctx) +} + +func (s *Store) HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string) (bool, error) { + cnt, err := s.c.Lock. + Query(). + Where( + lock.And( + lock.RepoID(r.ID), + lock.EnvEQ(env), + ), + ). + WithUser(). + WithRepo(). + Count(ctx) + if err != nil { + return false, err + } + + return cnt > 0, nil +} + +func (s *Store) FindLockByID(ctx context.Context, id int) (*ent.Lock, error) { + return s.c.Lock. + Query(). + Where( + lock.IDEQ(id), + ). + WithUser(). + WithRepo(). + Only(ctx) +} + +func (s *Store) CreateLock(ctx context.Context, l *ent.Lock) (*ent.Lock, error) { + return s.c.Lock. + Create(). + SetEnv(l.Env). + SetRepoID(l.RepoID). + SetUserID(l.UserID). + Save(ctx) +} + +func (s *Store) DeleteLock(ctx context.Context, l *ent.Lock) error { + return s.c.Lock. + DeleteOne(l). + Exec(ctx) +} diff --git a/internal/pkg/store/repo.go b/internal/pkg/store/repo.go index 9e142d8c..b0a2fef3 100644 --- a/internal/pkg/store/repo.go +++ b/internal/pkg/store/repo.go @@ -127,7 +127,6 @@ func (s *Store) UpdateRepo(ctx context.Context, r *ent.Repo) (*ent.Repo, error) return s.c.Repo. UpdateOne(r). SetConfigPath(r.ConfigPath). - SetLocked(r.Locked). Save(ctx) } diff --git a/internal/server/api/v1/repos/deployment.go b/internal/server/api/v1/repos/deployment.go index 9a956784..4451d96e 100644 --- a/internal/server/api/v1/repos/deployment.go +++ b/internal/server/api/v1/repos/deployment.go @@ -87,20 +87,10 @@ func (r *Repo) CreateDeployment(c *gin.Context) { vr, _ := c.Get(KeyRepo) re := vr.(*ent.Repo) - if re.Locked { - r.log.Warn("The repository is locked. It blocks to deploy.") - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The repository is locked. It blocks to deploy.") - return - } - cf, err := r.i.GetConfig(ctx, u, re) - if vo.IsConfigNotFoundError(err) { - r.log.Warn("failed to get the config.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to find the configuraton file.") - return - } else if vo.IsConfigParseError(err) { - r.log.Warn("failed to parse the config.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to parse the configuraton file.") + if vo.IsConfigNotFoundError(err) || vo.IsConfigParseError(err) { + r.log.Warn("The config is invalid.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The config is invalid.") return } else if err != nil { r.log.Error("failed to get the configuration file.", zap.Error(err)) @@ -109,14 +99,24 @@ func (r *Repo) CreateDeployment(c *gin.Context) { } if !cf.HasEnv(p.Env) { - r.log.Warn("failed to get the env.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to get the env in the configuration file.") + r.log.Warn("The env is not defined in the config.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The env is not defined in the config.") return } if err := cf.GetEnv(p.Env).Eval(&vo.EvalValues{}); err != nil { - r.log.Warn("It has failed to eval the env.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to eval the env in the configuration file.") + r.log.Warn("It has failed to eval variables in the config.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to eval variables in the config.") + return + } + + if locked, err := r.i.HasLockOfRepoForEnv(ctx, re, p.Env); locked { + r.log.Info("The env is locked.", zap.String("env", p.Env)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The env is locked. You should unlock the env before deploying.") + return + } else if err != nil { + r.log.Error("It has failed to check the lock.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to check the lock.") return } @@ -192,20 +192,10 @@ func (r *Repo) UpdateDeployment(c *gin.Context) { gb.ErrorResponse(c, http.StatusNotFound, "The deployment is not found.") } - if re.Locked { - r.log.Warn("The repository is locked. It blocks to deploy.") - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The repository is locked. It blocks to deploy.") - return - } - cf, err := r.i.GetConfig(ctx, u, re) - if vo.IsConfigNotFoundError(err) { - r.log.Warn("failed to get the config.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to find the configuraton file.") - return - } else if vo.IsConfigParseError(err) { - r.log.Warn("failed to parse the config.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to parse the configuraton file.") + if vo.IsConfigNotFoundError(err) || vo.IsConfigParseError(err) { + r.log.Warn("The config is invalid.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The config is invalid.") return } else if err != nil { r.log.Error("failed to get the configuration file.", zap.Error(err)) @@ -214,14 +204,24 @@ func (r *Repo) UpdateDeployment(c *gin.Context) { } if !cf.HasEnv(d.Env) { - r.log.Warn("failed to get the env.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to get the env in the configuration file.") + r.log.Warn("The env is not defined in the config.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The env is not defined in the config.") return } if err := cf.GetEnv(d.Env).Eval(&vo.EvalValues{IsRollback: d.IsRollback}); err != nil { - r.log.Warn("It has failed to eval the env.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to eval the env in the configuration file.") + r.log.Warn("It has failed to eval variables in the config.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to eval variables in the config.") + return + } + + if locked, err := r.i.HasLockOfRepoForEnv(ctx, re, d.Env); locked { + r.log.Info("The env is locked.", zap.String("env", d.Env)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The env is locked. You should unlock the env before deploying.") + return + } else if err != nil { + r.log.Error("It has failed to check the lock.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to check the lock.") return } @@ -281,20 +281,10 @@ func (r *Repo) RollbackDeployment(c *gin.Context) { return } - if re.Locked { - r.log.Warn("The repository is locked. It blocks to deploy.") - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The repository is locked. It blocks to deploy.") - return - } - cf, err := r.i.GetConfig(ctx, u, re) - if vo.IsConfigNotFoundError(err) { - r.log.Warn("failed to get the config.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to find the configuraton file.") - return - } else if vo.IsConfigParseError(err) { - r.log.Warn("failed to parse the config.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to parse the configuraton file.") + if vo.IsConfigNotFoundError(err) || vo.IsConfigParseError(err) { + r.log.Warn("The config is invalid.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The config is invalid.") return } else if err != nil { r.log.Error("failed to get the configuration file.", zap.Error(err)) @@ -303,14 +293,24 @@ func (r *Repo) RollbackDeployment(c *gin.Context) { } if !cf.HasEnv(d.Env) { - r.log.Warn("failed to get the env.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to get the env in the configuration file.") + r.log.Warn("The env is not defined in the config.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The env is not defined in the config.") return } if err := cf.GetEnv(d.Env).Eval(&vo.EvalValues{IsRollback: true}); err != nil { - r.log.Warn("It has failed to eval the env.", zap.Error(err)) - gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to eval the env in the configuration file.") + r.log.Warn("It has failed to eval variables in the config.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "It has failed to eval variables in the config.") + return + } + + if locked, err := r.i.HasLockOfRepoForEnv(ctx, re, d.Env); locked { + r.log.Info("The env is locked.", zap.String("env", d.Env)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The env is locked. You should unlock the env before deploying.") + return + } else if err != nil { + r.log.Error("It has failed to check the lock.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to check the lock.") return } diff --git a/internal/server/api/v1/repos/deployment_test.go b/internal/server/api/v1/repos/deployment_test.go index d0542d83..4f12e56c 100644 --- a/internal/server/api/v1/repos/deployment_test.go +++ b/internal/server/api/v1/repos/deployment_test.go @@ -106,41 +106,6 @@ func TestRepo_ListDeploymentChanges(t *testing.T) { } func TestRepo_CreateDeployment(t *testing.T) { - t.Run("422 error when request to the locked repo.", func(t *testing.T) { - input := struct { - payload *deploymentPostPayload - }{ - payload: &deploymentPostPayload{ - Type: "branch", - Ref: "main", - Env: "prod", - }, - } - - ctrl := gomock.NewController(t) - m := mock.NewMockInteractor(ctrl) - - r := NewRepo(RepoConfig{}, m) - - gin.SetMode(gin.ReleaseMode) - router := gin.New() - router.POST("/repos/:id/deployments", func(c *gin.Context) { - c.Set(global.KeyUser, &ent.User{}) - // The repos is locked. - c.Set(KeyRepo, &ent.Repo{Locked: true}) - }, r.CreateDeployment) - - body, _ := json.Marshal(input.payload) - req, _ := http.NewRequest("POST", "/repos/1/deployments", bytes.NewBuffer(body)) - w := httptest.NewRecorder() - - router.ServeHTTP(w, req) - if w.Code != http.StatusUnprocessableEntity { - t.Fatalf("Code = %v, wanted %v. Body=%v", w.Code, http.StatusCreated, w.Body) - } - - }) - t.Run("a new deployment entity.", func(t *testing.T) { input := struct { payload *deploymentPostPayload @@ -165,6 +130,12 @@ func TestRepo_CreateDeployment(t *testing.T) { }, }, nil) + t.Log("Check the lock for env.") + m. + EXPECT(). + HasLockOfRepoForEnv(gomock.Any(), gomock.AssignableToTypeOf(&ent.Repo{}), input.payload.Env). + Return(false, nil) + t.Log("Return the next deployment number.") m. EXPECT(). diff --git a/internal/server/api/v1/repos/interface.go b/internal/server/api/v1/repos/interface.go index 57c4bd61..66877f80 100644 --- a/internal/server/api/v1/repos/interface.go +++ b/internal/server/api/v1/repos/interface.go @@ -39,6 +39,12 @@ type ( UpdateApproval(ctx context.Context, a *ent.Approval) (*ent.Approval, error) DeleteApproval(ctx context.Context, a *ent.Approval) error + ListLocksOfRepo(ctx context.Context, r *ent.Repo) ([]*ent.Lock, error) + HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string) (bool, error) + FindLockByID(ctx context.Context, id int) (*ent.Lock, error) + CreateLock(ctx context.Context, l *ent.Lock) (*ent.Lock, error) + DeleteLock(ctx context.Context, l *ent.Lock) error + CreateEvent(ctx context.Context, e *ent.Event) (*ent.Event, error) ListCommits(ctx context.Context, u *ent.User, r *ent.Repo, branch string, page, perPage int) ([]*vo.Commit, error) diff --git a/internal/server/api/v1/repos/lock.go b/internal/server/api/v1/repos/lock.go new file mode 100644 index 00000000..7bf65b97 --- /dev/null +++ b/internal/server/api/v1/repos/lock.go @@ -0,0 +1,137 @@ +package repos + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "go.uber.org/zap" + + "github.com/gitploy-io/gitploy/ent" + "github.com/gitploy-io/gitploy/internal/server/global" + gb "github.com/gitploy-io/gitploy/internal/server/global" + "github.com/gitploy-io/gitploy/vo" +) + +type ( + lockPostPayload struct { + Env string `json:"env"` + } +) + +func (r *Repo) ListLocks(c *gin.Context) { + ctx := c.Request.Context() + + vr, _ := c.Get(KeyRepo) + re := vr.(*ent.Repo) + + locks, err := r.i.ListLocksOfRepo(ctx, re) + if err != nil { + r.log.Error("It has failed to list locks.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to list locks.") + return + } + + gb.Response(c, http.StatusOK, locks) +} + +func (r *Repo) CreateLock(c *gin.Context) { + ctx := c.Request.Context() + + p := &lockPostPayload{} + if err := c.ShouldBindBodyWith(p, binding.JSON); err != nil { + r.log.Error("It has failed to bind the payload.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusBadRequest, "It has failed to bind the payload.") + return + } + + vr, _ := c.Get(KeyRepo) + re := vr.(*ent.Repo) + + vu, _ := c.Get(global.KeyUser) + u := vu.(*ent.User) + + // Validate the payload, it check whether the env exist or not in deploy.yml. + cfg, err := r.i.GetConfig(ctx, u, re) + if vo.IsConfigNotFoundError(err) || vo.IsConfigParseError(err) { + r.log.Warn("The config is invalid.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The config is invalid.") + return + } else if err != nil { + r.log.Error("It has failed to get the config file.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to get the config file.") + return + } + + if !cfg.HasEnv(p.Env) { + r.log.Warn("The env is not found.", zap.String("env", p.Env)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, fmt.Sprintf("The '%s' env is not found.", p.Env)) + return + } + + if ok, err := r.i.HasLockOfRepoForEnv(ctx, re, p.Env); ok { + r.log.Warn("The lock already exist.", zap.String("env", p.Env)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The lock already exist.") + return + } else if err != nil { + r.log.Error("It has failed to check the lock.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to check the lock.") + return + } + + // Lock the environment. + l, err := r.i.CreateLock(ctx, &ent.Lock{ + Env: p.Env, + UserID: u.ID, + RepoID: re.ID, + }) + if err != nil { + r.log.Error("It has failed to lock the env.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to lock the env.") + return + } + + if nl, err := r.i.FindLockByID(ctx, l.ID); err == nil { + l = nl + } + + r.log.Debug("Lock the env.", zap.String("env", p.Env)) + gb.Response(c, http.StatusCreated, l) +} + +func (r *Repo) DeleteLock(c *gin.Context) { + ctx := c.Request.Context() + + var ( + sid = c.Param("lockID") + ) + + id, err := strconv.Atoi(sid) + if err != nil { + r.log.Error("The lock ID must to be number.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusBadRequest, "The lock ID must to be number.") + return + } + + l, err := r.i.FindLockByID(ctx, id) + if ent.IsNotFound(err) { + r.log.Warn("The lock is not found.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusUnprocessableEntity, "The lock is not found.") + return + } else if err != nil { + r.log.Error("It has failed to find the lock.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to find the lock.") + return + } + + if err := r.i.DeleteLock(ctx, l); err != nil { + r.log.Error("It has failed to delete the lock.", zap.Error(err)) + gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to delete the lock.") + return + } + + r.log.Debug("Unlock the env.", zap.String("env", l.Env)) + gb.Response(c, http.StatusOK, nil) +} diff --git a/internal/server/api/v1/repos/lock_test.go b/internal/server/api/v1/repos/lock_test.go new file mode 100644 index 00000000..1407975d --- /dev/null +++ b/internal/server/api/v1/repos/lock_test.go @@ -0,0 +1,167 @@ +package repos + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/golang/mock/gomock" + + "github.com/gitploy-io/gitploy/ent" + "github.com/gitploy-io/gitploy/internal/server/api/v1/repos/mock" + "github.com/gitploy-io/gitploy/internal/server/global" + "github.com/gitploy-io/gitploy/vo" +) + +func TestRepo_CreateLock(t *testing.T) { + t.Run("Return 422 when the env is not found", func(t *testing.T) { + input := struct { + payload *lockPostPayload + }{ + payload: &lockPostPayload{ + Env: "production", + }, + } + + ctrl := gomock.NewController(t) + m := mock.NewMockInteractor(ctrl) + + t.Log("Read deploy.yml and check the env.") + m. + EXPECT(). + GetConfig(gomock.Any(), gomock.AssignableToTypeOf(&ent.User{}), gomock.AssignableToTypeOf(&ent.Repo{})). + Return(&vo.Config{ + Envs: []*vo.Env{ + { + Name: "dev", + }, + }, + }, nil) + + r := NewRepo(RepoConfig{}, m) + + gin.SetMode(gin.ReleaseMode) + + router := gin.New() + router.POST("repos/:id/locks", func(c *gin.Context) { + c.Set(global.KeyUser, &ent.User{}) + c.Set(KeyRepo, &ent.Repo{}) + }, r.CreateLock) + + body, _ := json.Marshal(input.payload) + req, _ := http.NewRequest("POST", "/repos/1/locks", bytes.NewBuffer(body)) + w := httptest.NewRecorder() + + router.ServeHTTP(w, req) + if w.Code != http.StatusUnprocessableEntity { + t.Fatalf("Code = %v, wanted %v. Body=%v", w.Code, http.StatusCreated, w.Body) + } + }) + + t.Run("Lock the env", func(t *testing.T) { + input := struct { + payload *lockPostPayload + }{ + payload: &lockPostPayload{ + Env: "production", + }, + } + + ctrl := gomock.NewController(t) + m := mock.NewMockInteractor(ctrl) + + t.Log("Read deploy.yml and check the env.") + m. + EXPECT(). + GetConfig(gomock.Any(), gomock.AssignableToTypeOf(&ent.User{}), gomock.AssignableToTypeOf(&ent.Repo{})). + Return(&vo.Config{ + Envs: []*vo.Env{ + { + Name: "production", + }, + }, + }, nil) + + t.Log("Check whether the env is locked or not.") + m. + EXPECT(). + HasLockOfRepoForEnv(gomock.Any(), gomock.AssignableToTypeOf(&ent.Repo{}), input.payload.Env). + Return(false, nil) + + t.Log("Lock the env.") + m. + EXPECT(). + CreateLock(gomock.Any(), gomock.AssignableToTypeOf(&ent.Lock{})). + Return(&ent.Lock{ID: 1}, nil) + + t.Log("Get the lock with edges") + m. + EXPECT(). + FindLockByID(gomock.Any(), gomock.AssignableToTypeOf(&ent.Lock{})). + Return(&ent.Lock{ID: 1}, nil) + + r := NewRepo(RepoConfig{}, m) + + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.POST("repos/:id/locks", func(c *gin.Context) { + c.Set(global.KeyUser, &ent.User{}) + c.Set(KeyRepo, &ent.Repo{}) + }, r.CreateLock) + + body, _ := json.Marshal(input.payload) + req, _ := http.NewRequest("POST", "/repos/1/locks", bytes.NewBuffer(body)) + w := httptest.NewRecorder() + + router.ServeHTTP(w, req) + if w.Code != http.StatusCreated { + t.Fatalf("Code = %v, wanted %v. Body=%v", w.Code, http.StatusCreated, w.Body) + } + }) +} + +func TestRepo_DeleteLock(t *testing.T) { + t.Run("Unlock the env", func(t *testing.T) { + input := struct { + id int + }{ + id: 1, + } + + ctrl := gomock.NewController(t) + m := mock.NewMockInteractor(ctrl) + + t.Log("Find the lock") + m. + EXPECT(). + FindLockByID(gomock.Any(), input.id). + Return(&ent.Lock{ID: input.id}, nil) + + t.Log("Delete the lock") + m. + EXPECT(). + DeleteLock(gomock.Any(), gomock.Eq(&ent.Lock{ID: input.id})). + Return(nil) + + r := NewRepo(RepoConfig{}, m) + + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.DELETE("repos/:id/locks/:lockID", func(c *gin.Context) { + c.Set(global.KeyUser, &ent.User{}) + c.Set(KeyRepo, &ent.Repo{}) + }, r.DeleteLock) + + req, _ := http.NewRequest("DELETE", fmt.Sprintf("/repos/1/locks/%d", input.id), nil) + w := httptest.NewRecorder() + + router.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("Code = %v, wanted %v. Body=%v", w.Code, http.StatusCreated, w.Body) + } + }) +} diff --git a/internal/server/api/v1/repos/mock/interactor.go b/internal/server/api/v1/repos/mock/interactor.go index c08051ec..f1e5fd36 100644 --- a/internal/server/api/v1/repos/mock/interactor.go +++ b/internal/server/api/v1/repos/mock/interactor.go @@ -96,6 +96,21 @@ func (mr *MockInteractorMockRecorder) CreateEvent(ctx, e interface{}) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateEvent", reflect.TypeOf((*MockInteractor)(nil).CreateEvent), ctx, e) } +// CreateLock mocks base method. +func (m *MockInteractor) CreateLock(ctx context.Context, l *ent.Lock) (*ent.Lock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateLock", ctx, l) + ret0, _ := ret[0].(*ent.Lock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateLock indicates an expected call of CreateLock. +func (mr *MockInteractorMockRecorder) CreateLock(ctx, l interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLock", reflect.TypeOf((*MockInteractor)(nil).CreateLock), ctx, l) +} + // CreateRemoteDeployment mocks base method. func (m *MockInteractor) CreateRemoteDeployment(ctx context.Context, u *ent.User, re *ent.Repo, d *ent.Deployment, env *vo.Env) (*ent.Deployment, error) { m.ctrl.T.Helper() @@ -140,6 +155,20 @@ func (mr *MockInteractorMockRecorder) DeleteApproval(ctx, a interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteApproval", reflect.TypeOf((*MockInteractor)(nil).DeleteApproval), ctx, a) } +// DeleteLock mocks base method. +func (m *MockInteractor) DeleteLock(ctx context.Context, l *ent.Lock) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteLock", ctx, l) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteLock indicates an expected call of DeleteLock. +func (mr *MockInteractorMockRecorder) DeleteLock(ctx, l interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLock", reflect.TypeOf((*MockInteractor)(nil).DeleteLock), ctx, l) +} + // Deploy mocks base method. func (m *MockInteractor) Deploy(ctx context.Context, u *ent.User, re *ent.Repo, d *ent.Deployment, env *vo.Env) (*ent.Deployment, error) { m.ctrl.T.Helper() @@ -215,6 +244,21 @@ func (mr *MockInteractorMockRecorder) FindDeploymentOfRepoByNumber(ctx, r, numbe return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDeploymentOfRepoByNumber", reflect.TypeOf((*MockInteractor)(nil).FindDeploymentOfRepoByNumber), ctx, r, number) } +// FindLockByID mocks base method. +func (m *MockInteractor) FindLockByID(ctx context.Context, id int) (*ent.Lock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindLockByID", ctx, id) + ret0, _ := ret[0].(*ent.Lock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindLockByID indicates an expected call of FindLockByID. +func (mr *MockInteractorMockRecorder) FindLockByID(ctx, id interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindLockByID", reflect.TypeOf((*MockInteractor)(nil).FindLockByID), ctx, id) +} + // FindPermOfRepo mocks base method. func (m *MockInteractor) FindPermOfRepo(ctx context.Context, r *ent.Repo, u *ent.User) (*ent.Perm, error) { m.ctrl.T.Helper() @@ -260,21 +304,6 @@ func (mr *MockInteractorMockRecorder) FindRepoOfUserByID(ctx, u, id interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindRepoOfUserByID", reflect.TypeOf((*MockInteractor)(nil).FindRepoOfUserByID), ctx, u, id) } -// FindRepoOfUserByNamespaceName mocks base method. -func (m *MockInteractor) FindRepoOfUserByNamespaceName(ctx context.Context, u *ent.User, namespace, name string) (*ent.Repo, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FindRepoOfUserByNamespaceName", ctx, u, namespace, name) - ret0, _ := ret[0].(*ent.Repo) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FindRepoOfUserByNamespaceName indicates an expected call of FindRepoOfUserByNamespaceName. -func (mr *MockInteractorMockRecorder) FindRepoOfUserByNamespaceName(ctx, u, namespace, name interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindRepoOfUserByNamespaceName", reflect.TypeOf((*MockInteractor)(nil).FindRepoOfUserByNamespaceName), ctx, u, namespace, name) -} - // FindUserByID mocks base method. func (m *MockInteractor) FindUserByID(ctx context.Context, id string) (*ent.User, error) { m.ctrl.T.Helper() @@ -365,6 +394,21 @@ func (mr *MockInteractorMockRecorder) GetTag(ctx, u, r, tag interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTag", reflect.TypeOf((*MockInteractor)(nil).GetTag), ctx, u, r, tag) } +// HasLockOfRepoForEnv mocks base method. +func (m *MockInteractor) HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasLockOfRepoForEnv", ctx, r, env) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HasLockOfRepoForEnv indicates an expected call of HasLockOfRepoForEnv. +func (mr *MockInteractorMockRecorder) HasLockOfRepoForEnv(ctx, r, env interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasLockOfRepoForEnv", reflect.TypeOf((*MockInteractor)(nil).HasLockOfRepoForEnv), ctx, r, env) +} + // IsApproved mocks base method. func (m *MockInteractor) IsApproved(ctx context.Context, d *ent.Deployment) bool { m.ctrl.T.Helper() @@ -454,6 +498,21 @@ func (mr *MockInteractorMockRecorder) ListDeploymentsOfRepo(ctx, r, env, status, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDeploymentsOfRepo", reflect.TypeOf((*MockInteractor)(nil).ListDeploymentsOfRepo), ctx, r, env, status, page, perPage) } +// ListLocksOfRepo mocks base method. +func (m *MockInteractor) ListLocksOfRepo(ctx context.Context, r *ent.Repo) ([]*ent.Lock, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListLocksOfRepo", ctx, r) + ret0, _ := ret[0].([]*ent.Lock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListLocksOfRepo indicates an expected call of ListLocksOfRepo. +func (mr *MockInteractorMockRecorder) ListLocksOfRepo(ctx, r interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListLocksOfRepo", reflect.TypeOf((*MockInteractor)(nil).ListLocksOfRepo), ctx, r) +} + // ListPermsOfRepo mocks base method. func (m *MockInteractor) ListPermsOfRepo(ctx context.Context, r *ent.Repo, q string, page, perPage int) ([]*ent.Perm, error) { m.ctrl.T.Helper() diff --git a/internal/server/api/v1/repos/repo.go b/internal/server/api/v1/repos/repo.go index 6cebdab2..12179cc0 100644 --- a/internal/server/api/v1/repos/repo.go +++ b/internal/server/api/v1/repos/repo.go @@ -28,7 +28,6 @@ type ( repoPatchPayload struct { ConfigPath *string `json:"config_path"` Active *bool `json:"active"` - Locked *bool `json:"locked"` } ) @@ -123,18 +122,6 @@ func (r *Repo) UpdateRepo(c *gin.Context) { } } - if p.Locked != nil { - if *p.Locked != re.Locked { - re.Locked = *p.Locked - - if re, err = r.i.UpdateRepo(ctx, re); err != nil { - r.log.Error("It has failed to update the repo", zap.Error(err)) - gb.ErrorResponse(c, http.StatusInternalServerError, "It has failed to update the repository.") - return - } - } - } - gb.Response(c, http.StatusOK, re) } diff --git a/internal/server/api/v1/repos/repo_test.go b/internal/server/api/v1/repos/repo_test.go index af77e849..65f32a0a 100644 --- a/internal/server/api/v1/repos/repo_test.go +++ b/internal/server/api/v1/repos/repo_test.go @@ -113,52 +113,4 @@ func TestRepo_UpdateRepo(t *testing.T) { t.Fatalf("Code = %v, wanted %v", w.Code, http.StatusOK) } }) - - t.Run("Patch locked field.", func(t *testing.T) { - input := struct { - payload *repoPatchPayload - }{ - payload: &repoPatchPayload{ - Locked: pointer.ToBool(true), - }, - } - - const ( - r1 = "1" - ) - - ctrl := gomock.NewController(t) - m := mock.NewMockInteractor(ctrl) - - t.Log("Update the repe to set locked field true.") - m. - EXPECT(). - UpdateRepo(gomock.Any(), gomock.Eq(&ent.Repo{ - ID: r1, - Locked: *input.payload.Locked, - })). - DoAndReturn(func(ctx context.Context, r *ent.Repo) (*ent.Repo, error) { - return r, nil - }) - - gin.SetMode(gin.ReleaseMode) - router := gin.New() - - r := NewRepo(RepoConfig{}, m) - router.PATCH("/repos/:id", func(c *gin.Context) { - t.Log("Set up fake middleware") - c.Set(global.KeyUser, &ent.User{}) - c.Set(KeyRepo, &ent.Repo{ID: r1}) - }, r.UpdateRepo) - - p, _ := json.Marshal(input.payload) - req, _ := http.NewRequest("PATCH", fmt.Sprintf("/repos/%s", r1), bytes.NewBuffer(p)) - w := httptest.NewRecorder() - - router.ServeHTTP(w, req) - - if w.Code != http.StatusOK { - t.Fatalf("Code = %v, wanted %v", w.Code, http.StatusOK) - } - }) } diff --git a/internal/server/router.go b/internal/server/router.go index 2ce69fd6..290e0a06 100644 --- a/internal/server/router.go +++ b/internal/server/router.go @@ -144,6 +144,9 @@ func NewRouter(c *RouterConfig) *gin.Engine { repov1.PATCH("/:id/deployments/:number/approval", rm.RepoReadPerm(), r.UpdateApproval) repov1.GET("/:id/approvals/:aid", rm.RepoReadPerm(), r.GetApproval) repov1.DELETE("/:id/approvals/:aid", rm.RepoReadPerm(), r.DeleteApproval) + repov1.GET("/:id/locks", rm.RepoReadPerm(), r.ListLocks) + repov1.POST("/:id/locks", rm.RepoWritePerm(), r.CreateLock) + repov1.DELETE("/:id/locks/:lockID", rm.RepoWritePerm(), r.DeleteLock) repov1.GET("/:id/perms", rm.RepoReadPerm(), r.ListPerms) repov1.GET("/:id/config", rm.RepoReadPerm(), r.GetConfig) } diff --git a/internal/server/slack/deploy.go b/internal/server/slack/deploy.go index 6abff57c..caf5c00d 100644 --- a/internal/server/slack/deploy.go +++ b/internal/server/slack/deploy.go @@ -265,19 +265,9 @@ func (s *Slack) interactDeploy(c *gin.Context) { return } - if cb.Edges.Repo.Locked { - postBotMessage(cu, fmt.Sprintf("The `%s` repository is locked. It blocks to deploy.", cb.Edges.Repo.GetFullName())) - c.Status(http.StatusOK) - return - } - - cf, err := s.i.GetConfig(ctx, cu.Edges.User, cb.Edges.Repo) - if vo.IsConfigNotFoundError(err) { - postBotMessage(cu, "The config file is not found.") - c.Status(http.StatusOK) - return - } else if vo.IsConfigParseError(err) { - postBotMessage(cu, "The config file is invliad format.") + cfg, err := s.i.GetConfig(ctx, cu.Edges.User, cb.Edges.Repo) + if vo.IsConfigNotFoundError(err) || vo.IsConfigParseError(err) { + postBotMessage(cu, "The config is invlid.") c.Status(http.StatusOK) return } else if err != nil { @@ -286,18 +276,27 @@ func (s *Slack) interactDeploy(c *gin.Context) { return } - if !cf.HasEnv(sm.Env) { - postBotMessage(cu, "The environment is not defined.") + if !cfg.HasEnv(sm.Env) { + postBotMessage(cu, "The env is not defined in the config.") c.Status(http.StatusOK) return } - env := cf.GetEnv(sm.Env) - + env := cfg.GetEnv(sm.Env) if err := env.Eval(&vo.EvalValues{}); err != nil { - postBotMessage(cu, "The environment is invalid. It has failed to eval the environment.") + postBotMessage(cu, "It has failed to eval variables in the config.") + c.Status(http.StatusOK) + return + } + + if locked, err := s.i.HasLockOfRepoForEnv(ctx, cb.Edges.Repo, sm.Env); locked { + postBotMessage(cu, "The env is locked. You should unlock the env before deploying.") c.Status(http.StatusOK) return + } else if err != nil { + s.log.Error("It has failed to check the lock.", zap.Error(err)) + c.Status(http.StatusInternalServerError) + return } number, err := s.i.GetNextDeploymentNumberOfRepo(ctx, cb.Edges.Repo) diff --git a/internal/server/slack/deploy_test.go b/internal/server/slack/deploy_test.go index 4b4cedfe..f6f57a69 100644 --- a/internal/server/slack/deploy_test.go +++ b/internal/server/slack/deploy_test.go @@ -11,9 +11,7 @@ import ( "github.com/gin-gonic/gin" "github.com/golang/mock/gomock" - "github.com/slack-go/slack" "go.uber.org/zap" - "gopkg.in/h2non/gock.v1" "github.com/gitploy-io/gitploy/ent" "github.com/gitploy-io/gitploy/ent/deployment" @@ -26,77 +24,6 @@ const ( ) func TestSlack_interactDeploy(t *testing.T) { - t.Run("Post a message when the repository is locked.", func(t *testing.T) { - m := mock.NewMockInteractor(gomock.NewController(t)) - - // These values are in "./testdata/deploy-interact.json" - const ( - callbackID = "nafyVuEqzcchuVmV" - chatUserID = "U025KUBB2" - branch = "main" - env = "prod" - ) - - t.Log("Find the callback which was stored by the Slash command.") - m. - EXPECT(). - FindCallbackByHash(gomock.Any(), callbackID). - Return(&ent.Callback{ - Edges: ent.CallbackEdges{ - Repo: &ent.Repo{ - ID: "1", - Locked: true, - }, - }, - }, nil) - - t.Log("Find the chat-user who sent the payload.") - m. - EXPECT(). - FindChatUserByID(gomock.Any(), chatUserID). - Return(&ent.ChatUser{}, nil) - - t.Log("Get branch to validate the payload.") - m. - EXPECT(). - GetBranch(gomock.Any(), gomock.AssignableToTypeOf(&ent.User{}), gomock.AssignableToTypeOf(&ent.Repo{}), branch). - Return(&vo.Branch{ - Name: branch, - }, nil) - - t.Log("Post a message when the repository is locked.") - gock. - New(slack.APIURL). - Post(pathPostMessage). - Reply(200) - - s := &Slack{i: m, log: zap.L()} - - gin.SetMode(gin.ReleaseMode) - router := gin.New() - router.POST("/interact", s.interactDeploy) - - // Build the Slack payload. - bytes, err := ioutil.ReadFile("./testdata/deploy-interact.json") - if err != nil { - t.Errorf("It has failed to open the JSON file: %s", err) - t.FailNow() - } - - form := url.Values{} - form.Add("payload", string(bytes)) - - req, _ := http.NewRequest("POST", "/interact", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != http.StatusOK { - t.Fatalf("w.Code = %d, wanted %d. Body = %v", w.Code, http.StatusOK, w.Body) - } - }) - t.Run("Create a new deployment with payload.", func(t *testing.T) { m := mock.NewMockInteractor(gomock.NewController(t)) @@ -142,6 +69,12 @@ func TestSlack_interactDeploy(t *testing.T) { }, }, nil) + t.Log("Check the lock.") + m. + EXPECT(). + HasLockOfRepoForEnv(gomock.Any(), gomock.AssignableToTypeOf(&ent.Repo{}), env). + Return(false, nil) + t.Log("Get the next number of deployment.") m. EXPECT(). diff --git a/internal/server/slack/interface.go b/internal/server/slack/interface.go index fff74de7..3b768c26 100644 --- a/internal/server/slack/interface.go +++ b/internal/server/slack/interface.go @@ -35,6 +35,8 @@ type ( CreateApproval(ctx context.Context, a *ent.Approval) (*ent.Approval, error) + HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string) (bool, error) + SubscribeEvent(fn func(e *ent.Event)) error UnsubscribeEvent(fn func(e *ent.Event)) error diff --git a/internal/server/slack/lock.go b/internal/server/slack/lock.go index 3ec76db7..f5c5da29 100644 --- a/internal/server/slack/lock.go +++ b/internal/server/slack/lock.go @@ -68,14 +68,13 @@ func (s *Slack) handleLockCmd(c *gin.Context) { return } - r.Locked = true + // r.Locked = true if r, err = s.i.UpdateRepo(ctx, r); err != nil { s.log.Error("It has failed to update the repo.", zap.Error(err)) c.Status(http.StatusInternalServerError) return } - s.log.Debug("Lock the repository successfully.", zap.Bool("locked", r.Locked)) postResponseMessage(cmd.ChannelID, cmd.ResponseURL, fmt.Sprintf("Lock the `%s` repository successfully.", args[1])) c.Status(http.StatusOK) } @@ -135,14 +134,13 @@ func (s *Slack) handleUnlockCmd(c *gin.Context) { return } - r.Locked = false + // r.Locked = false if r, err = s.i.UpdateRepo(ctx, r); err != nil { s.log.Error("It has failed to update the repo.", zap.Error(err)) c.Status(http.StatusInternalServerError) return } - s.log.Debug("Unlock the repository successfully.", zap.Bool("locked", r.Locked)) postResponseMessage(cmd.ChannelID, cmd.ResponseURL, fmt.Sprintf("Unlock the `%s` repository successfully.", args[1])) c.Status(http.StatusOK) } diff --git a/internal/server/slack/mock/interactor.go b/internal/server/slack/mock/interactor.go index 6a93a98c..67dfea71 100644 --- a/internal/server/slack/mock/interactor.go +++ b/internal/server/slack/mock/interactor.go @@ -304,6 +304,21 @@ func (mr *MockInteractorMockRecorder) GetTag(ctx, u, r, tag interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTag", reflect.TypeOf((*MockInteractor)(nil).GetTag), ctx, u, r, tag) } +// HasLockOfRepoForEnv mocks base method. +func (m *MockInteractor) HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasLockOfRepoForEnv", ctx, r, env) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HasLockOfRepoForEnv indicates an expected call of HasLockOfRepoForEnv. +func (mr *MockInteractorMockRecorder) HasLockOfRepoForEnv(ctx, r, env interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasLockOfRepoForEnv", reflect.TypeOf((*MockInteractor)(nil).HasLockOfRepoForEnv), ctx, r, env) +} + // ListDeploymentsOfRepo mocks base method. func (m *MockInteractor) ListDeploymentsOfRepo(ctx context.Context, r *ent.Repo, env, status string, page, perPage int) ([]*ent.Deployment, error) { m.ctrl.T.Helper() @@ -334,21 +349,6 @@ func (mr *MockInteractorMockRecorder) ListPermsOfRepo(ctx, r, q, page, perPage i return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPermsOfRepo", reflect.TypeOf((*MockInteractor)(nil).ListPermsOfRepo), ctx, r, q, page, perPage) } -// ListUsersOfEvent mocks base method. -func (m *MockInteractor) ListUsersOfEvent(ctx context.Context, e *ent.Event) ([]*ent.User, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListUsersOfEvent", ctx, e) - ret0, _ := ret[0].([]*ent.User) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListUsersOfEvent indicates an expected call of ListUsersOfEvent. -func (mr *MockInteractorMockRecorder) ListUsersOfEvent(ctx, e interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsersOfEvent", reflect.TypeOf((*MockInteractor)(nil).ListUsersOfEvent), ctx, e) -} - // SubscribeEvent mocks base method. func (m *MockInteractor) SubscribeEvent(fn func(*ent.Event)) error { m.ctrl.T.Helper() diff --git a/internal/server/slack/rollback.go b/internal/server/slack/rollback.go index f45b0d10..d3889eab 100644 --- a/internal/server/slack/rollback.go +++ b/internal/server/slack/rollback.go @@ -228,15 +228,9 @@ func (s *Slack) interactRollback(c *gin.Context) { return } - if cb.Edges.Repo.Locked { - postBotMessage(cu, fmt.Sprintf("The `%s` repository is locked. It blocks to deploy.", cb.Edges.Repo.GetFullName())) - c.Status(http.StatusOK) - return - } - - cf, err := s.i.GetConfig(ctx, cu.Edges.User, cb.Edges.Repo) - if vo.IsConfigNotFoundError(err) { - postBotMessage(cu, "The config file is not found.") + cfg, err := s.i.GetConfig(ctx, cu.Edges.User, cb.Edges.Repo) + if vo.IsConfigNotFoundError(err) || vo.IsConfigParseError(err) { + postBotMessage(cu, "The config is invlid.") c.Status(http.StatusOK) return } else if vo.IsConfigParseError(err) { @@ -249,19 +243,29 @@ func (s *Slack) interactRollback(c *gin.Context) { return } - if !cf.HasEnv(d.Env) { - postBotMessage(cu, "The environment is not defined.") + if !cfg.HasEnv(d.Env) { + postBotMessage(cu, "The env is not defined in the config.") c.Status(http.StatusOK) return } - if err := cf.GetEnv(d.Env).Eval(&vo.EvalValues{IsRollback: true}); err != nil { - postBotMessage(cu, "The environment is invalid. It has failed to eval the environment.") + env := cfg.GetEnv(d.Env) + + if err := env.Eval(&vo.EvalValues{IsRollback: true}); err != nil { + postBotMessage(cu, "It has failed to eval variables in the config.") c.Status(http.StatusOK) return } - env := cf.GetEnv(d.Env) + if locked, err := s.i.HasLockOfRepoForEnv(ctx, cb.Edges.Repo, d.Env); locked { + postBotMessage(cu, "The env is locked. You should unlock the env before deploying.") + c.Status(http.StatusOK) + return + } else if err != nil { + s.log.Error("It has failed to check the lock.", zap.Error(err)) + c.Status(http.StatusInternalServerError) + return + } next, err := s.i.GetNextDeploymentNumberOfRepo(ctx, cb.Edges.Repo) if err != nil { diff --git a/internal/server/slack/rollback_test.go b/internal/server/slack/rollback_test.go index de66d9dc..3765ff8f 100644 --- a/internal/server/slack/rollback_test.go +++ b/internal/server/slack/rollback_test.go @@ -11,9 +11,7 @@ import ( "github.com/gin-gonic/gin" "github.com/golang/mock/gomock" - "github.com/slack-go/slack" "go.uber.org/zap" - "gopkg.in/h2non/gock.v1" "github.com/gitploy-io/gitploy/ent" "github.com/gitploy-io/gitploy/ent/deployment" @@ -22,76 +20,6 @@ import ( ) func TestSlack_interactRollback(t *testing.T) { - t.Run("Post a message when the repository is locked.", func(t *testing.T) { - m := mock.NewMockInteractor(gomock.NewController(t)) - - // These values are in "./testdata/rollback-interact.json" - const ( - callbackID = "hZUZvJgWhxYvdekUGESXKjSusKWWIRKr" - chatUserID = "U025KUBB2" - deploymentID = 33 - ) - - t.Log("Find the callback which has the locked repository.") - m. - EXPECT(). - FindCallbackByHash(gomock.Any(), callbackID). - Return(&ent.Callback{ - Edges: ent.CallbackEdges{ - Repo: &ent.Repo{ - ID: "1", - Locked: true, - }, - }, - }, nil) - - t.Log("Find the chat-user who sent the payload.") - m. - EXPECT(). - FindChatUserByID(gomock.Any(), chatUserID). - Return(&ent.ChatUser{}, nil) - - t.Log("Find the deployment by ID.") - m. - EXPECT(). - FindDeploymentByID(gomock.Any(), deploymentID). - Return(&ent.Deployment{ - ID: deploymentID, - }, nil) - - t.Log("Post a message when the repository is locked.") - gock. - New(slack.APIURL). - Post(pathPostMessage). - Reply(200) - - s := &Slack{i: m, log: zap.L()} - - gin.SetMode(gin.ReleaseMode) - router := gin.New() - router.POST("/interact", s.interactRollback) - - // Build the payload to interact. - bytes, err := ioutil.ReadFile("./testdata/rollback-interact.json") - if err != nil { - t.Errorf("It has failed to open the JSON file: %s", err) - t.FailNow() - } - - form := url.Values{} - form.Add("payload", string(bytes)) - - req, _ := http.NewRequest("POST", "/interact", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != http.StatusOK { - t.Fatalf("w.Code = %d, wanted %d. Body = %v", w.Code, http.StatusOK, w.Body) - } - }) - t.Run("Rollback with the returned deployment.", func(t *testing.T) { m := mock.NewMockInteractor(gomock.NewController(t)) @@ -140,6 +68,12 @@ func TestSlack_interactRollback(t *testing.T) { }, }, nil) + t.Log("Check the lock.") + m. + EXPECT(). + HasLockOfRepoForEnv(gomock.Any(), gomock.AssignableToTypeOf(&ent.Repo{}), "prod"). + Return(false, nil) + t.Log("Get the next number of deployment.") m. EXPECT(). diff --git a/internal/server/slack/slack.go b/internal/server/slack/slack.go index db644014..df655e91 100644 --- a/internal/server/slack/slack.go +++ b/internal/server/slack/slack.go @@ -63,10 +63,6 @@ func (s *Slack) Cmd(c *gin.Context) { s.handleDeployCmd(c) } else if args[0] == "rollback" && len(args) == 2 { s.handleRollbackCmd(c) - } else if args[0] == "lock" && len(args) == 2 { - s.handleLockCmd(c) - } else if args[0] == "unlock" && len(args) == 2 { - s.handleUnlockCmd(c) } else { s.handleHelpCmd(cmd.ChannelID, cmd.ResponseURL) } diff --git a/openapi.yml b/openapi.yml index cb60034e..9dfda42e 100644 --- a/openapi.yml +++ b/openapi.yml @@ -116,8 +116,6 @@ paths: default: deploy.yml active: type: boolean - locked: - type: boolean parameters: - in: path name: id @@ -911,6 +909,105 @@ paths: $ref: '#/components/responses/404NotFound' '500': $ref: '#/components/responses/500InternalError' + /repos/{id}/locks: + get: + tags: + - Repo + summary: List locked environments. + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The repository id. + responses: + '200': + description: Locks + content: + application/json: + schema: + $ref: '#/components/schemas/Locks' + '401': + $ref: '#/components/responses/401Unauthorized' + '402': + $ref: '#/components/responses/402PaymentRequired' + '403': + $ref: '#/components/responses/403Forbidden' + '500': + $ref: '#/components/responses/500InternalError' + post: + tags: + - Repo + summary: Lock the environment. + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The repository id. + requestBody: + content: + application/json: + schema: + type: object + properties: + env: + type: string + description: The name of env in deploy.yml + responses: + '201': + description: Lock + content: + application/json: + schema: + $ref: '#/components/schemas/Lock' + '401': + $ref: '#/components/responses/401Unauthorized' + '402': + $ref: '#/components/responses/402PaymentRequired' + '403': + $ref: '#/components/responses/403Forbidden' + '422': + $ref: '#/components/responses/422UnprocessableEntity' + '500': + $ref: '#/components/responses/500InternalError' + /repos/{id}/locks/{lockId}: + delete: + tags: + - Repo + summary: Unlock the environment. + parameters: + - in: path + name: id + required: true + schema: + type: integer + description: The repository id. + - in: path + name: lockId + required: true + schema: + type: integer + description: The lock id. + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + '401': + $ref: '#/components/responses/401Unauthorized' + '402': + $ref: '#/components/responses/402PaymentRequired' + '403': + $ref: '#/components/responses/403Forbidden' + '404': + $ref: '#/components/responses/404NotFound' + '500': + $ref: '#/components/responses/500InternalError' /repos/{id}/perms: get: tags: @@ -1378,8 +1475,6 @@ components: type: boolean webhook_id: type: integer - locked: - type: boolean synced_at: type: string created_at: @@ -1400,7 +1495,6 @@ components: - description - config_path - active - - locked - created_at - updated_at Perms: @@ -1591,6 +1685,30 @@ components: - status - created_at - updated_at + Locks: + type: array + items: + $ref: '#/components/schemas/Lock' + Lock: + type: object + properties: + id: + type: number + env: + type: string + created_at: + type: string + edges: + type: object + properties: + repo: + $ref: '#/components/schemas/Repository' + user: + $ref: '#/components/schemas/User' + required: + - id + - env + - created_at Config: type: object properties: diff --git a/ui/src/apis/index.ts b/ui/src/apis/index.ts index 438f998c..e96e6ec7 100644 --- a/ui/src/apis/index.ts +++ b/ui/src/apis/index.ts @@ -33,6 +33,11 @@ import { setApprovalApproved, setApprovalDeclined } from "./approval" +import { + listLocks, + lock, + unlock +} from "./lock" import { getLicense } from "./license" import { subscribeEvents } from "./events" @@ -74,6 +79,9 @@ export { getMyApproval, setApprovalApproved, setApprovalDeclined, + listLocks, + lock, + unlock, getLicense, subscribeEvents } \ No newline at end of file diff --git a/ui/src/apis/lock.ts b/ui/src/apis/lock.ts new file mode 100644 index 00000000..fc3affbc --- /dev/null +++ b/ui/src/apis/lock.ts @@ -0,0 +1,68 @@ +import { StatusCodes } from "http-status-codes" + +import { instance, headers } from "./setting" +import { _fetch } from "./_base" +import { UserData, mapDataToUser } from "./user" +import { Repo, Lock, User, HttpUnprocessableEntityError } from "../models" + +interface LockData { + id: number + env: string + created_at: string + edges: { + user?: UserData + } +} + +const mapDataToLock = (data: LockData): Lock => { + let user: User | undefined + + if (data.edges.user) { + user = mapDataToUser(data.edges.user) + } + + return { + id: data.id, + env: data.env, + createdAt: new Date(data.created_at), + user, + } +} + +export const listLocks = async (repo: Repo): Promise => { + const locks = await _fetch(`${instance}/api/v1/repos/${repo.id}/locks`, { + headers, + credentials: 'same-origin', + }) + .then(res => res.json()) + .then(datas => datas.map((d: any): Lock => mapDataToLock(d))) + + return locks +} + +export const lock = async (repo: Repo, env: string): Promise => { + const res = await _fetch(`${instance}/api/v1/repos/${repo.id}/locks`, { + headers, + credentials: 'same-origin', + method: "POST", + body: JSON.stringify({env}) + }) + + if (res.status === StatusCodes.UNPROCESSABLE_ENTITY) { + const {message} = await res.json() + throw new HttpUnprocessableEntityError(message) + } + + const lock = await res + .json() + .then(data => mapDataToLock(data)) + return lock +} + +export const unlock = async (repo: Repo, lock: Lock): Promise => { + await _fetch(`${instance}/api/v1/repos/${repo.id}/locks/${lock.id}`, { + headers, + credentials: 'same-origin', + method: "DELETE", + }) +} \ No newline at end of file diff --git a/ui/src/apis/repo.ts b/ui/src/apis/repo.ts index 856cad76..80a5bede 100644 --- a/ui/src/apis/repo.ts +++ b/ui/src/apis/repo.ts @@ -38,7 +38,6 @@ export const mapDataToRepo = (data: RepoData): Repo => { configPath: data.config_path, active: data.active, webhookId: data.webhook_id, - locked: data.locked, createdAt: new Date(data.created_at), updatedAt: new Date(data.updated_at), deployments, diff --git a/ui/src/components/LockList.tsx b/ui/src/components/LockList.tsx new file mode 100644 index 00000000..a3ccb3a1 --- /dev/null +++ b/ui/src/components/LockList.tsx @@ -0,0 +1,62 @@ +import { List, Button } from "antd" +import { LockOutlined, UnlockOutlined } from "@ant-design/icons" +import moment from 'moment' + +import { Env, Lock } from "../models" +import UserAvatar from './UserAvatar' + +interface LockListProps { + envs: Env[] + locks: Lock[] + onClickLock(env: string): void + onClickUnlock(env: string): void +} + +export default function LockList(props: LockListProps): JSX.Element { + return ( + { + const lock = props.locks.find((lock) => lock.env === env.name) + + return (lock)? + + {env.name.toUpperCase()} } + description={} + /> + + : + + {env.name.toUpperCase()} } + description={} + /> + + + }} + /> + ) +} + +interface LockDescriptionProps { + lock?: Lock +} + +function LockDescription(props: LockDescriptionProps) { + + return ( + (props.lock)? + Locked by {moment(props.lock.createdAt).fromNow()}: + + ) +} \ No newline at end of file diff --git a/ui/src/components/RepoSettingsForm.tsx b/ui/src/components/RepoSettingsForm.tsx index 51a684d4..20b7a1be 100644 --- a/ui/src/components/RepoSettingsForm.tsx +++ b/ui/src/components/RepoSettingsForm.tsx @@ -5,8 +5,6 @@ export interface RepoSettingsFormProps { repo: Repo saving: boolean onClickSave(payload: {configPath: string}): void - onClickLock(): void - onClickUnlock(): void onClickDeactivate(): void } @@ -57,21 +55,6 @@ export default function RepoSettingForm(props: RepoSettingsFormProps): JSX.Eleme - {(props.repo.locked)? - : - - } + } + /> + ) + } + + const onClickLock = (env: string) => { + dispatch(lock(env)) + } + + const onClickUnlock = (env: string) => { + dispatch(unlock(env)) + } + + return
+
+ +
+
+ +
+
+} \ No newline at end of file diff --git a/ui/src/views/RepoRollback.tsx b/ui/src/views/RepoRollback.tsx index d5e8bf95..4698e634 100644 --- a/ui/src/views/RepoRollback.tsx +++ b/ui/src/views/RepoRollback.tsx @@ -72,16 +72,6 @@ export default function RepoHome(): JSX.Element { return
} - if (repo.locked) { - return ( - - ) - } - if (repo && !config) { return ( { - dispatch(lock()) - } - - const onClickUnlock = () => { - dispatch(unlock()) - } - const onClickDeactivate = () => { dispatch(deactivate()) } @@ -60,8 +52,6 @@ export default function RepoSettings(): JSX.Element { repo={repo} saving={saving === RequestStatus.Pending} onClickSave={onClickSave} - onClickLock={onClickLock} - onClickUnlock={onClickUnlock} onClickDeactivate={onClickDeactivate} />