From 20f85bfd8f0336f590f2ee54b4823479009ffd09 Mon Sep 17 00:00:00 2001 From: noah Date: Fri, 24 Sep 2021 21:01:11 +0900 Subject: [PATCH 1/9] Remove the 'locked' field in Repo --- ent/migrate/schema.go | 1 - ent/mutation.go | 56 +------------- ent/repo.go | 12 +-- ent/repo/repo.go | 5 -- ent/repo/where.go | 21 ------ ent/repo_create.go | 29 -------- ent/repo_update.go | 42 ----------- ent/runtime.go | 8 +- ent/schema/repo.go | 2 - internal/pkg/store/repo.go | 1 - internal/server/api/v1/repos/deployment.go | 18 ----- .../server/api/v1/repos/deployment_test.go | 35 --------- internal/server/api/v1/repos/repo.go | 13 ---- internal/server/api/v1/repos/repo_test.go | 48 ------------ internal/server/slack/deploy.go | 6 -- internal/server/slack/deploy_test.go | 73 ------------------- internal/server/slack/lock.go | 6 +- internal/server/slack/rollback.go | 6 -- internal/server/slack/rollback_test.go | 72 ------------------ internal/server/slack/slack.go | 4 - openapi.yml | 2 - 21 files changed, 6 insertions(+), 454 deletions(-) diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 72e8072f..dce5dd63 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -286,7 +286,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}, diff --git a/ent/mutation.go b/ent/mutation.go index 208271cc..9fe02de1 100644 --- a/ent/mutation.go +++ b/ent/mutation.go @@ -5988,7 +5988,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 @@ -6342,42 +6341,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 @@ -6680,7 +6643,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 +6662,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 +6691,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 +6718,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 +6775,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 +6893,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 diff --git a/ent/repo.go b/ent/repo.go index c9954621..fe1668f6 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. @@ -86,7 +84,7 @@ 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 +149,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]) @@ -230,8 +222,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..8eba2080 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. @@ -71,7 +69,6 @@ var Columns = []string{ FieldConfigPath, FieldActive, FieldWebhookID, - FieldLocked, FieldCreatedAt, FieldUpdatedAt, FieldLatestDeployedAt, @@ -92,8 +89,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..53cb9535 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) { diff --git a/ent/repo_create.go b/ent/repo_create.go index 1e70936f..423d29f2 100644 --- a/ent/repo_create.go +++ b/ent/repo_create.go @@ -83,20 +83,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) @@ -269,10 +255,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 +282,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 +365,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, diff --git a/ent/repo_update.go b/ent/repo_update.go index 6e3d0023..c9cc988f 100644 --- a/ent/repo_update.go +++ b/ent/repo_update.go @@ -103,20 +103,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) @@ -406,13 +392,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, @@ -694,20 +673,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) @@ -1021,13 +986,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, diff --git a/ent/runtime.go b/ent/runtime.go index 40fa5e8b..96a3deb8 100644 --- a/ent/runtime.go +++ b/ent/runtime.go @@ -129,16 +129,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/repo.go b/ent/schema/repo.go index a10d303d..0f379cae 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"). 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..573e592d 100644 --- a/internal/server/api/v1/repos/deployment.go +++ b/internal/server/api/v1/repos/deployment.go @@ -87,12 +87,6 @@ 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)) @@ -192,12 +186,6 @@ 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)) @@ -281,12 +269,6 @@ 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)) diff --git a/internal/server/api/v1/repos/deployment_test.go b/internal/server/api/v1/repos/deployment_test.go index d0542d83..3d58b191 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 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/slack/deploy.go b/internal/server/slack/deploy.go index 6abff57c..4470b74d 100644 --- a/internal/server/slack/deploy.go +++ b/internal/server/slack/deploy.go @@ -265,12 +265,6 @@ 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.") diff --git a/internal/server/slack/deploy_test.go b/internal/server/slack/deploy_test.go index 4b4cedfe..bf60fef4 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)) 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/rollback.go b/internal/server/slack/rollback.go index f45b0d10..1825b215 100644 --- a/internal/server/slack/rollback.go +++ b/internal/server/slack/rollback.go @@ -228,12 +228,6 @@ 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.") diff --git a/internal/server/slack/rollback_test.go b/internal/server/slack/rollback_test.go index de66d9dc..3a867f82 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)) 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..5fe6b9ab 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 From 540933d506d8081a3ac6cb64e5984deee0afbbfc Mon Sep 17 00:00:00 2001 From: noah Date: Fri, 24 Sep 2021 21:35:57 +0900 Subject: [PATCH 2/9] Add Lock entity --- ent/client.go | 161 ++++++ ent/config.go | 1 + ent/ent.go | 2 + ent/hook/hook.go | 13 + ent/lock.go | 186 +++++++ ent/lock/lock.go | 66 +++ ent/lock/where.go | 619 +++++++++++++++++++++ ent/lock_create.go | 332 +++++++++++ ent/lock_delete.go | 111 ++++ ent/lock_query.go | 1083 ++++++++++++++++++++++++++++++++++++ ent/lock_update.go | 552 ++++++++++++++++++ ent/migrate/schema.go | 31 ++ ent/mutation.go | 734 +++++++++++++++++++++++- ent/predicate/predicate.go | 3 + ent/repo.go | 18 +- ent/repo/repo.go | 9 + ent/repo/where.go | 28 + ent/repo_create.go | 35 ++ ent/repo_query.go | 64 ++- ent/repo_update.go | 181 ++++++ ent/runtime.go | 7 + ent/schema/lock.go | 46 ++ ent/schema/repo.go | 4 + ent/schema/user.go | 1 + ent/tx.go | 3 + ent/user.go | 18 +- ent/user/user.go | 9 + ent/user/where.go | 28 + ent/user_create.go | 35 ++ ent/user_query.go | 64 ++- ent/user_update.go | 181 ++++++ openapi.yml | 125 +++++ 32 files changed, 4740 insertions(+), 10 deletions(-) create mode 100644 ent/lock.go create mode 100644 ent/lock/lock.go create mode 100644 ent/lock/where.go create mode 100644 ent/lock_create.go create mode 100644 ent/lock_delete.go create mode 100644 ent/lock_query.go create mode 100644 ent/lock_update.go create mode 100644 ent/schema/lock.go 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 dce5dd63..831445af 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -217,6 +217,34 @@ 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, + }, + }, + } // NotificationRecordsColumns holds the columns for the "notification_records" table. NotificationRecordsColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, @@ -335,6 +363,7 @@ var ( DeploymentsTable, DeploymentStatusTable, EventsTable, + LocksTable, NotificationRecordsTable, PermsTable, ReposTable, @@ -352,6 +381,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 9fe02de1..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 @@ -6001,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 @@ -6624,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...) @@ -6908,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) } @@ -6918,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 } @@ -6943,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) } @@ -6959,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 } @@ -6984,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) } @@ -7000,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 } @@ -7013,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 } @@ -7038,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) } @@ -7069,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 @@ -7684,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...) @@ -7938,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) } @@ -7951,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 } @@ -7980,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) } @@ -7996,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 } @@ -8021,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) } @@ -8040,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 } @@ -8055,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 } @@ -8086,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 fe1668f6..2d6cd256 100644 --- a/ent/repo.go +++ b/ent/repo.go @@ -47,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 @@ -79,6 +81,15 @@ 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)) @@ -187,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. diff --git a/ent/repo/repo.go b/ent/repo/repo.go index 8eba2080..e52a36e5 100644 --- a/ent/repo/repo.go +++ b/ent/repo/repo.go @@ -35,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. @@ -58,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. diff --git a/ent/repo/where.go b/ent/repo/where.go index 53cb9535..a3e3e3b7 100644 --- a/ent/repo/where.go +++ b/ent/repo/where.go @@ -1030,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 423d29f2..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" ) @@ -176,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 @@ -446,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 c9cc988f..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" @@ -188,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 @@ -256,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 ( @@ -581,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} @@ -758,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 @@ -826,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 { @@ -1175,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 96a3deb8..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. diff --git a/ent/schema/lock.go b/ent/schema/lock.go new file mode 100644 index 00000000..13b9aecb --- /dev/null +++ b/ent/schema/lock.go @@ -0,0 +1,46 @@ +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" +) + +// 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{} +} diff --git a/ent/schema/repo.go b/ent/schema/repo.go index 0f379cae..f411c4cd 100644 --- a/ent/schema/repo.go +++ b/ent/schema/repo.go @@ -55,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/openapi.yml b/openapi.yml index 5fe6b9ab..757157b1 100644 --- a/openapi.yml +++ b/openapi.yml @@ -909,6 +909,107 @@ 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: + '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' + '409': + $ref: '#/components/responses/409Conflict' + '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: Locks + content: + application/json: + schema: + $ref: '#/components/schemas/Locks' + '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: @@ -1589,6 +1690,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: From 630de287dff9324625261aeebee80ea639c54a79 Mon Sep 17 00:00:00 2001 From: noah Date: Fri, 24 Sep 2021 21:39:16 +0900 Subject: [PATCH 3/9] Add the unique constraint for Lock --- ent/migrate/schema.go | 7 +++++++ ent/schema/lock.go | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ent/migrate/schema.go b/ent/migrate/schema.go index 831445af..237856b8 100644 --- a/ent/migrate/schema.go +++ b/ent/migrate/schema.go @@ -244,6 +244,13 @@ var ( 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{ diff --git a/ent/schema/lock.go b/ent/schema/lock.go index 13b9aecb..50695a74 100644 --- a/ent/schema/lock.go +++ b/ent/schema/lock.go @@ -6,6 +6,7 @@ import ( "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. @@ -42,5 +43,8 @@ func (Lock) Edges() []ent.Edge { } func (Lock) Indexes() []ent.Index { - return []ent.Index{} + return []ent.Index{ + index.Fields("repo_id", "env"). + Unique(), + } } From 6539ea62de0bd4c7ff7470de1a042b8ed2df2bf5 Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 25 Sep 2021 11:52:17 +0900 Subject: [PATCH 4/9] Add APIs for Lock --- internal/interactor/interface.go | 6 + internal/interactor/mock/pkg.go | 74 ++++++++ internal/pkg/store/lock.go | 56 ++++++ internal/server/api/v1/repos/interface.go | 6 + internal/server/api/v1/repos/lock.go | 132 ++++++++++++++ internal/server/api/v1/repos/lock_test.go | 161 ++++++++++++++++++ .../server/api/v1/repos/mock/interactor.go | 89 ++++++++-- internal/server/router.go | 3 + internal/server/slack/mock/interactor.go | 15 -- openapi.yml | 12 +- 10 files changed, 517 insertions(+), 37 deletions(-) create mode 100644 internal/pkg/store/lock.go create mode 100644 internal/server/api/v1/repos/lock.go create mode 100644 internal/server/api/v1/repos/lock_test.go 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..a0849c9f --- /dev/null +++ b/internal/pkg/store/lock.go @@ -0,0 +1,56 @@ +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. + Get(ctx, id) +} + +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/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..592ffdf5 --- /dev/null +++ b/internal/server/api/v1/repos/lock.go @@ -0,0 +1,132 @@ +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 + } + + 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 + } + + 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..13b1a1e7 --- /dev/null +++ b/internal/server/api/v1/repos/lock_test.go @@ -0,0 +1,161 @@ +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) + + 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/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/mock/interactor.go b/internal/server/slack/mock/interactor.go index 6a93a98c..648e7f61 100644 --- a/internal/server/slack/mock/interactor.go +++ b/internal/server/slack/mock/interactor.go @@ -334,21 +334,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/openapi.yml b/openapi.yml index 757157b1..bce5d820 100644 --- a/openapi.yml +++ b/openapi.yml @@ -957,20 +957,18 @@ paths: type: string description: The name of env in deploy.yml responses: - '200': - description: Locks + '201': + description: Lock content: application/json: schema: - $ref: '#/components/schemas/Locks' + $ref: '#/components/schemas/Lock' '401': $ref: '#/components/responses/401Unauthorized' '402': $ref: '#/components/responses/402PaymentRequired' '403': $ref: '#/components/responses/403Forbidden' - '409': - $ref: '#/components/responses/409Conflict' '422': $ref: '#/components/responses/422UnprocessableEntity' '500': @@ -995,11 +993,11 @@ paths: description: The lock id. responses: '200': - description: Locks + description: Ok content: application/json: schema: - $ref: '#/components/schemas/Locks' + type: object '401': $ref: '#/components/responses/401Unauthorized' '402': From 0b4340f3a6d338a183a4ec734ec3dccbca98f779 Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 25 Sep 2021 12:41:28 +0900 Subject: [PATCH 5/9] Add lock validation in API and slack --- internal/server/api/v1/repos/deployment.go | 84 +++++++++++-------- .../server/api/v1/repos/deployment_test.go | 6 ++ internal/server/slack/deploy.go | 29 ++++--- internal/server/slack/deploy_test.go | 6 ++ internal/server/slack/interface.go | 2 + internal/server/slack/mock/interactor.go | 15 ++++ internal/server/slack/rollback.go | 26 ++++-- internal/server/slack/rollback_test.go | 6 ++ 8 files changed, 121 insertions(+), 53 deletions(-) diff --git a/internal/server/api/v1/repos/deployment.go b/internal/server/api/v1/repos/deployment.go index 573e592d..4451d96e 100644 --- a/internal/server/api/v1/repos/deployment.go +++ b/internal/server/api/v1/repos/deployment.go @@ -88,13 +88,9 @@ func (r *Repo) CreateDeployment(c *gin.Context) { re := vr.(*ent.Repo) 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)) @@ -103,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 } @@ -187,13 +193,9 @@ func (r *Repo) UpdateDeployment(c *gin.Context) { } 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)) @@ -202,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 } @@ -270,13 +282,9 @@ func (r *Repo) RollbackDeployment(c *gin.Context) { } 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)) @@ -285,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 3d58b191..4f12e56c 100644 --- a/internal/server/api/v1/repos/deployment_test.go +++ b/internal/server/api/v1/repos/deployment_test.go @@ -130,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/slack/deploy.go b/internal/server/slack/deploy.go index 4470b74d..caf5c00d 100644 --- a/internal/server/slack/deploy.go +++ b/internal/server/slack/deploy.go @@ -265,13 +265,9 @@ func (s *Slack) interactDeploy(c *gin.Context) { 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 { @@ -280,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 bf60fef4..f6f57a69 100644 --- a/internal/server/slack/deploy_test.go +++ b/internal/server/slack/deploy_test.go @@ -69,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/mock/interactor.go b/internal/server/slack/mock/interactor.go index 648e7f61..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() diff --git a/internal/server/slack/rollback.go b/internal/server/slack/rollback.go index 1825b215..d3889eab 100644 --- a/internal/server/slack/rollback.go +++ b/internal/server/slack/rollback.go @@ -228,9 +228,9 @@ func (s *Slack) interactRollback(c *gin.Context) { 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) { @@ -243,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 3a867f82..3765ff8f 100644 --- a/internal/server/slack/rollback_test.go +++ b/internal/server/slack/rollback_test.go @@ -68,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(). From aad8c76e6675bd1a535c57f3036071600ac0ce34 Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 25 Sep 2021 15:23:23 +0900 Subject: [PATCH 6/9] Update to eager loading after locking --- internal/pkg/store/lock.go | 8 +++++++- internal/server/api/v1/repos/lock.go | 5 +++++ internal/server/api/v1/repos/lock_test.go | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/internal/pkg/store/lock.go b/internal/pkg/store/lock.go index a0849c9f..9ec85efe 100644 --- a/internal/pkg/store/lock.go +++ b/internal/pkg/store/lock.go @@ -37,7 +37,13 @@ func (s *Store) HasLockOfRepoForEnv(ctx context.Context, r *ent.Repo, env string func (s *Store) FindLockByID(ctx context.Context, id int) (*ent.Lock, error) { return s.c.Lock. - Get(ctx, id) + Query(). + Where( + lock.IDEQ(id), + ). + WithUser(). + WithRepo(). + Only(ctx) } func (s *Store) CreateLock(ctx context.Context, l *ent.Lock) (*ent.Lock, error) { diff --git a/internal/server/api/v1/repos/lock.go b/internal/server/api/v1/repos/lock.go index 592ffdf5..7bf65b97 100644 --- a/internal/server/api/v1/repos/lock.go +++ b/internal/server/api/v1/repos/lock.go @@ -93,6 +93,10 @@ func (r *Repo) CreateLock(c *gin.Context) { 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) } @@ -128,5 +132,6 @@ func (r *Repo) DeleteLock(c *gin.Context) { 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 index 13b1a1e7..1407975d 100644 --- a/internal/server/api/v1/repos/lock_test.go +++ b/internal/server/api/v1/repos/lock_test.go @@ -98,6 +98,12 @@ func TestRepo_CreateLock(t *testing.T) { 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) From 25768e5629faacfa3082ce92443523701986761b Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 25 Sep 2021 15:26:29 +0900 Subject: [PATCH 7/9] Add Lock tab --- ui/src/apis/index.ts | 8 ++ ui/src/apis/lock.ts | 68 ++++++++++++++++ ui/src/components/LockList.tsx | 62 ++++++++++++++ ui/src/models/Lock.ts | 8 ++ ui/src/models/index.ts | 2 + ui/src/redux/repoLock.ts | 143 +++++++++++++++++++++++++++++++++ ui/src/redux/store.ts | 2 + ui/src/views/Repo.tsx | 7 ++ ui/src/views/RepoDeploy.tsx | 4 +- ui/src/views/RepoLock.tsx | 71 ++++++++++++++++ 10 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 ui/src/apis/lock.ts create mode 100644 ui/src/components/LockList.tsx create mode 100644 ui/src/models/Lock.ts create mode 100644 ui/src/redux/repoLock.ts create mode 100644 ui/src/views/RepoLock.tsx 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/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/models/Lock.ts b/ui/src/models/Lock.ts new file mode 100644 index 00000000..70a4c09b --- /dev/null +++ b/ui/src/models/Lock.ts @@ -0,0 +1,8 @@ +import User from "./User" + +export default interface Lock { + id: number + env: string + createdAt: Date + user?: User +} \ No newline at end of file diff --git a/ui/src/models/index.ts b/ui/src/models/index.ts index d7b50d89..beb746c9 100644 --- a/ui/src/models/index.ts +++ b/ui/src/models/index.ts @@ -11,6 +11,7 @@ import Branch from "./Branch" import Tag from "./Tag" import User, { ChatUser, RateLimit } from "./User" import Approval, { ApprovalStatus } from "./Approval" +import Lock from "./Lock" import License from "./License" import Event, {EventKindEnum, EventTypeEnum} from "./Event" import { @@ -40,6 +41,7 @@ export type { ChatUser, RateLimit, Approval, + Lock, License, Event } diff --git a/ui/src/redux/repoLock.ts b/ui/src/redux/repoLock.ts new file mode 100644 index 00000000..a53e9ef0 --- /dev/null +++ b/ui/src/redux/repoLock.ts @@ -0,0 +1,143 @@ +import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit' + +import { + Repo, + Config, + Lock +} from "../models" +import { + searchRepo, + getConfig, + listLocks as _listLocks, + lock as _lock, + unlock as _unlock +} from "../apis" + +interface RepoLockState { + display: boolean + repo?: Repo + config?: Config + locks: Lock[] +} + +const initialState: RepoLockState = { + display: false, + locks: [], +} + +export const init = createAsyncThunk( + 'repoLock/init', + async (params) => { + const repo = await searchRepo(params.namespace, params.name) + return repo + }, +) + +export const fetchConfig = createAsyncThunk( + "repoLock/fetchConfig", + async (_, { getState, rejectWithValue } ) => { + const { repo } = getState().repoLock + if (!repo) { + throw new Error("The repo is undefined.") + } + + try { + const config = await getConfig(repo.id) + return config + } catch (e) { + return rejectWithValue(e) + } + }, +) + +export const listLocks = createAsyncThunk( + 'repoLock/listLocks', + async (_, { getState, rejectWithValue }) => { + const { repo } = getState().repoLock + if (!repo) { + throw new Error("The repo is undefined.") + } + + try { + const locks = await _listLocks(repo) + return locks + } catch (e) { + return rejectWithValue(e) + } + }, +) + +export const lock = createAsyncThunk( + 'repoLock/lock', + async (env, { getState, rejectWithValue }) => { + const { repo } = getState().repoLock + if (!repo) { + throw new Error("The repo is undefined.") + } + + try { + const locks = await _lock(repo, env) + return locks + } catch (e) { + return rejectWithValue(e) + } + }, +) + +export const unlock = createAsyncThunk( + 'repoLock/unlock', + async (env, { getState, rejectWithValue }) => { + const { repo, locks } = getState().repoLock + if (!repo) { + throw new Error("The repo is undefined.") + } + + const lock = locks.find((lock) => lock.env === env) + if (!lock) { + throw new Error("The env is not found.") + } + + try { + await _unlock(repo, lock) + return lock + } catch (e) { + return rejectWithValue(e) + } + }, +) + +export const repoLockSlice = createSlice({ + name: "repoLock", + initialState, + reducers: { + setDisplay: (state, action: PayloadAction) => { + state.display = action.payload + }, + }, + extraReducers: builder => { + builder + .addCase(init.fulfilled, (state, action) => { + state.repo = action.payload + }) + + .addCase(fetchConfig.fulfilled, (state, action) => { + state.config = action.payload + }) + + .addCase(listLocks.fulfilled, (state, action) => { + state.locks = action.payload + }) + + .addCase(lock.fulfilled, (state, action) => { + state.locks.push(action.payload) + }) + + .addCase(unlock.fulfilled, (state, action) => { + const idx = state.locks.findIndex((lock) => lock.id === action.payload.id) + + if (idx !== -1) { + state.locks.splice(idx, 1) + } + }) + } +}) \ No newline at end of file diff --git a/ui/src/redux/store.ts b/ui/src/redux/store.ts index bf67e672..316eacdf 100644 --- a/ui/src/redux/store.ts +++ b/ui/src/redux/store.ts @@ -5,6 +5,7 @@ import { homeSlice } from "./home" import { repoSlice } from "./repo" import { repoHomeSlice } from "./repoHome" import { repoDeploySlice } from "./repoDeploy" +import { repoLockSlice } from "./repoLock" import { repoRollbackSlice } from "./repoRollback" import { repoSettingsSlice } from "./repoSettings" import { settingsSlice } from "./settings" @@ -17,6 +18,7 @@ export const store = configureStore({ home: homeSlice.reducer, repo: repoSlice.reducer, repoHome: repoHomeSlice.reducer, + repoLock: repoLockSlice.reducer, repoDeploy: repoDeploySlice.reducer, repoRollback: repoRollbackSlice.reducer, repoSettings: repoSettingsSlice.reducer, diff --git a/ui/src/views/Repo.tsx b/ui/src/views/Repo.tsx index 8050df2f..50d8803e 100644 --- a/ui/src/views/Repo.tsx +++ b/ui/src/views/Repo.tsx @@ -9,6 +9,7 @@ import { init, activate, repoSlice as slice } from '../redux/repo' import ActivateButton from "../components/ActivateButton" import Main from './Main' import RepoHome from './RepoHome' +import RepoLock from "./RepoLock"; import RepoDeploy from './RepoDeploy' import RepoRollabck from './RepoRollback' import RepoSettings from "./RepoSettings" @@ -83,6 +84,9 @@ export default function Repo(): JSX.Element { Home + + Lock + Deploy @@ -101,6 +105,9 @@ export default function Repo(): JSX.Element {
{(!tab || tab === "home") ? : null}
+
+ {(tab === "lock")? : null} +
{(tab === "deploy") ? : null}
diff --git a/ui/src/views/RepoDeploy.tsx b/ui/src/views/RepoDeploy.tsx index 9f9bcac1..86408a3a 100644 --- a/ui/src/views/RepoDeploy.tsx +++ b/ui/src/views/RepoDeploy.tsx @@ -1,6 +1,7 @@ +import { useEffect } from "react"; +import { shallowEqual } from "react-redux"; import { useParams } from "react-router-dom"; import { PageHeader, Result, Button } from "antd"; -import { shallowEqual } from "react-redux"; import { useAppSelector, useAppDispatch } from "../redux/hooks" import { User,DeploymentType, Branch, Commit, Tag, RequestStatus, Env } from "../models"; @@ -21,7 +22,6 @@ import { deploy} from "../redux/repoDeploy" import DeployForm, {Option} from "../components/DeployForm" -import { useEffect } from "react"; const { actions } = repoDeploySlice diff --git a/ui/src/views/RepoLock.tsx b/ui/src/views/RepoLock.tsx new file mode 100644 index 00000000..c99fb3f1 --- /dev/null +++ b/ui/src/views/RepoLock.tsx @@ -0,0 +1,71 @@ +import { useEffect } from "react"; +import { shallowEqual } from "react-redux"; +import { useParams } from "react-router-dom"; +import { PageHeader, Button } from 'antd' +import { Result } from "antd" + +import { useAppSelector, useAppDispatch } from "../redux/hooks" +import { init, fetchConfig, listLocks, lock, unlock, repoLockSlice as slice} from "../redux/repoLock" +import LockList from '../components/LockList' + +interface Params { + namespace: string + name: string +} + +export default function RepoLock(): JSX.Element { + const { namespace, name } = useParams() + const { display, repo, config, locks } = useAppSelector(state => state.repoLock, shallowEqual) + const dispatch = useAppDispatch() + + useEffect(() => { + const f = async () => { + await dispatch(init({namespace, name})) + await dispatch(fetchConfig()) + await dispatch(listLocks()) + await dispatch(slice.actions.setDisplay(true)) + } + f() + // eslint-disable-next-line + }, [dispatch]) + + if (!display || !repo) { + return <> + } + + if (repo && !config) { + return ( + + Read Document + + } + /> + ) + } + + const onClickLock = (env: string) => { + dispatch(lock(env)) + } + + const onClickUnlock = (env: string) => { + dispatch(unlock(env)) + } + + return
+
+ +
+
+ +
+
+} \ No newline at end of file From 8d489fff9446259e90fc9406a057b715357eb21c Mon Sep 17 00:00:00 2001 From: noah Date: Sat, 25 Sep 2021 15:32:02 +0900 Subject: [PATCH 8/9] Remove repository lock in UI --- openapi.yml | 3 --- ui/src/apis/repo.ts | 1 - ui/src/components/RepoSettingsForm.tsx | 17 ----------------- ui/src/models/Repo.ts | 1 - ui/src/views/Deployment.tsx | 3 +-- ui/src/views/RepoDeploy.tsx | 10 ---------- ui/src/views/RepoRollback.tsx | 10 ---------- ui/src/views/RepoSettings.tsx | 12 +----------- 8 files changed, 2 insertions(+), 55 deletions(-) diff --git a/openapi.yml b/openapi.yml index bce5d820..9dfda42e 100644 --- a/openapi.yml +++ b/openapi.yml @@ -1475,8 +1475,6 @@ components: type: boolean webhook_id: type: integer - locked: - type: boolean synced_at: type: string created_at: @@ -1497,7 +1495,6 @@ components: - description - config_path - active - - locked - created_at - updated_at Perms: 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/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)? - : - - }