package service import ( "context" "strings" "cmr-backend/internal/store/postgres" ) const ( LaunchSourceEventCurrentRelease = "event_current_release" LaunchModeManifestRelease = "manifest_release" ) type ResolvedReleaseView struct { LaunchMode string `json:"launchMode"` Source string `json:"source"` EventID string `json:"eventId"` ReleaseID string `json:"releaseId"` ConfigLabel string `json:"configLabel"` ManifestURL string `json:"manifestUrl"` ManifestChecksumSha256 *string `json:"manifestChecksumSha256,omitempty"` RouteCode *string `json:"routeCode,omitempty"` } type RuntimeSummaryView struct { RuntimeBindingID string `json:"runtimeBindingId"` PlaceID string `json:"placeId"` PlaceName *string `json:"placeName,omitempty"` MapID string `json:"mapId"` MapName *string `json:"mapName,omitempty"` TileReleaseID string `json:"tileReleaseId"` CourseSetID string `json:"courseSetId"` CourseVariantID string `json:"courseVariantId"` CourseVariantName *string `json:"courseVariantName,omitempty"` RouteCode *string `json:"routeCode,omitempty"` } type PresentationSummaryView struct { PresentationID string `json:"presentationId"` Name *string `json:"name,omitempty"` PresentationType *string `json:"presentationType,omitempty"` TemplateKey *string `json:"templateKey,omitempty"` Version *string `json:"version,omitempty"` } type ContentBundleSummaryView struct { ContentBundleID string `json:"contentBundleId"` Name *string `json:"name,omitempty"` BundleType *string `json:"bundleType,omitempty"` Version *string `json:"version,omitempty"` EntryURL *string `json:"entryUrl,omitempty"` AssetRootURL *string `json:"assetRootUrl,omitempty"` } func buildResolvedReleaseFromEvent(event *postgres.Event, source string) *ResolvedReleaseView { if event == nil || event.CurrentReleasePubID == nil || event.ConfigLabel == nil || event.ManifestURL == nil { return nil } return &ResolvedReleaseView{ LaunchMode: LaunchModeManifestRelease, Source: source, EventID: event.PublicID, ReleaseID: *event.CurrentReleasePubID, ConfigLabel: *event.ConfigLabel, ManifestURL: *event.ManifestURL, ManifestChecksumSha256: event.ManifestChecksum, RouteCode: event.RouteCode, } } func buildRuntimeSummaryFromEvent(event *postgres.Event) *RuntimeSummaryView { if event == nil || event.RuntimeBindingID == nil || event.PlacePublicID == nil || event.MapAssetPublicID == nil || event.TileReleasePublicID == nil || event.CourseSetPublicID == nil || event.CourseVariantID == nil { return nil } return &RuntimeSummaryView{ RuntimeBindingID: *event.RuntimeBindingID, PlaceID: *event.PlacePublicID, PlaceName: event.PlaceName, MapID: *event.MapAssetPublicID, MapName: event.MapAssetName, TileReleaseID: *event.TileReleasePublicID, CourseSetID: *event.CourseSetPublicID, CourseVariantID: *event.CourseVariantID, CourseVariantName: event.CourseVariantName, RouteCode: firstNonNilString(event.RuntimeRouteCode, event.RouteCode), } } func buildRuntimeSummaryFromRelease(release *postgres.EventRelease) *RuntimeSummaryView { if release == nil || release.RuntimeBindingID == nil || release.PlacePublicID == nil || release.MapAssetPublicID == nil || release.TileReleaseID == nil || release.CourseSetID == nil || release.CourseVariantID == nil { return nil } return &RuntimeSummaryView{ RuntimeBindingID: *release.RuntimeBindingID, PlaceID: *release.PlacePublicID, PlaceName: release.PlaceName, MapID: *release.MapAssetPublicID, MapName: release.MapAssetName, TileReleaseID: *release.TileReleaseID, CourseSetID: *release.CourseSetID, CourseVariantID: *release.CourseVariantID, CourseVariantName: release.CourseVariantName, RouteCode: firstNonNilString(release.RuntimeRouteCode, release.RouteCode), } } func buildPresentationSummaryFromEvent(event *postgres.Event) *PresentationSummaryView { if event == nil || event.PresentationID == nil { return nil } return &PresentationSummaryView{ PresentationID: *event.PresentationID, Name: event.PresentationName, PresentationType: event.PresentationType, } } func buildPresentationSummaryFromRelease(release *postgres.EventRelease) *PresentationSummaryView { if release == nil || release.PresentationID == nil { return nil } return &PresentationSummaryView{ PresentationID: *release.PresentationID, Name: release.PresentationName, PresentationType: release.PresentationType, } } func buildContentBundleSummaryFromEvent(event *postgres.Event) *ContentBundleSummaryView { if event == nil || event.ContentBundleID == nil { return nil } return &ContentBundleSummaryView{ ContentBundleID: *event.ContentBundleID, Name: event.ContentBundleName, EntryURL: event.ContentEntryURL, AssetRootURL: event.ContentAssetRootURL, } } func buildContentBundleSummaryFromRelease(release *postgres.EventRelease) *ContentBundleSummaryView { if release == nil || release.ContentBundleID == nil { return nil } return &ContentBundleSummaryView{ ContentBundleID: *release.ContentBundleID, Name: release.ContentBundleName, EntryURL: release.ContentEntryURL, AssetRootURL: release.ContentAssetURL, } } func buildResolvedReleaseFromSession(session *postgres.Session, source string) *ResolvedReleaseView { if session == nil || session.ReleasePublicID == nil || session.ConfigLabel == nil || session.ManifestURL == nil { return nil } view := &ResolvedReleaseView{ LaunchMode: LaunchModeManifestRelease, Source: source, ReleaseID: *session.ReleasePublicID, ConfigLabel: *session.ConfigLabel, ManifestURL: *session.ManifestURL, ManifestChecksumSha256: session.ManifestChecksum, RouteCode: session.RouteCode, } if session.EventPublicID != nil { view.EventID = *session.EventPublicID } return view } func loadPresentationSummaryByPublicID(ctx context.Context, store *postgres.Store, publicID *string) (*PresentationSummaryView, error) { if store == nil || publicID == nil || strings.TrimSpace(*publicID) == "" { return nil, nil } record, err := store.GetEventPresentationByPublicID(ctx, strings.TrimSpace(*publicID)) if err != nil { return nil, err } if record == nil { return nil, nil } return buildPresentationSummaryFromRecord(record) } func loadContentBundleSummaryByPublicID(ctx context.Context, store *postgres.Store, publicID *string) (*ContentBundleSummaryView, error) { if store == nil || publicID == nil || strings.TrimSpace(*publicID) == "" { return nil, nil } record, err := store.GetContentBundleByPublicID(ctx, strings.TrimSpace(*publicID)) if err != nil { return nil, err } if record == nil { return nil, nil } return buildContentBundleSummaryFromRecord(record) } func buildPresentationSummaryFromRecord(record *postgres.EventPresentation) (*PresentationSummaryView, error) { if record == nil { return nil, nil } summary := &PresentationSummaryView{ PresentationID: record.PublicID, Name: &record.Name, PresentationType: &record.PresentationType, } schema, err := decodeJSONObject(record.SchemaJSON) if err != nil { return nil, err } summary.TemplateKey = readStringField(schema, "templateKey") summary.Version = readStringField(schema, "version") return summary, nil } func buildContentBundleSummaryFromRecord(record *postgres.ContentBundle) (*ContentBundleSummaryView, error) { if record == nil { return nil, nil } summary := &ContentBundleSummaryView{ ContentBundleID: record.PublicID, Name: &record.Name, EntryURL: record.EntryURL, AssetRootURL: record.AssetRootURL, } metadata, err := decodeJSONObject(record.MetadataJSON) if err != nil { return nil, err } summary.BundleType = readStringField(metadata, "bundleType") summary.Version = readStringField(metadata, "version") return summary, nil } func readStringField(object map[string]any, key string) *string { if object == nil { return nil } value, ok := object[key] if !ok { return nil } text, ok := value.(string) if !ok { return nil } text = strings.TrimSpace(text) if text == "" { return nil } return &text } func firstNonNilString(values ...*string) *string { for _, value := range values { if value != nil { return value } } return nil }