M api/api_test.go => api/api_test.go +11 -11
@@ 142,7 142,7 @@ func TestDirective(t *testing.T) {
var resultAddLink GraphQLResponseAddLink
q = `mutation AddLink($title: String!, $url: String!, $description: String,
- $visibility: Int!, $unread: Boolean!, $starred: Boolean!,
+ $visibility: LinkVisibility!, $unread: Boolean!, $starred: Boolean!,
$archive: Boolean! $slug: String!, $tags: String) {
addLink(input: {
title: $title,
@@ 391,7 391,7 @@ func TestAPI(t *testing.T) {
var result GraphQLResponse
q := `mutation AddLink($title: String!, $url: String!, $description: String,
- $visibility: Int!, $unread: Boolean!, $starred: Boolean!,
+ $visibility: LinkVisibility!, $unread: Boolean!, $starred: Boolean!,
$archive: Boolean! $slug: String!, $tags: String) {
addLink(input: {
title: $title,
@@ 440,7 440,7 @@ func TestAPI(t *testing.T) {
op = gqlclient.NewOperation(q)
op.Var("title", "testing link")
op.Var("url", "https://netlandish.com")
- op.Var("visibility", 100)
+ op.Var("visibility", "NOT_VALID")
op.Var("tags", "one, two")
op.Var("slug", "personal-org")
op.Var("unread", true)
@@ 448,7 448,7 @@ func TestAPI(t *testing.T) {
op.Var("archive", false)
err = links.Execute(ctx, op, &result)
c.Error(err)
- c.Equal("gqlclient: server failure: Invalid Visibility value.", err.Error())
+ c.Equal("gqlclient: HTTP server error (422 Unprocessable Entity): gqlclient: server failure: NOT_VALID is not a valid LinkVisibility", err.Error())
})
t.Run("create link", func(t *testing.T) {
@@ 458,7 458,7 @@ func TestAPI(t *testing.T) {
var result GraphQLResponse
q := `mutation AddLink($title: String!, $url: String!, $description: String,
- $visibility: Int!, $unread: Boolean!, $starred: Boolean!,
+ $visibility: LinkVisibility!, $unread: Boolean!, $starred: Boolean!,
$archive: Boolean! $slug: String!, $tags: String) {
addLink(input: {
title: $title,
@@ 547,7 547,7 @@ func TestAPI(t *testing.T) {
}
var result GraphQLResponse
q := `mutation UpdateLink($hash: String!, $title: String!,
- $url: String!, $visibility: Int!,
+ $url: String!, $visibility: LinkVisibility!,
$tags: String) {
updateLink(input: {
title: $title,
@@ 581,11 581,11 @@ func TestAPI(t *testing.T) {
op.Var("title", "New Title")
op.Var("hash", ol.Hash)
op.Var("url", "https://netlandish.com")
- op.Var("visibility", 100)
+ op.Var("visibility", "NOT_VALID")
op.Var("tags", "one, two, three")
err = links.Execute(ctx, op, &result)
c.Error(err)
- c.Equal("gqlclient: server failure: Invalid Visibility value.", err.Error())
+ c.Equal("gqlclient: HTTP server error (422 Unprocessable Entity): gqlclient: server failure: NOT_VALID is not a valid LinkVisibility", err.Error())
})
t.Run("org link update", func(t *testing.T) {
@@ 602,7 602,7 @@ func TestAPI(t *testing.T) {
}
var result GraphQLResponse
q := `mutation UpdateLink($hash: String!, $title: String!,
- $url: String!, $visibility: Int!,
+ $url: String!, $visibility: LinkVisibility!,
$tags: String) {
updateLink(input: {
title: $title,
@@ 1852,7 1852,7 @@ func TestAPI(t *testing.T) {
var result GraphQLResponse
q := `mutation AddLink($title: String!, $url: String!, $description: String,
- $visibility: Int!, $unread: Boolean!, $starred: Boolean!,
+ $visibility: LinkVisibility!, $unread: Boolean!, $starred: Boolean!,
$archive: Boolean! $slug: String!, $tags: String) {
addLink(input: {
title: $title,
@@ 1892,7 1892,7 @@ func TestAPI(t *testing.T) {
}
var result GraphQLResponse
q := `mutation UpdateLink($hash: String!, $title: String!,
- $url: String!, $visibility: Int!,
+ $url: String!, $visibility: LinkVisibility!,
$tags: String) {
updateLink(input: {
title: $title,
M api/graph/generated.go => api/graph/generated.go +145 -23
@@ 45,6 45,7 @@ type ResolverRoot interface {
Mutation() MutationResolver
OrgLink() OrgLinkResolver
Organization() OrganizationResolver
+ OrganizationSettings() OrganizationSettingsResolver
Query() QueryResolver
User() UserResolver
}
@@ 473,10 474,15 @@ type MutationResolver interface {
}
type OrgLinkResolver interface {
BaseURLID(ctx context.Context, obj *models.OrgLink) (*model.NullInt, error)
+
+ Visibility(ctx context.Context, obj *models.OrgLink) (model.LinkVisibility, error)
}
type OrganizationResolver interface {
Image(ctx context.Context, obj *models.Organization) (*graphql.Upload, error)
}
+type OrganizationSettingsResolver interface {
+ DefaultPerm(ctx context.Context, obj *models.OrganizationSettings) (model.LinkVisibility, error)
+}
type QueryResolver interface {
Version(ctx context.Context) (*model.Version, error)
Me(ctx context.Context) (*models.User, error)
@@ 12681,8 12687,36 @@ func (ec *executionContext) _OrgLink_visibility(ctx context.Context, field graph
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
- ctx = rctx // use context from middleware stack in children
- return obj.Visibility, nil
+ directive0 := func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.OrgLink().Visibility(rctx, obj)
+ }
+ directive1 := func(ctx context.Context) (interface{}, error) {
+ scope, err := ec.unmarshalNAccessScope2linksᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "LINKS")
+ if err != nil {
+ return nil, err
+ }
+ kind, err := ec.unmarshalNAccessKind2linksᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RO")
+ if err != nil {
+ return nil, err
+ }
+ if ec.directives.Access == nil {
+ return nil, errors.New("directive access is not implemented")
+ }
+ return ec.directives.Access(ctx, obj, directive0, scope, kind)
+ }
+
+ tmp, err := directive1(rctx)
+ if err != nil {
+ return nil, graphql.ErrorOnPath(ctx, err)
+ }
+ if tmp == nil {
+ return nil, nil
+ }
+ if data, ok := tmp.(model.LinkVisibility); ok {
+ return data, nil
+ }
+ return nil, fmt.Errorf(`unexpected type %T from directive, should be links/api/graph/model.LinkVisibility`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@@ 12694,19 12728,19 @@ func (ec *executionContext) _OrgLink_visibility(ctx context.Context, field graph
}
return graphql.Null
}
- res := resTmp.(int)
+ res := resTmp.(model.LinkVisibility)
fc.Result = res
- return ec.marshalNInt2int(ctx, field.Selections, res)
+ return ec.marshalNLinkVisibility2linksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_OrgLink_visibility(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "OrgLink",
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 LinkVisibility does not have child fields")
},
}
return fc, nil
@@ 14187,7 14221,7 @@ func (ec *executionContext) _OrganizationSettings_defaultPerm(ctx context.Contex
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
- return obj.DefaultPerm, nil
+ return ec.resolvers.OrganizationSettings().DefaultPerm(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ 14199,19 14233,19 @@ func (ec *executionContext) _OrganizationSettings_defaultPerm(ctx context.Contex
}
return graphql.Null
}
- res := resTmp.(int)
+ res := resTmp.(model.LinkVisibility)
fc.Result = res
- return ec.marshalNInt2int(ctx, field.Selections, res)
+ return ec.marshalNLinkVisibility2linksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_OrganizationSettings_defaultPerm(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "OrganizationSettings",
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 LinkVisibility does not have child fields")
},
}
return fc, nil
@@ 22617,7 22651,7 @@ func (ec *executionContext) unmarshalInputLinkInput(ctx context.Context, obj int
it.Description = data
case "visibility":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("visibility"))
- data, err := ec.unmarshalOInt2ᚖint(ctx, v)
+ data, err := ec.unmarshalOLinkVisibility2ᚖlinksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx, v)
if err != nil {
return it, err
}
@@ 22803,7 22837,7 @@ func (ec *executionContext) unmarshalInputNoteInput(ctx context.Context, obj int
it.Description = data
case "visibility":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("visibility"))
- data, err := ec.unmarshalNInt2int(ctx, v)
+ data, err := ec.unmarshalNLinkVisibility2linksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx, v)
if err != nil {
return it, err
}
@@ 23154,7 23188,7 @@ func (ec *executionContext) unmarshalInputUpdateLinkInput(ctx context.Context, o
it.URL = data
case "visibility":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("visibility"))
- data, err := ec.unmarshalOInt2ᚖint(ctx, v)
+ data, err := ec.unmarshalOLinkVisibility2ᚖlinksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx, v)
if err != nil {
return it, err
}
@@ 23479,7 23513,7 @@ func (ec *executionContext) unmarshalInputUpdateOrganizationInput(ctx context.Co
it.DeleteImg = data
case "defaultPerm":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("defaultPerm"))
- data, err := ec.unmarshalOInt2ᚖint(ctx, v)
+ data, err := ec.unmarshalOLinkVisibility2ᚖlinksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx, v)
if err != nil {
return it, err
}
@@ 25228,10 25262,41 @@ func (ec *executionContext) _OrgLink(ctx context.Context, sel ast.SelectionSet,
case "userId":
out.Values[i] = ec._OrgLink_userId(ctx, field, obj)
case "visibility":
- out.Values[i] = ec._OrgLink_visibility(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._OrgLink_visibility(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 "unread":
out.Values[i] = ec._OrgLink_unread(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ 25521,10 25586,41 @@ func (ec *executionContext) _OrganizationSettings(ctx context.Context, sel ast.S
case "__typename":
out.Values[i] = graphql.MarshalString("OrganizationSettings")
case "defaultPerm":
- out.Values[i] = ec._OrganizationSettings_defaultPerm(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- out.Invalids++
+ 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._OrganizationSettings_defaultPerm(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 "billing":
out.Values[i] = ec._OrganizationSettings_billing(ctx, field, obj)
default:
@@ 27611,6 27707,16 @@ func (ec *executionContext) marshalNLinkShortCursor2ᚖlinksᚋapiᚋgraphᚋmod
return ec._LinkShortCursor(ctx, sel, v)
}
+func (ec *executionContext) unmarshalNLinkVisibility2linksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx context.Context, v interface{}) (model.LinkVisibility, error) {
+ var res model.LinkVisibility
+ err := res.UnmarshalGQL(v)
+ return res, graphql.ErrorOnPath(ctx, err)
+}
+
+func (ec *executionContext) marshalNLinkVisibility2linksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx context.Context, sel ast.SelectionSet, v model.LinkVisibility) graphql.Marshaler {
+ return v
+}
+
func (ec *executionContext) marshalNListing2linksᚋmodelsᚐListing(ctx context.Context, sel ast.SelectionSet, v models.Listing) graphql.Marshaler {
return ec._Listing(ctx, sel, &v)
}
@@ 28766,6 28872,22 @@ func (ec *executionContext) unmarshalOLinkShortInput2ᚖlinksᚋapiᚋgraphᚋmo
return &res, graphql.ErrorOnPath(ctx, err)
}
+func (ec *executionContext) unmarshalOLinkVisibility2ᚖlinksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx context.Context, v interface{}) (*model.LinkVisibility, error) {
+ if v == nil {
+ return nil, nil
+ }
+ var res = new(model.LinkVisibility)
+ err := res.UnmarshalGQL(v)
+ return res, graphql.ErrorOnPath(ctx, err)
+}
+
+func (ec *executionContext) marshalOLinkVisibility2ᚖlinksᚋapiᚋgraphᚋmodelᚐLinkVisibility(ctx context.Context, sel ast.SelectionSet, v *model.LinkVisibility) graphql.Marshaler {
+ if v == nil {
+ return graphql.Null
+ }
+ return v
+}
+
func (ec *executionContext) marshalOListing2ᚖlinksᚋmodelsᚐListing(ctx context.Context, sel ast.SelectionSet, v *models.Listing) graphql.Marshaler {
if v == nil {
return graphql.Null
M api/graph/model/models_gen.go => api/graph/model/models_gen.go +67 -24
@@ 223,15 223,15 @@ type GetUserInput struct {
}
type LinkInput struct {
- URL string `json:"url"`
- Description *string `json:"description,omitempty"`
- Visibility *int `json:"visibility,omitempty"`
- Unread bool `json:"unread"`
- Starred bool `json:"starred"`
- Archive bool `json:"archive"`
- OrgSlug string `json:"orgSlug"`
- Title string `json:"title"`
- Tags *string `json:"tags,omitempty"`
+ URL string `json:"url"`
+ Description *string `json:"description,omitempty"`
+ Visibility *LinkVisibility `json:"visibility,omitempty"`
+ Unread bool `json:"unread"`
+ Starred bool `json:"starred"`
+ Archive bool `json:"archive"`
+ OrgSlug string `json:"orgSlug"`
+ Title string `json:"title"`
+ Tags *string `json:"tags,omitempty"`
}
type LinkShortCursor struct {
@@ 269,12 269,12 @@ type Mutation struct {
}
type NoteInput struct {
- Title string `json:"title"`
- Description string `json:"description"`
- Visibility int `json:"visibility"`
- Tags *string `json:"tags,omitempty"`
- Starred bool `json:"starred"`
- OrgSlug string `json:"orgSlug"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Visibility LinkVisibility `json:"visibility"`
+ Tags *string `json:"tags,omitempty"`
+ Starred bool `json:"starred"`
+ OrgSlug string `json:"orgSlug"`
}
type NullInt struct {
@@ 384,14 384,14 @@ type UpdateAdminDomainInput struct {
}
type UpdateLinkInput struct {
- Hash string `json:"hash"`
- Title *string `json:"title,omitempty"`
- Description *string `json:"description,omitempty"`
- URL *string `json:"url,omitempty"`
- Visibility *int `json:"visibility,omitempty"`
- Unread *bool `json:"unread,omitempty"`
- Starred *bool `json:"starred,omitempty"`
- Tags *string `json:"tags,omitempty"`
+ Hash string `json:"hash"`
+ Title *string `json:"title,omitempty"`
+ Description *string `json:"description,omitempty"`
+ URL *string `json:"url,omitempty"`
+ Visibility *LinkVisibility `json:"visibility,omitempty"`
+ Unread *bool `json:"unread,omitempty"`
+ Starred *bool `json:"starred,omitempty"`
+ Tags *string `json:"tags,omitempty"`
}
type UpdateLinkShortInput struct {
@@ 435,7 435,7 @@ type UpdateOrganizationInput struct {
Image *graphql.Upload `json:"image,omitempty"`
IsActive *bool `json:"isActive,omitempty"`
DeleteImg *bool `json:"deleteImg,omitempty"`
- DefaultPerm *int `json:"defaultPerm,omitempty"`
+ DefaultPerm *LinkVisibility `json:"defaultPerm,omitempty"`
}
type UpdateUserInput struct {
@@ 682,3 682,46 @@ func (e *DomainStatus) UnmarshalGQL(v interface{}) error {
func (e DomainStatus) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
+
+type LinkVisibility string
+
+const (
+ LinkVisibilityPublic LinkVisibility = "PUBLIC"
+ LinkVisibilityPrivate LinkVisibility = "PRIVATE"
+ LinkVisibilityRestricted LinkVisibility = "RESTRICTED"
+)
+
+var AllLinkVisibility = []LinkVisibility{
+ LinkVisibilityPublic,
+ LinkVisibilityPrivate,
+ LinkVisibilityRestricted,
+}
+
+func (e LinkVisibility) IsValid() bool {
+ switch e {
+ case LinkVisibilityPublic, LinkVisibilityPrivate, LinkVisibilityRestricted:
+ return true
+ }
+ return false
+}
+
+func (e LinkVisibility) String() string {
+ return string(e)
+}
+
+func (e *LinkVisibility) UnmarshalGQL(v interface{}) error {
+ str, ok := v.(string)
+ if !ok {
+ return fmt.Errorf("enums must be strings")
+ }
+
+ *e = LinkVisibility(str)
+ if !e.IsValid() {
+ return fmt.Errorf("%s is not a valid LinkVisibility", str)
+ }
+ return nil
+}
+
+func (e LinkVisibility) MarshalGQL(w io.Writer) {
+ fmt.Fprint(w, strconv.Quote(e.String()))
+}
M api/graph/schema.graphqls => api/graph/schema.graphqls +12 -6
@@ 80,6 80,12 @@ enum DomainStatus {
ERROR
}
+enum LinkVisibility {
+ PUBLIC
+ PRIVATE
+ RESTRICTED
+}
+
# Considering removing these Null* fields:
# https://todo.code.netlandish.com/~netlandish/links/75
@@ 112,7 118,7 @@ type BillingSettings {
}
type OrganizationSettings {
- defaultPerm: Int!
+ defaultPerm: LinkVisibility!
billing: BillingSettings
}
@@ 184,7 190,7 @@ type OrgLink {
baseUrlId: NullInt! @access(scope: LINKS, kind: RO)
orgId: Int! @access(scope: LINKS, kind: RO)
userId: Int @access(scope: LINKS, kind: RO)
- visibility: Int!
+ visibility: LinkVisibility! @access(scope: LINKS, kind: RO)
unread: Boolean! @access(scope: LINKS, kind: RO)
starred: Boolean! @access(scope: LINKS, kind: RO)
archiveUrl: String!
@@ 373,7 379,7 @@ type QRObject {
input LinkInput {
url: String!
description: String
- visibility: Int
+ visibility: LinkVisibility
unread: Boolean!
starred: Boolean!
archive: Boolean!
@@ 385,7 391,7 @@ input LinkInput {
input NoteInput {
title: String!
description: String!
- visibility: Int!
+ visibility: LinkVisibility!
tags: String
starred: Boolean!
orgSlug: String!
@@ 540,7 546,7 @@ input UpdateLinkInput{
title: String
description: String
url: String
- visibility: Int
+ visibility: LinkVisibility
unread: Boolean
starred: Boolean
tags: String
@@ 621,7 627,7 @@ input UpdateOrganizationInput {
image: Upload
isActive: Boolean
deleteImg: Boolean
- defaultPerm: Int
+ defaultPerm: LinkVisibility
}
input RegisterInput {
M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +40 -27
@@ 186,8 186,7 @@ func (r *mutationResolver) AddLink(ctx context.Context, input *model.LinkInput)
validator := valid.New(ctx)
if input.Visibility != nil {
- validator.Expect(*input.Visibility == models.OrgLinkVisibilityPublic ||
- *input.Visibility == models.OrgLinkVisibilityPrivate,
+ validator.Expect(models.ValidateLinkVisibility(string(*input.Visibility)),
lt.Translate("Invalid Visibility value.")).
WithField("visibility").
WithCode(valid.ErrValidationCode)
@@ 253,11 252,11 @@ func (r *mutationResolver) AddLink(ctx context.Context, input *model.LinkInput)
return nil, nil
}
- var visibility int
+ var visibility string
if input.Visibility == nil {
visibility = org.Settings.DefaultPerm
} else {
- visibility = *input.Visibility
+ visibility = string(*input.Visibility)
}
srv := server.ForContext(ctx)
@@ 358,8 357,7 @@ func (r *mutationResolver) UpdateLink(ctx context.Context, input *model.UpdateLi
}
if input.Visibility != nil {
- validator.Expect(*input.Visibility == models.OrgLinkVisibilityPublic ||
- *input.Visibility == models.OrgLinkVisibilityPrivate,
+ validator.Expect(models.ValidateLinkVisibility(string(*input.Visibility)),
lt.Translate("Invalid Visibility value.")).
WithField("visibility").
WithCode(valid.ErrValidationCode)
@@ 441,14 439,14 @@ func (r *mutationResolver) UpdateLink(ctx context.Context, input *model.UpdateLi
if input.Visibility != nil {
// We want to restrict private links only to paid users
- if *input.Visibility == models.OrgLinkVisibilityPrivate &&
+ if string(*input.Visibility) == models.OrgLinkVisibilityPrivate &&
links.BillingEnabled(srv.Config) &&
org.IsRestricted([]int{models.BillingStatusFree}) {
validator.Error(lt.Translate("Free organizations are not allowed to create private links. Please upgrade")).
WithField("visibility").
WithCode(valid.ErrValidationCode)
return nil, nil
- } else if *input.Visibility == models.OrgLinkVisibilityPublic && orgLink.Type == models.NoteType {
+ } else if string(*input.Visibility) == models.OrgLinkVisibilityPublic && orgLink.Type == models.NoteType {
// NOTE: when making a note public we want to process its url and creata a base url obj
BaseURL := &models.BaseURL{
URL: orgLink.URL,
@@ 463,7 461,7 @@ func (r *mutationResolver) UpdateLink(ctx context.Context, input *model.UpdateLi
}
srv.QueueTask("general", core.ParseBaseURLTask(srv, BaseURL))
}
- orgLink.Visibility = *input.Visibility
+ orgLink.Visibility = string(*input.Visibility)
}
if input.Title != nil {
@@ 634,8 632,7 @@ func (r *mutationResolver) AddNote(ctx context.Context, input *model.NoteInput)
validator.Expect(input.Description != "", lt.Translate("Description is required.")).
WithField("description").
WithCode(valid.ErrValidationCode)
- validator.Expect(input.Visibility == models.OrgLinkVisibilityPublic ||
- input.Visibility == models.OrgLinkVisibilityPrivate,
+ validator.Expect(models.ValidateLinkVisibility(string(input.Visibility)),
lt.Translate("Invalid Visibility value.")).
WithField("visibility").
WithCode(valid.ErrValidationCode)
@@ 686,7 683,7 @@ func (r *mutationResolver) AddNote(ctx context.Context, input *model.NoteInput)
srv := server.ForContext(ctx)
// We want to restrict private links only to paid users
- if input.Visibility == models.OrgLinkVisibilityPrivate &&
+ if string(input.Visibility) == models.OrgLinkVisibilityPrivate &&
links.BillingEnabled(srv.Config) && org.IsRestricted([]int{models.BillingStatusFree}) {
validator.Error(lt.Translate("Free organizations are not allowed to create private notes. Please upgrade")).
WithField("visibility").
@@ 699,14 696,15 @@ func (r *mutationResolver) AddNote(ctx context.Context, input *model.NoteInput)
// If the note is public we create a based link
var BaseURL *models.BaseURL
- if input.Visibility == models.OrgLinkVisibilityPublic {
- BaseURL = &models.BaseURL{
- URL: noteURL,
- }
- err = BaseURL.Store(ctx)
- if err != nil {
- return nil, err
- }
+ BaseURL = &models.BaseURL{
+ URL: noteURL,
+ }
+ err = BaseURL.Store(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ if string(input.Visibility) == models.OrgLinkVisibilityPublic {
err = BaseURL.UpdateCounter(ctx, true)
if err != nil {
return nil, err
@@ 718,7 716,7 @@ func (r *mutationResolver) AddNote(ctx context.Context, input *model.NoteInput)
OrgID: org.ID,
Description: input.Description,
BaseURLID: sql.NullInt64{Valid: BaseURL.ID > 0, Int64: int64(BaseURL.ID)},
- Visibility: input.Visibility,
+ Visibility: string(input.Visibility),
Starred: input.Starred,
URL: noteURL,
UserID: int(user.ID),
@@ 1702,7 1700,7 @@ func (r *mutationResolver) UpdateOrganization(ctx context.Context, input *model.
if input.DefaultPerm != nil {
if org.Settings.Billing.Status == models.BillingStatusFree &&
- *input.DefaultPerm == models.OrgLinkVisibilityPrivate {
+ string(*input.DefaultPerm) == models.OrgLinkVisibilityPrivate {
validator.Error(lt.Translate(
"Free organizations can not use Private permission. Please upgrade to use " +
"Private permission")).
@@ 1710,14 1708,13 @@ func (r *mutationResolver) UpdateOrganization(ctx context.Context, input *model.
WithCode(valid.ErrValidationCode)
return nil, nil
}
- if *input.DefaultPerm < models.OrgLinkVisibilityPublic ||
- *input.DefaultPerm > models.OrgLinkVisibilityPrivate {
+ if !models.ValidateLinkVisibility(string(*input.DefaultPerm)) {
validator.Error(lt.Translate("Invalid default permission value")).
WithField("defaultPerm").
WithCode(valid.ErrValidationCode)
return nil, nil
}
- org.Settings.DefaultPerm = *input.DefaultPerm
+ org.Settings.DefaultPerm = string(*input.DefaultPerm)
}
err = org.Store(ctx)
@@ 4195,11 4192,21 @@ func (r *orgLinkResolver) BaseURLID(ctx context.Context, obj *models.OrgLink) (*
return &model.NullInt{Int64: int(obj.BaseURLID.Int64), Valid: obj.BaseURLID.Valid}, nil
}
+// Visibility is the resolver for the visibility field.
+func (r *orgLinkResolver) Visibility(ctx context.Context, obj *models.OrgLink) (model.LinkVisibility, error) {
+ return model.LinkVisibility(obj.Visibility), 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"))
}
+// DefaultPerm is the resolver for the defaultPerm field.
+func (r *organizationSettingsResolver) DefaultPerm(ctx context.Context, obj *models.OrganizationSettings) (model.LinkVisibility, error) {
+ return model.LinkVisibility(obj.DefaultPerm), nil
+}
+
// Version is the resolver for the version field.
func (r *queryResolver) Version(ctx context.Context) (*model.Version, error) {
return &model.Version{
@@ 4554,7 4561,7 @@ func (r *queryResolver) GetOrgLink(ctx context.Context, hash string) (*models.Or
sq.And{
sq.Eq{"ou.user_id": user.ID},
sq.Eq{"ou.is_active": true},
- sq.Eq{"ol.visibility": []int{
+ sq.Eq{"ol.visibility": []string{
models.OrgLinkVisibilityPublic,
models.OrgLinkVisibilityPrivate},
},
@@ 4630,7 4637,7 @@ func (r *queryResolver) GetOrgLinks(ctx context.Context, input *model.GetLinkInp
if user.IsAuthenticated() && org.CanRead(ctx, user) {
linkOpts.Filter = sq.And{
linkOpts.Filter,
- sq.Eq{"ol.visibility": []int{
+ sq.Eq{"ol.visibility": []string{
models.OrgLinkVisibilityPrivate, models.OrgLinkVisibilityPublic}},
}
} else {
@@ 6686,6 6693,11 @@ func (r *Resolver) OrgLink() OrgLinkResolver { return &orgLinkResolver{r} }
// Organization returns OrganizationResolver implementation.
func (r *Resolver) Organization() OrganizationResolver { return &organizationResolver{r} }
+// OrganizationSettings returns OrganizationSettingsResolver implementation.
+func (r *Resolver) OrganizationSettings() OrganizationSettingsResolver {
+ return &organizationSettingsResolver{r}
+}
+
// Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
@@ 6696,6 6708,7 @@ type domainResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type orgLinkResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
+type organizationSettingsResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type userResolver struct{ *Resolver }
M core/import.go => core/import.go +1 -1
@@ 262,7 262,7 @@ func processOrgLinks(obj importObj, baseURLMap map[string]int,
return nil
}
- var vis int
+ var vis string
if obj.IsPublic() {
vis = models.OrgLinkVisibilityPublic
} else if billEnabled && org.IsRestricted([]int{models.BillingStatusFree}) {
M core/inputs.go => core/inputs.go +6 -6
@@ 55,7 55,7 @@ type UpdateOrganizationForm struct {
Slug string `form:"slug" validate:"required"`
Enabled bool `from:"is_enabled"`
DeleteImage bool `form:"delete"`
- DefaultPerm int `form:"default_perm" validate:"number,gte=0"`
+ DefaultPerm string `form:"default_perm" validate:"oneof=PUBLIC PRIVATE RESTRICTED"`
}
// Validate ...
@@ 65,7 65,7 @@ func (uo *UpdateOrganizationForm) Validate(c echo.Context) error {
String("slug", &uo.Slug).
Bool("is_enabled", &uo.Enabled).
Bool("delete", &uo.DeleteImage).
- Int("default_perm", &uo.DefaultPerm).
+ String("default_perm", &uo.DefaultPerm).
BindErrors()
if errs != nil {
return validate.GetInputErrors(errs)
@@ 100,7 100,7 @@ type LinkForm struct {
Title string `form:"title" validate:"required"`
Description string `form:"description"`
OrgSlug string `form:"org_slug" validate:"required"`
- Visibility int `form:"visibility" validate:"number,gte=0"`
+ Visibility string `form:"visibility" validate:"oneof=PUBLIC PRIVATE RESTRICTED"`
Unread bool `form:"unread"`
Starred bool `form:"starred"`
Archive bool `form:"archive"`
@@ 122,7 122,7 @@ func (l *LinkForm) Validate(c echo.Context) error {
Bool("unread", &l.Unread).
Bool("starred", &l.Starred).
Bool("archive", &l.Archive).
- Int("visibility", &l.Visibility).
+ String("visibility", &l.Visibility).
BindErrors()
if errs != nil {
return validate.GetInputErrors(errs)
@@ 230,7 230,7 @@ type NoteForm struct {
Title string `form:"title" validate:"required"`
Description string `form:"description" validate:"required"`
OrgSlug string `form:"org_slug" validate:"required"`
- Visibility int `form:"visibility" validate:"number,gte=0"`
+ Visibility string `form:"visibility" validate:"oneof=PUBLIC PRIVATE RESTRICTED"`
Starred bool `form:"starred"`
Tags string `form:"tags"`
}
@@ 243,7 243,7 @@ func (n *NoteForm) Validate(c echo.Context) error {
String("org_slug", &n.OrgSlug).
String("tags", &n.Tags).
Bool("starred", &n.Starred).
- Int("visibility", &n.Visibility).
+ String("visibility", &n.Visibility).
BindErrors()
if errs != nil {
return validate.GetInputErrors(errs)
M core/routes.go => core/routes.go +16 -12
@@ 734,7 734,7 @@ func (s *Service) OrgCreate(c echo.Context) error {
"Sorry, you have exceeded the amount of free accounts available. Please update your current free account to create one more")
form := &OrganizationForm{}
- visibilityOpt := map[int]string{
+ visibilityOpt := map[string]string{
models.OrgLinkVisibilityPublic: lt.Translate("Public"),
models.OrgLinkVisibilityPrivate: lt.Translate("Private"),
}
@@ 883,7 883,7 @@ func (s *Service) OrgUpdate(c echo.Context) error {
pd.Data["visibility"] = lt.Translate("Default Bookmark Visibility")
form := &UpdateOrganizationForm{}
- visibilityOpt := map[int]string{
+ visibilityOpt := map[string]string{
models.OrgLinkVisibilityPublic: lt.Translate("Public"),
models.OrgLinkVisibilityPrivate: lt.Translate("Private"),
}
@@ 1376,7 1376,7 @@ func (s *Service) OrgLinksCreate(c echo.Context) error {
pd.Data["back"] = lt.Translate("Back")
pd.Data["archive"] = lt.Translate("Archive URL")
- visibilityOpt := map[int]string{
+ visibilityOpt := map[string]string{
models.OrgLinkVisibilityPublic: lt.Translate("Public"),
models.OrgLinkVisibilityPrivate: lt.Translate("Private"),
}
@@ 1410,7 1410,7 @@ func (s *Service) OrgLinksCreate(c echo.Context) error {
var result GraphQLResponse
op := gqlclient.NewOperation(
`mutation AddLink($title: String!, $url: String!, $description: String,
- $visibility: Int!, $unread: Boolean!, $starred: Boolean!,
+ $visibility: LinkVisibility!, $unread: Boolean!, $starred: Boolean!,
$archive: Boolean! $slug: String!, $tags: String) {
addLink(input: {
title: $title,
@@ 2274,6 2274,7 @@ func (s *Service) OrgLinkDetail(c echo.Context) error {
userId
type
hash
+ visibility
createdOn
tags {
id
@@ 2453,7 2454,7 @@ func (s *Service) OrgLinkUpdate(c echo.Context) error {
pd.Data["confirm_message"] = lt.Translate("Do you want to delete")
pd.Data["archive"] = lt.Translate("Archive")
pd.Data["tags_helper"] = lt.Translate("Use commas to separate your tags. Example: tag 1, tag 2, tag 3")
- visibilityOpt := map[int]string{
+ visibilityOpt := map[string]string{
models.OrgLinkVisibilityPublic: lt.Translate("Public"),
models.OrgLinkVisibilityPrivate: lt.Translate("Private"),
}
@@ 2487,7 2488,7 @@ func (s *Service) OrgLinkUpdate(c echo.Context) error {
var result GraphQLResponse
op := gqlclient.NewOperation(
`mutation UpdateLink($hash: String!, $title: String!, $description: String,
- $url: String!, $visibility: Int!, $unread: Boolean,
+ $url: String!, $visibility: LinkVisibility!, $unread: Boolean,
$starred: Boolean, $tags: String) {
updateLink(input: {
title: $title,
@@ 3273,7 3274,7 @@ func (s *Service) NoteUpdate(c echo.Context) error {
pd.Data["starred"] = lt.Translate("Starred")
pd.Data["confirm_message"] = lt.Translate("Do you want to delete")
pd.Data["tags_helper"] = lt.Translate("Use commas to separate your tags. Example: tag 1, tag 2, tag 3")
- visibilityOpt := map[int]string{
+ visibilityOpt := map[string]string{
models.OrgLinkVisibilityPublic: lt.Translate("Public"),
models.OrgLinkVisibilityPrivate: lt.Translate("Private"),
}
@@ 3307,7 3308,7 @@ func (s *Service) NoteUpdate(c echo.Context) error {
var result GraphQLResponse
op := gqlclient.NewOperation(
`mutation UpdateLink($hash: String!, $title: String!, $description: String,
- $visibility: Int!, $starred: Boolean, $tags: String) {
+ $visibility: LinkVisibility!, $starred: Boolean, $tags: String) {
updateLink(input: {
hash: $hash,
title: $title,
@@ 3317,6 3318,7 @@ func (s *Service) NoteUpdate(c echo.Context) error {
tags: $tags,
}) {
id
+ hash
url
}
}`)
@@ 3340,7 3342,7 @@ func (s *Service) NoteUpdate(c echo.Context) error {
return err
}
messages.Success(c, lt.Translate("Link successfully updated."))
- return c.Redirect(http.StatusMovedPermanently, result.Link.URL)
+ return c.Redirect(http.StatusMovedPermanently, c.Echo().Reverse("core:note_detail", result.Link.Hash))
}
var canEdit bool
@@ 3385,6 3387,7 @@ func (s *Service) NoteDetail(c echo.Context) error {
userId
type
visibility
+ hash
createdOn
updatedOn
tags {
@@ 3470,7 3473,7 @@ func (s *Service) NoteCreate(c echo.Context) error {
pd.Data["back"] = lt.Translate("Back")
pd.Data["organization"] = lt.Translate("Organization")
- visibilityOpt := map[int]string{
+ visibilityOpt := map[string]string{
models.OrgLinkVisibilityPublic: lt.Translate("Public"),
models.OrgLinkVisibilityPrivate: lt.Translate("Private"),
}
@@ 3502,7 3505,7 @@ func (s *Service) NoteCreate(c echo.Context) error {
var result GraphQLResponse
op := gqlclient.NewOperation(
- `mutation AddNote($title: String!, $description: String!, $visibility: Int!,
+ `mutation AddNote($title: String!, $description: String!, $visibility: LinkVisibility!,
$starred: Boolean!, $slug: String!, $tags: String) {
addNote(input: {
title: $title,
@@ 3512,6 3515,7 @@ func (s *Service) NoteCreate(c echo.Context) error {
orgSlug: $slug,
tags: $tags}) {
id
+ hash
url
}
}`)
@@ 3536,7 3540,7 @@ func (s *Service) NoteCreate(c echo.Context) error {
}
messages.Success(c, lt.Translate("An note was successfully created."))
- return c.Redirect(http.StatusMovedPermanently, result.Link.URL)
+ return c.Redirect(http.StatusMovedPermanently, c.Echo().Reverse("core:note_detail", result.Link.Hash))
}
form := &NoteForm{
M core/routes_test.go => core/routes_test.go +4 -4
@@ 164,7 164,7 @@ func TestHandlers(t *testing.T) {
f.Set("url", "http://foo.com")
f.Set("title", "Title")
f.Set("org_slug", "slug")
- f.Set("visibility", "1")
+ f.Set("visibility", "PRIVATE")
request := httptest.NewRequest(http.MethodPost, "/add", strings.NewReader(f.Encode()))
request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
recorder := httptest.NewRecorder()
@@ 192,7 192,7 @@ func TestHandlers(t *testing.T) {
f.Set("url", "http://foo.com")
f.Set("title", "Title")
f.Set("org_slug", "slug")
- f.Set("visibility", "1")
+ f.Set("visibility", "PRIVATE")
request := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
recorder := httptest.NewRecorder()
@@ 234,7 234,7 @@ func TestHandlers(t *testing.T) {
f.Set("url", "http://foo.bar.com")
f.Set("title", "Title")
f.Set("org_slug", "slug")
- f.Set("visibility", "1")
+ f.Set("visibility", "PRIVATE")
request := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
recorder := httptest.NewRecorder()
@@ 269,7 269,7 @@ func TestHandlers(t *testing.T) {
f.Set("url", "http://foo.com")
f.Set("title", "Title")
f.Set("org_slug", "slug")
- f.Set("visibility", "1")
+ f.Set("visibility", "PRIVATE")
request := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode()))
request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
recorder := httptest.NewRecorder()
M core/samples/create_link.json => core/samples/create_link.json +1 -1
@@ 9,7 9,7 @@
},
"orgId": 1,
"userId": 1,
- "visibility": 0,
+ "visibility": "PUBLIC",
"createdOn": "2023-06-08T22:44:32.35373Z",
"updatedOn": "2023-06-08T22:44:32.35373Z"
}
M core/samples/detail_link.json => core/samples/detail_link.json +1 -1
@@ 12,7 12,7 @@
},
"orgId": 1,
"userId": 1,
- "visibility": 0,
+ "visibility": "PUBLIC",
"createdOn": "2023-05-30T18:41:38.891196Z",
"updatedOn": "2023-05-30T18:41:38.891196Z"
}
M core/samples/update_link.json => core/samples/update_link.json +1 -1
@@ 9,7 9,7 @@
},
"orgId": 1,
"userId": 1,
- "visibility": 0,
+ "visibility": "PUBLIC",
"createdOn": "2023-05-23T17:27:21.013293Z",
"updatedOn": "2023-06-08T23:47:11.521328Z"
}
M migrations/0001_initial.down.sql => migrations/0001_initial.down.sql +1 -0
@@ 36,3 36,4 @@ DROP TYPE IF EXISTS domain_level;
DROP TYPE IF EXISTS domain_service;
DROP TYPE IF EXISTS domain_status;
DROP TYPE IF EXISTS invoice_status;
+DROP TYPE IF EXISTS org_link_visibility;
M migrations/0001_initial.up.sql => migrations/0001_initial.up.sql +11 -1
@@ 51,6 51,16 @@ EXCEPTION
WHEN duplicate_object THEN null;
END $$;
+DO $$ BEGIN
+ CREATE TYPE org_link_visibility AS ENUM (
+ 'PUBLIC',
+ 'PRIVATE',
+ 'RESTRICTED'
+ );
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+
CREATE TABLE users (
id SERIAL PRIMARY KEY,
@@ 146,7 156,7 @@ 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,
archive_url TEXT DEFAULT '',
M migrations/test_migration.up.sql => migrations/test_migration.up.sql +2 -2
@@ 10,10 10,10 @@ INSERT INTO organizations (owner_id, name, slug) VALUES (2, 'api test org', 'api
INSERT INTO base_urls (url, hash) VALUES ('http://base.com', 'abcdefg');
INSERT INTO org_links (title, url, base_url_id, user_id, org_id, visibility, hash) VALUES
- ('Public Business url', 'http://base.com?vis=public', 1, 1, 2, 0, 'hash1');
+ ('Public Business url', 'http://base.com?vis=public', 1, 1, 2, 'PUBLIC', 'hash1');
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');
+ ('Private Business url', 'http://base.com?vis=private', 1, 1, 2, 'PRIVATE', 'hash2');
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');
M models/models.go => models/models.go +1 -1
@@ 76,7 76,7 @@ type OrgLink struct {
BaseURLID sql.NullInt64 `db:"base_url_id" json:"baseURLId"`
OrgID int `db:"org_id" json:"orgId"`
UserID int `db:"user_id" json:"userId"`
- Visibility int `db:"visibility" json:"visibility"`
+ Visibility string `db:"visibility" json:"visibility"`
Unread bool `db:"unread" json:"unread"`
Starred bool `db:"starred" json:"starred"`
ArchiveURL string `db:"archive_url" json:"archiveUrl"`
M models/org_link.go => models/org_link.go +20 -4
@@ 16,9 16,9 @@ import (
)
const (
- OrgLinkVisibilityPublic int = iota
- OrgLinkVisibilityPrivate
- OrgLinkVisibilityRestricted
+ OrgLinkVisibilityPublic string = "PUBLIC"
+ OrgLinkVisibilityPrivate string = "PRIVATE"
+ OrgLinkVisibilityRestricted string = "RESTRICTED"
)
const (
@@ 134,6 134,10 @@ func (o *OrgLink) Store(ctx context.Context) error {
if o.Hash == "" {
o.Hash = ksuid.New().String()
}
+ // Added for fail safes after migration to enums. Can be removed later
+ if o.Visibility == "" {
+ o.Visibility = OrgLinkVisibilityPublic
+ }
err = sq.
Insert("org_links").
Columns("title", "url", "description", "base_url_id", "org_id", "user_id", "visibility",
@@ 348,7 352,7 @@ func ExportOrgLinks(ctx context.Context, opts *database.FilterOptions) ([]*Expor
return links, nil
}
-func ToggleOrgLinksVisibilityBatch(ctx context.Context, opts *database.FilterOptions, vis int) error {
+func ToggleOrgLinksVisibilityBatch(ctx context.Context, opts *database.FilterOptions, vis string) error {
err := database.WithTx(ctx, nil, func(tx *sql.Tx) error {
var err error
_, err = sq.
@@ 396,3 400,15 @@ func GetOrgLinksCount(ctx context.Context, opts *database.FilterOptions) (int, e
}
return count, nil
}
+
+// ValidateLinkVisibility ensures proper value for OrgLink.Visibility
+func ValidateLinkVisibility(vis string) bool {
+ visMap := map[string]bool{
+ OrgLinkVisibilityPublic: true,
+ OrgLinkVisibilityPrivate: true,
+ OrgLinkVisibilityRestricted: true,
+ }
+ _, ok := visMap[vis]
+ return ok
+
+}
M models/organization.go => models/organization.go +1 -1
@@ 37,7 37,7 @@ type BillingSettings struct {
// OrganizationSettings ...
type OrganizationSettings struct {
- DefaultPerm int `json:"default_perm"` // default: OrgLinkVisibilityPublic
+ DefaultPerm string `json:"default_perm"` // default: OrgLinkVisibilityPublic
Billing BillingSettings `json:"billing"`
}
M models/schema.sql => models/schema.sql +7 -0
@@ 34,6 34,12 @@ CREATE TYPE invoice_status AS ENUM (
'PARTIAL_REFUND'
);
+CREATE TYPE org_link_visibility AS ENUM (
+ 'PUBLIC',
+ 'PRIVATE',
+ 'RESTRICTED'
+);
+
CREATE TABLE users (
id SERIAL PRIMARY KEY,
full_name VARCHAR ( 150 ) NOT NULL,
@@ 129,6 135,7 @@ CREATE TABLE org_links (
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,
archive_url TEXT DEFAULT '',