From 6ca5ff1aeeb12fb6a1be98977e8ea7fff435b669 Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Sat, 21 Mar 2026 21:38:49 +0100 Subject: [PATCH] added expand query parameter to /groups endpoints see #790 --- src/api/api.go | 43 ++++++++++++-- src/client/client.go | 130 +++++++++++++++++++++++++++++------------- src/docs/docs.go | 12 ++++ src/docs/swagger.json | 12 ++++ src/docs/swagger.yaml | 8 +++ 5 files changed, 160 insertions(+), 45 deletions(-) diff --git a/src/api/api.go b/src/api/api.go index b36a5d9..d12efdd 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -1024,6 +1024,7 @@ func (a *Api) RemoveAdminsFromGroup(c *gin.Context) { // @Success 200 {object} []client.GroupEntry // @Failure 400 {object} Error // @Param number path string true "Registered Phone Number" +// @Param expand query bool false "Expand the response to show more details (default: false)" // @Router /v1/groups/{number} [get] func (a *Api) GetGroups(c *gin.Context) { number, err := url.PathUnescape(c.Param("number")) @@ -1032,12 +1033,26 @@ func (a *Api) GetGroups(c *gin.Context) { return } - groups, err := a.signalClient.GetGroups(number) - if err != nil { - c.JSON(400, Error{Msg: err.Error()}) + expand := c.DefaultQuery("expand", "false") + if expand != "true" && expand != "false" { + c.JSON(400, Error{Msg: "Couldn't process request - expand parameter needs to be either 'true' or 'false'"}) return } + var groups any + if StringToBool(expand) { + groups, err = a.signalClient.GetGroupsExpanded(number) + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + } else { + groups, err = a.signalClient.GetGroups(number) + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + } c.JSON(200, groups) } @@ -1050,6 +1065,7 @@ func (a *Api) GetGroups(c *gin.Context) { // @Failure 400 {object} Error // @Param number path string true "Registered Phone Number" // @Param groupid path string true "Group ID" +// @Param expand query bool false "Expand the response to show more details (default: false)" // @Router /v1/groups/{number}/{groupid} [get] func (a *Api) GetGroup(c *gin.Context) { number, err := url.PathUnescape(c.Param("number")) @@ -1059,12 +1075,27 @@ func (a *Api) GetGroup(c *gin.Context) { } groupId := c.Param("groupid") - groupEntry, err := a.signalClient.GetGroup(number, groupId) - if err != nil { - c.JSON(400, Error{Msg: err.Error()}) + expand := c.DefaultQuery("expand", "false") + if expand != "true" && expand != "false" { + c.JSON(400, Error{Msg: "Couldn't process request - expand parameter needs to be either 'true' or 'false'"}) return } + var groupEntry any + if StringToBool(expand) { + groupEntry, err = a.signalClient.GetGroupExpanded(number, groupId) + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + } else { + groupEntry, err = a.signalClient.GetGroup(number, groupId) + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + } + if groupEntry != nil { c.JSON(200, groupEntry) } else { diff --git a/src/client/client.go b/src/client/client.go index fa3f937..5bf2fea 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -126,6 +126,30 @@ type GroupEntry struct { Permissions ds.GroupPermissions `json:"permissions"` } +type GroupMember struct { + Number string `json:"number"` + Uuid string `json:"uuid"` +} + +type GroupAdmin struct { + Number string `json:"number"` + Uuid string `json:"uuid"` +} + +type ExpandedGroupEntry struct { + Name string `json:"name"` + Description string `json:"description"` + Id string `json:"id"` + InternalId string `json:"internal_id"` + Members []GroupMember `json:"members"` + Blocked bool `json:"blocked"` + PendingInvites []GroupMember `json:"pending_invites"` + PendingRequests []GroupMember `json:"pending_requests"` + InviteLink string `json:"invite_link"` + Admins []GroupAdmin `json:"admins"` + Permissions ds.GroupPermissions `json:"permissions"` +} + type IdentityEntry struct { Number string `json:"number"` Status string `json:"status"` @@ -135,31 +159,21 @@ type IdentityEntry struct { Uuid string `json:"uuid"` } -type SignalCliGroupMember struct { - Number string `json:"number"` - Uuid string `json:"uuid"` -} - -type SignalCliGroupAdmin struct { - Number string `json:"number"` - Uuid string `json:"uuid"` -} - type SignalCliGroupEntry struct { - Name string `json:"name"` - Description string `json:"description"` - Id string `json:"id"` - IsMember bool `json:"isMember"` - IsBlocked bool `json:"isBlocked"` - Members []SignalCliGroupMember `json:"members"` - PendingMembers []SignalCliGroupMember `json:"pendingMembers"` - RequestingMembers []SignalCliGroupMember `json:"requestingMembers"` - GroupInviteLink string `json:"groupInviteLink"` - Admins []SignalCliGroupAdmin `json:"admins"` - Uuid string `json:"uuid"` - PermissionEditDetails string `json:"permissionEditDetails"` - PermissionAddMember string `json:"permissionAddMember"` - PermissionSendMessage string `json:"permissionSendMessage"` + Name string `json:"name"` + Description string `json:"description"` + Id string `json:"id"` + IsMember bool `json:"isMember"` + IsBlocked bool `json:"isBlocked"` + Members []GroupMember `json:"members"` + PendingMembers []GroupMember `json:"pendingMembers"` + RequestingMembers []GroupMember `json:"requestingMembers"` + GroupInviteLink string `json:"groupInviteLink"` + Admins []GroupAdmin `json:"admins"` + Uuid string `json:"uuid"` + PermissionEditDetails string `json:"permissionEditDetails"` + PermissionAddMember string `json:"permissionAddMember"` + PermissionSendMessage string `json:"permissionSendMessage"` } type SignalCliIdentityEntry struct { @@ -1300,8 +1314,8 @@ func (s *SignalClient) RemoveAdminsFromGroup(number string, groupId string, admi return s.updateGroupAdmins(number, groupId, admins, false) } -func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) { - groupEntries := []GroupEntry{} +func (s *SignalClient) GetGroupsExpanded(number string) ([]ExpandedGroupEntry, error) { + groupEntries := []ExpandedGroupEntry{} var signalCliGroupEntries []SignalCliGroupEntry var err error @@ -1329,7 +1343,7 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) { } for _, signalCliGroupEntry := range signalCliGroupEntries { - var groupEntry GroupEntry + var groupEntry ExpandedGroupEntry groupEntry.InternalId = signalCliGroupEntry.Id groupEntry.Name = signalCliGroupEntry.Name groupEntry.Id = convertInternalGroupIdToGroupId(signalCliGroupEntry.Id) @@ -1338,9 +1352,32 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) { groupEntry.Permissions.SendMessages = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage) groupEntry.Permissions.EditGroup = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage) groupEntry.Permissions.AddMembers = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionAddMember) + groupEntry.Members = signalCliGroupEntry.Members + groupEntry.PendingInvites = signalCliGroupEntry.PendingMembers + groupEntry.PendingRequests = signalCliGroupEntry.RequestingMembers + groupEntry.Admins = signalCliGroupEntry.Admins + groupEntry.InviteLink = signalCliGroupEntry.GroupInviteLink + + groupEntries = append(groupEntries, groupEntry) + } + + return groupEntries, nil +} + +func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) { + expandedGroupEntries, err := s.GetGroupsExpanded(number) + if err != nil { + return []GroupEntry{}, err + } + + groupEntries := []GroupEntry{} + for _, expandedGroupEntry := range expandedGroupEntries { + groupEntry := GroupEntry{InternalId: expandedGroupEntry.InternalId, Name: expandedGroupEntry.Name, + Id: expandedGroupEntry.Id, Blocked: expandedGroupEntry.Blocked, Description: expandedGroupEntry.Description, + Permissions: expandedGroupEntry.Permissions, InviteLink: expandedGroupEntry.InviteLink} members := []string{} - for _, val := range signalCliGroupEntry.Members { + for _, val := range expandedGroupEntry.Members { identifier := val.Number if identifier == "" { identifier = val.Uuid @@ -1349,28 +1386,28 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) { } groupEntry.Members = members - pendingMembers := []string{} - for _, val := range signalCliGroupEntry.PendingMembers { + pendingInvites := []string{} + for _, val := range expandedGroupEntry.PendingInvites { identifier := val.Number if identifier == "" { identifier = val.Uuid } - pendingMembers = append(pendingMembers, identifier) + pendingInvites = append(pendingInvites, identifier) } - groupEntry.PendingInvites = pendingMembers + groupEntry.PendingInvites = pendingInvites - requestingMembers := []string{} - for _, val := range signalCliGroupEntry.RequestingMembers { + pendingRequests := []string{} + for _, val := range expandedGroupEntry.PendingRequests { identifier := val.Number if identifier == "" { identifier = val.Uuid } - requestingMembers = append(requestingMembers, identifier) + pendingRequests = append(pendingRequests, identifier) } - groupEntry.PendingRequests = requestingMembers + groupEntry.PendingRequests = pendingRequests admins := []string{} - for _, val := range signalCliGroupEntry.Admins { + for _, val := range expandedGroupEntry.Admins { identifier := val.Number if identifier == "" { identifier = val.Uuid @@ -1379,8 +1416,6 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) { } groupEntry.Admins = admins - groupEntry.InviteLink = signalCliGroupEntry.GroupInviteLink - groupEntries = append(groupEntries, groupEntry) } @@ -1404,6 +1439,23 @@ func (s *SignalClient) GetGroup(number string, groupId string) (*GroupEntry, err return nil, nil } +func (s *SignalClient) GetGroupExpanded(number string, groupId string) (*ExpandedGroupEntry, error) { + groupEntry := ExpandedGroupEntry{} + groups, err := s.GetGroupsExpanded(number) + if err != nil { + return nil, err + } + + for _, group := range groups { + if group.Id == groupId { + groupEntry = group + return &groupEntry, nil + } + } + + return nil, nil +} + func (s *SignalClient) GetAvatar(number string, id string, avatarType AvatarType) ([]byte, error) { var err error var rawData string diff --git a/src/docs/docs.go b/src/docs/docs.go index 8137eb7..e46b0dd 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -917,6 +917,12 @@ const docTemplate = `{ "name": "number", "in": "path", "required": true + }, + { + "type": "boolean", + "description": "Expand the response to show more details (default: false)", + "name": "expand", + "in": "query" } ], "responses": { @@ -1010,6 +1016,12 @@ const docTemplate = `{ "name": "groupid", "in": "path", "required": true + }, + { + "type": "boolean", + "description": "Expand the response to show more details (default: false)", + "name": "expand", + "in": "query" } ], "responses": { diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 5337561..8ab740d 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -914,6 +914,12 @@ "name": "number", "in": "path", "required": true + }, + { + "type": "boolean", + "description": "Expand the response to show more details (default: false)", + "name": "expand", + "in": "query" } ], "responses": { @@ -1007,6 +1013,12 @@ "name": "groupid", "in": "path", "required": true + }, + { + "type": "boolean", + "description": "Expand the response to show more details (default: false)", + "name": "expand", + "in": "query" } ], "responses": { diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index f1638c7..b189493 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -1163,6 +1163,10 @@ paths: name: number required: true type: string + - description: 'Expand the response to show more details (default: false)' + in: query + name: expand + type: boolean produces: - application/json responses: @@ -1254,6 +1258,10 @@ paths: name: groupid required: true type: string + - description: 'Expand the response to show more details (default: false)' + in: query + name: expand + type: boolean produces: - application/json responses: