diff --git a/charts/lfx-v2-indexer-service/Chart.yaml b/charts/lfx-v2-indexer-service/Chart.yaml index 79a5c53..160b8fe 100644 --- a/charts/lfx-v2-indexer-service/Chart.yaml +++ b/charts/lfx-v2-indexer-service/Chart.yaml @@ -6,5 +6,5 @@ apiVersion: v2 name: lfx-v2-indexer-service description: LFX Platform V2 Indexer Service chart type: application -version: 0.4.10 +version: 0.4.11 appVersion: "latest" diff --git a/internal/domain/services/indexer_service.go b/internal/domain/services/indexer_service.go index 7204203..22bab90 100644 --- a/internal/domain/services/indexer_service.go +++ b/internal/domain/services/indexer_service.go @@ -98,6 +98,7 @@ func NewIndexerService( enrichers.NewV1MeetingEnricher(), enrichers.NewV1PastMeetingEnricher(), enrichers.NewV1MeetingRegistrantEnricher(), + enrichers.NewV1MeetingRSVPEnricher(), enrichers.NewV1PastMeetingParticipantEnricher(), enrichers.NewV1PastMeetingRecordingEnricher(), enrichers.NewV1PastMeetingTranscriptEnricher(), diff --git a/internal/enrichers/v1_meeting_rsvp_enricher.go b/internal/enrichers/v1_meeting_rsvp_enricher.go new file mode 100644 index 0000000..f31d2f8 --- /dev/null +++ b/internal/enrichers/v1_meeting_rsvp_enricher.go @@ -0,0 +1,134 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +// Package enrichers provides data enrichment functionality for different object types. +package enrichers + +import ( + "fmt" + "regexp" + "strings" + + "github.com/linuxfoundation/lfx-v2-indexer-service/internal/domain/contracts" + "github.com/linuxfoundation/lfx-v2-indexer-service/pkg/constants" +) + +// V1MeetingRSVPEnricher handles V1 meeting-RSVP-specific enrichment logic +type V1MeetingRSVPEnricher struct { + defaultEnricher Enricher +} + +// ObjectType returns the object type this enricher handles. +func (e *V1MeetingRSVPEnricher) ObjectType() string { + return e.defaultEnricher.ObjectType() +} + +// EnrichData enriches V1 meeting-RSVP-specific data +func (e *V1MeetingRSVPEnricher) EnrichData(body *contracts.TransactionBody, transaction *contracts.LFXTransaction) error { + return e.defaultEnricher.EnrichData(body, transaction) +} + +// setAccessControl provides V1 meeting-RSVP-specific access control logic +func (e *V1MeetingRSVPEnricher) setAccessControl(body *contracts.TransactionBody, data map[string]any, objectType, objectID string) { + meetingLevelPermission := func(data map[string]any) string { + if value, ok := data["meeting_id"]; ok { + if meetingUID, ok := value.(string); ok { + return fmt.Sprintf("%s:%s", constants.ObjectTypeV1Meeting, meetingUID) + } + } + return fmt.Sprintf("%s:%s", objectType, objectID) + } + + // Build access control values + var accessObject, accessRelation string + var historyObject, historyRelation string + + // Set access control with V1 meeting-RSVP-specific logic + // Only apply defaults when fields are completely missing from data + if accessCheckObject, ok := data["accessCheckObject"].(string); ok { + // Field exists in data (even if empty) - use data value + accessObject = accessCheckObject + } else if _, exists := data["accessCheckObject"]; !exists { + // Field doesn't exist in data - use computed default with objectType prefix + accessObject = meetingLevelPermission(data) + } + // If field exists but is not a string, leave empty (no override) + + if accessCheckRelation, ok := data["accessCheckRelation"].(string); ok { + accessRelation = accessCheckRelation + } else if _, exists := data["accessCheckRelation"]; !exists { + accessRelation = "auditor" + } + + if historyCheckObject, ok := data["historyCheckObject"].(string); ok { + historyObject = historyCheckObject + } else if _, exists := data["historyCheckObject"]; !exists { + historyObject = meetingLevelPermission(data) + } + + if historyCheckRelation, ok := data["historyCheckRelation"].(string); ok { + historyRelation = historyCheckRelation + } else if _, exists := data["historyCheckRelation"]; !exists { + historyRelation = "writer" + } + + // Assign to body fields (deprecated fields) + body.AccessCheckObject = accessObject + body.AccessCheckRelation = accessRelation + body.HistoryCheckObject = historyObject + body.HistoryCheckRelation = historyRelation + + // Build and assign the query strings + if accessObject != "" && accessRelation != "" { + body.AccessCheckQuery = contracts.JoinFgaQuery(accessObject, accessRelation) + } + if historyObject != "" && historyRelation != "" { + body.HistoryCheckQuery = contracts.JoinFgaQuery(historyObject, historyRelation) + } +} + +// extractSortName extracts the sort name from the V1 meeting RSVP data +func (e *V1MeetingRSVPEnricher) extractSortName(data map[string]any) string { + if value, ok := data["email"]; ok { + if strValue, isString := value.(string); isString && strValue != "" { + return strings.TrimSpace(strValue) + } + } + return "" +} + +// extractNameAndAliases extracts the name and aliases from the V1 meeting RSVP data +func (e *V1MeetingRSVPEnricher) extractNameAndAliases(data map[string]any) []string { + var nameAndAliases []string + seen := make(map[string]bool) // Deduplicate names + + // Compile regex pattern for name-like fields + aliasRegex := regexp.MustCompile(`(?i)^(username|email)$`) + + // Collect all name-like fields using regex pattern + for key, value := range data { + if aliasRegex.MatchString(key) { + if strValue, ok := value.(string); ok && strValue != "" { + trimmed := strings.TrimSpace(strValue) + if trimmed != "" && !seen[trimmed] { + nameAndAliases = append(nameAndAliases, trimmed) + seen[trimmed] = true + } + } + } + } + + return nameAndAliases +} + +// NewV1MeetingRSVPEnricher creates a new V1 meeting RSVP enricher +func NewV1MeetingRSVPEnricher() Enricher { + enricher := &V1MeetingRSVPEnricher{} + enricher.defaultEnricher = newDefaultEnricher( + constants.ObjectTypeV1MeetingRSVP, + WithAccessControl(enricher.setAccessControl), + WithNameAndAliases(enricher.extractNameAndAliases), + WithSortName(enricher.extractSortName), + ) + return enricher +} diff --git a/pkg/constants/messaging.go b/pkg/constants/messaging.go index c832fa9..d21cc10 100644 --- a/pkg/constants/messaging.go +++ b/pkg/constants/messaging.go @@ -66,6 +66,7 @@ const ( ObjectTypeV1Meeting = "v1_meeting" ObjectTypeV1PastMeeting = "v1_past_meeting" ObjectTypeV1MeetingRegistrant = "v1_meeting_registrant" + ObjectTypeV1MeetingRSVP = "v1_meeting_rsvp" ObjectTypeV1PastMeetingParticipant = "v1_past_meeting_participant" ObjectTypeV1PastMeetingRecording = "v1_past_meeting_recording" ObjectTypeV1PastMeetingTranscript = "v1_past_meeting_transcript"