Skip to content

Commit 4fd87d9

Browse files
authored
Merge pull request #13 from linuxfoundation/andrest50/meeting-attachments
[LFXV2-712] Add handlers for meeting and past meeting attachment access messages
2 parents a6a4305 + c4e179c commit 4fd87d9

File tree

6 files changed

+267
-0
lines changed

6 files changed

+267
-0
lines changed

CLAUDE.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,23 @@ Simple meeting UID string.
139139
}
140140
```
141141

142+
### Meeting Attachment Update Message (`lfx.update_access.meeting_attachment`)
143+
144+
```json
145+
{
146+
"uid": "attachment-123",
147+
"meeting_uid": "meeting-456"
148+
}
149+
```
150+
151+
### Meeting Attachment Delete Message (`lfx.delete_access.meeting_attachment`)
152+
153+
```text
154+
attachment-123
155+
```
156+
157+
Simple attachment UID string.
158+
142159
### Past Meeting Recording Update Message (`lfx.update_access.past_meeting_recording`)
143160

144161
```json
@@ -255,6 +272,23 @@ When a participant is removed:
255272
1. Removes all their relations from the past meeting
256273
2. Removes their viewer access from all artifacts (regardless of artifact_visibility)
257274

275+
### Past Meeting Attachment Update Message (`lfx.update_access.past_meeting_attachment`)
276+
277+
```json
278+
{
279+
"uid": "attachment-123",
280+
"past_meeting_uid": "past-meeting-456"
281+
}
282+
```
283+
284+
### Past Meeting Attachment Delete Message (`lfx.delete_access.past_meeting_attachment`)
285+
286+
```text
287+
attachment-123
288+
```
289+
290+
Simple attachment UID string.
291+
258292
## Testing
259293

260294
### Running Tests

handler_meeting.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,97 @@ func (h *HandlerService) meetingRegistrantPutHandler(message INatsMsg) error {
330330
func (h *HandlerService) meetingRegistrantRemoveHandler(message INatsMsg) error {
331331
return h.processRegistrantMessage(message, registrantRemove)
332332
}
333+
334+
type meetingAttachmentStub struct {
335+
UID string `json:"uid"`
336+
MeetingUID string `json:"meeting_uid"`
337+
}
338+
339+
// buildMeetingAttachmentTuples builds all of the tuples for a meeting attachment object.
340+
func (h *HandlerService) buildMeetingAttachmentTuples(
341+
object string,
342+
attachment *meetingAttachmentStub,
343+
) ([]client.ClientTupleKey, error) {
344+
tuples := h.fgaService.NewTupleKeySlice(1)
345+
346+
// Add the meeting relation to associate this attachment with its meeting
347+
if attachment.MeetingUID != "" {
348+
tuples = append(
349+
tuples,
350+
h.fgaService.TupleKey(constants.ObjectTypeMeeting+attachment.MeetingUID, constants.RelationMeeting, object),
351+
)
352+
}
353+
354+
return tuples, nil
355+
}
356+
357+
// meetingAttachmentUpdateAccessHandler handles meeting attachment access control updates.
358+
func (h *HandlerService) meetingAttachmentUpdateAccessHandler(message INatsMsg) error {
359+
ctx := context.Background()
360+
361+
logger.With("message", string(message.Data())).InfoContext(ctx, "handling meeting attachment access control update")
362+
363+
// Parse the event data.
364+
attachment := new(meetingAttachmentStub)
365+
var err error
366+
err = json.Unmarshal(message.Data(), attachment)
367+
if err != nil {
368+
logger.With(errKey, err).ErrorContext(ctx, "event data parse error")
369+
return err
370+
}
371+
372+
// Validate required fields.
373+
if attachment.UID == "" {
374+
logger.ErrorContext(ctx, "meeting attachment UID not found")
375+
return errors.New("meeting attachment UID not found")
376+
}
377+
if attachment.MeetingUID == "" {
378+
logger.ErrorContext(ctx, "meeting UID not found")
379+
return errors.New("meeting UID not found")
380+
}
381+
382+
object := constants.ObjectTypeMeetingAttachment + attachment.UID
383+
384+
// Build a list of tuples to sync.
385+
//
386+
// It is important that all tuples that should exist with respect to the meeting attachment object
387+
// should be added to this tuples list because when SyncObjectTuples is called, it will delete
388+
// all tuples that are not in the tuples list parameter.
389+
tuples, err := h.buildMeetingAttachmentTuples(object, attachment)
390+
if err != nil {
391+
logger.With(errKey, err, "object", object).ErrorContext(ctx, "failed to build meeting attachment tuples")
392+
return err
393+
}
394+
395+
tuplesWrites, tuplesDeletes, err := h.fgaService.SyncObjectTuples(ctx, object, tuples)
396+
if err != nil {
397+
logger.With(errKey, err, "tuples", tuples, "object", object).ErrorContext(ctx, "failed to sync tuples")
398+
return err
399+
}
400+
401+
logger.With(
402+
"tuples", tuples,
403+
"object", object,
404+
"writes", tuplesWrites,
405+
"deletes", tuplesDeletes,
406+
).InfoContext(ctx, "synced tuples")
407+
408+
if message.Reply() != "" {
409+
// Send a reply if an inbox was provided.
410+
if err = message.Respond([]byte("OK")); err != nil {
411+
logger.With(errKey, err).WarnContext(ctx, "failed to send reply")
412+
return err
413+
}
414+
415+
logger.With("object", object).InfoContext(ctx, "sent meeting attachment access control update response")
416+
}
417+
418+
return nil
419+
}
420+
421+
// meetingAttachmentDeleteAccessHandler handles deleting all tuples for a meeting attachment object.
422+
//
423+
// This should happen when a meeting attachment is deleted.
424+
func (h *HandlerService) meetingAttachmentDeleteAccessHandler(message INatsMsg) error {
425+
return h.processDeleteAllAccessMessage(message, constants.ObjectTypeMeetingAttachment, "meeting attachment")
426+
}

handler_past_meeting.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,3 +496,104 @@ func (h *HandlerService) pastMeetingParticipantPutHandler(message INatsMsg) erro
496496
func (h *HandlerService) pastMeetingParticipantRemoveHandler(message INatsMsg) error {
497497
return h.processPastMeetingParticipantMessage(message, pastMeetingParticipantRemove)
498498
}
499+
500+
type pastMeetingAttachmentStub struct {
501+
UID string `json:"uid"`
502+
PastMeetingUID string `json:"past_meeting_uid"`
503+
}
504+
505+
// buildPastMeetingAttachmentTuples builds all of the tuples for a past meeting attachment object.
506+
func (h *HandlerService) buildPastMeetingAttachmentTuples(
507+
object string,
508+
attachment *pastMeetingAttachmentStub,
509+
) ([]client.ClientTupleKey, error) {
510+
tuples := h.fgaService.NewTupleKeySlice(1)
511+
512+
// Add the past_meeting relation to associate this attachment with its past meeting
513+
if attachment.PastMeetingUID != "" {
514+
tuples = append(
515+
tuples,
516+
h.fgaService.TupleKey(
517+
constants.ObjectTypePastMeeting+attachment.PastMeetingUID,
518+
constants.RelationPastMeeting,
519+
object,
520+
),
521+
)
522+
}
523+
524+
return tuples, nil
525+
}
526+
527+
// pastMeetingAttachmentUpdateAccessHandler handles past meeting attachment access control updates.
528+
func (h *HandlerService) pastMeetingAttachmentUpdateAccessHandler(message INatsMsg) error {
529+
ctx := context.Background()
530+
531+
logger.With("message", string(message.Data())).InfoContext(
532+
ctx,
533+
"handling past meeting attachment access control update",
534+
)
535+
536+
// Parse the event data.
537+
attachment := new(pastMeetingAttachmentStub)
538+
var err error
539+
err = json.Unmarshal(message.Data(), attachment)
540+
if err != nil {
541+
logger.With(errKey, err).ErrorContext(ctx, "event data parse error")
542+
return err
543+
}
544+
545+
// Validate required fields.
546+
if attachment.UID == "" {
547+
logger.ErrorContext(ctx, "past meeting attachment UID not found")
548+
return errors.New("past meeting attachment UID not found")
549+
}
550+
if attachment.PastMeetingUID == "" {
551+
logger.ErrorContext(ctx, "past meeting UID not found")
552+
return errors.New("past meeting UID not found")
553+
}
554+
555+
object := constants.ObjectTypePastMeetingAttachment + attachment.UID
556+
557+
// Build a list of tuples to sync.
558+
//
559+
// It is important that all tuples that should exist with respect to the past meeting attachment object
560+
// should be added to this tuples list because when SyncObjectTuples is called, it will delete
561+
// all tuples that are not in the tuples list parameter.
562+
tuples, err := h.buildPastMeetingAttachmentTuples(object, attachment)
563+
if err != nil {
564+
logger.With(errKey, err, "object", object).ErrorContext(ctx, "failed to build past meeting attachment tuples")
565+
return err
566+
}
567+
568+
tuplesWrites, tuplesDeletes, err := h.fgaService.SyncObjectTuples(ctx, object, tuples)
569+
if err != nil {
570+
logger.With(errKey, err, "tuples", tuples, "object", object).ErrorContext(ctx, "failed to sync tuples")
571+
return err
572+
}
573+
574+
logger.With(
575+
"tuples", tuples,
576+
"object", object,
577+
"writes", tuplesWrites,
578+
"deletes", tuplesDeletes,
579+
).InfoContext(ctx, "synced tuples")
580+
581+
if message.Reply() != "" {
582+
// Send a reply if an inbox was provided.
583+
if err = message.Respond([]byte("OK")); err != nil {
584+
logger.With(errKey, err).WarnContext(ctx, "failed to send reply")
585+
return err
586+
}
587+
588+
logger.With("object", object).InfoContext(ctx, "sent past meeting attachment access control update response")
589+
}
590+
591+
return nil
592+
}
593+
594+
// pastMeetingAttachmentDeleteAccessHandler handles deleting all tuples for a past meeting attachment object.
595+
//
596+
// This should happen when a past meeting attachment is deleted.
597+
func (h *HandlerService) pastMeetingAttachmentDeleteAccessHandler(message INatsMsg) error {
598+
return h.processDeleteAllAccessMessage(message, constants.ObjectTypePastMeetingAttachment, "past meeting attachment")
599+
}

main.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,16 @@ func createQueueSubscriptions(handlerService HandlerService) error {
340340
handler: handlerService.meetingRegistrantRemoveHandler,
341341
description: "meeting registrant remove",
342342
},
343+
{
344+
subject: constants.MeetingAttachmentUpdateAccessSubject,
345+
handler: handlerService.meetingAttachmentUpdateAccessHandler,
346+
description: "meeting attachment update access",
347+
},
348+
{
349+
subject: constants.MeetingAttachmentDeleteAccessSubject,
350+
handler: handlerService.meetingAttachmentDeleteAccessHandler,
351+
description: "meeting attachment delete access",
352+
},
343353
{
344354
subject: constants.PastMeetingUpdateAccessSubject,
345355
handler: handlerService.pastMeetingUpdateAccessHandler,
@@ -405,6 +415,16 @@ func createQueueSubscriptions(handlerService HandlerService) error {
405415
handler: handlerService.pastMeetingSummaryUpdateAccessHandler,
406416
description: "past meeting summary update access",
407417
},
418+
{
419+
subject: constants.PastMeetingAttachmentUpdateAccessSubject,
420+
handler: handlerService.pastMeetingAttachmentUpdateAccessHandler,
421+
description: "past meeting attachment update access",
422+
},
423+
{
424+
subject: constants.PastMeetingAttachmentDeleteAccessSubject,
425+
handler: handlerService.pastMeetingAttachmentDeleteAccessHandler,
426+
description: "past meeting attachment delete access",
427+
},
408428
}
409429

410430
// Subscribe to each subject using the helper function

pkg/constants/fga.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ const (
3636
ObjectTypeCommittee = "committee:"
3737
ObjectTypeTeam = "team:"
3838
ObjectTypeMeeting = "meeting:"
39+
ObjectTypeMeetingAttachment = "meeting_attachment:"
3940
ObjectTypePastMeeting = "past_meeting:"
41+
ObjectTypePastMeetingAttachment = "past_meeting_attachment:"
4042
ObjectTypePastMeetingRecording = "past_meeting_recording:"
4143
ObjectTypePastMeetingTranscript = "past_meeting_transcript:"
4244
ObjectTypePastMeetingSummary = "past_meeting_summary:"

pkg/constants/nats.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ const (
3939
// The subject is of the form: lfx.remove_registrant.meeting
4040
MeetingRegistrantRemoveSubject = "lfx.remove_registrant.meeting"
4141

42+
// MeetingAttachmentUpdateAccessSubject is the subject for the meeting attachment access control updates.
43+
// The subject is of the form: lfx.update_access.meeting_attachment
44+
MeetingAttachmentUpdateAccessSubject = "lfx.update_access.meeting_attachment"
45+
46+
// MeetingAttachmentDeleteAccessSubject is the subject for the meeting attachment access control deletion.
47+
// The subject is of the form: lfx.delete_access.meeting_attachment
48+
MeetingAttachmentDeleteAccessSubject = "lfx.delete_access.meeting_attachment"
49+
4250
// PastMeetingUpdateAccessSubject is the subject for the past meeting access control updates.
4351
// The subject is of the form: lfx.update_access.past_meeting
4452
PastMeetingUpdateAccessSubject = "lfx.update_access.past_meeting"
@@ -90,6 +98,14 @@ const (
9098
// PastMeetingSummaryUpdateAccessSubject is the subject for the past meeting summary access control updates.
9199
// The subject is of the form: lfx.update_access.past_meeting_summary
92100
PastMeetingSummaryUpdateAccessSubject = "lfx.update_access.past_meeting_summary"
101+
102+
// PastMeetingAttachmentUpdateAccessSubject is the subject for the past meeting attachment access control updates.
103+
// The subject is of the form: lfx.update_access.past_meeting_attachment
104+
PastMeetingAttachmentUpdateAccessSubject = "lfx.update_access.past_meeting_attachment"
105+
106+
// PastMeetingAttachmentDeleteAccessSubject is the subject for the past meeting attachment access control deletion.
107+
// The subject is of the form: lfx.delete_access.past_meeting_attachment
108+
PastMeetingAttachmentDeleteAccessSubject = "lfx.delete_access.past_meeting_attachment"
93109
)
94110

95111
// NATS queue subjects that the FGA sync service handles messages about.

0 commit comments

Comments
 (0)