package services import ( "context" "encoding/json" "fmt" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/sns" ) var CatalogueClient Catalogue // CatalogueVideo is what cms announces when a video becomes ready. It is a // published contract — every subscriber of the catalogue topic decodes it — // so the JSON names here are as much an API as the ones in internal/api, and // changing one is a change every subscriber sees. // // Categories are named rather than numbered on purpose: the ids are cms's // own, and a consumer holding a name needs nothing from cms to make sense // of it. type CatalogueVideo struct { VideoID string `json:"videoId"` Title string `json:"title"` PlaybackURL string `json:"playbackUrl"` Categories []string `json:"categories"` } type Catalogue interface { AnnounceVideo(ctx context.Context, video CatalogueVideo) error } type CatalogueConcrete struct { SNSClient *sns.Client TopicARN string } // AnnounceVideo publishes one video to the catalogue topic, which fans it out // to whatever has subscribed. Delivery is at-least-once: the caller // republishes when a later step fails, so a subscriber has to tolerate seeing // the same video twice. // // A topic delivers only to the subscriptions that exist at the moment it // publishes, so a subscriber that wants the backlog needs its queue in place // before the announcement, not after. func (svc CatalogueConcrete) AnnounceVideo(ctx context.Context, video CatalogueVideo) error { // A nil slice marshals to null, which would make "has no categories" and // "categories unknown" indistinguishable to a reader. An empty list says // the first, which is what an unfiled video means. if video.Categories == nil { video.Categories = []string{} } body, err := json.Marshal(video) if err != nil { return err } _, err = svc.SNSClient.Publish(ctx, &sns.PublishInput{ TopicArn: aws.String(svc.TopicARN), Message: aws.String(string(body)), }) return err } // AssertSuccessfulConnection verifies the topic exists and is readable with // the configured credentials. It panics on failure: a service that cannot // announce would transcode videos the catalogue never hears about. func (svc CatalogueConcrete) AssertSuccessfulConnection(ctx context.Context) { if _, err := svc.SNSClient.GetTopicAttributes(ctx, &sns.GetTopicAttributesInput{ TopicArn: aws.String(svc.TopicARN), }); err != nil { panic(fmt.Errorf("sns: cannot connect to catalogue topic %q: %w", svc.TopicARN, err)) } }