Add meeting display fields
This commit is contained in:
+104
-4
@@ -1926,8 +1926,9 @@ func serveIMMeetingList(wrt http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
items := make([]map[string]any, 0, len(meetings))
|
items := make([]map[string]any, 0, len(meetings))
|
||||||
|
userSummaries := meetingUserSummaries(principal.TenantID, meetings)
|
||||||
for i := range meetings {
|
for i := range meetings {
|
||||||
items = append(items, meetingPayload(&meetings[i], principal.Uid))
|
items = append(items, meetingPayloadWithUserSummaries(&meetings[i], principal.Uid, userSummaries))
|
||||||
}
|
}
|
||||||
writeIMSuccess(wrt, req, map[string]any{
|
writeIMSuccess(wrt, req, map[string]any{
|
||||||
"items": items,
|
"items": items,
|
||||||
@@ -7273,22 +7274,37 @@ func organizationSearchPayloads(items []types.OrganizationSearchItem) []map[stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func meetingPayload(meeting *types.Meeting, viewer types.Uid) map[string]any {
|
func meetingPayload(meeting *types.Meeting, viewer types.Uid) map[string]any {
|
||||||
participants := meetingParticipantPayloads(meeting.Participants)
|
return meetingPayloadWithUserSummaries(meeting, viewer, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func meetingPayloadWithUserSummaries(meeting *types.Meeting, viewer types.Uid, userSummaries map[types.Uid]map[string]any) map[string]any {
|
||||||
|
participants := meetingParticipantPayloadsWithSummaries(meeting.Participants, userSummaries)
|
||||||
|
organizer := meetingUserSummary(meeting.OrganizerID, userSummaries)
|
||||||
|
startAt := meeting.StartAt.UTC()
|
||||||
|
endAt := meeting.EndAt.UTC()
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
"meeting_id": meeting.MeetingID,
|
"meeting_id": meeting.MeetingID,
|
||||||
"title": meeting.Title,
|
"title": meeting.Title,
|
||||||
"organizer_id": meeting.OrganizerID.UserId(),
|
"organizer_id": meeting.OrganizerID.UserId(),
|
||||||
"start_at": meeting.StartAt.UTC().Format(time.RFC3339Nano),
|
"organizer": organizer,
|
||||||
"end_at": meeting.EndAt.UTC().Format(time.RFC3339Nano),
|
"start_at": startAt.Format(time.RFC3339Nano),
|
||||||
|
"end_at": endAt.Format(time.RFC3339Nano),
|
||||||
|
"meeting_time": meetingTimeRange(startAt, endAt),
|
||||||
|
"time_range": meetingTimeRange(startAt, endAt),
|
||||||
"location": meeting.Location,
|
"location": meeting.Location,
|
||||||
"state": meeting.State,
|
"state": meeting.State,
|
||||||
"reminder_minutes": meeting.ReminderMinutes,
|
"reminder_minutes": meeting.ReminderMinutes,
|
||||||
"version": meeting.Version,
|
"version": meeting.Version,
|
||||||
"participants": participants,
|
"participants": participants,
|
||||||
|
"participant_count": len(participants),
|
||||||
|
"participant_names": meetingParticipantNames(participants),
|
||||||
"permissions": meetingPermissionsPayload(meeting, viewer),
|
"permissions": meetingPermissionsPayload(meeting, viewer),
|
||||||
"created_at": meeting.CreatedAt.UTC().Format(time.RFC3339Nano),
|
"created_at": meeting.CreatedAt.UTC().Format(time.RFC3339Nano),
|
||||||
"updated_at": meeting.UpdatedAt.UTC().Format(time.RFC3339Nano),
|
"updated_at": meeting.UpdatedAt.UTC().Format(time.RFC3339Nano),
|
||||||
}
|
}
|
||||||
|
if displayName, ok := organizer["display_name"]; ok {
|
||||||
|
data["organizer_display_name"] = displayName
|
||||||
|
}
|
||||||
if meeting.CanceledAt != nil {
|
if meeting.CanceledAt != nil {
|
||||||
data["canceled_at"] = meeting.CanceledAt.UTC().Format(time.RFC3339Nano)
|
data["canceled_at"] = meeting.CanceledAt.UTC().Format(time.RFC3339Nano)
|
||||||
}
|
}
|
||||||
@@ -7302,12 +7318,24 @@ func meetingPayload(meeting *types.Meeting, viewer types.Uid) map[string]any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func meetingParticipantPayloads(participants []types.MeetingParticipant) []map[string]any {
|
func meetingParticipantPayloads(participants []types.MeetingParticipant) []map[string]any {
|
||||||
|
return meetingParticipantPayloadsWithSummaries(participants, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func meetingParticipantPayloadsWithSummaries(participants []types.MeetingParticipant, userSummaries map[types.Uid]map[string]any) []map[string]any {
|
||||||
items := make([]map[string]any, 0, len(participants))
|
items := make([]map[string]any, 0, len(participants))
|
||||||
for _, participant := range participants {
|
for _, participant := range participants {
|
||||||
item := map[string]any{
|
item := map[string]any{
|
||||||
"user_id": participant.UserID.UserId(),
|
"user_id": participant.UserID.UserId(),
|
||||||
"response": participant.Response,
|
"response": participant.Response,
|
||||||
}
|
}
|
||||||
|
if summary := meetingUserSummary(participant.UserID, userSummaries); summary != nil {
|
||||||
|
if displayName, ok := summary["display_name"]; ok {
|
||||||
|
item["display_name"] = displayName
|
||||||
|
}
|
||||||
|
if avatar, ok := summary["avatar_file_id"]; ok {
|
||||||
|
item["avatar_file_id"] = avatar
|
||||||
|
}
|
||||||
|
}
|
||||||
if participant.ResponseAt != nil {
|
if participant.ResponseAt != nil {
|
||||||
item["response_at"] = participant.ResponseAt.UTC().Format(time.RFC3339Nano)
|
item["response_at"] = participant.ResponseAt.UTC().Format(time.RFC3339Nano)
|
||||||
}
|
}
|
||||||
@@ -7316,6 +7344,78 @@ func meetingParticipantPayloads(participants []types.MeetingParticipant) []map[s
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func meetingUserSummaries(tenantID types.TenantID, meetings []types.Meeting) map[types.Uid]map[string]any {
|
||||||
|
if store.Users == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
seen := make(map[types.Uid]bool)
|
||||||
|
uids := make([]types.Uid, 0, len(meetings)*2)
|
||||||
|
for _, meeting := range meetings {
|
||||||
|
if !meeting.OrganizerID.IsZero() && !seen[meeting.OrganizerID] {
|
||||||
|
seen[meeting.OrganizerID] = true
|
||||||
|
uids = append(uids, meeting.OrganizerID)
|
||||||
|
}
|
||||||
|
for _, participant := range meeting.Participants {
|
||||||
|
if !participant.UserID.IsZero() && !seen[participant.UserID] {
|
||||||
|
seen[participant.UserID] = true
|
||||||
|
uids = append(uids, participant.UserID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(uids) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
users, err := store.Users.GetAll(tenantID, uids...)
|
||||||
|
if err != nil {
|
||||||
|
logs.Warn.Printf("im http: failed to load meeting user summaries: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
summaries := make(map[types.Uid]map[string]any, len(users))
|
||||||
|
for i := range users {
|
||||||
|
uid := users[i].Uid()
|
||||||
|
if uid.IsZero() {
|
||||||
|
uid = types.ParseUid(users[i].Id)
|
||||||
|
}
|
||||||
|
if !uid.IsZero() {
|
||||||
|
summaries[uid] = userSummaryPayload(&users[i], "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return summaries
|
||||||
|
}
|
||||||
|
|
||||||
|
func meetingUserSummary(uid types.Uid, summaries map[types.Uid]map[string]any) map[string]any {
|
||||||
|
if uid.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if summary, ok := summaries[uid]; ok && summary != nil {
|
||||||
|
return summary
|
||||||
|
}
|
||||||
|
return map[string]any{
|
||||||
|
"user_id": uid.UserId(),
|
||||||
|
"display_name": uid.UserId(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func meetingParticipantNames(participants []map[string]any) []string {
|
||||||
|
names := make([]string, 0, len(participants))
|
||||||
|
for _, participant := range participants {
|
||||||
|
if name := strings.TrimSpace(fmt.Sprint(participant["display_name"])); name != "" {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func meetingTimeRange(startAt, endAt time.Time) string {
|
||||||
|
if startAt.IsZero() || endAt.IsZero() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if startAt.Format("2006-01-02") == endAt.Format("2006-01-02") {
|
||||||
|
return startAt.Format("2006-01-02 15:04") + "-" + endAt.Format("15:04")
|
||||||
|
}
|
||||||
|
return startAt.Format("2006-01-02 15:04") + "-" + endAt.Format("2006-01-02 15:04")
|
||||||
|
}
|
||||||
|
|
||||||
func meetingParticipantUIDs(meeting *types.Meeting) []types.Uid {
|
func meetingParticipantUIDs(meeting *types.Meeting) []types.Uid {
|
||||||
if meeting == nil {
|
if meeting == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+18
-1
@@ -2374,6 +2374,7 @@ func TestServeIMMeetingsList(t *testing.T) {
|
|||||||
viewer := types.Uid(42)
|
viewer := types.Uid(42)
|
||||||
expectIMBearer(t, ctrl, "token-1", 7, viewer)
|
expectIMBearer(t, ctrl, "token-1", 7, viewer)
|
||||||
oldMeetings := store.Meetings
|
oldMeetings := store.Meetings
|
||||||
|
oldUsers := store.Users
|
||||||
now := time.Now().UTC().Round(time.Millisecond)
|
now := time.Now().UTC().Round(time.Millisecond)
|
||||||
meetings := &testMeetingsStore{list: []types.Meeting{{
|
meetings := &testMeetingsStore{list: []types.Meeting{{
|
||||||
MeetingID: "mtg_test",
|
MeetingID: "mtg_test",
|
||||||
@@ -2391,7 +2392,15 @@ func TestServeIMMeetingsList(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}}}
|
}}}
|
||||||
store.Meetings = meetings
|
store.Meetings = meetings
|
||||||
defer func() { store.Meetings = oldMeetings }()
|
users := mock_store.NewMockUsersPersistenceInterface(ctrl)
|
||||||
|
organizer := types.User{Public: map[string]any{"fn": "mzb"}}
|
||||||
|
organizer.SetUid(viewer)
|
||||||
|
users.EXPECT().GetAll(types.TenantID(7), viewer).Return([]types.User{organizer}, nil)
|
||||||
|
store.Users = users
|
||||||
|
defer func() {
|
||||||
|
store.Meetings = oldMeetings
|
||||||
|
store.Users = oldUsers
|
||||||
|
}()
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/v1/im/meetings?limit=20&cursor=c1", nil)
|
req := httptest.NewRequest(http.MethodGet, "/v1/im/meetings?limit=20&cursor=c1", nil)
|
||||||
req.Header.Set("Authorization", "Bearer token-1")
|
req.Header.Set("Authorization", "Bearer token-1")
|
||||||
@@ -2409,6 +2418,14 @@ func TestServeIMMeetingsList(t *testing.T) {
|
|||||||
if items[0].(map[string]any)["meeting_id"] != "mtg_test" || data["has_more"] != true {
|
if items[0].(map[string]any)["meeting_id"] != "mtg_test" || data["has_more"] != true {
|
||||||
t.Fatalf("unexpected meeting payload: %#v", data)
|
t.Fatalf("unexpected meeting payload: %#v", data)
|
||||||
}
|
}
|
||||||
|
item := items[0].(map[string]any)
|
||||||
|
if item["organizer_display_name"] != "mzb" || item["participant_count"].(float64) != 1 {
|
||||||
|
t.Fatalf("missing meeting display fields: %#v", item)
|
||||||
|
}
|
||||||
|
participants := item["participants"].([]any)
|
||||||
|
if participants[0].(map[string]any)["display_name"] != "mzb" {
|
||||||
|
t.Fatalf("missing participant display name: %#v", participants)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServeIMMeetingMyResponse(t *testing.T) {
|
func TestServeIMMeetingMyResponse(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user