From 829bbc118e574f009361d83c403ec09176e28bda Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Tue, 26 Nov 2024 07:08:39 -0600 Subject: [PATCH] Moving organizations org_type to enum --- api/graph/generated.go | 61 +++++++++++++++++++++++++++----- api/graph/model/models_gen.go | 41 +++++++++++++++++++++ api/graph/schema.graphqls | 8 ++++- api/graph/schema.resolvers.go | 5 +++ migrations/0001_initial.down.sql | 1 + migrations/0001_initial.up.sql | 11 +++++- migrations/test_migration.up.sql | 2 +- models/models.go | 2 +- models/organization.go | 8 +++-- models/schema.sql | 9 +++-- 10 files changed, 131 insertions(+), 17 deletions(-) diff --git a/api/graph/generated.go b/api/graph/generated.go index 414302a..72772ab 100644 --- a/api/graph/generated.go +++ b/api/graph/generated.go @@ -480,6 +480,8 @@ type OrgLinkResolver interface { Type(ctx context.Context, obj *models.OrgLink) (model.LinkType, error) } type OrganizationResolver interface { + OrgType(ctx context.Context, obj *models.Organization) (model.OrgType, error) + Image(ctx context.Context, obj *models.Organization) (*graphql.Upload, error) } type OrganizationSettingsResolver interface { @@ -13549,7 +13551,7 @@ func (ec *executionContext) _Organization_orgType(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.OrgType, nil + return ec.resolvers.Organization().OrgType(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -13561,19 +13563,19 @@ func (ec *executionContext) _Organization_orgType(ctx context.Context, field gra } return graphql.Null } - res := resTmp.(int) + res := resTmp.(model.OrgType) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalNOrgType2linksᚋapiᚋgraphᚋmodelᚐOrgType(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Organization_orgType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Organization", 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 OrgType does not have child fields") }, } return fc, nil @@ -25466,10 +25468,41 @@ func (ec *executionContext) _Organization(ctx context.Context, sel ast.Selection atomic.AddUint32(&out.Invalids, 1) } case "orgType": - out.Values[i] = ec._Organization_orgType(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._Organization_orgType(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 "name": out.Values[i] = ec._Organization_name(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -28000,6 +28033,16 @@ func (ec *executionContext) marshalNOrgLinkCursor2ᚖlinksᚋapiᚋgraphᚋmodel return ec._OrgLinkCursor(ctx, sel, v) } +func (ec *executionContext) unmarshalNOrgType2linksᚋapiᚋgraphᚋmodelᚐOrgType(ctx context.Context, v interface{}) (model.OrgType, error) { + var res model.OrgType + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNOrgType2linksᚋapiᚋgraphᚋmodelᚐOrgType(ctx context.Context, sel ast.SelectionSet, v model.OrgType) graphql.Marshaler { + return v +} + func (ec *executionContext) marshalNOrganization2linksᚋmodelsᚐOrganization(ctx context.Context, sel ast.SelectionSet, v models.Organization) graphql.Marshaler { return ec._Organization(ctx, sel, &v) } diff --git a/api/graph/model/models_gen.go b/api/graph/model/models_gen.go index 935bb4c..3032dcd 100644 --- a/api/graph/model/models_gen.go +++ b/api/graph/model/models_gen.go @@ -809,3 +809,44 @@ func (e *MemberPermission) UnmarshalGQL(v interface{}) error { func (e MemberPermission) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } + +type OrgType string + +const ( + OrgTypeUser OrgType = "USER" + OrgTypeNormal OrgType = "NORMAL" +) + +var AllOrgType = []OrgType{ + OrgTypeUser, + OrgTypeNormal, +} + +func (e OrgType) IsValid() bool { + switch e { + case OrgTypeUser, OrgTypeNormal: + return true + } + return false +} + +func (e OrgType) String() string { + return string(e) +} + +func (e *OrgType) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = OrgType(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid OrgType", str) + } + return nil +} + +func (e OrgType) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index d2c727d..a7ca3c4 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -97,6 +97,12 @@ enum MemberPermission { ADMIN_WRITE } +enum OrgType { + USER + NORMAL +} + + # Considering removing these Null* fields: # https://todo.code.netlandish.com/~netlandish/links/75 type NullInt { @@ -135,7 +141,7 @@ type OrganizationSettings { type Organization { id: Int! ownerId: Int! @access(scope: ORGS, kind: RO) - orgType: Int! + orgType: OrgType! name: String! slug: String! image: Upload diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index 2fb23a7..120fc78 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -4205,6 +4205,11 @@ func (r *orgLinkResolver) Type(ctx context.Context, obj *models.OrgLink) (model. return model.LinkType(obj.Type), nil } +// OrgType is the resolver for the orgType field. +func (r *organizationResolver) OrgType(ctx context.Context, obj *models.Organization) (model.OrgType, error) { + return model.OrgType(obj.OrgType), nil +} + // Image is the resolver for the image field. func (r *organizationResolver) Image(ctx context.Context, obj *models.Organization) (*graphql.Upload, error) { panic(fmt.Errorf("not implemented: Image - image")) diff --git a/migrations/0001_initial.down.sql b/migrations/0001_initial.down.sql index 8fbbf96..b3423b0 100644 --- a/migrations/0001_initial.down.sql +++ b/migrations/0001_initial.down.sql @@ -39,3 +39,4 @@ DROP TYPE IF EXISTS invoice_status; DROP TYPE IF EXISTS org_link_visibility; DROP TYPE IF EXISTS org_link_type; DROP TYPE IF EXISTS org_user_perm; +DROP TYPE IF EXISTS org_type; diff --git a/migrations/0001_initial.up.sql b/migrations/0001_initial.up.sql index f0be92b..ac4a756 100644 --- a/migrations/0001_initial.up.sql +++ b/migrations/0001_initial.up.sql @@ -80,6 +80,15 @@ EXCEPTION WHEN duplicate_object THEN null; END $$; +DO $$ BEGIN + CREATE TYPE org_type AS ENUM ( + 'USER', + 'NORMAL' + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + CREATE TABLE users ( id SERIAL PRIMARY KEY, @@ -102,7 +111,7 @@ CREATE INDEX users_id_idx ON users (id); CREATE TABLE organizations ( id SERIAL PRIMARY KEY, owner_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL, - org_type INT DEFAULT 0, + org_type org_type DEFAULT 'USER', name VARCHAR ( 150 ) NOT NULL, slug VARCHAR ( 150 ) UNIQUE NOT NULL, image VARCHAR(1024) DEFAULT '', diff --git a/migrations/test_migration.up.sql b/migrations/test_migration.up.sql index 91f7cf0..bc12ac9 100644 --- a/migrations/test_migration.up.sql +++ b/migrations/test_migration.up.sql @@ -3,7 +3,7 @@ INSERT INTO users (full_name, password, email, is_verified) VALUES ('test_api_us INSERT INTO users (full_name, password, email, is_verified, is_superuser) VALUES ('superuser', 'qwerty', 'superuser@api.com', true, true); INSERT INTO organizations (owner_id, name, slug) VALUES (1, 'personal org', 'personal-org'); -INSERT INTO organizations (owner_id, name, slug, org_type) VALUES (1, 'business org', 'business_org', 1); +INSERT INTO organizations (owner_id, name, slug, org_type) VALUES (1, 'business org', 'business_org', 'NORMAL'); INSERT INTO organizations (owner_id, name, slug) VALUES (2, 'api test org', 'api-test-org'); diff --git a/models/models.go b/models/models.go index 2816e5d..8b83e18 100644 --- a/models/models.go +++ b/models/models.go @@ -37,7 +37,7 @@ type User struct { type Organization struct { ID int `db:"id"` OwnerID int `db:"owner_id"` - OrgType int `db:"org_type"` + OrgType string `db:"org_type"` Name string `db:"name"` Slug string `db:"slug"` Image string `db:"image"` diff --git a/models/organization.go b/models/organization.go index c26e10a..76fdb88 100644 --- a/models/organization.go +++ b/models/organization.go @@ -16,9 +16,13 @@ import ( "netlandish.com/x/gobwebs/timezone" ) +// Organization type is used to define which was created as the "user" org during +// registration and which is a "normal" org created after registration. +// USER = Automatically created during registration based off provided username +// NORMAL = User created additional organization const ( - OrgTypeUser int = iota - OrgTypeNormal + OrgTypeUser string = "USER" + OrgTypeNormal string = "NORMAL" ) // Billing status, used for org billing setting/metadata diff --git a/models/schema.sql b/models/schema.sql index f9b31a9..53372c1 100644 --- a/models/schema.sql +++ b/models/schema.sql @@ -51,6 +51,12 @@ CREATE TYPE org_link_perm AS ENUM ( 'ADMIN_WRITE' ); +CREATE TYPE org_type AS ENUM ( + 'USER', + 'NORMAL' +); + + CREATE TABLE users ( id SERIAL PRIMARY KEY, full_name VARCHAR ( 150 ) NOT NULL, @@ -72,7 +78,7 @@ CREATE INDEX users_id_idx ON users (id); CREATE TABLE organizations ( id SERIAL PRIMARY KEY, owner_id INT REFERENCES users (id) ON DELETE CASCADE NOT NULL, - org_type INT DEFAULT 0, + org_type org_type DEFAULT 'USER', name VARCHAR ( 150 ) NOT NULL, slug VARCHAR ( 150 ) UNIQUE NOT NULL, image VARCHAR(1024) DEFAULT '', @@ -145,7 +151,6 @@ CREATE TABLE org_links ( base_url_id INT REFERENCES base_urls (id) ON DELETE CASCADE, org_id INT REFERENCES organizations (id) ON DELETE CASCADE NOT NULL, user_id INT REFERENCES users (id) ON DELETE SET NULL, - visibility INT DEFAULT 0, visibility org_link_visibility DEFAULT 'PUBLIC', unread BOOLEAN DEFAULT FALSE NOT NULL, starred BOOLEAN DEFAULT FALSE NOT NULL, -- 2.45.2