Changelog-added: organizations and domains to GraphQL User type
Changelog-updated: api version bumped to 0.13.0
---
api/graph/generated.go | 333 ++++++++++++++++++++++++++++++++++
api/graph/helpers.go | 91 ++++++++++
api/graph/schema.graphqls | 2 +
api/graph/schema.resolvers.go | 111 +++---------
models/listing_link.go | 4 +-
5 files changed, 458 insertions(+), 83 deletions(-)
create mode 100644 api/graph/helpers.go
diff --git a/api/graph/generated.go b/api/graph/generated.go
index bcd11e0..af1105f 100644
--- a/api/graph/generated.go
+++ b/api/graph/generated.go
@@ -472,12 +472,14 @@ type ComplexityRoot struct {
User struct {
CreatedOn func(childComplexity int) int
+ Domains func(childComplexity int) int
Email func(childComplexity int) int
ID func(childComplexity int) int
IsEmailVerified func(childComplexity int) int
IsLocked func(childComplexity int) int
LockReason func(childComplexity int) int
Name func(childComplexity int) int
+ Organizations func(childComplexity int) int
}
UserCursor struct {
@@ -605,6 +607,9 @@ type QueryResolver interface {
}
type UserResolver interface {
ID(ctx context.Context, obj *models.User) (int, error)
+
+ Organizations(ctx context.Context, obj *models.User) ([]*models.Organization, error)
+ Domains(ctx context.Context, obj *models.User) ([]*models.Domain, error)
}
type executableSchema struct {
@@ -2906,6 +2911,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.User.CreatedOn(childComplexity), true
+ case "User.domains":
+ if e.complexity.User.Domains == nil {
+ break
+ }
+
+ return e.complexity.User.Domains(childComplexity), true
+
case "User.email":
if e.complexity.User.Email == nil {
break
@@ -2948,6 +2960,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.User.Name(childComplexity), true
+ case "User.organizations":
+ if e.complexity.User.Organizations == nil {
+ break
+ }
+
+ return e.complexity.User.Organizations(childComplexity), true
+
case "UserCursor.pageInfo":
if e.complexity.UserCursor.PageInfo == nil {
break
@@ -7104,6 +7123,10 @@ func (ec *executionContext) fieldContext_EmailPostUser_user(_ context.Context, f
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -11080,6 +11103,10 @@ func (ec *executionContext) fieldContext_Mutation_register(ctx context.Context,
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -11183,6 +11210,10 @@ func (ec *executionContext) fieldContext_Mutation_completeRegister(ctx context.C
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -11286,6 +11317,10 @@ func (ec *executionContext) fieldContext_Mutation_updateProfile(ctx context.Cont
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -13520,6 +13555,10 @@ func (ec *executionContext) fieldContext_Mutation_updateAdminUser(ctx context.Co
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -18315,6 +18354,10 @@ func (ec *executionContext) fieldContext_Query_me(_ context.Context, field graph
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -19353,6 +19396,10 @@ func (ec *executionContext) fieldContext_Query_getOrgMembers(ctx context.Context
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -20941,6 +20988,10 @@ func (ec *executionContext) fieldContext_Query_getUser(ctx context.Context, fiel
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -22525,6 +22576,212 @@ func (ec *executionContext) fieldContext_User_lockReason(_ context.Context, fiel
return fc, nil
}
+func (ec *executionContext) _User_organizations(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_User_organizations(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ directive0 := func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.User().Organizations(rctx, obj)
+ }
+
+ directive1 := func(ctx context.Context) (any, error) {
+ scope, err := ec.unmarshalNAccessScope2linksᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "ORGS")
+ if err != nil {
+ var zeroVal []*models.Organization
+ return zeroVal, err
+ }
+ kind, err := ec.unmarshalNAccessKind2linksᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RO")
+ if err != nil {
+ var zeroVal []*models.Organization
+ return zeroVal, err
+ }
+ if ec.directives.Access == nil {
+ var zeroVal []*models.Organization
+ return zeroVal, 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.([]*models.Organization); ok {
+ return data, nil
+ }
+ return nil, fmt.Errorf(`unexpected type %T from directive, should be []*links/models.Organization`, tmp)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.([]*models.Organization)
+ fc.Result = res
+ return ec.marshalNOrganization2ᚕᚖlinksᚋmodelsᚐOrganizationᚄ(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_User_organizations(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "User",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "id":
+ return ec.fieldContext_Organization_id(ctx, field)
+ case "ownerId":
+ return ec.fieldContext_Organization_ownerId(ctx, field)
+ case "orgType":
+ return ec.fieldContext_Organization_orgType(ctx, field)
+ case "name":
+ return ec.fieldContext_Organization_name(ctx, field)
+ case "slug":
+ return ec.fieldContext_Organization_slug(ctx, field)
+ case "image":
+ return ec.fieldContext_Organization_image(ctx, field)
+ case "imageUrl":
+ return ec.fieldContext_Organization_imageUrl(ctx, field)
+ case "timezone":
+ return ec.fieldContext_Organization_timezone(ctx, field)
+ case "settings":
+ return ec.fieldContext_Organization_settings(ctx, field)
+ case "isActive":
+ return ec.fieldContext_Organization_isActive(ctx, field)
+ case "visibility":
+ return ec.fieldContext_Organization_visibility(ctx, field)
+ case "createdOn":
+ return ec.fieldContext_Organization_createdOn(ctx, field)
+ case "updatedOn":
+ return ec.fieldContext_Organization_updatedOn(ctx, field)
+ case "ownerName":
+ return ec.fieldContext_Organization_ownerName(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type Organization", field.Name)
+ },
+ }
+ return fc, nil
+}
+
+func (ec *executionContext) _User_domains(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_User_domains(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ directive0 := func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.User().Domains(rctx, obj)
+ }
+
+ directive1 := func(ctx context.Context) (any, error) {
+ scope, err := ec.unmarshalNAccessScope2linksᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "DOMAINS")
+ if err != nil {
+ var zeroVal []*models.Domain
+ return zeroVal, err
+ }
+ kind, err := ec.unmarshalNAccessKind2linksᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RO")
+ if err != nil {
+ var zeroVal []*models.Domain
+ return zeroVal, err
+ }
+ if ec.directives.Access == nil {
+ var zeroVal []*models.Domain
+ return zeroVal, 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.([]*models.Domain); ok {
+ return data, nil
+ }
+ return nil, fmt.Errorf(`unexpected type %T from directive, should be []*links/models.Domain`, tmp)
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.([]*models.Domain)
+ fc.Result = res
+ return ec.marshalNDomain2ᚕᚖlinksᚋmodelsᚐDomain(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_User_domains(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "User",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "id":
+ return ec.fieldContext_Domain_id(ctx, field)
+ case "name":
+ return ec.fieldContext_Domain_name(ctx, field)
+ case "lookupName":
+ return ec.fieldContext_Domain_lookupName(ctx, field)
+ case "orgId":
+ return ec.fieldContext_Domain_orgId(ctx, field)
+ case "orgSlug":
+ return ec.fieldContext_Domain_orgSlug(ctx, field)
+ case "service":
+ return ec.fieldContext_Domain_service(ctx, field)
+ case "level":
+ return ec.fieldContext_Domain_level(ctx, field)
+ case "status":
+ return ec.fieldContext_Domain_status(ctx, field)
+ case "isActive":
+ return ec.fieldContext_Domain_isActive(ctx, field)
+ case "createdOn":
+ return ec.fieldContext_Domain_createdOn(ctx, field)
+ case "updatedOn":
+ return ec.fieldContext_Domain_updatedOn(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type Domain", field.Name)
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _UserCursor_result(ctx context.Context, field graphql.CollectedField, obj *model.UserCursor) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_UserCursor_result(ctx, field)
if err != nil {
@@ -22578,6 +22835,10 @@ func (ec *executionContext) fieldContext_UserCursor_result(_ context.Context, fi
return ec.fieldContext_User_isLocked(ctx, field)
case "lockReason":
return ec.fieldContext_User_lockReason(ctx, field)
+ case "organizations":
+ return ec.fieldContext_User_organizations(ctx, field)
+ case "domains":
+ return ec.fieldContext_User_domains(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -31170,6 +31431,78 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
+ case "organizations":
+ 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._User_organizations(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 "domains":
+ 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._User_domains(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) })
default:
panic("unknown field " + strconv.Quote(field.Name))
}
diff --git a/api/graph/helpers.go b/api/graph/helpers.go
new file mode 100644
index 0000000..5dc7d08
--- /dev/null
+++ b/api/graph/helpers.go
@@ -0,0 +1,91 @@
+package graph
+
+import (
+ "context"
+ "links"
+ "links/api/graph/model"
+ "links/models"
+
+ sq "github.com/Masterminds/squirrel"
+ "netlandish.com/x/gobwebs/database"
+)
+
+// freshDBContext returns a context with a fresh DBI instance, preventing
+// concurrent reuse of the request's shared transaction. Required when gqlgen
+// resolves multiple DB-hitting fields in parallel on the same parent type.
+func freshDBContext(ctx context.Context) context.Context {
+ dbi := database.DBIForContext(ctx)
+ if dbi != nil {
+ ctx = database.DBIContext(ctx, database.NewDBI(dbi.GetDB()))
+ }
+ return ctx
+}
+
+func getOrganizationsForUser(ctx context.Context, userID int, search *string) ([]*models.Organization, error) {
+ opts := &database.FilterOptions{
+ Filter: sq.Eq{"o.owner_id": userID},
+ OrderBy: "o.created_on ASC",
+ }
+ if search != nil && *search != "" {
+ s := links.ParseSearch(*search)
+ opts.Filter = sq.And{
+ opts.Filter,
+ sq.Expr(`to_tsvector('simple', o.name || ' ' || o.slug )
+ @@ to_tsquery('simple', ?)`, s),
+ }
+ }
+ return models.GetOrganizations(ctx, opts)
+}
+
+func getDomainsForUser(ctx context.Context, user *models.User, orgSlug *string, service *model.DomainService) ([]*models.Domain, bool, error) {
+ var (
+ org *models.Organization
+ orgSent bool
+ )
+ orgs, err := user.GetOrgs(ctx, models.OrgUserPermissionRead)
+ if err != nil {
+ return nil, false, err
+ }
+ if orgSlug == nil || *orgSlug == "" {
+ for _, o := range orgs {
+ if o.OwnerID == int(user.ID) && o.OrgType == models.OrgTypeUser {
+ org = o
+ break
+ }
+ }
+ } else {
+ orgSent = true
+ for _, o := range orgs {
+ if o.Slug == *orgSlug {
+ org = o
+ break
+ }
+ }
+ }
+ if org == nil {
+ return nil, true, nil
+ }
+ opts := &database.FilterOptions{
+ Filter: sq.Eq{"d.org_id": org.ID},
+ }
+ if !orgSent {
+ opts.Filter = sq.Or{
+ opts.Filter,
+ sq.And{
+ sq.Eq{"d.org_id": nil},
+ sq.Eq{"d.level": models.DomainLevelSystem},
+ },
+ }
+ }
+ if service != nil {
+ opts.Filter = sq.And{
+ opts.Filter,
+ sq.Eq{"d.service": *service},
+ }
+ }
+ domains, err := models.GetDomains(ctx, opts)
+ if err != nil {
+ return nil, false, err
+ }
+ return domains, false, nil
+}
diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls
index a4d3713..efe1df7 100644
--- a/api/graph/schema.graphqls
+++ b/api/graph/schema.graphqls
@@ -170,6 +170,8 @@ type User {
isEmailVerified: Boolean! @access(scope: PROFILE, kind: RO)
isLocked: Boolean! @access(scope: PROFILE, kind: RO)
lockReason: String! @access(scope: PROFILE, kind: RO)
+ organizations: [Organization!]! @access(scope: ORGS, kind: RO)
+ domains: [Domain]! @access(scope: DOMAINS, kind: RO)
}
type BillingSettings {
diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go
index 3dc1210..bdb6ccc 100644
--- a/api/graph/schema.resolvers.go
+++ b/api/graph/schema.resolvers.go
@@ -4479,6 +4479,7 @@ func (r *mutationResolver) RenameTag(ctx context.Context, input model.TagInput)
return payload, nil
}
+// DeleteAccount is the resolver for the deleteAccount field.
func (r *mutationResolver) DeleteAccount(ctx context.Context, input model.DeleteAccountInput) (*model.DeletePayload, error) {
tokenUser := oauth2.ForContext(ctx)
if tokenUser == nil {
@@ -5197,7 +5198,7 @@ func (r *qRCodeResolver) ImageURL(ctx context.Context, obj *models.QRCode) (*str
func (r *queryResolver) Version(ctx context.Context) (*model.Version, error) {
return &model.Version{
Major: 0,
- Minor: 12,
+ Minor: 13,
Patch: 0,
DeprecationDate: nil,
}, nil
@@ -5220,36 +5221,7 @@ func (r *queryResolver) GetOrganizations(ctx context.Context, input *model.GetOr
return nil, valid.ErrAuthorization
}
user := tokenUser.User.(*models.User)
- opts := &database.FilterOptions{
- Filter: sq.Eq{"o.owner_id": user.ID},
- OrderBy: "o.created_on ASC",
- }
- // XXX Uncomment when we decide on a path for org members with admin write permissions
- //opts := &database.FilterOptions{
- // Filter: sq.Or{
- // sq.Eq{"o.owner_id": user.ID},
- // sq.And{
- // sq.Eq{"ou.user_id": user.ID},
- // sq.GtOrEq{"ou.permission": models.OrgUserPermissionAdminWrite},
- // sq.Eq{"ou.is_active": true},
- // },
- // },
- // OrderBy: "o.created_on ASC",
- //}
- if input.Search != nil && *input.Search != "" {
- s := links.ParseSearch(*input.Search)
- opts.Filter = sq.And{
- opts.Filter,
- sq.Expr(`to_tsvector('simple', o.name || ' ' || o.slug )
- @@ to_tsquery('simple', ?)`, s),
- }
- }
-
- orgs, err := models.GetOrganizations(ctx, opts)
- if err != nil {
- return nil, err
- }
- return orgs, err
+ return getOrganizationsForUser(ctx, int(user.ID), input.Search)
}
// GetOrganization is the resolver for the getOrganization field.
@@ -6036,67 +6008,19 @@ func (r *queryResolver) GetDomains(ctx context.Context, orgSlug *string, service
user := tokenUser.User.(*models.User)
lang := links.GetLangFromRequest(server.EchoForContext(ctx).Request(), user)
lt := localizer.GetLocalizer(lang)
-
ctx = timezone.Context(ctx, links.GetUserTZ(user))
- var (
- org *models.Organization
- orgSent bool
- )
- orgs, err := user.GetOrgs(ctx, models.OrgUserPermissionRead)
+ domains, notFound, err := getDomainsForUser(ctx, user, orgSlug, service)
if err != nil {
return nil, err
}
- if orgSlug == nil || *orgSlug == "" {
- // No org given, default to user
- for _, o := range orgs {
- if o.OwnerID == int(user.ID) && o.OrgType == models.OrgTypeUser {
- org = o
- break
- }
- }
- } else {
- orgSent = true
- for _, o := range orgs {
- if o.Slug == *orgSlug {
- org = o
- break
- }
- }
- }
-
- if org == nil {
+ if notFound {
validator := valid.New(ctx)
validator.Error(
"%s", lt.Translate("Unable to find suitable organization")).
WithCode(valid.ErrNotFoundCode)
return nil, nil
}
-
- opts := &database.FilterOptions{
- Filter: sq.Eq{"d.org_id": org.ID},
- }
- if !orgSent {
- // If no organization is specified then include system level domains as well
- opts.Filter = sq.Or{
- opts.Filter,
- sq.And{
- sq.Eq{"d.org_id": nil},
- sq.Eq{"d.level": models.DomainLevelSystem},
- },
- }
- }
- if service != nil {
- opts.Filter = sq.And{
- opts.Filter,
- sq.Eq{"d.service": *service},
- }
- }
-
- domains, err := models.GetDomains(ctx, opts)
- if err != nil {
- return nil, err
- }
return domains, nil
}
@@ -7701,6 +7625,31 @@ func (r *userResolver) ID(ctx context.Context, obj *models.User) (int, error) {
return int(obj.ID), nil
}
+// Organizations is the resolver for the organizations field.
+func (r *userResolver) Organizations(ctx context.Context, obj *models.User) ([]*models.Organization, error) {
+ return getOrganizationsForUser(freshDBContext(ctx), int(obj.ID), nil)
+}
+
+// Domains is the resolver for the domains field.
+func (r *userResolver) Domains(ctx context.Context, obj *models.User) ([]*models.Domain, error) {
+ ctx = freshDBContext(ctx)
+ lang := links.GetLangFromRequest(server.EchoForContext(ctx).Request(), obj)
+ lt := localizer.GetLocalizer(lang)
+ ctx = timezone.Context(ctx, links.GetUserTZ(obj))
+
+ domains, notFound, err := getDomainsForUser(ctx, obj, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ if notFound {
+ validator := valid.New(ctx)
+ validator.Error("%s", lt.Translate("Unable to find suitable organization")).
+ WithCode(valid.ErrNotFoundCode)
+ return nil, nil
+ }
+ return domains, nil
+}
+
// AuditLog returns AuditLogResolver implementation.
func (r *Resolver) AuditLog() AuditLogResolver { return &auditLogResolver{r} }
diff --git a/models/listing_link.go b/models/listing_link.go
index 36f7cec..addbec1 100644
--- a/models/listing_link.go
+++ b/models/listing_link.go
@@ -162,7 +162,7 @@ func GetListingLinksAnalytics(ctx context.Context, opts *database.FilterOptions)
if err := database.WithTx(ctx, database.TxOptionsRO, func(tx *sql.Tx) error {
q := opts.GetBuilder(nil)
rows, err := q.
- Columns("ll.id", "ll.title", "ll.url", "SUM(dt.clicks) AS counter").
+ Columns("ll.id", "ll.title", "ll.url", "ll.created_on", "ll.updated_on", "SUM(dt.clicks) AS counter").
From("listing_links ll").
Join("daily_totals dt ON ll.id = dt.listing_link_id").
GroupBy("ll.id").
@@ -180,7 +180,7 @@ func GetListingLinksAnalytics(ctx context.Context, opts *database.FilterOptions)
for rows.Next() {
var l ListingLink
- if err = rows.Scan(&l.ID, &l.Title, &l.URL, &l.Clicks); err != nil {
+ if err = rows.Scan(&l.ID, &l.Title, &l.URL, &l.CreatedOn, &l.UpdatedOn, &l.Clicks); err != nil {
return err
}
err = l.ToLocalTZ(tz)
--
2.52.0