Skip to content
This repository was archived by the owner on Dec 17, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions internal/gql/v3/resolvers/query/query.users.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,32 @@ func (r *Resolver) Users(ctx context.Context, queryArg string, pageArg *int, lim

return result, err
}

func (r *Resolver) UserByConnection(ctx context.Context, connectionPlatform model.ConnectionPlatform, id string) (*model.User, error) {
l, ok := r.Ctx.Inst().Loaders.UserByConnectionID(structures.UserConnectionPlatform(connectionPlatform))
if !ok {
return nil, errors.ErrInvalidRequest().SetDetail("Unknown connection platform")
}

user, err := l.Load(id)
if err != nil {
return nil, err
}

if user.ID.IsZero() || user.ID == structures.DeletedUser.ID {
return nil, errors.ErrUnknownUser()
}

bans, err := r.Ctx.Inst().Query.Bans(ctx, query.BanQueryOptions{ // remove emotes made by users who own nothing and are happy
Filter: bson.M{"effects": bson.M{"$bitsAnySet": structures.BanEffectMemoryHole}},
})
if err != nil {
return nil, err
}

if _, ok := bans.MemoryHole[user.ID]; ok {
return nil, errors.ErrUnknownUser()
}

return helpers.UserStructureToModel(user, r.Ctx.Config().CdnURL), nil
}
1 change: 1 addition & 0 deletions internal/gql/v3/schema/users.gql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ extend type Query {
actor: User
user(id: ObjectID!): User!
usersByID(list: [ObjectID!]!): [UserPartial!]!
userByConnection(platform: ConnectionPlatform!, id: String!): User!
users(query: String!, page: Int, limit: Int): [UserPartial!]!
}

Expand Down
17 changes: 15 additions & 2 deletions internal/loaders/loaders.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const LoadersKey = utils.Key("dataloaders")
type Instance interface {
UserByID() UserLoaderByID
UserByUsername() UserLoaderByUsername
UserByConnectionID(structures.UserConnectionPlatform) (UserByConnectionID, bool)
EmoteByID() EmoteLoaderByID
EmoteByOwnerID() BatchEmoteLoaderByID
EmoteSetByID() EmoteSetLoaderByID
Expand All @@ -25,8 +26,9 @@ type Instance interface {

type inst struct {
// User Loaders
userByID UserLoaderByID
userByUsername UserLoaderByUsername
userByID UserLoaderByID
userByUsername UserLoaderByUsername
userByConnectionID map[structures.UserConnectionPlatform]UserByConnectionID

// Emote Loaders
emoteByID EmoteLoaderByID
Expand All @@ -52,6 +54,11 @@ func New(ctx context.Context, mngo mongo.Instance, rdis redis.Instance, quer *qu

l.userByID = userLoader[primitive.ObjectID](ctx, l, "_id")
l.userByUsername = userLoader[string](ctx, l, "username")
l.userByConnectionID = map[structures.UserConnectionPlatform]*dataloader.DataLoader[string, structures.User]{
structures.UserConnectionPlatformTwitch: userByConnectionLoader(ctx, l, structures.UserConnectionPlatformTwitch),
structures.UserConnectionPlatformYouTube: userByConnectionLoader(ctx, l, structures.UserConnectionPlatformYouTube),
structures.UserConnectionPlatformDiscord: userByConnectionLoader(ctx, l, structures.UserConnectionPlatformDiscord),
}
l.emoteByID = emoteLoader(ctx, l, "versions.id")
l.emoteByOwnerID = batchEmoteLoader(ctx, l, "owner_id")
l.emoteSetByID = emoteSetByID(ctx, l)
Expand All @@ -68,6 +75,11 @@ func (l inst) UserByUsername() UserLoaderByUsername {
return l.userByUsername
}

func (l inst) UserByConnectionID(platform structures.UserConnectionPlatform) (UserByConnectionID, bool) {
loader, ok := l.userByConnectionID[platform]
return loader, ok
}

func (l inst) EmoteByID() EmoteLoaderByID {
return l.emoteByID
}
Expand All @@ -88,6 +100,7 @@ func (l *inst) EmoteByOwnerID() BatchEmoteLoaderByID {
type (
UserLoaderByID = *dataloader.DataLoader[primitive.ObjectID, structures.User]
UserLoaderByUsername = *dataloader.DataLoader[string, structures.User]
UserByConnectionID = *dataloader.DataLoader[string, structures.User]
EmoteLoaderByID = *dataloader.DataLoader[primitive.ObjectID, structures.Emote]
BatchEmoteLoaderByID = *dataloader.DataLoader[primitive.ObjectID, []structures.Emote]
EmoteSetLoaderByID = *dataloader.DataLoader[primitive.ObjectID, structures.EmoteSet]
Expand Down
53 changes: 53 additions & 0 deletions internal/loaders/user.loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,56 @@ func userLoader[T comparable](ctx context.Context, x inst, keyName string) *data
},
})
}

func userByConnectionLoader(ctx context.Context, x inst, platform structures.UserConnectionPlatform) *dataloader.DataLoader[string, structures.User] {
return dataloader.New(dataloader.Config[string, structures.User]{
Wait: time.Millisecond * 25,
Fetch: func(keys []string) ([]structures.User, []error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()

items := make([]structures.User, len(keys))
errs := make([]error, len(keys))

// Initially fill the response with deleted emotes in case some cannot be found
for i := 0; i < len(items); i++ {
items[i] = structures.DeletedUser
}

// Fetch users
result := x.query.Users(ctx, bson.M{
"connections.id": bson.M{"$in": keys},
"connections.platform": platform,
})
if result.Empty() {
return items, errs
}
users, err := result.Items()

if err == nil {
m := make(map[string]structures.User)
for _, u := range users {
for _, c := range u.Connections {
if c.Platform == platform {
m[c.ID] = u
}
}
}

for i, v := range keys {
if x, ok := m[v]; ok {
items[i] = x
} else {
errs[i] = errors.ErrUnknownUser()
}
}
} else {
for i := range errs {
errs[i] = err
}
}

return items, errs
},
})
}