diff --git a/PluralKit.API/Controllers/v2/GroupControllerV2.cs b/PluralKit.API/Controllers/v2/GroupControllerV2.cs index 4b351e67..63f4e6a4 100644 --- a/PluralKit.API/Controllers/v2/GroupControllerV2.cs +++ b/PluralKit.API/Controllers/v2/GroupControllerV2.cs @@ -18,7 +18,7 @@ namespace PluralKit.API public GroupControllerV2(IServiceProvider svc) : base(svc) { } [HttpGet("systems/{systemRef}/groups")] - public async Task GetSystemGroups(string systemRef) + public async Task GetSystemGroups(string systemRef, [FromQuery] bool with_members) { var system = await ResolveSystem(systemRef); if (system == null) @@ -26,14 +26,29 @@ namespace PluralKit.API var ctx = this.ContextFor(system); + if (with_members && !system.MemberListPrivacy.CanAccess(ctx)) + throw Errors.UnauthorizedMemberList; + if (!system.GroupListPrivacy.CanAccess(User.ContextFor(system))) throw Errors.UnauthorizedGroupList; var groups = _repo.GetSystemGroups(system.Id); - return Ok(await groups + + var j_groups = await groups .Where(g => g.Visibility.CanAccess(ctx)) - .Select(g => g.ToJson(ctx)) - .ToListAsync()); + .Select(g => g.ToJson(ctx, needsMembersArray: with_members)) + .ToListAsync(); + + if (with_members && j_groups.Count > 0) + { + var q = await _repo.GetGroupMemberInfo(await groups.Select(x => x.Id).ToListAsync()); + + foreach (var row in q) + if (row.MemberVisibility.CanAccess(ctx)) + ((JArray)j_groups.Find(x => x.Value("id") == row.Group)["members"]).Add(row.MemberUuid); + } + + return Ok(j_groups); } [HttpPost("groups")] diff --git a/PluralKit.Core/Database/Repository/ModelRepository.GroupMember.cs b/PluralKit.Core/Database/Repository/ModelRepository.GroupMember.cs index 06ff7319..9c9ffaa8 100644 --- a/PluralKit.Core/Database/Repository/ModelRepository.GroupMember.cs +++ b/PluralKit.Core/Database/Repository/ModelRepository.GroupMember.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using SqlKata; @@ -25,6 +27,15 @@ namespace PluralKit.Core return _db.QueryStream(query); } + public Task> GetGroupMemberInfo(IEnumerable ids) + { + return _db.Query(new Query("group_members") + .LeftJoin("groups", "groups.id", "group_members.group_id") + .LeftJoin("members", "members.id", "group_members.member_id") + .Select("groups.hid as group", "members.hid as member", "members.uuid as member_uuid", "members.member_visibility") + .WhereIn("group_members.group_id", ids.Select(x => x.Value).ToArray())); + } + // todo: add this to metrics tracking public async Task AddGroupsToMember(MemberId member, IReadOnlyCollection groups) { @@ -93,4 +104,12 @@ namespace PluralKit.Core return _db.ExecuteQuery(query); } } + + public class GroupMember + { + public string Group { get; set; } + public string Member { get; set; } + public Guid MemberUuid { get; set; } + public PrivacyLevel MemberVisibility { get; set; } + } } \ No newline at end of file diff --git a/PluralKit.Core/Models/PKGroup.cs b/PluralKit.Core/Models/PKGroup.cs index 989a64eb..f4523d5e 100644 --- a/PluralKit.Core/Models/PKGroup.cs +++ b/PluralKit.Core/Models/PKGroup.cs @@ -61,7 +61,7 @@ namespace PluralKit.Core public static string? IconFor(this PKGroup group, LookupContext ctx) => group.IconPrivacy.Get(ctx, group.Icon?.TryGetCleanCdnUrl()); - public static JObject ToJson(this PKGroup group, LookupContext ctx, string? systemStr = null, bool isExport = false) + public static JObject ToJson(this PKGroup group, LookupContext ctx, string? systemStr = null, bool needsMembersArray = false) { var o = new JObject(); @@ -80,7 +80,7 @@ namespace PluralKit.Core o.Add("created", group.Created.FormatExport()); - if (isExport) + if (needsMembersArray) o.Add("members", new JArray()); if (ctx == LookupContext.ByOwner) diff --git a/PluralKit.Core/Services/DataFileService.cs b/PluralKit.Core/Services/DataFileService.cs index 63ad91b6..7e96f8be 100644 --- a/PluralKit.Core/Services/DataFileService.cs +++ b/PluralKit.Core/Services/DataFileService.cs @@ -40,15 +40,11 @@ namespace PluralKit.Core o.Add("members", new JArray((await _repo.GetSystemMembers(system.Id).ToListAsync()).Select(m => m.ToJson(LookupContext.ByOwner)))); var groups = (await _repo.GetSystemGroups(system.Id).ToListAsync()); - var j_groups = groups.Select(x => x.ToJson(LookupContext.ByOwner, isExport: true)).ToList(); + var j_groups = groups.Select(x => x.ToJson(LookupContext.ByOwner, needsMembersArray: true)).ToList(); if (groups.Count > 0) { - var q = await conn.QueryAsync(@$"select groups.hid as group, members.hid as member from group_members - left join groups on groups.id = group_members.group_id - left join members on members.id = group_members.member_id - where group_members.group_id in ({string.Join(", ", groups.Select(x => x.Id.Value.ToString()))}) - "); + var q = await _repo.GetGroupMemberInfo(groups.Select(x => x.Id)); foreach (var row in q) ((JArray)j_groups.Find(x => x.Value("id") == row.Group)["members"]).Add(row.Member); @@ -80,10 +76,4 @@ namespace PluralKit.Core } } - - public class GroupMember - { - public string Group { get; set; } - public string Member { get; set; } - } } \ No newline at end of file diff --git a/docs/content/api/endpoints.md b/docs/content/api/endpoints.md index 62740029..21e7e9f2 100644 --- a/docs/content/api/endpoints.md +++ b/docs/content/api/endpoints.md @@ -136,6 +136,11 @@ Returns a [member guild settings](/api/models#member-guild-settings) object on s GET `/systems/{systemRef}/groups` +Query String Parameters +|name|type|description +|---|---|---| +|with_members|boolean|includes `members` key with array of member UUIDs in each group object| + Returns a list of [group objects](/api/models/#group-model). ### Create Group