From d1d4f9a9bc1f8e0d23ee54a4b9cf268385018768 Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Fri, 22 Nov 2024 17:51:34 -0600 Subject: [PATCH] Moving domain status to enum --- admin/input.go | 4 +- admin/routes.go | 8 ++-- api/graph/generated.go | 64 ++++++++++++++++++++++++++------ api/graph/model/models_gen.go | 47 ++++++++++++++++++++++- api/graph/schema.graphqls | 11 ++++-- api/graph/schema.resolvers.go | 17 +++++---- domain/logic.go | 11 ++++++ migrations/0001_initial.down.sql | 1 + migrations/0001_initial.up.sql | 11 +++++- migrations/test_migration.up.sql | 4 +- models/domains.go | 9 +++-- models/models.go | 2 +- models/schema.sql | 9 ++++- 13 files changed, 161 insertions(+), 37 deletions(-) diff --git a/admin/input.go b/admin/input.go index f3c4dcd..4a78018 100644 --- a/admin/input.go +++ b/admin/input.go @@ -59,7 +59,7 @@ type DomainForm struct { LookupName string `form:"lookup_name" validate:"required"` Service string `form:"service" validate:"oneof=LINKS SHORT LIST"` Level string `form:"level" validate:"oneof=SYSTEM USER"` - Status int `form:"status" validate:"oneof=0 1 2"` + Status string `form:"status" validate:"oneof=PENDING APPROVED ERROR"` IsActive bool `form:"is_active"` OrgSlug string `form:"org_slug"` MigrateLinks bool `form:"migrate_links"` @@ -73,7 +73,7 @@ func (d *DomainForm) Validate(c echo.Context) error { String("lookup_name", &d.LookupName). String("service", &d.Service). String("level", &d.Level). - Int("status", &d.Status). + String("status", &d.Status). String("org_slug", &d.OrgSlug). Bool("is_active", &d.IsActive). Bool("migrate_links", &d.MigrateLinks). diff --git a/admin/routes.go b/admin/routes.go index 57846b0..97ec5e5 100644 --- a/admin/routes.go +++ b/admin/routes.go @@ -618,7 +618,7 @@ func (s *Service) DomainUpdate(c echo.Context) error { models.DomainLevelSystem: lt.Translate("System"), models.DomainLevelUser: lt.Translate("User"), } - statusOpt := map[int]string{ + statusOpt := map[string]string{ models.DomainStatusPending: lt.Translate("Pending"), models.DomainStatusApproved: lt.Translate("Approved"), models.DomainStatusError: lt.Translate("Error"), @@ -665,7 +665,7 @@ func (s *Service) DomainUpdate(c echo.Context) error { var result GraphQLResponse op := gqlclient.NewOperation( - `mutation UpdateAdminDomain($id: Int!, $name: String!, $lookupName: String!, $orgSlug: String, $level: DomainLevel!, $status: Int! + `mutation UpdateAdminDomain($id: Int!, $name: String!, $lookupName: String!, $orgSlug: String, $level: DomainLevel!, $status: DomainStatus! $service: DomainService!, $migrate: Boolean, $isActive: Boolean!) { updateAdminDomain(input: { id: $id, @@ -747,7 +747,7 @@ func (s *Service) DomainCreate(c echo.Context) error { models.DomainLevelSystem: lt.Translate("System"), models.DomainLevelUser: lt.Translate("User"), } - statusOpt := map[int]string{ + statusOpt := map[string]string{ models.DomainStatusPending: lt.Translate("Pending"), models.DomainStatusApproved: lt.Translate("Approved"), models.DomainStatusError: lt.Translate("Error"), @@ -793,7 +793,7 @@ func (s *Service) DomainCreate(c echo.Context) error { } op := gqlclient.NewOperation( - `mutation AddAdminDomain($name: String!, $lookupName: String!, $orgSlug: String, $level: DomainLevel!, $status: Int! + `mutation AddAdminDomain($name: String!, $lookupName: String!, $orgSlug: String, $level: DomainLevel!, $status: DomainStatus! $service: DomainService!, $migrate: Boolean, $isActive: Boolean!) { addAdminDomain(input: { name: $name, diff --git a/api/graph/generated.go b/api/graph/generated.go index bbee43f..7980e43 100644 --- a/api/graph/generated.go +++ b/api/graph/generated.go @@ -435,6 +435,7 @@ type DomainResolver interface { OrgSlug(ctx context.Context, obj *models.Domain) (*model.NullString, error) Service(ctx context.Context, obj *models.Domain) (model.DomainService, error) Level(ctx context.Context, obj *models.Domain) (model.DomainLevel, error) + Status(ctx context.Context, obj *models.Domain) (model.DomainStatus, error) } type MutationResolver interface { AddOrganization(ctx context.Context, input model.OrganizationInput) (*models.Organization, error) @@ -5725,7 +5726,7 @@ func (ec *executionContext) _Domain_status(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Status, nil + return ec.resolvers.Domain().Status(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -5737,19 +5738,19 @@ func (ec *executionContext) _Domain_status(ctx context.Context, field graphql.Co } return graphql.Null } - res := resTmp.(int) + res := resTmp.(model.DomainStatus) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalNDomainStatus2linksᚋapiᚋgraphᚋmodelᚐDomainStatus(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Domain_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Domain", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type DomainStatus does not have child fields") }, } return fc, nil @@ -21776,7 +21777,7 @@ func (ec *executionContext) unmarshalInputAdminDomainInput(ctx context.Context, it.Level = data case "status": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status")) - data, err := ec.unmarshalNInt2int(ctx, v) + data, err := ec.unmarshalNDomainStatus2linksᚋapiᚋgraphᚋmodelᚐDomainStatus(ctx, v) if err != nil { return it, err } @@ -23084,7 +23085,7 @@ func (ec *executionContext) unmarshalInputUpdateAdminDomainInput(ctx context.Con it.Level = data case "status": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status")) - data, err := ec.unmarshalNInt2int(ctx, v) + data, err := ec.unmarshalNDomainStatus2linksᚋapiᚋgraphᚋmodelᚐDomainStatus(ctx, v) if err != nil { return it, err } @@ -24146,10 +24147,41 @@ func (ec *executionContext) _Domain(ctx context.Context, sel ast.SelectionSet, o out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "status": - out.Values[i] = ec._Domain_status(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Domain_status(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "isActive": out.Values[i] = ec._Domain_isActive(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -27459,6 +27491,16 @@ func (ec *executionContext) marshalNDomainService2linksᚋapiᚋgraphᚋmodelᚐ return v } +func (ec *executionContext) unmarshalNDomainStatus2linksᚋapiᚋgraphᚋmodelᚐDomainStatus(ctx context.Context, v interface{}) (model.DomainStatus, error) { + var res model.DomainStatus + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNDomainStatus2linksᚋapiᚋgraphᚋmodelᚐDomainStatus(ctx context.Context, sel ast.SelectionSet, v model.DomainStatus) graphql.Marshaler { + return v +} + func (ec *executionContext) marshalNFollowPayload2linksᚋapiᚋgraphᚋmodelᚐFollowPayload(ctx context.Context, sel ast.SelectionSet, v model.FollowPayload) graphql.Marshaler { return ec._FollowPayload(ctx, sel, &v) } diff --git a/api/graph/model/models_gen.go b/api/graph/model/models_gen.go index 45b1c98..4912e36 100644 --- a/api/graph/model/models_gen.go +++ b/api/graph/model/models_gen.go @@ -72,7 +72,7 @@ type AdminDomainInput struct { OrgSlug *string `json:"orgSlug,omitempty"` Service DomainService `json:"service"` Level DomainLevel `json:"level"` - Status int `json:"status"` + Status DomainStatus `json:"status"` IsActive bool `json:"isActive"` MigrateLinks *bool `json:"migrateLinks,omitempty"` } @@ -378,7 +378,7 @@ type UpdateAdminDomainInput struct { OrgSlug *string `json:"orgSlug,omitempty"` Service DomainService `json:"service"` Level DomainLevel `json:"level"` - Status int `json:"status"` + Status DomainStatus `json:"status"` IsActive bool `json:"isActive"` MigrateLinks *bool `json:"migrateLinks,omitempty"` } @@ -639,3 +639,46 @@ func (e *DomainService) UnmarshalGQL(v interface{}) error { func (e DomainService) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } + +type DomainStatus string + +const ( + DomainStatusPending DomainStatus = "PENDING" + DomainStatusApproved DomainStatus = "APPROVED" + DomainStatusError DomainStatus = "ERROR" +) + +var AllDomainStatus = []DomainStatus{ + DomainStatusPending, + DomainStatusApproved, + DomainStatusError, +} + +func (e DomainStatus) IsValid() bool { + switch e { + case DomainStatusPending, DomainStatusApproved, DomainStatusError: + return true + } + return false +} + +func (e DomainStatus) String() string { + return string(e) +} + +func (e *DomainStatus) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = DomainStatus(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid DomainStatus", str) + } + return nil +} + +func (e DomainStatus) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index 06c1d0e..d301fb0 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -74,6 +74,11 @@ enum DomainService { LIST } +enum DomainStatus { + PENDING + APPROVED + ERROR +} # Considering removing these Null* fields: @@ -256,7 +261,7 @@ type Domain { orgSlug: NullString! @access(scope: DOMAINS, kind: RO) service: DomainService! level: DomainLevel! - status: Int! + status: DomainStatus! isActive: Boolean! @access(scope: DOMAINS, kind: RO) createdOn: Time! @access(scope: DOMAINS, kind: RO) updatedOn: Time! @access(scope: DOMAINS, kind: RO) @@ -641,7 +646,7 @@ input AdminDomainInput { orgSlug: String service: DomainService! level: DomainLevel! - status: Int! + status: DomainStatus! isActive: Boolean! migrateLinks: Boolean } @@ -653,7 +658,7 @@ input UpdateAdminDomainInput { orgSlug: String service: DomainService! level: DomainLevel! - status: Int! + status: DomainStatus! isActive: Boolean! migrateLinks: Boolean } diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index 0a529ed..ccc4103 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -39,7 +39,7 @@ import ( "golang.org/x/image/draw" "golang.org/x/net/idna" "netlandish.com/x/gobwebs" - "netlandish.com/x/gobwebs-oauth2" + oauth2 "netlandish.com/x/gobwebs-oauth2" gaccounts "netlandish.com/x/gobwebs/accounts" "netlandish.com/x/gobwebs/crypto" "netlandish.com/x/gobwebs/database" @@ -70,6 +70,11 @@ func (r *domainResolver) Level(ctx context.Context, obj *models.Domain) (model.D return model.DomainLevel(obj.Level), nil } +// Status is the resolver for the status field. +func (r *domainResolver) Status(ctx context.Context, obj *models.Domain) (model.DomainStatus, error) { + return model.DomainStatus(obj.Status), nil +} + // AddOrganization is the resolver for the addOrganization field. func (r *mutationResolver) AddOrganization(ctx context.Context, input model.OrganizationInput) (*models.Organization, error) { tokenUser := oauth2.ForContext(ctx) @@ -3714,8 +3719,7 @@ func (r *mutationResolver) AddAdminDomain(ctx context.Context, input model.Admin lt.Translate("Invalid level value.")). WithField("level"). WithCode(valid.ErrValidationCode) - validator.Expect(input.Status >= models.DomainStatusPending && - input.Status <= models.DomainStatusError, + validator.Expect(domain.ValidateDomainStatus(string(input.Status)), lt.Translate("Invalid status value.")). WithField("status"). WithCode(valid.ErrValidationCode) @@ -3833,7 +3837,7 @@ func (r *mutationResolver) AddAdminDomain(ctx context.Context, input model.Admin LookupName: host, Level: string(input.Level), Service: string(input.Service), - Status: input.Status, + Status: string(input.Status), IsActive: input.IsActive, } if org != nil { @@ -3889,8 +3893,7 @@ func (r *mutationResolver) UpdateAdminDomain(ctx context.Context, input model.Up lt.Translate("Invalid level value.")). WithField("level"). WithCode(valid.ErrValidationCode) - validator.Expect(input.Status >= models.DomainStatusPending && - input.Status <= models.DomainStatusError, + validator.Expect(domain.ValidateDomainStatus(string(input.Status)), lt.Translate("Invalid status value.")). WithField("status"). WithCode(valid.ErrValidationCode) @@ -4027,7 +4030,7 @@ func (r *mutationResolver) UpdateAdminDomain(ctx context.Context, input model.Up ldomain.LookupName = host ldomain.Level = string(input.Level) ldomain.Service = string(input.Service) - ldomain.Status = input.Status + ldomain.Status = string(input.Status) ldomain.IsActive = input.IsActive if org != nil { diff --git a/domain/logic.go b/domain/logic.go index cfa342e..28c7e7c 100644 --- a/domain/logic.go +++ b/domain/logic.go @@ -137,3 +137,14 @@ func ValidateDomainService(service string) bool { _, ok := serviceMap[service] return ok } + +// ValidateDomainStatus is a helper to verify the service given is valid +func ValidateDomainStatus(status string) bool { + statusMap := map[string]bool{ + models.DomainStatusPending: true, + models.DomainStatusApproved: true, + models.DomainStatusError: true, + } + _, ok := statusMap[status] + return ok +} diff --git a/migrations/0001_initial.down.sql b/migrations/0001_initial.down.sql index e7ed75b..49e1f98 100644 --- a/migrations/0001_initial.down.sql +++ b/migrations/0001_initial.down.sql @@ -34,3 +34,4 @@ DROP TABLE IF EXISTS users; DROP TYPE IF EXISTS domain_level; DROP TYPE IF EXISTS domain_service; +DROP TYPE IF EXISTS domain_status; diff --git a/migrations/0001_initial.up.sql b/migrations/0001_initial.up.sql index 5ddaf92..c594f54 100644 --- a/migrations/0001_initial.up.sql +++ b/migrations/0001_initial.up.sql @@ -30,6 +30,15 @@ EXCEPTION WHEN duplicate_object THEN null; END $$; +DO $$ BEGIN + CREATE TYPE domain_status AS ENUM ( + 'PENDING', + 'APPROVED', + 'ERROR' + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; CREATE TABLE users ( @@ -165,7 +174,7 @@ CREATE TABLE domains ( org_id INT REFERENCES organizations (id) ON DELETE CASCADE, level domain_level NOT NULL, service domain_service NOT NULL, - status INT DEFAULT 0 NOT NULL, + status domain_status DEFAULT 'PENDING' NOT NULL, is_active BOOLEAN DEFAULT TRUE, last_action TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, diff --git a/migrations/test_migration.up.sql b/migrations/test_migration.up.sql index 2f67674..ddacd2c 100644 --- a/migrations/test_migration.up.sql +++ b/migrations/test_migration.up.sql @@ -15,8 +15,8 @@ INSERT INTO org_links (title, url, base_url_id, user_id, org_id, visibility, has INSERT INTO org_links (title, url, base_url_id, user_id, org_id, visibility, hash) VALUES ('Private Business url', 'http://base.com?vis=private', 1, 1, 2, 1, 'hash2'); -INSERT INTO domains (name, lookup_name, org_id, level, service, status) VALUES ('short domain', 'short.domain.org', 1, 'SYSTEM', 'SHORT', 1); -INSERT INTO domains (name, lookup_name, org_id, service, status, level) VALUES ('listing domain', 'list.domain.org', 1, 'LIST', 1, 'USER'); +INSERT INTO domains (name, lookup_name, org_id, level, service, status) VALUES ('short domain', 'short.domain.org', 1, 'SYSTEM', 'SHORT', 'APPROVED'); +INSERT INTO domains (name, lookup_name, org_id, service, status, level) VALUES ('listing domain', 'list.domain.org', 1, 'LIST', 'APPROVED', 'USER'); INSERT INTO link_shorts (id, title, url, domain_id, org_id, short_code, user_id) VALUES (100, 'testing short', 'http://test.domain.org', 1, 1, 'foo', 1); INSERT INTO listings (id, title, slug, domain_id, is_default, user_id, org_id) VALUES (100, 'test listing', 'test-listing-slug', 2, false, 1, 1); diff --git a/models/domains.go b/models/domains.go index 732d7cd..f9dde88 100644 --- a/models/domains.go +++ b/models/domains.go @@ -31,11 +31,11 @@ const ( const ( // DomainStatusPending ... - DomainStatusPending int = iota + DomainStatusPending string = "PENDING" // DomainStatusApproved ... - DomainStatusApproved + DomainStatusApproved string = "APPROVED" // DomainStatusError ... - DomainStatusError + DomainStatusError string = "ERROR" ) // GetDomains ... @@ -140,6 +140,9 @@ func (d *Domain) Store(ctx context.Context) error { if d.Service == "" { d.Service = DomainServiceLinks } + if d.Status == "" { + d.Status = DomainStatusPending + } err = sq. Insert("domains"). Columns("name", "lookup_name", "org_id", "level", "service", "status", "is_active"). diff --git a/models/models.go b/models/models.go index 66f9c32..b713d22 100644 --- a/models/models.go +++ b/models/models.go @@ -111,7 +111,7 @@ type Domain struct { OrgID sql.NullInt64 `db:"org_id"` Level string `db:"level"` Service string `db:"service"` - Status int `db:"status"` + Status string `db:"status"` IsActive bool `db:"is_active"` LastAction time.Time `db:"last_action"` CreatedOn time.Time `db:"created_on"` diff --git a/models/schema.sql b/models/schema.sql index e762afa..4e53242 100644 --- a/models/schema.sql +++ b/models/schema.sql @@ -21,6 +21,13 @@ CREATE TYPE domain_service AS ENUM ( 'LIST' ); +CREATE TYPE domain_status AS ENUM ( + 'PENDING', + 'APPROVED', + 'ERROR' +); + + CREATE TABLE users ( id SERIAL PRIMARY KEY, full_name VARCHAR ( 150 ) NOT NULL, @@ -154,7 +161,7 @@ CREATE TABLE domains ( org_id INT REFERENCES organizations (id) ON DELETE CASCADE, level domain_level NOT NULL, service domain_service NOT NULL, - status INT DEFAULT 0 NOT NULL, + status domain_status DEFAULT 'PENDING' NOT NULL, is_active BOOLEAN DEFAULT TRUE, last_action TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, created_on TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 2.45.2