From 9b8f3a3a5d8ac2dca49a911effdcc5a238260c48 Mon Sep 17 00:00:00 2001 From: tan Date: Sat, 18 Jul 2026 21:31:41 -0600 Subject: [PATCH 1/5] feat: add watch sync provider capability --- README.md | 15 + pkg/pluginproto/silo/plugin/v1/common.pb.go | 116 +- .../silo/plugin/v1/watch_sync_provider.pb.go | 2229 +++++++++++++++++ .../plugin/v1/watch_sync_provider_grpc.pb.go | 357 +++ pkg/pluginsdk/capability/capability.go | 26 +- pkg/pluginsdk/convert/convert.go | 23 + .../convert/watch_sync_provider_test.go | 33 + pkg/pluginsdk/manifest/manifest.go | 45 +- .../manifest/watch_sync_provider_test.go | 55 + pkg/pluginsdk/runtime/runtime.go | 30 +- .../runtime/watch_sync_provider_test.go | 27 + proto/silo/plugin/v1/common.proto | 6 + .../silo/plugin/v1/watch_sync_provider.proto | 241 ++ 13 files changed, 3129 insertions(+), 74 deletions(-) create mode 100644 pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go create mode 100644 pkg/pluginproto/silo/plugin/v1/watch_sync_provider_grpc.pb.go create mode 100644 pkg/pluginsdk/convert/watch_sync_provider_test.go create mode 100644 pkg/pluginsdk/manifest/watch_sync_provider_test.go create mode 100644 pkg/pluginsdk/runtime/watch_sync_provider_test.go create mode 100644 proto/silo/plugin/v1/watch_sync_provider.proto diff --git a/README.md b/README.md index af8a539..9699420 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ The SDK ships protobuf contracts for every capability the host understands: - `http_routes.v1` - `request_router.v1` - `scan_source.v1` +- `watch_sync_provider.v1` - `audiobook_backend.v1` - `ebook_backend.v1` @@ -121,6 +122,20 @@ err = host.CallPluginJSON(ctx, runtimehost.CallPluginJSONRequest{ The `auth_provider.v1` capability also exposes OAuth-flow RPCs (`InitAuthorize`, `ExchangeCode`, `RefreshSession`) for plugins that wrap external identity providers. +## Watch sync providers + +`watch_sync_provider.v1` lets external plugins participate in Silo's host-owned +watch-provider pipeline. The host owns encrypted per-profile credentials, +OAuth state, durable desired-state events, retries, ordering, and +reconciliation. Plugins are stateless protocol adapters: they receive secrets +only for the duration of an RPC, map rich movie/episode identity to an upstream +service, and return typed apply or retry outcomes. + +Watch-sync plugins must not persist or log credentials, authorization codes, +provider flow state, or secret configuration. `ApplyEvents` is an at-least-once +contract; plugins must treat `event_id` as stable across retries and implement +convergent desired-state updates rather than increments. + ## Scan sources The `scan_source.v1` capability is for Autoscan providers. The host owns the diff --git a/pkg/pluginproto/silo/plugin/v1/common.pb.go b/pkg/pluginproto/silo/plugin/v1/common.pb.go index 3b1114c..68289e8 100644 --- a/pkg/pluginproto/silo/plugin/v1/common.pb.go +++ b/pkg/pluginproto/silo/plugin/v1/common.pb.go @@ -972,9 +972,13 @@ type CapabilityDescriptor struct { // Optional URL to a logo for this auth provider, rendered in silo's // login UI as the icon for a "Sign in with X" button. Plugin-served path // (e.g., /api/v1/plugins//assets/whmcs-logo.svg). - IconUrl string `protobuf:"bytes,9,opt,name=icon_url,json=iconUrl,proto3" json:"icon_url,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + IconUrl string `protobuf:"bytes,9,opt,name=icon_url,json=iconUrl,proto3" json:"icon_url,omitempty"` + // Typed watch-provider contract metadata. Only meaningful for capabilities + // of type "watch_sync_provider.v1". Keeping this typed lets the host build + // connection and authorization UI without launching the plugin. + WatchSyncProvider *WatchSyncProviderDescriptor `protobuf:"bytes,10,opt,name=watch_sync_provider,json=watchSyncProvider,proto3" json:"watch_sync_provider,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *CapabilityDescriptor) Reset() { @@ -1070,6 +1074,13 @@ func (x *CapabilityDescriptor) GetIconUrl() string { return "" } +func (x *CapabilityDescriptor) GetWatchSyncProvider() *WatchSyncProviderDescriptor { + if x != nil { + return x.WatchSyncProvider + } + return nil +} + // PluginPresentation describes a plugin for server operators and links them to // its publisher, source, support, and release history. These values are // self-declared by the plugin and never determine catalog approval. @@ -1588,7 +1599,7 @@ var File_silo_plugin_v1_common_proto protoreflect.FileDescriptor const file_silo_plugin_v1_common_proto_rawDesc = "" + "\n" + - "\x1bsilo/plugin/v1/common.proto\x12\x0esilo.plugin.v1\x1a\x1cgoogle/protobuf/struct.proto\"7\n" + + "\x1bsilo/plugin/v1/common.proto\x12\x0esilo.plugin.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a(silo/plugin/v1/watch_sync_provider.proto\"7\n" + "\x11SupportedPlatform\x12\x0e\n" + "\x02os\x18\x01 \x01(\tR\x02os\x12\x12\n" + "\x04arch\x18\x02 \x01(\tR\x04arch\"\xd9\x01\n" + @@ -1665,7 +1676,7 @@ const file_silo_plugin_v1_common_proto_rawDesc = "" + "\rPackagedAsset\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\x12!\n" + "\fcontent_type\x18\x02 \x01(\tR\vcontentType\x12\x1c\n" + - "\tintegrity\x18\x03 \x01(\tR\tintegrity\"\xd7\x02\n" + + "\tintegrity\x18\x03 \x01(\tR\tintegrity\"\xb4\x03\n" + "\x14CapabilityDescriptor\x12\x12\n" + "\x04type\x18\x01 \x01(\tR\x04type\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12!\n" + @@ -1676,7 +1687,9 @@ const file_silo_plugin_v1_common_proto_rawDesc = "" + "\bmetadata\x18\a \x01(\v2\x17.google.protobuf.StructR\bmetadata\x12\x1d\n" + "\n" + "auth_modes\x18\b \x03(\tR\tauthModes\x12\x19\n" + - "\bicon_url\x18\t \x01(\tR\aiconUrl\"\xa2\x03\n" + + "\bicon_url\x18\t \x01(\tR\aiconUrl\x12[\n" + + "\x13watch_sync_provider\x18\n" + + " \x01(\v2+.silo.plugin.v1.WatchSyncProviderDescriptorR\x11watchSyncProvider\"\xa2\x03\n" + "\x12PluginPresentation\x12!\n" + "\fdisplay_name\x18\x01 \x01(\tR\vdisplayName\x12\x18\n" + "\asummary\x18\x02 \x01(\tR\asummary\x121\n" + @@ -1746,29 +1759,30 @@ func file_silo_plugin_v1_common_proto_rawDescGZIP() []byte { var file_silo_plugin_v1_common_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_silo_plugin_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_silo_plugin_v1_common_proto_goTypes = []any{ - (AdminFormControl)(0), // 0: silo.plugin.v1.AdminFormControl - (*SupportedPlatform)(nil), // 1: silo.plugin.v1.SupportedPlatform - (*ConfigSchema)(nil), // 2: silo.plugin.v1.ConfigSchema - (*AdminFormOption)(nil), // 3: silo.plugin.v1.AdminFormOption - (*AdminFormField)(nil), // 4: silo.plugin.v1.AdminFormField - (*AdminFormCondition)(nil), // 5: silo.plugin.v1.AdminFormCondition - (*AdminFormValidation)(nil), // 6: silo.plugin.v1.AdminFormValidation - (*AdminFormSection)(nil), // 7: silo.plugin.v1.AdminFormSection - (*AdminFormDescriptor)(nil), // 8: silo.plugin.v1.AdminFormDescriptor - (*ConfigEntry)(nil), // 9: silo.plugin.v1.ConfigEntry - (*HttpRouteDescriptor)(nil), // 10: silo.plugin.v1.HttpRouteDescriptor - (*PackagedAsset)(nil), // 11: silo.plugin.v1.PackagedAsset - (*CapabilityDescriptor)(nil), // 12: silo.plugin.v1.CapabilityDescriptor - (*PluginPresentation)(nil), // 13: silo.plugin.v1.PluginPresentation - (*PluginManifest)(nil), // 14: silo.plugin.v1.PluginManifest - (*GetManifestRequest)(nil), // 15: silo.plugin.v1.GetManifestRequest - (*GetManifestResponse)(nil), // 16: silo.plugin.v1.GetManifestResponse - (*ConfigureRequest)(nil), // 17: silo.plugin.v1.ConfigureRequest - (*ConfigureResponse)(nil), // 18: silo.plugin.v1.ConfigureResponse - (*BindHostBrokerRequest)(nil), // 19: silo.plugin.v1.BindHostBrokerRequest - (*BindHostBrokerResponse)(nil), // 20: silo.plugin.v1.BindHostBrokerResponse - (*structpb.Value)(nil), // 21: google.protobuf.Value - (*structpb.Struct)(nil), // 22: google.protobuf.Struct + (AdminFormControl)(0), // 0: silo.plugin.v1.AdminFormControl + (*SupportedPlatform)(nil), // 1: silo.plugin.v1.SupportedPlatform + (*ConfigSchema)(nil), // 2: silo.plugin.v1.ConfigSchema + (*AdminFormOption)(nil), // 3: silo.plugin.v1.AdminFormOption + (*AdminFormField)(nil), // 4: silo.plugin.v1.AdminFormField + (*AdminFormCondition)(nil), // 5: silo.plugin.v1.AdminFormCondition + (*AdminFormValidation)(nil), // 6: silo.plugin.v1.AdminFormValidation + (*AdminFormSection)(nil), // 7: silo.plugin.v1.AdminFormSection + (*AdminFormDescriptor)(nil), // 8: silo.plugin.v1.AdminFormDescriptor + (*ConfigEntry)(nil), // 9: silo.plugin.v1.ConfigEntry + (*HttpRouteDescriptor)(nil), // 10: silo.plugin.v1.HttpRouteDescriptor + (*PackagedAsset)(nil), // 11: silo.plugin.v1.PackagedAsset + (*CapabilityDescriptor)(nil), // 12: silo.plugin.v1.CapabilityDescriptor + (*PluginPresentation)(nil), // 13: silo.plugin.v1.PluginPresentation + (*PluginManifest)(nil), // 14: silo.plugin.v1.PluginManifest + (*GetManifestRequest)(nil), // 15: silo.plugin.v1.GetManifestRequest + (*GetManifestResponse)(nil), // 16: silo.plugin.v1.GetManifestResponse + (*ConfigureRequest)(nil), // 17: silo.plugin.v1.ConfigureRequest + (*ConfigureResponse)(nil), // 18: silo.plugin.v1.ConfigureResponse + (*BindHostBrokerRequest)(nil), // 19: silo.plugin.v1.BindHostBrokerRequest + (*BindHostBrokerResponse)(nil), // 20: silo.plugin.v1.BindHostBrokerResponse + (*structpb.Value)(nil), // 21: google.protobuf.Value + (*structpb.Struct)(nil), // 22: google.protobuf.Struct + (*WatchSyncProviderDescriptor)(nil), // 23: silo.plugin.v1.WatchSyncProviderDescriptor } var file_silo_plugin_v1_common_proto_depIdxs = []int32{ 8, // 0: silo.plugin.v1.ConfigSchema.admin_form:type_name -> silo.plugin.v1.AdminFormDescriptor @@ -1783,27 +1797,28 @@ var file_silo_plugin_v1_common_proto_depIdxs = []int32{ 22, // 9: silo.plugin.v1.ConfigEntry.value:type_name -> google.protobuf.Struct 2, // 10: silo.plugin.v1.CapabilityDescriptor.config_schema:type_name -> silo.plugin.v1.ConfigSchema 22, // 11: silo.plugin.v1.CapabilityDescriptor.metadata:type_name -> google.protobuf.Struct - 1, // 12: silo.plugin.v1.PluginManifest.supported_platforms:type_name -> silo.plugin.v1.SupportedPlatform - 12, // 13: silo.plugin.v1.PluginManifest.capabilities:type_name -> silo.plugin.v1.CapabilityDescriptor - 2, // 14: silo.plugin.v1.PluginManifest.global_config_schema:type_name -> silo.plugin.v1.ConfigSchema - 2, // 15: silo.plugin.v1.PluginManifest.user_config_schema:type_name -> silo.plugin.v1.ConfigSchema - 10, // 16: silo.plugin.v1.PluginManifest.http_routes:type_name -> silo.plugin.v1.HttpRouteDescriptor - 11, // 17: silo.plugin.v1.PluginManifest.assets:type_name -> silo.plugin.v1.PackagedAsset - 22, // 18: silo.plugin.v1.PluginManifest.metadata:type_name -> google.protobuf.Struct - 13, // 19: silo.plugin.v1.PluginManifest.presentation:type_name -> silo.plugin.v1.PluginPresentation - 14, // 20: silo.plugin.v1.GetManifestResponse.manifest:type_name -> silo.plugin.v1.PluginManifest - 9, // 21: silo.plugin.v1.ConfigureRequest.config:type_name -> silo.plugin.v1.ConfigEntry - 15, // 22: silo.plugin.v1.Runtime.GetManifest:input_type -> silo.plugin.v1.GetManifestRequest - 17, // 23: silo.plugin.v1.Runtime.Configure:input_type -> silo.plugin.v1.ConfigureRequest - 19, // 24: silo.plugin.v1.Runtime.BindHostBroker:input_type -> silo.plugin.v1.BindHostBrokerRequest - 16, // 25: silo.plugin.v1.Runtime.GetManifest:output_type -> silo.plugin.v1.GetManifestResponse - 18, // 26: silo.plugin.v1.Runtime.Configure:output_type -> silo.plugin.v1.ConfigureResponse - 20, // 27: silo.plugin.v1.Runtime.BindHostBroker:output_type -> silo.plugin.v1.BindHostBrokerResponse - 25, // [25:28] is the sub-list for method output_type - 22, // [22:25] is the sub-list for method input_type - 22, // [22:22] is the sub-list for extension type_name - 22, // [22:22] is the sub-list for extension extendee - 0, // [0:22] is the sub-list for field type_name + 23, // 12: silo.plugin.v1.CapabilityDescriptor.watch_sync_provider:type_name -> silo.plugin.v1.WatchSyncProviderDescriptor + 1, // 13: silo.plugin.v1.PluginManifest.supported_platforms:type_name -> silo.plugin.v1.SupportedPlatform + 12, // 14: silo.plugin.v1.PluginManifest.capabilities:type_name -> silo.plugin.v1.CapabilityDescriptor + 2, // 15: silo.plugin.v1.PluginManifest.global_config_schema:type_name -> silo.plugin.v1.ConfigSchema + 2, // 16: silo.plugin.v1.PluginManifest.user_config_schema:type_name -> silo.plugin.v1.ConfigSchema + 10, // 17: silo.plugin.v1.PluginManifest.http_routes:type_name -> silo.plugin.v1.HttpRouteDescriptor + 11, // 18: silo.plugin.v1.PluginManifest.assets:type_name -> silo.plugin.v1.PackagedAsset + 22, // 19: silo.plugin.v1.PluginManifest.metadata:type_name -> google.protobuf.Struct + 13, // 20: silo.plugin.v1.PluginManifest.presentation:type_name -> silo.plugin.v1.PluginPresentation + 14, // 21: silo.plugin.v1.GetManifestResponse.manifest:type_name -> silo.plugin.v1.PluginManifest + 9, // 22: silo.plugin.v1.ConfigureRequest.config:type_name -> silo.plugin.v1.ConfigEntry + 15, // 23: silo.plugin.v1.Runtime.GetManifest:input_type -> silo.plugin.v1.GetManifestRequest + 17, // 24: silo.plugin.v1.Runtime.Configure:input_type -> silo.plugin.v1.ConfigureRequest + 19, // 25: silo.plugin.v1.Runtime.BindHostBroker:input_type -> silo.plugin.v1.BindHostBrokerRequest + 16, // 26: silo.plugin.v1.Runtime.GetManifest:output_type -> silo.plugin.v1.GetManifestResponse + 18, // 27: silo.plugin.v1.Runtime.Configure:output_type -> silo.plugin.v1.ConfigureResponse + 20, // 28: silo.plugin.v1.Runtime.BindHostBroker:output_type -> silo.plugin.v1.BindHostBrokerResponse + 26, // [26:29] is the sub-list for method output_type + 23, // [23:26] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name } func init() { file_silo_plugin_v1_common_proto_init() } @@ -1811,6 +1826,7 @@ func file_silo_plugin_v1_common_proto_init() { if File_silo_plugin_v1_common_proto != nil { return } + file_silo_plugin_v1_watch_sync_provider_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go new file mode 100644 index 0000000..65f25fb --- /dev/null +++ b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go @@ -0,0 +1,2229 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: silo/plugin/v1/watch_sync_provider.proto + +package pluginv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + structpb "google.golang.org/protobuf/types/known/structpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type WatchSyncAuthMethod int32 + +const ( + WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_UNSPECIFIED WatchSyncAuthMethod = 0 + WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE WatchSyncAuthMethod = 1 + WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_API_KEY WatchSyncAuthMethod = 2 +) + +// Enum value maps for WatchSyncAuthMethod. +var ( + WatchSyncAuthMethod_name = map[int32]string{ + 0: "WATCH_SYNC_AUTH_METHOD_UNSPECIFIED", + 1: "WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE", + 2: "WATCH_SYNC_AUTH_METHOD_API_KEY", + } + WatchSyncAuthMethod_value = map[string]int32{ + "WATCH_SYNC_AUTH_METHOD_UNSPECIFIED": 0, + "WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE": 1, + "WATCH_SYNC_AUTH_METHOD_API_KEY": 2, + } +) + +func (x WatchSyncAuthMethod) Enum() *WatchSyncAuthMethod { + p := new(WatchSyncAuthMethod) + *p = x + return p +} + +func (x WatchSyncAuthMethod) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WatchSyncAuthMethod) Descriptor() protoreflect.EnumDescriptor { + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[0].Descriptor() +} + +func (WatchSyncAuthMethod) Type() protoreflect.EnumType { + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[0] +} + +func (x WatchSyncAuthMethod) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WatchSyncAuthMethod.Descriptor instead. +func (WatchSyncAuthMethod) EnumDescriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{0} +} + +type WatchSyncOperation int32 + +const ( + WatchSyncOperation_WATCH_SYNC_OPERATION_UNSPECIFIED WatchSyncOperation = 0 + WatchSyncOperation_WATCH_SYNC_OPERATION_MARK_WATCHED WatchSyncOperation = 1 + WatchSyncOperation_WATCH_SYNC_OPERATION_MARK_UNWATCHED WatchSyncOperation = 2 +) + +// Enum value maps for WatchSyncOperation. +var ( + WatchSyncOperation_name = map[int32]string{ + 0: "WATCH_SYNC_OPERATION_UNSPECIFIED", + 1: "WATCH_SYNC_OPERATION_MARK_WATCHED", + 2: "WATCH_SYNC_OPERATION_MARK_UNWATCHED", + } + WatchSyncOperation_value = map[string]int32{ + "WATCH_SYNC_OPERATION_UNSPECIFIED": 0, + "WATCH_SYNC_OPERATION_MARK_WATCHED": 1, + "WATCH_SYNC_OPERATION_MARK_UNWATCHED": 2, + } +) + +func (x WatchSyncOperation) Enum() *WatchSyncOperation { + p := new(WatchSyncOperation) + *p = x + return p +} + +func (x WatchSyncOperation) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WatchSyncOperation) Descriptor() protoreflect.EnumDescriptor { + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[1].Descriptor() +} + +func (WatchSyncOperation) Type() protoreflect.EnumType { + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[1] +} + +func (x WatchSyncOperation) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WatchSyncOperation.Descriptor instead. +func (WatchSyncOperation) EnumDescriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{1} +} + +type WatchSyncOrigin int32 + +const ( + WatchSyncOrigin_WATCH_SYNC_ORIGIN_UNSPECIFIED WatchSyncOrigin = 0 + WatchSyncOrigin_WATCH_SYNC_ORIGIN_PLAYBACK_COMPLETION WatchSyncOrigin = 1 + WatchSyncOrigin_WATCH_SYNC_ORIGIN_MANUAL WatchSyncOrigin = 2 + WatchSyncOrigin_WATCH_SYNC_ORIGIN_RECONCILIATION WatchSyncOrigin = 3 +) + +// Enum value maps for WatchSyncOrigin. +var ( + WatchSyncOrigin_name = map[int32]string{ + 0: "WATCH_SYNC_ORIGIN_UNSPECIFIED", + 1: "WATCH_SYNC_ORIGIN_PLAYBACK_COMPLETION", + 2: "WATCH_SYNC_ORIGIN_MANUAL", + 3: "WATCH_SYNC_ORIGIN_RECONCILIATION", + } + WatchSyncOrigin_value = map[string]int32{ + "WATCH_SYNC_ORIGIN_UNSPECIFIED": 0, + "WATCH_SYNC_ORIGIN_PLAYBACK_COMPLETION": 1, + "WATCH_SYNC_ORIGIN_MANUAL": 2, + "WATCH_SYNC_ORIGIN_RECONCILIATION": 3, + } +) + +func (x WatchSyncOrigin) Enum() *WatchSyncOrigin { + p := new(WatchSyncOrigin) + *p = x + return p +} + +func (x WatchSyncOrigin) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WatchSyncOrigin) Descriptor() protoreflect.EnumDescriptor { + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[2].Descriptor() +} + +func (WatchSyncOrigin) Type() protoreflect.EnumType { + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[2] +} + +func (x WatchSyncOrigin) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WatchSyncOrigin.Descriptor instead. +func (WatchSyncOrigin) EnumDescriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{2} +} + +type WatchSyncApplyStatus int32 + +const ( + WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_UNSPECIFIED WatchSyncApplyStatus = 0 + WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_APPLIED WatchSyncApplyStatus = 1 + WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_NO_CHANGE WatchSyncApplyStatus = 2 + WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_RETRY WatchSyncApplyStatus = 3 + WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_REJECTED WatchSyncApplyStatus = 4 +) + +// Enum value maps for WatchSyncApplyStatus. +var ( + WatchSyncApplyStatus_name = map[int32]string{ + 0: "WATCH_SYNC_APPLY_STATUS_UNSPECIFIED", + 1: "WATCH_SYNC_APPLY_STATUS_APPLIED", + 2: "WATCH_SYNC_APPLY_STATUS_NO_CHANGE", + 3: "WATCH_SYNC_APPLY_STATUS_RETRY", + 4: "WATCH_SYNC_APPLY_STATUS_REJECTED", + } + WatchSyncApplyStatus_value = map[string]int32{ + "WATCH_SYNC_APPLY_STATUS_UNSPECIFIED": 0, + "WATCH_SYNC_APPLY_STATUS_APPLIED": 1, + "WATCH_SYNC_APPLY_STATUS_NO_CHANGE": 2, + "WATCH_SYNC_APPLY_STATUS_RETRY": 3, + "WATCH_SYNC_APPLY_STATUS_REJECTED": 4, + } +) + +func (x WatchSyncApplyStatus) Enum() *WatchSyncApplyStatus { + p := new(WatchSyncApplyStatus) + *p = x + return p +} + +func (x WatchSyncApplyStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WatchSyncApplyStatus) Descriptor() protoreflect.EnumDescriptor { + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[3].Descriptor() +} + +func (WatchSyncApplyStatus) Type() protoreflect.EnumType { + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[3] +} + +func (x WatchSyncApplyStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WatchSyncApplyStatus.Descriptor instead. +func (WatchSyncApplyStatus) EnumDescriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{3} +} + +type WatchSyncFaultCode int32 + +const ( + WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_UNSPECIFIED WatchSyncFaultCode = 0 + WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_TEMPORARY WatchSyncFaultCode = 1 + WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_RATE_LIMITED WatchSyncFaultCode = 2 + WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_INVALID_CREDENTIAL WatchSyncFaultCode = 3 + WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_PERMISSION_DENIED WatchSyncFaultCode = 4 + WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_INVALID_REQUEST WatchSyncFaultCode = 5 + WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_PERMANENT WatchSyncFaultCode = 6 +) + +// Enum value maps for WatchSyncFaultCode. +var ( + WatchSyncFaultCode_name = map[int32]string{ + 0: "WATCH_SYNC_FAULT_CODE_UNSPECIFIED", + 1: "WATCH_SYNC_FAULT_CODE_TEMPORARY", + 2: "WATCH_SYNC_FAULT_CODE_RATE_LIMITED", + 3: "WATCH_SYNC_FAULT_CODE_INVALID_CREDENTIAL", + 4: "WATCH_SYNC_FAULT_CODE_PERMISSION_DENIED", + 5: "WATCH_SYNC_FAULT_CODE_INVALID_REQUEST", + 6: "WATCH_SYNC_FAULT_CODE_PERMANENT", + } + WatchSyncFaultCode_value = map[string]int32{ + "WATCH_SYNC_FAULT_CODE_UNSPECIFIED": 0, + "WATCH_SYNC_FAULT_CODE_TEMPORARY": 1, + "WATCH_SYNC_FAULT_CODE_RATE_LIMITED": 2, + "WATCH_SYNC_FAULT_CODE_INVALID_CREDENTIAL": 3, + "WATCH_SYNC_FAULT_CODE_PERMISSION_DENIED": 4, + "WATCH_SYNC_FAULT_CODE_INVALID_REQUEST": 5, + "WATCH_SYNC_FAULT_CODE_PERMANENT": 6, + } +) + +func (x WatchSyncFaultCode) Enum() *WatchSyncFaultCode { + p := new(WatchSyncFaultCode) + *p = x + return p +} + +func (x WatchSyncFaultCode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WatchSyncFaultCode) Descriptor() protoreflect.EnumDescriptor { + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[4].Descriptor() +} + +func (WatchSyncFaultCode) Type() protoreflect.EnumType { + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[4] +} + +func (x WatchSyncFaultCode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WatchSyncFaultCode.Descriptor instead. +func (WatchSyncFaultCode) EnumDescriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{4} +} + +type WatchSyncProviderDescriptor struct { + state protoimpl.MessageState `protogen:"open.v1"` + AuthMethods []WatchSyncAuthMethod `protobuf:"varint,1,rep,packed,name=auth_methods,json=authMethods,proto3,enum=silo.plugin.v1.WatchSyncAuthMethod" json:"auth_methods,omitempty"` + ExportWatched bool `protobuf:"varint,2,opt,name=export_watched,json=exportWatched,proto3" json:"export_watched,omitempty"` + ExportUnwatched bool `protobuf:"varint,3,opt,name=export_unwatched,json=exportUnwatched,proto3" json:"export_unwatched,omitempty"` + ImportWatched bool `protobuf:"varint,4,opt,name=import_watched,json=importWatched,proto3" json:"import_watched,omitempty"` + ImportProgress bool `protobuf:"varint,5,opt,name=import_progress,json=importProgress,proto3" json:"import_progress,omitempty"` + SupportedMediaTypes []string `protobuf:"bytes,6,rep,name=supported_media_types,json=supportedMediaTypes,proto3" json:"supported_media_types,omitempty"` + ExternalIdNamespaces []string `protobuf:"bytes,7,rep,name=external_id_namespaces,json=externalIdNamespaces,proto3" json:"external_id_namespaces,omitempty"` + MaxBatchSize int32 `protobuf:"varint,8,opt,name=max_batch_size,json=maxBatchSize,proto3" json:"max_batch_size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncProviderDescriptor) Reset() { + *x = WatchSyncProviderDescriptor{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncProviderDescriptor) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncProviderDescriptor) ProtoMessage() {} + +func (x *WatchSyncProviderDescriptor) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncProviderDescriptor.ProtoReflect.Descriptor instead. +func (*WatchSyncProviderDescriptor) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{0} +} + +func (x *WatchSyncProviderDescriptor) GetAuthMethods() []WatchSyncAuthMethod { + if x != nil { + return x.AuthMethods + } + return nil +} + +func (x *WatchSyncProviderDescriptor) GetExportWatched() bool { + if x != nil { + return x.ExportWatched + } + return false +} + +func (x *WatchSyncProviderDescriptor) GetExportUnwatched() bool { + if x != nil { + return x.ExportUnwatched + } + return false +} + +func (x *WatchSyncProviderDescriptor) GetImportWatched() bool { + if x != nil { + return x.ImportWatched + } + return false +} + +func (x *WatchSyncProviderDescriptor) GetImportProgress() bool { + if x != nil { + return x.ImportProgress + } + return false +} + +func (x *WatchSyncProviderDescriptor) GetSupportedMediaTypes() []string { + if x != nil { + return x.SupportedMediaTypes + } + return nil +} + +func (x *WatchSyncProviderDescriptor) GetExternalIdNamespaces() []string { + if x != nil { + return x.ExternalIdNamespaces + } + return nil +} + +func (x *WatchSyncProviderDescriptor) GetMaxBatchSize() int32 { + if x != nil { + return x.MaxBatchSize + } + return 0 +} + +type WatchSyncProviderConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Values map[string]string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // The host encrypts these values at rest and includes them only in RPCs that + // need them. They are never Runtime.Configure entries. + SecretValues map[string]string `protobuf:"bytes,2,rep,name=secret_values,json=secretValues,proto3" json:"secret_values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncProviderConfig) Reset() { + *x = WatchSyncProviderConfig{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncProviderConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncProviderConfig) ProtoMessage() {} + +func (x *WatchSyncProviderConfig) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncProviderConfig.ProtoReflect.Descriptor instead. +func (*WatchSyncProviderConfig) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{1} +} + +func (x *WatchSyncProviderConfig) GetValues() map[string]string { + if x != nil { + return x.Values + } + return nil +} + +func (x *WatchSyncProviderConfig) GetSecretValues() map[string]string { + if x != nil { + return x.SecretValues + } + return nil +} + +type WatchSyncCredentials struct { + state protoimpl.MessageState `protogen:"open.v1"` + AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"` + ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` + TokenType string `protobuf:"bytes,4,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` + Scopes []string `protobuf:"bytes,5,rep,name=scopes,proto3" json:"scopes,omitempty"` + // Provider-specific token fields. The host encrypts the credential as one + // opaque record and does not interpret these values. + SecretAttributes map[string]string `protobuf:"bytes,6,rep,name=secret_attributes,json=secretAttributes,proto3" json:"secret_attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncCredentials) Reset() { + *x = WatchSyncCredentials{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncCredentials) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncCredentials) ProtoMessage() {} + +func (x *WatchSyncCredentials) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncCredentials.ProtoReflect.Descriptor instead. +func (*WatchSyncCredentials) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{2} +} + +func (x *WatchSyncCredentials) GetAccessToken() string { + if x != nil { + return x.AccessToken + } + return "" +} + +func (x *WatchSyncCredentials) GetRefreshToken() string { + if x != nil { + return x.RefreshToken + } + return "" +} + +func (x *WatchSyncCredentials) GetExpiresAt() *timestamppb.Timestamp { + if x != nil { + return x.ExpiresAt + } + return nil +} + +func (x *WatchSyncCredentials) GetTokenType() string { + if x != nil { + return x.TokenType + } + return "" +} + +func (x *WatchSyncCredentials) GetScopes() []string { + if x != nil { + return x.Scopes + } + return nil +} + +func (x *WatchSyncCredentials) GetSecretAttributes() map[string]string { + if x != nil { + return x.SecretAttributes + } + return nil +} + +type WatchSyncAccount struct { + state protoimpl.MessageState `protogen:"open.v1"` + ExternalSubject string `protobuf:"bytes,1,opt,name=external_subject,json=externalSubject,proto3" json:"external_subject,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + AvatarUrl string `protobuf:"bytes,4,opt,name=avatar_url,json=avatarUrl,proto3" json:"avatar_url,omitempty"` + ProfileUrl string `protobuf:"bytes,5,opt,name=profile_url,json=profileUrl,proto3" json:"profile_url,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncAccount) Reset() { + *x = WatchSyncAccount{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncAccount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncAccount) ProtoMessage() {} + +func (x *WatchSyncAccount) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncAccount.ProtoReflect.Descriptor instead. +func (*WatchSyncAccount) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{3} +} + +func (x *WatchSyncAccount) GetExternalSubject() string { + if x != nil { + return x.ExternalSubject + } + return "" +} + +func (x *WatchSyncAccount) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *WatchSyncAccount) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *WatchSyncAccount) GetAvatarUrl() string { + if x != nil { + return x.AvatarUrl + } + return "" +} + +func (x *WatchSyncAccount) GetProfileUrl() string { + if x != nil { + return x.ProfileUrl + } + return "" +} + +type WatchSyncFault struct { + state protoimpl.MessageState `protogen:"open.v1"` + Code WatchSyncFaultCode `protobuf:"varint,1,opt,name=code,proto3,enum=silo.plugin.v1.WatchSyncFaultCode" json:"code,omitempty"` + // Safe for persistence and operator display. Must not contain credentials, + // authorization codes, provider response bodies, or other secrets. + SafeMessage string `protobuf:"bytes,2,opt,name=safe_message,json=safeMessage,proto3" json:"safe_message,omitempty"` + RetryAfter *durationpb.Duration `protobuf:"bytes,3,opt,name=retry_after,json=retryAfter,proto3" json:"retry_after,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncFault) Reset() { + *x = WatchSyncFault{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncFault) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncFault) ProtoMessage() {} + +func (x *WatchSyncFault) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncFault.ProtoReflect.Descriptor instead. +func (*WatchSyncFault) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{4} +} + +func (x *WatchSyncFault) GetCode() WatchSyncFaultCode { + if x != nil { + return x.Code + } + return WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_UNSPECIFIED +} + +func (x *WatchSyncFault) GetSafeMessage() string { + if x != nil { + return x.SafeMessage + } + return "" +} + +func (x *WatchSyncFault) GetRetryAfter() *durationpb.Duration { + if x != nil { + return x.RetryAfter + } + return nil +} + +type WatchSyncInitAuthorizeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + RedirectUri string `protobuf:"bytes,3,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"` + // Generated, stored, and validated by the host. + State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncInitAuthorizeRequest) Reset() { + *x = WatchSyncInitAuthorizeRequest{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncInitAuthorizeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncInitAuthorizeRequest) ProtoMessage() {} + +func (x *WatchSyncInitAuthorizeRequest) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncInitAuthorizeRequest.ProtoReflect.Descriptor instead. +func (*WatchSyncInitAuthorizeRequest) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{5} +} + +func (x *WatchSyncInitAuthorizeRequest) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncInitAuthorizeRequest) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncInitAuthorizeRequest) GetRedirectUri() string { + if x != nil { + return x.RedirectUri + } + return "" +} + +func (x *WatchSyncInitAuthorizeRequest) GetState() string { + if x != nil { + return x.State + } + return "" +} + +type WatchSyncInitAuthorizeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + AuthorizationUrl string `protobuf:"bytes,1,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"` + // Plugin-defined flow data. The host encrypts it until callback completion + // and never returns it through the public HTTP API. + ProviderState []byte `protobuf:"bytes,2,opt,name=provider_state,json=providerState,proto3" json:"provider_state,omitempty"` + Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncInitAuthorizeResponse) Reset() { + *x = WatchSyncInitAuthorizeResponse{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncInitAuthorizeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncInitAuthorizeResponse) ProtoMessage() {} + +func (x *WatchSyncInitAuthorizeResponse) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncInitAuthorizeResponse.ProtoReflect.Descriptor instead. +func (*WatchSyncInitAuthorizeResponse) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{6} +} + +func (x *WatchSyncInitAuthorizeResponse) GetAuthorizationUrl() string { + if x != nil { + return x.AuthorizationUrl + } + return "" +} + +func (x *WatchSyncInitAuthorizeResponse) GetProviderState() []byte { + if x != nil { + return x.ProviderState + } + return nil +} + +func (x *WatchSyncInitAuthorizeResponse) GetFault() *WatchSyncFault { + if x != nil { + return x.Fault + } + return nil +} + +type WatchSyncExchangeCodeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + RedirectUri string `protobuf:"bytes,3,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"` + AuthorizationCode string `protobuf:"bytes,4,opt,name=authorization_code,json=authorizationCode,proto3" json:"authorization_code,omitempty"` + ProviderState []byte `protobuf:"bytes,5,opt,name=provider_state,json=providerState,proto3" json:"provider_state,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncExchangeCodeRequest) Reset() { + *x = WatchSyncExchangeCodeRequest{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncExchangeCodeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncExchangeCodeRequest) ProtoMessage() {} + +func (x *WatchSyncExchangeCodeRequest) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncExchangeCodeRequest.ProtoReflect.Descriptor instead. +func (*WatchSyncExchangeCodeRequest) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{7} +} + +func (x *WatchSyncExchangeCodeRequest) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncExchangeCodeRequest) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncExchangeCodeRequest) GetRedirectUri() string { + if x != nil { + return x.RedirectUri + } + return "" +} + +func (x *WatchSyncExchangeCodeRequest) GetAuthorizationCode() string { + if x != nil { + return x.AuthorizationCode + } + return "" +} + +func (x *WatchSyncExchangeCodeRequest) GetProviderState() []byte { + if x != nil { + return x.ProviderState + } + return nil +} + +type WatchSyncExchangeAPIKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + // API key or manually-issued bearer token. The host does not persist this + // request field unless the plugin validates and returns it as credentials. + ApiKey string `protobuf:"bytes,3,opt,name=api_key,json=apiKey,proto3" json:"api_key,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncExchangeAPIKeyRequest) Reset() { + *x = WatchSyncExchangeAPIKeyRequest{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncExchangeAPIKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncExchangeAPIKeyRequest) ProtoMessage() {} + +func (x *WatchSyncExchangeAPIKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncExchangeAPIKeyRequest.ProtoReflect.Descriptor instead. +func (*WatchSyncExchangeAPIKeyRequest) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{8} +} + +func (x *WatchSyncExchangeAPIKeyRequest) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncExchangeAPIKeyRequest) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncExchangeAPIKeyRequest) GetApiKey() string { + if x != nil { + return x.ApiKey + } + return "" +} + +type WatchSyncRefreshCredentialsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncRefreshCredentialsRequest) Reset() { + *x = WatchSyncRefreshCredentialsRequest{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncRefreshCredentialsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncRefreshCredentialsRequest) ProtoMessage() {} + +func (x *WatchSyncRefreshCredentialsRequest) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncRefreshCredentialsRequest.ProtoReflect.Descriptor instead. +func (*WatchSyncRefreshCredentialsRequest) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{9} +} + +func (x *WatchSyncRefreshCredentialsRequest) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncRefreshCredentialsRequest) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncRefreshCredentialsRequest) GetCredentials() *WatchSyncCredentials { + if x != nil { + return x.Credentials + } + return nil +} + +type WatchSyncCredentialResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Credentials *WatchSyncCredentials `protobuf:"bytes,1,opt,name=credentials,proto3" json:"credentials,omitempty"` + Account *WatchSyncAccount `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"` + Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncCredentialResponse) Reset() { + *x = WatchSyncCredentialResponse{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncCredentialResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncCredentialResponse) ProtoMessage() {} + +func (x *WatchSyncCredentialResponse) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncCredentialResponse.ProtoReflect.Descriptor instead. +func (*WatchSyncCredentialResponse) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{10} +} + +func (x *WatchSyncCredentialResponse) GetCredentials() *WatchSyncCredentials { + if x != nil { + return x.Credentials + } + return nil +} + +func (x *WatchSyncCredentialResponse) GetAccount() *WatchSyncAccount { + if x != nil { + return x.Account + } + return nil +} + +func (x *WatchSyncCredentialResponse) GetFault() *WatchSyncFault { + if x != nil { + return x.Fault + } + return nil +} + +type WatchSyncGetAccountRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncGetAccountRequest) Reset() { + *x = WatchSyncGetAccountRequest{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncGetAccountRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncGetAccountRequest) ProtoMessage() {} + +func (x *WatchSyncGetAccountRequest) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncGetAccountRequest.ProtoReflect.Descriptor instead. +func (*WatchSyncGetAccountRequest) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{11} +} + +func (x *WatchSyncGetAccountRequest) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncGetAccountRequest) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncGetAccountRequest) GetCredentials() *WatchSyncCredentials { + if x != nil { + return x.Credentials + } + return nil +} + +type WatchSyncGetAccountResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Account *WatchSyncAccount `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + Fault *WatchSyncFault `protobuf:"bytes,2,opt,name=fault,proto3" json:"fault,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncGetAccountResponse) Reset() { + *x = WatchSyncGetAccountResponse{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncGetAccountResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncGetAccountResponse) ProtoMessage() {} + +func (x *WatchSyncGetAccountResponse) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncGetAccountResponse.ProtoReflect.Descriptor instead. +func (*WatchSyncGetAccountResponse) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{12} +} + +func (x *WatchSyncGetAccountResponse) GetAccount() *WatchSyncAccount { + if x != nil { + return x.Account + } + return nil +} + +func (x *WatchSyncGetAccountResponse) GetFault() *WatchSyncFault { + if x != nil { + return x.Fault + } + return nil +} + +type WatchSyncMedia struct { + state protoimpl.MessageState `protogen:"open.v1"` + MediaItemId string `protobuf:"bytes,1,opt,name=media_item_id,json=mediaItemId,proto3" json:"media_item_id,omitempty"` + MediaType string `protobuf:"bytes,2,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` + OriginalTitle string `protobuf:"bytes,4,opt,name=original_title,json=originalTitle,proto3" json:"original_title,omitempty"` + Year int32 `protobuf:"varint,5,opt,name=year,proto3" json:"year,omitempty"` + ExternalIds map[string]string `protobuf:"bytes,6,rep,name=external_ids,json=externalIds,proto3" json:"external_ids,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + SeriesMediaItemId string `protobuf:"bytes,7,opt,name=series_media_item_id,json=seriesMediaItemId,proto3" json:"series_media_item_id,omitempty"` + SeriesTitle string `protobuf:"bytes,8,opt,name=series_title,json=seriesTitle,proto3" json:"series_title,omitempty"` + SeriesYear int32 `protobuf:"varint,9,opt,name=series_year,json=seriesYear,proto3" json:"series_year,omitempty"` + SeriesExternalIds map[string]string `protobuf:"bytes,10,rep,name=series_external_ids,json=seriesExternalIds,proto3" json:"series_external_ids,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + SeasonNumber int32 `protobuf:"varint,11,opt,name=season_number,json=seasonNumber,proto3" json:"season_number,omitempty"` + EpisodeNumber int32 `protobuf:"varint,12,opt,name=episode_number,json=episodeNumber,proto3" json:"episode_number,omitempty"` + AbsoluteEpisodeNumber int32 `protobuf:"varint,13,opt,name=absolute_episode_number,json=absoluteEpisodeNumber,proto3" json:"absolute_episode_number,omitempty"` + SeriesEpisodeCount int32 `protobuf:"varint,14,opt,name=series_episode_count,json=seriesEpisodeCount,proto3" json:"series_episode_count,omitempty"` + Metadata *structpb.Struct `protobuf:"bytes,15,opt,name=metadata,proto3" json:"metadata,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncMedia) Reset() { + *x = WatchSyncMedia{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncMedia) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncMedia) ProtoMessage() {} + +func (x *WatchSyncMedia) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncMedia.ProtoReflect.Descriptor instead. +func (*WatchSyncMedia) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{13} +} + +func (x *WatchSyncMedia) GetMediaItemId() string { + if x != nil { + return x.MediaItemId + } + return "" +} + +func (x *WatchSyncMedia) GetMediaType() string { + if x != nil { + return x.MediaType + } + return "" +} + +func (x *WatchSyncMedia) GetTitle() string { + if x != nil { + return x.Title + } + return "" +} + +func (x *WatchSyncMedia) GetOriginalTitle() string { + if x != nil { + return x.OriginalTitle + } + return "" +} + +func (x *WatchSyncMedia) GetYear() int32 { + if x != nil { + return x.Year + } + return 0 +} + +func (x *WatchSyncMedia) GetExternalIds() map[string]string { + if x != nil { + return x.ExternalIds + } + return nil +} + +func (x *WatchSyncMedia) GetSeriesMediaItemId() string { + if x != nil { + return x.SeriesMediaItemId + } + return "" +} + +func (x *WatchSyncMedia) GetSeriesTitle() string { + if x != nil { + return x.SeriesTitle + } + return "" +} + +func (x *WatchSyncMedia) GetSeriesYear() int32 { + if x != nil { + return x.SeriesYear + } + return 0 +} + +func (x *WatchSyncMedia) GetSeriesExternalIds() map[string]string { + if x != nil { + return x.SeriesExternalIds + } + return nil +} + +func (x *WatchSyncMedia) GetSeasonNumber() int32 { + if x != nil { + return x.SeasonNumber + } + return 0 +} + +func (x *WatchSyncMedia) GetEpisodeNumber() int32 { + if x != nil { + return x.EpisodeNumber + } + return 0 +} + +func (x *WatchSyncMedia) GetAbsoluteEpisodeNumber() int32 { + if x != nil { + return x.AbsoluteEpisodeNumber + } + return 0 +} + +func (x *WatchSyncMedia) GetSeriesEpisodeCount() int32 { + if x != nil { + return x.SeriesEpisodeCount + } + return 0 +} + +func (x *WatchSyncMedia) GetMetadata() *structpb.Struct { + if x != nil { + return x.Metadata + } + return nil +} + +type WatchSyncEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Stable across retries and unique within one provider connection. + EventId string `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` + Operation WatchSyncOperation `protobuf:"varint,2,opt,name=operation,proto3,enum=silo.plugin.v1.WatchSyncOperation" json:"operation,omitempty"` + Origin WatchSyncOrigin `protobuf:"varint,3,opt,name=origin,proto3,enum=silo.plugin.v1.WatchSyncOrigin" json:"origin,omitempty"` + OccurredAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=occurred_at,json=occurredAt,proto3" json:"occurred_at,omitempty"` + Media *WatchSyncMedia `protobuf:"bytes,5,opt,name=media,proto3" json:"media,omitempty"` + WatchHistoryId string `protobuf:"bytes,6,opt,name=watch_history_id,json=watchHistoryId,proto3" json:"watch_history_id,omitempty"` + PlaybackSessionId string `protobuf:"bytes,7,opt,name=playback_session_id,json=playbackSessionId,proto3" json:"playback_session_id,omitempty"` + PositionSeconds float64 `protobuf:"fixed64,8,opt,name=position_seconds,json=positionSeconds,proto3" json:"position_seconds,omitempty"` + DurationSeconds float64 `protobuf:"fixed64,9,opt,name=duration_seconds,json=durationSeconds,proto3" json:"duration_seconds,omitempty"` + CompletionPercent float64 `protobuf:"fixed64,10,opt,name=completion_percent,json=completionPercent,proto3" json:"completion_percent,omitempty"` + PlayCount int32 `protobuf:"varint,11,opt,name=play_count,json=playCount,proto3" json:"play_count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncEvent) Reset() { + *x = WatchSyncEvent{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncEvent) ProtoMessage() {} + +func (x *WatchSyncEvent) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncEvent.ProtoReflect.Descriptor instead. +func (*WatchSyncEvent) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{14} +} + +func (x *WatchSyncEvent) GetEventId() string { + if x != nil { + return x.EventId + } + return "" +} + +func (x *WatchSyncEvent) GetOperation() WatchSyncOperation { + if x != nil { + return x.Operation + } + return WatchSyncOperation_WATCH_SYNC_OPERATION_UNSPECIFIED +} + +func (x *WatchSyncEvent) GetOrigin() WatchSyncOrigin { + if x != nil { + return x.Origin + } + return WatchSyncOrigin_WATCH_SYNC_ORIGIN_UNSPECIFIED +} + +func (x *WatchSyncEvent) GetOccurredAt() *timestamppb.Timestamp { + if x != nil { + return x.OccurredAt + } + return nil +} + +func (x *WatchSyncEvent) GetMedia() *WatchSyncMedia { + if x != nil { + return x.Media + } + return nil +} + +func (x *WatchSyncEvent) GetWatchHistoryId() string { + if x != nil { + return x.WatchHistoryId + } + return "" +} + +func (x *WatchSyncEvent) GetPlaybackSessionId() string { + if x != nil { + return x.PlaybackSessionId + } + return "" +} + +func (x *WatchSyncEvent) GetPositionSeconds() float64 { + if x != nil { + return x.PositionSeconds + } + return 0 +} + +func (x *WatchSyncEvent) GetDurationSeconds() float64 { + if x != nil { + return x.DurationSeconds + } + return 0 +} + +func (x *WatchSyncEvent) GetCompletionPercent() float64 { + if x != nil { + return x.CompletionPercent + } + return 0 +} + +func (x *WatchSyncEvent) GetPlayCount() int32 { + if x != nil { + return x.PlayCount + } + return 0 +} + +type WatchSyncApplyEventsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` + // Ordered desired-state events. Duplicate event IDs must not be interpreted + // as increments; ApplyEvents is an at-least-once delivery contract. + Events []*WatchSyncEvent `protobuf:"bytes,4,rep,name=events,proto3" json:"events,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncApplyEventsRequest) Reset() { + *x = WatchSyncApplyEventsRequest{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncApplyEventsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncApplyEventsRequest) ProtoMessage() {} + +func (x *WatchSyncApplyEventsRequest) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncApplyEventsRequest.ProtoReflect.Descriptor instead. +func (*WatchSyncApplyEventsRequest) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{15} +} + +func (x *WatchSyncApplyEventsRequest) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncApplyEventsRequest) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncApplyEventsRequest) GetCredentials() *WatchSyncCredentials { + if x != nil { + return x.Credentials + } + return nil +} + +func (x *WatchSyncApplyEventsRequest) GetEvents() []*WatchSyncEvent { + if x != nil { + return x.Events + } + return nil +} + +type WatchSyncApplyResult struct { + state protoimpl.MessageState `protogen:"open.v1"` + EventId string `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` + Status WatchSyncApplyStatus `protobuf:"varint,2,opt,name=status,proto3,enum=silo.plugin.v1.WatchSyncApplyStatus" json:"status,omitempty"` + SafeMessage string `protobuf:"bytes,3,opt,name=safe_message,json=safeMessage,proto3" json:"safe_message,omitempty"` + RetryAfter *durationpb.Duration `protobuf:"bytes,4,opt,name=retry_after,json=retryAfter,proto3" json:"retry_after,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncApplyResult) Reset() { + *x = WatchSyncApplyResult{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncApplyResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncApplyResult) ProtoMessage() {} + +func (x *WatchSyncApplyResult) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncApplyResult.ProtoReflect.Descriptor instead. +func (*WatchSyncApplyResult) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{16} +} + +func (x *WatchSyncApplyResult) GetEventId() string { + if x != nil { + return x.EventId + } + return "" +} + +func (x *WatchSyncApplyResult) GetStatus() WatchSyncApplyStatus { + if x != nil { + return x.Status + } + return WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_UNSPECIFIED +} + +func (x *WatchSyncApplyResult) GetSafeMessage() string { + if x != nil { + return x.SafeMessage + } + return "" +} + +func (x *WatchSyncApplyResult) GetRetryAfter() *durationpb.Duration { + if x != nil { + return x.RetryAfter + } + return nil +} + +type WatchSyncApplyEventsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Results []*WatchSyncApplyResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` + UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,2,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` + Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncApplyEventsResponse) Reset() { + *x = WatchSyncApplyEventsResponse{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncApplyEventsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncApplyEventsResponse) ProtoMessage() {} + +func (x *WatchSyncApplyEventsResponse) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncApplyEventsResponse.ProtoReflect.Descriptor instead. +func (*WatchSyncApplyEventsResponse) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{17} +} + +func (x *WatchSyncApplyEventsResponse) GetResults() []*WatchSyncApplyResult { + if x != nil { + return x.Results + } + return nil +} + +func (x *WatchSyncApplyEventsResponse) GetUpdatedCredentials() *WatchSyncCredentials { + if x != nil { + return x.UpdatedCredentials + } + return nil +} + +func (x *WatchSyncApplyEventsResponse) GetFault() *WatchSyncFault { + if x != nil { + return x.Fault + } + return nil +} + +type WatchSyncListRemoteStateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` + Cursor string `protobuf:"bytes,4,opt,name=cursor,proto3" json:"cursor,omitempty"` + PageToken string `protobuf:"bytes,5,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + PageSize int32 `protobuf:"varint,6,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncListRemoteStateRequest) Reset() { + *x = WatchSyncListRemoteStateRequest{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncListRemoteStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncListRemoteStateRequest) ProtoMessage() {} + +func (x *WatchSyncListRemoteStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncListRemoteStateRequest.ProtoReflect.Descriptor instead. +func (*WatchSyncListRemoteStateRequest) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{18} +} + +func (x *WatchSyncListRemoteStateRequest) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncListRemoteStateRequest) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncListRemoteStateRequest) GetCredentials() *WatchSyncCredentials { + if x != nil { + return x.Credentials + } + return nil +} + +func (x *WatchSyncListRemoteStateRequest) GetCursor() string { + if x != nil { + return x.Cursor + } + return "" +} + +func (x *WatchSyncListRemoteStateRequest) GetPageToken() string { + if x != nil { + return x.PageToken + } + return "" +} + +func (x *WatchSyncListRemoteStateRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +type WatchSyncRemoteState struct { + state protoimpl.MessageState `protogen:"open.v1"` + ProviderItemKey string `protobuf:"bytes,1,opt,name=provider_item_key,json=providerItemKey,proto3" json:"provider_item_key,omitempty"` + Media *WatchSyncMedia `protobuf:"bytes,2,opt,name=media,proto3" json:"media,omitempty"` + Progress int32 `protobuf:"varint,3,opt,name=progress,proto3" json:"progress,omitempty"` + Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncRemoteState) Reset() { + *x = WatchSyncRemoteState{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncRemoteState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncRemoteState) ProtoMessage() {} + +func (x *WatchSyncRemoteState) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncRemoteState.ProtoReflect.Descriptor instead. +func (*WatchSyncRemoteState) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{19} +} + +func (x *WatchSyncRemoteState) GetProviderItemKey() string { + if x != nil { + return x.ProviderItemKey + } + return "" +} + +func (x *WatchSyncRemoteState) GetMedia() *WatchSyncMedia { + if x != nil { + return x.Media + } + return nil +} + +func (x *WatchSyncRemoteState) GetProgress() int32 { + if x != nil { + return x.Progress + } + return 0 +} + +func (x *WatchSyncRemoteState) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *WatchSyncRemoteState) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +type WatchSyncListRemoteStateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Items []*WatchSyncRemoteState `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + NextCursor string `protobuf:"bytes,3,opt,name=next_cursor,json=nextCursor,proto3" json:"next_cursor,omitempty"` + CompleteSnapshot bool `protobuf:"varint,4,opt,name=complete_snapshot,json=completeSnapshot,proto3" json:"complete_snapshot,omitempty"` + UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,5,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` + Fault *WatchSyncFault `protobuf:"bytes,6,opt,name=fault,proto3" json:"fault,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncListRemoteStateResponse) Reset() { + *x = WatchSyncListRemoteStateResponse{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncListRemoteStateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncListRemoteStateResponse) ProtoMessage() {} + +func (x *WatchSyncListRemoteStateResponse) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncListRemoteStateResponse.ProtoReflect.Descriptor instead. +func (*WatchSyncListRemoteStateResponse) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{20} +} + +func (x *WatchSyncListRemoteStateResponse) GetItems() []*WatchSyncRemoteState { + if x != nil { + return x.Items + } + return nil +} + +func (x *WatchSyncListRemoteStateResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +func (x *WatchSyncListRemoteStateResponse) GetNextCursor() string { + if x != nil { + return x.NextCursor + } + return "" +} + +func (x *WatchSyncListRemoteStateResponse) GetCompleteSnapshot() bool { + if x != nil { + return x.CompleteSnapshot + } + return false +} + +func (x *WatchSyncListRemoteStateResponse) GetUpdatedCredentials() *WatchSyncCredentials { + if x != nil { + return x.UpdatedCredentials + } + return nil +} + +func (x *WatchSyncListRemoteStateResponse) GetFault() *WatchSyncFault { + if x != nil { + return x.Fault + } + return nil +} + +var File_silo_plugin_v1_watch_sync_provider_proto protoreflect.FileDescriptor + +const file_silo_plugin_v1_watch_sync_provider_proto_rawDesc = "" + + "\n" + + "(silo/plugin/v1/watch_sync_provider.proto\x12\x0esilo.plugin.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x97\x03\n" + + "\x1bWatchSyncProviderDescriptor\x12F\n" + + "\fauth_methods\x18\x01 \x03(\x0e2#.silo.plugin.v1.WatchSyncAuthMethodR\vauthMethods\x12%\n" + + "\x0eexport_watched\x18\x02 \x01(\bR\rexportWatched\x12)\n" + + "\x10export_unwatched\x18\x03 \x01(\bR\x0fexportUnwatched\x12%\n" + + "\x0eimport_watched\x18\x04 \x01(\bR\rimportWatched\x12'\n" + + "\x0fimport_progress\x18\x05 \x01(\bR\x0eimportProgress\x122\n" + + "\x15supported_media_types\x18\x06 \x03(\tR\x13supportedMediaTypes\x124\n" + + "\x16external_id_namespaces\x18\a \x03(\tR\x14externalIdNamespaces\x12$\n" + + "\x0emax_batch_size\x18\b \x01(\x05R\fmaxBatchSize\"\xc2\x02\n" + + "\x17WatchSyncProviderConfig\x12K\n" + + "\x06values\x18\x01 \x03(\v23.silo.plugin.v1.WatchSyncProviderConfig.ValuesEntryR\x06values\x12^\n" + + "\rsecret_values\x18\x02 \x03(\v29.silo.plugin.v1.WatchSyncProviderConfig.SecretValuesEntryR\fsecretValues\x1a9\n" + + "\vValuesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a?\n" + + "\x11SecretValuesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xfe\x02\n" + + "\x14WatchSyncCredentials\x12!\n" + + "\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12#\n" + + "\rrefresh_token\x18\x02 \x01(\tR\frefreshToken\x129\n" + + "\n" + + "expires_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x12\x1d\n" + + "\n" + + "token_type\x18\x04 \x01(\tR\ttokenType\x12\x16\n" + + "\x06scopes\x18\x05 \x03(\tR\x06scopes\x12g\n" + + "\x11secret_attributes\x18\x06 \x03(\v2:.silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntryR\x10secretAttributes\x1aC\n" + + "\x15SecretAttributesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xbc\x01\n" + + "\x10WatchSyncAccount\x12)\n" + + "\x10external_subject\x18\x01 \x01(\tR\x0fexternalSubject\x12\x1a\n" + + "\busername\x18\x02 \x01(\tR\busername\x12!\n" + + "\fdisplay_name\x18\x03 \x01(\tR\vdisplayName\x12\x1d\n" + + "\n" + + "avatar_url\x18\x04 \x01(\tR\tavatarUrl\x12\x1f\n" + + "\vprofile_url\x18\x05 \x01(\tR\n" + + "profileUrl\"\xa7\x01\n" + + "\x0eWatchSyncFault\x126\n" + + "\x04code\x18\x01 \x01(\x0e2\".silo.plugin.v1.WatchSyncFaultCodeR\x04code\x12!\n" + + "\fsafe_message\x18\x02 \x01(\tR\vsafeMessage\x12:\n" + + "\vretry_after\x18\x03 \x01(\v2\x19.google.protobuf.DurationR\n" + + "retryAfter\"\xcf\x01\n" + + "\x1dWatchSyncInitAuthorizeRequest\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12!\n" + + "\fredirect_uri\x18\x03 \x01(\tR\vredirectUri\x12\x14\n" + + "\x05state\x18\x04 \x01(\tR\x05state\"\xaa\x01\n" + + "\x1eWatchSyncInitAuthorizeResponse\x12+\n" + + "\x11authorization_url\x18\x01 \x01(\tR\x10authorizationUrl\x12%\n" + + "\x0eprovider_state\x18\x02 \x01(\fR\rproviderState\x124\n" + + "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\x8e\x02\n" + + "\x1cWatchSyncExchangeCodeRequest\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12!\n" + + "\fredirect_uri\x18\x03 \x01(\tR\vredirectUri\x12-\n" + + "\x12authorization_code\x18\x04 \x01(\tR\x11authorizationCode\x12%\n" + + "\x0eprovider_state\x18\x05 \x01(\fR\rproviderState\"\xb0\x01\n" + + "\x1eWatchSyncExchangeAPIKeyRequest\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12\x17\n" + + "\aapi_key\x18\x03 \x01(\tR\x06apiKey\"\xe3\x01\n" + + "\"WatchSyncRefreshCredentialsRequest\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + + "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\"\xd7\x01\n" + + "\x1bWatchSyncCredentialResponse\x12F\n" + + "\vcredentials\x18\x01 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\x12:\n" + + "\aaccount\x18\x02 \x01(\v2 .silo.plugin.v1.WatchSyncAccountR\aaccount\x124\n" + + "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xdb\x01\n" + + "\x1aWatchSyncGetAccountRequest\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + + "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\"\x8f\x01\n" + + "\x1bWatchSyncGetAccountResponse\x12:\n" + + "\aaccount\x18\x01 \x01(\v2 .silo.plugin.v1.WatchSyncAccountR\aaccount\x124\n" + + "\x05fault\x18\x02 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xc5\x06\n" + + "\x0eWatchSyncMedia\x12\"\n" + + "\rmedia_item_id\x18\x01 \x01(\tR\vmediaItemId\x12\x1d\n" + + "\n" + + "media_type\x18\x02 \x01(\tR\tmediaType\x12\x14\n" + + "\x05title\x18\x03 \x01(\tR\x05title\x12%\n" + + "\x0eoriginal_title\x18\x04 \x01(\tR\roriginalTitle\x12\x12\n" + + "\x04year\x18\x05 \x01(\x05R\x04year\x12R\n" + + "\fexternal_ids\x18\x06 \x03(\v2/.silo.plugin.v1.WatchSyncMedia.ExternalIdsEntryR\vexternalIds\x12/\n" + + "\x14series_media_item_id\x18\a \x01(\tR\x11seriesMediaItemId\x12!\n" + + "\fseries_title\x18\b \x01(\tR\vseriesTitle\x12\x1f\n" + + "\vseries_year\x18\t \x01(\x05R\n" + + "seriesYear\x12e\n" + + "\x13series_external_ids\x18\n" + + " \x03(\v25.silo.plugin.v1.WatchSyncMedia.SeriesExternalIdsEntryR\x11seriesExternalIds\x12#\n" + + "\rseason_number\x18\v \x01(\x05R\fseasonNumber\x12%\n" + + "\x0eepisode_number\x18\f \x01(\x05R\repisodeNumber\x126\n" + + "\x17absolute_episode_number\x18\r \x01(\x05R\x15absoluteEpisodeNumber\x120\n" + + "\x14series_episode_count\x18\x0e \x01(\x05R\x12seriesEpisodeCount\x123\n" + + "\bmetadata\x18\x0f \x01(\v2\x17.google.protobuf.StructR\bmetadata\x1a>\n" + + "\x10ExternalIdsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1aD\n" + + "\x16SeriesExternalIdsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x97\x04\n" + + "\x0eWatchSyncEvent\x12\x19\n" + + "\bevent_id\x18\x01 \x01(\tR\aeventId\x12@\n" + + "\toperation\x18\x02 \x01(\x0e2\".silo.plugin.v1.WatchSyncOperationR\toperation\x127\n" + + "\x06origin\x18\x03 \x01(\x0e2\x1f.silo.plugin.v1.WatchSyncOriginR\x06origin\x12;\n" + + "\voccurred_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "occurredAt\x124\n" + + "\x05media\x18\x05 \x01(\v2\x1e.silo.plugin.v1.WatchSyncMediaR\x05media\x12(\n" + + "\x10watch_history_id\x18\x06 \x01(\tR\x0ewatchHistoryId\x12.\n" + + "\x13playback_session_id\x18\a \x01(\tR\x11playbackSessionId\x12)\n" + + "\x10position_seconds\x18\b \x01(\x01R\x0fpositionSeconds\x12)\n" + + "\x10duration_seconds\x18\t \x01(\x01R\x0fdurationSeconds\x12-\n" + + "\x12completion_percent\x18\n" + + " \x01(\x01R\x11completionPercent\x12\x1d\n" + + "\n" + + "play_count\x18\v \x01(\x05R\tplayCount\"\x94\x02\n" + + "\x1bWatchSyncApplyEventsRequest\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + + "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\x126\n" + + "\x06events\x18\x04 \x03(\v2\x1e.silo.plugin.v1.WatchSyncEventR\x06events\"\xce\x01\n" + + "\x14WatchSyncApplyResult\x12\x19\n" + + "\bevent_id\x18\x01 \x01(\tR\aeventId\x12<\n" + + "\x06status\x18\x02 \x01(\x0e2$.silo.plugin.v1.WatchSyncApplyStatusR\x06status\x12!\n" + + "\fsafe_message\x18\x03 \x01(\tR\vsafeMessage\x12:\n" + + "\vretry_after\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\n" + + "retryAfter\"\xeb\x01\n" + + "\x1cWatchSyncApplyEventsResponse\x12>\n" + + "\aresults\x18\x01 \x03(\v2$.silo.plugin.v1.WatchSyncApplyResultR\aresults\x12U\n" + + "\x13updated_credentials\x18\x02 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\x12updatedCredentials\x124\n" + + "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xb4\x02\n" + + "\x1fWatchSyncListRemoteStateRequest\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + + "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\x12\x16\n" + + "\x06cursor\x18\x04 \x01(\tR\x06cursor\x12\x1d\n" + + "\n" + + "page_token\x18\x05 \x01(\tR\tpageToken\x12\x1b\n" + + "\tpage_size\x18\x06 \x01(\x05R\bpageSize\"\xe7\x01\n" + + "\x14WatchSyncRemoteState\x12*\n" + + "\x11provider_item_key\x18\x01 \x01(\tR\x0fproviderItemKey\x124\n" + + "\x05media\x18\x02 \x01(\v2\x1e.silo.plugin.v1.WatchSyncMediaR\x05media\x12\x1a\n" + + "\bprogress\x18\x03 \x01(\x05R\bprogress\x12\x16\n" + + "\x06status\x18\x04 \x01(\tR\x06status\x129\n" + + "\n" + + "updated_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\"\xe1\x02\n" + + " WatchSyncListRemoteStateResponse\x12:\n" + + "\x05items\x18\x01 \x03(\v2$.silo.plugin.v1.WatchSyncRemoteStateR\x05items\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\x12\x1f\n" + + "\vnext_cursor\x18\x03 \x01(\tR\n" + + "nextCursor\x12+\n" + + "\x11complete_snapshot\x18\x04 \x01(\bR\x10completeSnapshot\x12U\n" + + "\x13updated_credentials\x18\x05 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\x12updatedCredentials\x124\n" + + "\x05fault\x18\x06 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault*\x90\x01\n" + + "\x13WatchSyncAuthMethod\x12&\n" + + "\"WATCH_SYNC_AUTH_METHOD_UNSPECIFIED\x10\x00\x12-\n" + + ")WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE\x10\x01\x12\"\n" + + "\x1eWATCH_SYNC_AUTH_METHOD_API_KEY\x10\x02*\x8a\x01\n" + + "\x12WatchSyncOperation\x12$\n" + + " WATCH_SYNC_OPERATION_UNSPECIFIED\x10\x00\x12%\n" + + "!WATCH_SYNC_OPERATION_MARK_WATCHED\x10\x01\x12'\n" + + "#WATCH_SYNC_OPERATION_MARK_UNWATCHED\x10\x02*\xa3\x01\n" + + "\x0fWatchSyncOrigin\x12!\n" + + "\x1dWATCH_SYNC_ORIGIN_UNSPECIFIED\x10\x00\x12)\n" + + "%WATCH_SYNC_ORIGIN_PLAYBACK_COMPLETION\x10\x01\x12\x1c\n" + + "\x18WATCH_SYNC_ORIGIN_MANUAL\x10\x02\x12$\n" + + " WATCH_SYNC_ORIGIN_RECONCILIATION\x10\x03*\xd4\x01\n" + + "\x14WatchSyncApplyStatus\x12'\n" + + "#WATCH_SYNC_APPLY_STATUS_UNSPECIFIED\x10\x00\x12#\n" + + "\x1fWATCH_SYNC_APPLY_STATUS_APPLIED\x10\x01\x12%\n" + + "!WATCH_SYNC_APPLY_STATUS_NO_CHANGE\x10\x02\x12!\n" + + "\x1dWATCH_SYNC_APPLY_STATUS_RETRY\x10\x03\x12$\n" + + " WATCH_SYNC_APPLY_STATUS_REJECTED\x10\x04*\xb3\x02\n" + + "\x12WatchSyncFaultCode\x12%\n" + + "!WATCH_SYNC_FAULT_CODE_UNSPECIFIED\x10\x00\x12#\n" + + "\x1fWATCH_SYNC_FAULT_CODE_TEMPORARY\x10\x01\x12&\n" + + "\"WATCH_SYNC_FAULT_CODE_RATE_LIMITED\x10\x02\x12,\n" + + "(WATCH_SYNC_FAULT_CODE_INVALID_CREDENTIAL\x10\x03\x12+\n" + + "'WATCH_SYNC_FAULT_CODE_PERMISSION_DENIED\x10\x04\x12)\n" + + "%WATCH_SYNC_FAULT_CODE_INVALID_REQUEST\x10\x05\x12#\n" + + "\x1fWATCH_SYNC_FAULT_CODE_PERMANENT\x10\x062\x9b\x06\n" + + "\x11WatchSyncProvider\x12n\n" + + "\rInitAuthorize\x12-.silo.plugin.v1.WatchSyncInitAuthorizeRequest\x1a..silo.plugin.v1.WatchSyncInitAuthorizeResponse\x12i\n" + + "\fExchangeCode\x12,.silo.plugin.v1.WatchSyncExchangeCodeRequest\x1a+.silo.plugin.v1.WatchSyncCredentialResponse\x12m\n" + + "\x0eExchangeAPIKey\x12..silo.plugin.v1.WatchSyncExchangeAPIKeyRequest\x1a+.silo.plugin.v1.WatchSyncCredentialResponse\x12u\n" + + "\x12RefreshCredentials\x122.silo.plugin.v1.WatchSyncRefreshCredentialsRequest\x1a+.silo.plugin.v1.WatchSyncCredentialResponse\x12e\n" + + "\n" + + "GetAccount\x12*.silo.plugin.v1.WatchSyncGetAccountRequest\x1a+.silo.plugin.v1.WatchSyncGetAccountResponse\x12h\n" + + "\vApplyEvents\x12+.silo.plugin.v1.WatchSyncApplyEventsRequest\x1a,.silo.plugin.v1.WatchSyncApplyEventsResponse\x12t\n" + + "\x0fListRemoteState\x12/.silo.plugin.v1.WatchSyncListRemoteStateRequest\x1a0.silo.plugin.v1.WatchSyncListRemoteStateResponseBPZNgithub.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1;pluginv1b\x06proto3" + +var ( + file_silo_plugin_v1_watch_sync_provider_proto_rawDescOnce sync.Once + file_silo_plugin_v1_watch_sync_provider_proto_rawDescData []byte +) + +func file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP() []byte { + file_silo_plugin_v1_watch_sync_provider_proto_rawDescOnce.Do(func() { + file_silo_plugin_v1_watch_sync_provider_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_silo_plugin_v1_watch_sync_provider_proto_rawDesc), len(file_silo_plugin_v1_watch_sync_provider_proto_rawDesc))) + }) + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescData +} + +var file_silo_plugin_v1_watch_sync_provider_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_silo_plugin_v1_watch_sync_provider_proto_msgTypes = make([]protoimpl.MessageInfo, 26) +var file_silo_plugin_v1_watch_sync_provider_proto_goTypes = []any{ + (WatchSyncAuthMethod)(0), // 0: silo.plugin.v1.WatchSyncAuthMethod + (WatchSyncOperation)(0), // 1: silo.plugin.v1.WatchSyncOperation + (WatchSyncOrigin)(0), // 2: silo.plugin.v1.WatchSyncOrigin + (WatchSyncApplyStatus)(0), // 3: silo.plugin.v1.WatchSyncApplyStatus + (WatchSyncFaultCode)(0), // 4: silo.plugin.v1.WatchSyncFaultCode + (*WatchSyncProviderDescriptor)(nil), // 5: silo.plugin.v1.WatchSyncProviderDescriptor + (*WatchSyncProviderConfig)(nil), // 6: silo.plugin.v1.WatchSyncProviderConfig + (*WatchSyncCredentials)(nil), // 7: silo.plugin.v1.WatchSyncCredentials + (*WatchSyncAccount)(nil), // 8: silo.plugin.v1.WatchSyncAccount + (*WatchSyncFault)(nil), // 9: silo.plugin.v1.WatchSyncFault + (*WatchSyncInitAuthorizeRequest)(nil), // 10: silo.plugin.v1.WatchSyncInitAuthorizeRequest + (*WatchSyncInitAuthorizeResponse)(nil), // 11: silo.plugin.v1.WatchSyncInitAuthorizeResponse + (*WatchSyncExchangeCodeRequest)(nil), // 12: silo.plugin.v1.WatchSyncExchangeCodeRequest + (*WatchSyncExchangeAPIKeyRequest)(nil), // 13: silo.plugin.v1.WatchSyncExchangeAPIKeyRequest + (*WatchSyncRefreshCredentialsRequest)(nil), // 14: silo.plugin.v1.WatchSyncRefreshCredentialsRequest + (*WatchSyncCredentialResponse)(nil), // 15: silo.plugin.v1.WatchSyncCredentialResponse + (*WatchSyncGetAccountRequest)(nil), // 16: silo.plugin.v1.WatchSyncGetAccountRequest + (*WatchSyncGetAccountResponse)(nil), // 17: silo.plugin.v1.WatchSyncGetAccountResponse + (*WatchSyncMedia)(nil), // 18: silo.plugin.v1.WatchSyncMedia + (*WatchSyncEvent)(nil), // 19: silo.plugin.v1.WatchSyncEvent + (*WatchSyncApplyEventsRequest)(nil), // 20: silo.plugin.v1.WatchSyncApplyEventsRequest + (*WatchSyncApplyResult)(nil), // 21: silo.plugin.v1.WatchSyncApplyResult + (*WatchSyncApplyEventsResponse)(nil), // 22: silo.plugin.v1.WatchSyncApplyEventsResponse + (*WatchSyncListRemoteStateRequest)(nil), // 23: silo.plugin.v1.WatchSyncListRemoteStateRequest + (*WatchSyncRemoteState)(nil), // 24: silo.plugin.v1.WatchSyncRemoteState + (*WatchSyncListRemoteStateResponse)(nil), // 25: silo.plugin.v1.WatchSyncListRemoteStateResponse + nil, // 26: silo.plugin.v1.WatchSyncProviderConfig.ValuesEntry + nil, // 27: silo.plugin.v1.WatchSyncProviderConfig.SecretValuesEntry + nil, // 28: silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntry + nil, // 29: silo.plugin.v1.WatchSyncMedia.ExternalIdsEntry + nil, // 30: silo.plugin.v1.WatchSyncMedia.SeriesExternalIdsEntry + (*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 32: google.protobuf.Duration + (*structpb.Struct)(nil), // 33: google.protobuf.Struct +} +var file_silo_plugin_v1_watch_sync_provider_proto_depIdxs = []int32{ + 0, // 0: silo.plugin.v1.WatchSyncProviderDescriptor.auth_methods:type_name -> silo.plugin.v1.WatchSyncAuthMethod + 26, // 1: silo.plugin.v1.WatchSyncProviderConfig.values:type_name -> silo.plugin.v1.WatchSyncProviderConfig.ValuesEntry + 27, // 2: silo.plugin.v1.WatchSyncProviderConfig.secret_values:type_name -> silo.plugin.v1.WatchSyncProviderConfig.SecretValuesEntry + 31, // 3: silo.plugin.v1.WatchSyncCredentials.expires_at:type_name -> google.protobuf.Timestamp + 28, // 4: silo.plugin.v1.WatchSyncCredentials.secret_attributes:type_name -> silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntry + 4, // 5: silo.plugin.v1.WatchSyncFault.code:type_name -> silo.plugin.v1.WatchSyncFaultCode + 32, // 6: silo.plugin.v1.WatchSyncFault.retry_after:type_name -> google.protobuf.Duration + 6, // 7: silo.plugin.v1.WatchSyncInitAuthorizeRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 9, // 8: silo.plugin.v1.WatchSyncInitAuthorizeResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 6, // 9: silo.plugin.v1.WatchSyncExchangeCodeRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 6, // 10: silo.plugin.v1.WatchSyncExchangeAPIKeyRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 6, // 11: silo.plugin.v1.WatchSyncRefreshCredentialsRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 7, // 12: silo.plugin.v1.WatchSyncRefreshCredentialsRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 7, // 13: silo.plugin.v1.WatchSyncCredentialResponse.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 8, // 14: silo.plugin.v1.WatchSyncCredentialResponse.account:type_name -> silo.plugin.v1.WatchSyncAccount + 9, // 15: silo.plugin.v1.WatchSyncCredentialResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 6, // 16: silo.plugin.v1.WatchSyncGetAccountRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 7, // 17: silo.plugin.v1.WatchSyncGetAccountRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 8, // 18: silo.plugin.v1.WatchSyncGetAccountResponse.account:type_name -> silo.plugin.v1.WatchSyncAccount + 9, // 19: silo.plugin.v1.WatchSyncGetAccountResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 29, // 20: silo.plugin.v1.WatchSyncMedia.external_ids:type_name -> silo.plugin.v1.WatchSyncMedia.ExternalIdsEntry + 30, // 21: silo.plugin.v1.WatchSyncMedia.series_external_ids:type_name -> silo.plugin.v1.WatchSyncMedia.SeriesExternalIdsEntry + 33, // 22: silo.plugin.v1.WatchSyncMedia.metadata:type_name -> google.protobuf.Struct + 1, // 23: silo.plugin.v1.WatchSyncEvent.operation:type_name -> silo.plugin.v1.WatchSyncOperation + 2, // 24: silo.plugin.v1.WatchSyncEvent.origin:type_name -> silo.plugin.v1.WatchSyncOrigin + 31, // 25: silo.plugin.v1.WatchSyncEvent.occurred_at:type_name -> google.protobuf.Timestamp + 18, // 26: silo.plugin.v1.WatchSyncEvent.media:type_name -> silo.plugin.v1.WatchSyncMedia + 6, // 27: silo.plugin.v1.WatchSyncApplyEventsRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 7, // 28: silo.plugin.v1.WatchSyncApplyEventsRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 19, // 29: silo.plugin.v1.WatchSyncApplyEventsRequest.events:type_name -> silo.plugin.v1.WatchSyncEvent + 3, // 30: silo.plugin.v1.WatchSyncApplyResult.status:type_name -> silo.plugin.v1.WatchSyncApplyStatus + 32, // 31: silo.plugin.v1.WatchSyncApplyResult.retry_after:type_name -> google.protobuf.Duration + 21, // 32: silo.plugin.v1.WatchSyncApplyEventsResponse.results:type_name -> silo.plugin.v1.WatchSyncApplyResult + 7, // 33: silo.plugin.v1.WatchSyncApplyEventsResponse.updated_credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 9, // 34: silo.plugin.v1.WatchSyncApplyEventsResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 6, // 35: silo.plugin.v1.WatchSyncListRemoteStateRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 7, // 36: silo.plugin.v1.WatchSyncListRemoteStateRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 18, // 37: silo.plugin.v1.WatchSyncRemoteState.media:type_name -> silo.plugin.v1.WatchSyncMedia + 31, // 38: silo.plugin.v1.WatchSyncRemoteState.updated_at:type_name -> google.protobuf.Timestamp + 24, // 39: silo.plugin.v1.WatchSyncListRemoteStateResponse.items:type_name -> silo.plugin.v1.WatchSyncRemoteState + 7, // 40: silo.plugin.v1.WatchSyncListRemoteStateResponse.updated_credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 9, // 41: silo.plugin.v1.WatchSyncListRemoteStateResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 10, // 42: silo.plugin.v1.WatchSyncProvider.InitAuthorize:input_type -> silo.plugin.v1.WatchSyncInitAuthorizeRequest + 12, // 43: silo.plugin.v1.WatchSyncProvider.ExchangeCode:input_type -> silo.plugin.v1.WatchSyncExchangeCodeRequest + 13, // 44: silo.plugin.v1.WatchSyncProvider.ExchangeAPIKey:input_type -> silo.plugin.v1.WatchSyncExchangeAPIKeyRequest + 14, // 45: silo.plugin.v1.WatchSyncProvider.RefreshCredentials:input_type -> silo.plugin.v1.WatchSyncRefreshCredentialsRequest + 16, // 46: silo.plugin.v1.WatchSyncProvider.GetAccount:input_type -> silo.plugin.v1.WatchSyncGetAccountRequest + 20, // 47: silo.plugin.v1.WatchSyncProvider.ApplyEvents:input_type -> silo.plugin.v1.WatchSyncApplyEventsRequest + 23, // 48: silo.plugin.v1.WatchSyncProvider.ListRemoteState:input_type -> silo.plugin.v1.WatchSyncListRemoteStateRequest + 11, // 49: silo.plugin.v1.WatchSyncProvider.InitAuthorize:output_type -> silo.plugin.v1.WatchSyncInitAuthorizeResponse + 15, // 50: silo.plugin.v1.WatchSyncProvider.ExchangeCode:output_type -> silo.plugin.v1.WatchSyncCredentialResponse + 15, // 51: silo.plugin.v1.WatchSyncProvider.ExchangeAPIKey:output_type -> silo.plugin.v1.WatchSyncCredentialResponse + 15, // 52: silo.plugin.v1.WatchSyncProvider.RefreshCredentials:output_type -> silo.plugin.v1.WatchSyncCredentialResponse + 17, // 53: silo.plugin.v1.WatchSyncProvider.GetAccount:output_type -> silo.plugin.v1.WatchSyncGetAccountResponse + 22, // 54: silo.plugin.v1.WatchSyncProvider.ApplyEvents:output_type -> silo.plugin.v1.WatchSyncApplyEventsResponse + 25, // 55: silo.plugin.v1.WatchSyncProvider.ListRemoteState:output_type -> silo.plugin.v1.WatchSyncListRemoteStateResponse + 49, // [49:56] is the sub-list for method output_type + 42, // [42:49] is the sub-list for method input_type + 42, // [42:42] is the sub-list for extension type_name + 42, // [42:42] is the sub-list for extension extendee + 0, // [0:42] is the sub-list for field type_name +} + +func init() { file_silo_plugin_v1_watch_sync_provider_proto_init() } +func file_silo_plugin_v1_watch_sync_provider_proto_init() { + if File_silo_plugin_v1_watch_sync_provider_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_silo_plugin_v1_watch_sync_provider_proto_rawDesc), len(file_silo_plugin_v1_watch_sync_provider_proto_rawDesc)), + NumEnums: 5, + NumMessages: 26, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_silo_plugin_v1_watch_sync_provider_proto_goTypes, + DependencyIndexes: file_silo_plugin_v1_watch_sync_provider_proto_depIdxs, + EnumInfos: file_silo_plugin_v1_watch_sync_provider_proto_enumTypes, + MessageInfos: file_silo_plugin_v1_watch_sync_provider_proto_msgTypes, + }.Build() + File_silo_plugin_v1_watch_sync_provider_proto = out.File + file_silo_plugin_v1_watch_sync_provider_proto_goTypes = nil + file_silo_plugin_v1_watch_sync_provider_proto_depIdxs = nil +} diff --git a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_grpc.pb.go b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_grpc.pb.go new file mode 100644 index 0000000..4257841 --- /dev/null +++ b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_grpc.pb.go @@ -0,0 +1,357 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: silo/plugin/v1/watch_sync_provider.proto + +package pluginv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + WatchSyncProvider_InitAuthorize_FullMethodName = "/silo.plugin.v1.WatchSyncProvider/InitAuthorize" + WatchSyncProvider_ExchangeCode_FullMethodName = "/silo.plugin.v1.WatchSyncProvider/ExchangeCode" + WatchSyncProvider_ExchangeAPIKey_FullMethodName = "/silo.plugin.v1.WatchSyncProvider/ExchangeAPIKey" + WatchSyncProvider_RefreshCredentials_FullMethodName = "/silo.plugin.v1.WatchSyncProvider/RefreshCredentials" + WatchSyncProvider_GetAccount_FullMethodName = "/silo.plugin.v1.WatchSyncProvider/GetAccount" + WatchSyncProvider_ApplyEvents_FullMethodName = "/silo.plugin.v1.WatchSyncProvider/ApplyEvents" + WatchSyncProvider_ListRemoteState_FullMethodName = "/silo.plugin.v1.WatchSyncProvider/ListRemoteState" +) + +// WatchSyncProviderClient is the client API for WatchSyncProvider service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// WatchSyncProvider adapts an external watch-tracking service to Silo's +// host-owned credential, outbox, retry, and reconciliation pipeline. Plugins +// must treat credentials and provider secrets as transient request data and +// must not persist or log them. +type WatchSyncProviderClient interface { + InitAuthorize(ctx context.Context, in *WatchSyncInitAuthorizeRequest, opts ...grpc.CallOption) (*WatchSyncInitAuthorizeResponse, error) + ExchangeCode(ctx context.Context, in *WatchSyncExchangeCodeRequest, opts ...grpc.CallOption) (*WatchSyncCredentialResponse, error) + ExchangeAPIKey(ctx context.Context, in *WatchSyncExchangeAPIKeyRequest, opts ...grpc.CallOption) (*WatchSyncCredentialResponse, error) + RefreshCredentials(ctx context.Context, in *WatchSyncRefreshCredentialsRequest, opts ...grpc.CallOption) (*WatchSyncCredentialResponse, error) + GetAccount(ctx context.Context, in *WatchSyncGetAccountRequest, opts ...grpc.CallOption) (*WatchSyncGetAccountResponse, error) + ApplyEvents(ctx context.Context, in *WatchSyncApplyEventsRequest, opts ...grpc.CallOption) (*WatchSyncApplyEventsResponse, error) + ListRemoteState(ctx context.Context, in *WatchSyncListRemoteStateRequest, opts ...grpc.CallOption) (*WatchSyncListRemoteStateResponse, error) +} + +type watchSyncProviderClient struct { + cc grpc.ClientConnInterface +} + +func NewWatchSyncProviderClient(cc grpc.ClientConnInterface) WatchSyncProviderClient { + return &watchSyncProviderClient{cc} +} + +func (c *watchSyncProviderClient) InitAuthorize(ctx context.Context, in *WatchSyncInitAuthorizeRequest, opts ...grpc.CallOption) (*WatchSyncInitAuthorizeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WatchSyncInitAuthorizeResponse) + err := c.cc.Invoke(ctx, WatchSyncProvider_InitAuthorize_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *watchSyncProviderClient) ExchangeCode(ctx context.Context, in *WatchSyncExchangeCodeRequest, opts ...grpc.CallOption) (*WatchSyncCredentialResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WatchSyncCredentialResponse) + err := c.cc.Invoke(ctx, WatchSyncProvider_ExchangeCode_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *watchSyncProviderClient) ExchangeAPIKey(ctx context.Context, in *WatchSyncExchangeAPIKeyRequest, opts ...grpc.CallOption) (*WatchSyncCredentialResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WatchSyncCredentialResponse) + err := c.cc.Invoke(ctx, WatchSyncProvider_ExchangeAPIKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *watchSyncProviderClient) RefreshCredentials(ctx context.Context, in *WatchSyncRefreshCredentialsRequest, opts ...grpc.CallOption) (*WatchSyncCredentialResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WatchSyncCredentialResponse) + err := c.cc.Invoke(ctx, WatchSyncProvider_RefreshCredentials_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *watchSyncProviderClient) GetAccount(ctx context.Context, in *WatchSyncGetAccountRequest, opts ...grpc.CallOption) (*WatchSyncGetAccountResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WatchSyncGetAccountResponse) + err := c.cc.Invoke(ctx, WatchSyncProvider_GetAccount_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *watchSyncProviderClient) ApplyEvents(ctx context.Context, in *WatchSyncApplyEventsRequest, opts ...grpc.CallOption) (*WatchSyncApplyEventsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WatchSyncApplyEventsResponse) + err := c.cc.Invoke(ctx, WatchSyncProvider_ApplyEvents_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *watchSyncProviderClient) ListRemoteState(ctx context.Context, in *WatchSyncListRemoteStateRequest, opts ...grpc.CallOption) (*WatchSyncListRemoteStateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(WatchSyncListRemoteStateResponse) + err := c.cc.Invoke(ctx, WatchSyncProvider_ListRemoteState_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// WatchSyncProviderServer is the server API for WatchSyncProvider service. +// All implementations should embed UnimplementedWatchSyncProviderServer +// for forward compatibility. +// +// WatchSyncProvider adapts an external watch-tracking service to Silo's +// host-owned credential, outbox, retry, and reconciliation pipeline. Plugins +// must treat credentials and provider secrets as transient request data and +// must not persist or log them. +type WatchSyncProviderServer interface { + InitAuthorize(context.Context, *WatchSyncInitAuthorizeRequest) (*WatchSyncInitAuthorizeResponse, error) + ExchangeCode(context.Context, *WatchSyncExchangeCodeRequest) (*WatchSyncCredentialResponse, error) + ExchangeAPIKey(context.Context, *WatchSyncExchangeAPIKeyRequest) (*WatchSyncCredentialResponse, error) + RefreshCredentials(context.Context, *WatchSyncRefreshCredentialsRequest) (*WatchSyncCredentialResponse, error) + GetAccount(context.Context, *WatchSyncGetAccountRequest) (*WatchSyncGetAccountResponse, error) + ApplyEvents(context.Context, *WatchSyncApplyEventsRequest) (*WatchSyncApplyEventsResponse, error) + ListRemoteState(context.Context, *WatchSyncListRemoteStateRequest) (*WatchSyncListRemoteStateResponse, error) +} + +// UnimplementedWatchSyncProviderServer should be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedWatchSyncProviderServer struct{} + +func (UnimplementedWatchSyncProviderServer) InitAuthorize(context.Context, *WatchSyncInitAuthorizeRequest) (*WatchSyncInitAuthorizeResponse, error) { + return nil, status.Error(codes.Unimplemented, "method InitAuthorize not implemented") +} +func (UnimplementedWatchSyncProviderServer) ExchangeCode(context.Context, *WatchSyncExchangeCodeRequest) (*WatchSyncCredentialResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ExchangeCode not implemented") +} +func (UnimplementedWatchSyncProviderServer) ExchangeAPIKey(context.Context, *WatchSyncExchangeAPIKeyRequest) (*WatchSyncCredentialResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ExchangeAPIKey not implemented") +} +func (UnimplementedWatchSyncProviderServer) RefreshCredentials(context.Context, *WatchSyncRefreshCredentialsRequest) (*WatchSyncCredentialResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RefreshCredentials not implemented") +} +func (UnimplementedWatchSyncProviderServer) GetAccount(context.Context, *WatchSyncGetAccountRequest) (*WatchSyncGetAccountResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetAccount not implemented") +} +func (UnimplementedWatchSyncProviderServer) ApplyEvents(context.Context, *WatchSyncApplyEventsRequest) (*WatchSyncApplyEventsResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ApplyEvents not implemented") +} +func (UnimplementedWatchSyncProviderServer) ListRemoteState(context.Context, *WatchSyncListRemoteStateRequest) (*WatchSyncListRemoteStateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListRemoteState not implemented") +} +func (UnimplementedWatchSyncProviderServer) testEmbeddedByValue() {} + +// UnsafeWatchSyncProviderServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to WatchSyncProviderServer will +// result in compilation errors. +type UnsafeWatchSyncProviderServer interface { + mustEmbedUnimplementedWatchSyncProviderServer() +} + +func RegisterWatchSyncProviderServer(s grpc.ServiceRegistrar, srv WatchSyncProviderServer) { + // If the following call panics, it indicates UnimplementedWatchSyncProviderServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&WatchSyncProvider_ServiceDesc, srv) +} + +func _WatchSyncProvider_InitAuthorize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchSyncInitAuthorizeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WatchSyncProviderServer).InitAuthorize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WatchSyncProvider_InitAuthorize_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WatchSyncProviderServer).InitAuthorize(ctx, req.(*WatchSyncInitAuthorizeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WatchSyncProvider_ExchangeCode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchSyncExchangeCodeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WatchSyncProviderServer).ExchangeCode(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WatchSyncProvider_ExchangeCode_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WatchSyncProviderServer).ExchangeCode(ctx, req.(*WatchSyncExchangeCodeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WatchSyncProvider_ExchangeAPIKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchSyncExchangeAPIKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WatchSyncProviderServer).ExchangeAPIKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WatchSyncProvider_ExchangeAPIKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WatchSyncProviderServer).ExchangeAPIKey(ctx, req.(*WatchSyncExchangeAPIKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WatchSyncProvider_RefreshCredentials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchSyncRefreshCredentialsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WatchSyncProviderServer).RefreshCredentials(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WatchSyncProvider_RefreshCredentials_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WatchSyncProviderServer).RefreshCredentials(ctx, req.(*WatchSyncRefreshCredentialsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WatchSyncProvider_GetAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchSyncGetAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WatchSyncProviderServer).GetAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WatchSyncProvider_GetAccount_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WatchSyncProviderServer).GetAccount(ctx, req.(*WatchSyncGetAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WatchSyncProvider_ApplyEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchSyncApplyEventsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WatchSyncProviderServer).ApplyEvents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WatchSyncProvider_ApplyEvents_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WatchSyncProviderServer).ApplyEvents(ctx, req.(*WatchSyncApplyEventsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WatchSyncProvider_ListRemoteState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WatchSyncListRemoteStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WatchSyncProviderServer).ListRemoteState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: WatchSyncProvider_ListRemoteState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WatchSyncProviderServer).ListRemoteState(ctx, req.(*WatchSyncListRemoteStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// WatchSyncProvider_ServiceDesc is the grpc.ServiceDesc for WatchSyncProvider service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var WatchSyncProvider_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "silo.plugin.v1.WatchSyncProvider", + HandlerType: (*WatchSyncProviderServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "InitAuthorize", + Handler: _WatchSyncProvider_InitAuthorize_Handler, + }, + { + MethodName: "ExchangeCode", + Handler: _WatchSyncProvider_ExchangeCode_Handler, + }, + { + MethodName: "ExchangeAPIKey", + Handler: _WatchSyncProvider_ExchangeAPIKey_Handler, + }, + { + MethodName: "RefreshCredentials", + Handler: _WatchSyncProvider_RefreshCredentials_Handler, + }, + { + MethodName: "GetAccount", + Handler: _WatchSyncProvider_GetAccount_Handler, + }, + { + MethodName: "ApplyEvents", + Handler: _WatchSyncProvider_ApplyEvents_Handler, + }, + { + MethodName: "ListRemoteState", + Handler: _WatchSyncProvider_ListRemoteState_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "silo/plugin/v1/watch_sync_provider.proto", +} diff --git a/pkg/pluginsdk/capability/capability.go b/pkg/pluginsdk/capability/capability.go index 049c0b9..db5e1af 100644 --- a/pkg/pluginsdk/capability/capability.go +++ b/pkg/pluginsdk/capability/capability.go @@ -2,18 +2,19 @@ package capability const ( - EventConsumer = "event_consumer.v1" - HTTPRoutes = "http_routes.v1" - ScheduledTask = "scheduled_task.v1" - RequestRouter = "request_router.v1" - MediaAnalyzer = "media_analyzer.v1" - AuthProvider = "auth_provider.v1" - MetadataProvider = "metadata_provider.v1" - ImageResolver = "image_resolver.v1" - MarkerProvider = "marker_provider.v1" - AudiobookBackend = "audiobook_backend.v1" - EbookBackend = "ebook_backend.v1" - ScanSource = "scan_source.v1" + EventConsumer = "event_consumer.v1" + HTTPRoutes = "http_routes.v1" + ScheduledTask = "scheduled_task.v1" + RequestRouter = "request_router.v1" + MediaAnalyzer = "media_analyzer.v1" + AuthProvider = "auth_provider.v1" + MetadataProvider = "metadata_provider.v1" + ImageResolver = "image_resolver.v1" + MarkerProvider = "marker_provider.v1" + AudiobookBackend = "audiobook_backend.v1" + EbookBackend = "ebook_backend.v1" + ScanSource = "scan_source.v1" + WatchSyncProvider = "watch_sync_provider.v1" ) // KnownTypes lists every capability type recognized by this SDK version. @@ -30,4 +31,5 @@ var KnownTypes = []string{ AudiobookBackend, EbookBackend, ScanSource, + WatchSyncProvider, } diff --git a/pkg/pluginsdk/convert/convert.go b/pkg/pluginsdk/convert/convert.go index 8f535ce..a05658a 100644 --- a/pkg/pluginsdk/convert/convert.go +++ b/pkg/pluginsdk/convert/convert.go @@ -1,6 +1,7 @@ package convert import ( + "encoding/json" "fmt" "google.golang.org/protobuf/encoding/protojson" @@ -63,6 +64,17 @@ func DecodeCapability(record CapabilityRecord) (*pluginv1.CapabilityDescriptor, if iconURL, ok := record.Metadata["icon_url"].(string); ok { descriptor.IconUrl = iconURL } + if watchSync, ok := record.Metadata["watch_sync_provider"]; ok { + data, err := json.Marshal(watchSync) + if err != nil { + return nil, fmt.Errorf("encode watch sync provider descriptor: %w", err) + } + var typed pluginv1.WatchSyncProviderDescriptor + if err := protojson.Unmarshal(data, &typed); err != nil { + return nil, fmt.Errorf("decode watch sync provider descriptor: %w", err) + } + descriptor.WatchSyncProvider = &typed + } if configSchema, ok := record.Metadata["config_schema"]; ok { schemas, err := decodeConfigSchemas(configSchema) if err != nil { @@ -99,6 +111,17 @@ func capabilityMetadata(descriptor *pluginv1.CapabilityDescriptor) (map[string]a if descriptor.GetIconUrl() != "" { metadata["icon_url"] = descriptor.GetIconUrl() } + if descriptor.GetWatchSyncProvider() != nil { + data, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(descriptor.GetWatchSyncProvider()) + if err != nil { + return nil, fmt.Errorf("encode watch sync provider descriptor: %w", err) + } + var value map[string]any + if err := json.Unmarshal(data, &value); err != nil { + return nil, fmt.Errorf("decode watch sync provider descriptor JSON: %w", err) + } + metadata["watch_sync_provider"] = value + } if len(descriptor.GetConfigSchema()) > 0 { schemas := make([]map[string]any, 0, len(descriptor.GetConfigSchema())) for _, schema := range descriptor.GetConfigSchema() { diff --git a/pkg/pluginsdk/convert/watch_sync_provider_test.go b/pkg/pluginsdk/convert/watch_sync_provider_test.go new file mode 100644 index 0000000..192ebdc --- /dev/null +++ b/pkg/pluginsdk/convert/watch_sync_provider_test.go @@ -0,0 +1,33 @@ +package convert_test + +import ( + "testing" + + pluginv1 "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1" + "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/convert" +) + +func TestWatchSyncProviderDescriptorRoundTrip(t *testing.T) { + manifest := &pluginv1.PluginManifest{Capabilities: []*pluginv1.CapabilityDescriptor{{ + Type: "watch_sync_provider.v1", + Id: "anilist", + WatchSyncProvider: &pluginv1.WatchSyncProviderDescriptor{ + AuthMethods: []pluginv1.WatchSyncAuthMethod{pluginv1.WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE}, + ExportWatched: true, + SupportedMediaTypes: []string{"movie", "episode"}, + MaxBatchSize: 25, + }, + }}} + records, err := convert.CapabilityRecordsFromManifest(manifest) + if err != nil { + t.Fatal(err) + } + decoded, err := convert.DecodeCapability(records[0]) + if err != nil { + t.Fatal(err) + } + got := decoded.GetWatchSyncProvider() + if got == nil || !got.GetExportWatched() || got.GetMaxBatchSize() != 25 || len(got.GetAuthMethods()) != 1 { + t.Fatalf("decoded descriptor = %#v", got) + } +} diff --git a/pkg/pluginsdk/manifest/manifest.go b/pkg/pluginsdk/manifest/manifest.go index 08e2c85..5053db6 100644 --- a/pkg/pluginsdk/manifest/manifest.go +++ b/pkg/pluginsdk/manifest/manifest.go @@ -6,6 +6,7 @@ import ( "io/fs" "net/url" "os" + "regexp" "strings" "unicode" "unicode/utf8" @@ -22,7 +23,10 @@ import ( // Note: capability types are recorded purely for discovery — the host exposes // them via the admin plugins API so SPAs can filter by them client-side. // They do not drive any dispatch behavior on the server side. -var knownCapabilityTypes = knownCapabilityTypeSet() +var ( + knownCapabilityTypes = knownCapabilityTypeSet() + watchSyncSlugPattern = regexp.MustCompile(`^[a-z0-9]+(?:[._-][a-z0-9]+)*$`) +) const ( maxPresentationDisplayNameRunes = 120 @@ -106,6 +110,9 @@ func Validate(manifest *pluginv1.PluginManifest) error { if _, ok := knownCapabilityTypes[capability.Type]; !ok { return fmt.Errorf("plugin capability %q: unknown type %q", capability.Id, capability.Type) } + if err := validateWatchSyncCapability(capability); err != nil { + return err + } } for _, schema := range manifest.GlobalConfigSchema { if err := validateConfigSchema(schema); err != nil { @@ -127,6 +134,42 @@ func Validate(manifest *pluginv1.PluginManifest) error { return nil } +func validateWatchSyncCapability(descriptor *pluginv1.CapabilityDescriptor) error { + watchSync := descriptor.GetWatchSyncProvider() + if descriptor.GetType() != capability.WatchSyncProvider { + if watchSync != nil { + return fmt.Errorf("plugin capability %q: watch_sync_provider descriptor requires type %q", descriptor.GetId(), capability.WatchSyncProvider) + } + return nil + } + if !watchSyncSlugPattern.MatchString(descriptor.GetId()) { + return fmt.Errorf("plugin capability %q: watch sync capability id must be a path-safe lowercase slug", descriptor.GetId()) + } + if watchSync == nil { + return fmt.Errorf("plugin capability %q: watch_sync_provider descriptor is required", descriptor.GetId()) + } + if len(watchSync.GetAuthMethods()) == 0 { + return fmt.Errorf("plugin capability %q: at least one watch sync auth method is required", descriptor.GetId()) + } + if !watchSync.GetExportWatched() && !watchSync.GetExportUnwatched() && !watchSync.GetImportWatched() && !watchSync.GetImportProgress() { + return fmt.Errorf("plugin capability %q: at least one watch sync operation is required", descriptor.GetId()) + } + if watchSync.GetMaxBatchSize() < 1 || watchSync.GetMaxBatchSize() > 100 { + return fmt.Errorf("plugin capability %q: watch sync max_batch_size must be between 1 and 100", descriptor.GetId()) + } + for _, mediaType := range watchSync.GetSupportedMediaTypes() { + if mediaType != "movie" && mediaType != "episode" { + return fmt.Errorf("plugin capability %q: unsupported watch sync media type %q", descriptor.GetId(), mediaType) + } + } + for _, namespace := range watchSync.GetExternalIdNamespaces() { + if !watchSyncSlugPattern.MatchString(namespace) { + return fmt.Errorf("plugin capability %q: invalid external id namespace %q", descriptor.GetId(), namespace) + } + } + return nil +} + // ValidateCatalogPresentation applies the stricter presentation contract used // by curated catalogs. The general manifest validator keeps presentation // optional so older and directly uploaded plugins remain compatible. diff --git a/pkg/pluginsdk/manifest/watch_sync_provider_test.go b/pkg/pluginsdk/manifest/watch_sync_provider_test.go new file mode 100644 index 0000000..36726a2 --- /dev/null +++ b/pkg/pluginsdk/manifest/watch_sync_provider_test.go @@ -0,0 +1,55 @@ +package manifest_test + +import ( + "testing" + + pluginv1 "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1" + publicmanifest "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/manifest" +) + +func TestLoadWatchSyncProvider(t *testing.T) { + raw := []byte(`{ + "plugin_id":"silo.anilist", "version":"1.0.0", "silo_api_version":"v1", + "capabilities":[{ + "type":"watch_sync_provider.v1", "id":"anilist", "display_name":"AniList", + "watch_sync_provider":{ + "auth_methods":["WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE"], + "export_watched":true, + "supported_media_types":["movie","episode"], + "external_id_namespaces":["anilist","tmdb","tvdb"], + "max_batch_size":25 + } + }] + }`) + manifest, err := publicmanifest.Load(raw) + if err != nil { + t.Fatal(err) + } + descriptor := manifest.GetCapabilities()[0].GetWatchSyncProvider() + if descriptor == nil || !descriptor.GetExportWatched() || descriptor.GetMaxBatchSize() != 25 { + t.Fatalf("watch sync descriptor = %#v", descriptor) + } +} + +func TestValidateWatchSyncProviderRejectsMissingDescriptor(t *testing.T) { + manifest := &pluginv1.PluginManifest{ + PluginId: "silo.invalid", Version: "1.0.0", + Capabilities: []*pluginv1.CapabilityDescriptor{{Type: "watch_sync_provider.v1", Id: "invalid"}}, + } + if err := publicmanifest.Validate(manifest); err == nil { + t.Fatal("expected missing watch sync descriptor to fail") + } +} + +func TestValidateWatchSyncProviderRejectsDescriptorOnOtherCapability(t *testing.T) { + manifest := &pluginv1.PluginManifest{ + PluginId: "silo.invalid", Version: "1.0.0", + Capabilities: []*pluginv1.CapabilityDescriptor{{ + Type: "event_consumer.v1", Id: "events", + WatchSyncProvider: &pluginv1.WatchSyncProviderDescriptor{MaxBatchSize: 1}, + }}, + } + if err := publicmanifest.Validate(manifest); err == nil { + t.Fatal("expected misplaced watch sync descriptor to fail") + } +} diff --git a/pkg/pluginsdk/runtime/runtime.go b/pkg/pluginsdk/runtime/runtime.go index 2429290..4bd1285 100644 --- a/pkg/pluginsdk/runtime/runtime.go +++ b/pkg/pluginsdk/runtime/runtime.go @@ -23,17 +23,18 @@ const ( ) type CapabilityServers struct { - Runtime pluginv1.RuntimeServer - MetadataProvider pluginv1.MetadataProviderServer - ImageResolver pluginv1.ImageResolverServer - MarkerProvider pluginv1.MarkerProviderServer - MediaAnalyzer pluginv1.MediaAnalyzerServer - ScheduledTask pluginv1.ScheduledTaskServer - ScanSource pluginv1.ScanSourceServer - RequestRouter pluginv1.RequestRouterServer - EventConsumer pluginv1.EventConsumerServer - AuthProvider pluginv1.AuthProviderServer - HttpRoutes pluginv1.HttpRoutesServer + Runtime pluginv1.RuntimeServer + MetadataProvider pluginv1.MetadataProviderServer + ImageResolver pluginv1.ImageResolverServer + MarkerProvider pluginv1.MarkerProviderServer + MediaAnalyzer pluginv1.MediaAnalyzerServer + ScheduledTask pluginv1.ScheduledTaskServer + ScanSource pluginv1.ScanSourceServer + RequestRouter pluginv1.RequestRouterServer + EventConsumer pluginv1.EventConsumerServer + AuthProvider pluginv1.AuthProviderServer + HttpRoutes pluginv1.HttpRoutesServer + WatchSyncProvider pluginv1.WatchSyncProviderServer } // Client wraps the gRPC connection to a plugin and provides typed accessors @@ -128,6 +129,10 @@ func (c *Client) HttpRoutes() pluginv1.HttpRoutesClient { return pluginv1.NewHttpRoutesClient(c.conn) } +func (c *Client) WatchSyncProvider() pluginv1.WatchSyncProviderClient { + return pluginv1.NewWatchSyncProviderClient(c.conn) +} + type GRPCPlugin struct { plugin.Plugin Servers CapabilityServers @@ -170,6 +175,9 @@ func (p *GRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, server *grpc.Server) if p.Servers.HttpRoutes != nil { pluginv1.RegisterHttpRoutesServer(server, p.Servers.HttpRoutes) } + if p.Servers.WatchSyncProvider != nil { + pluginv1.RegisterWatchSyncProviderServer(server, p.Servers.WatchSyncProvider) + } return nil } diff --git a/pkg/pluginsdk/runtime/watch_sync_provider_test.go b/pkg/pluginsdk/runtime/watch_sync_provider_test.go new file mode 100644 index 0000000..4b5d408 --- /dev/null +++ b/pkg/pluginsdk/runtime/watch_sync_provider_test.go @@ -0,0 +1,27 @@ +package runtime_test + +import ( + "testing" + + pluginv1 "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1" + runtime "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/runtime" + "google.golang.org/grpc" +) + +type stubWatchSyncProvider struct { + pluginv1.UnimplementedWatchSyncProviderServer +} + +func TestGRPCServerRegistersWatchSyncProvider(t *testing.T) { + p := &runtime.GRPCPlugin{Servers: runtime.CapabilityServers{ + Runtime: stubRuntime{}, + WatchSyncProvider: stubWatchSyncProvider{}, + }} + srv := grpc.NewServer() + if err := p.GRPCServer(nil, srv); err != nil { + t.Fatalf("GRPCServer with WatchSyncProvider = %v, want nil", err) + } + if _, ok := srv.GetServiceInfo()["silo.plugin.v1.WatchSyncProvider"]; !ok { + t.Fatalf("WatchSyncProvider service not registered; got %v", srv.GetServiceInfo()) + } +} diff --git a/proto/silo/plugin/v1/common.proto b/proto/silo/plugin/v1/common.proto index f6de45c..adf3ef0 100644 --- a/proto/silo/plugin/v1/common.proto +++ b/proto/silo/plugin/v1/common.proto @@ -5,6 +5,7 @@ package silo.plugin.v1; option go_package = "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1;pluginv1"; import "google/protobuf/struct.proto"; +import "silo/plugin/v1/watch_sync_provider.proto"; message SupportedPlatform { string os = 1; @@ -136,6 +137,11 @@ message CapabilityDescriptor { // login UI as the icon for a "Sign in with X" button. Plugin-served path // (e.g., /api/v1/plugins//assets/whmcs-logo.svg). string icon_url = 9; + + // Typed watch-provider contract metadata. Only meaningful for capabilities + // of type "watch_sync_provider.v1". Keeping this typed lets the host build + // connection and authorization UI without launching the plugin. + WatchSyncProviderDescriptor watch_sync_provider = 10; } // PluginPresentation describes a plugin for server operators and links them to diff --git a/proto/silo/plugin/v1/watch_sync_provider.proto b/proto/silo/plugin/v1/watch_sync_provider.proto new file mode 100644 index 0000000..e362731 --- /dev/null +++ b/proto/silo/plugin/v1/watch_sync_provider.proto @@ -0,0 +1,241 @@ +syntax = "proto3"; + +package silo.plugin.v1; + +option go_package = "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1;pluginv1"; + +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; + +// WatchSyncProvider adapts an external watch-tracking service to Silo's +// host-owned credential, outbox, retry, and reconciliation pipeline. Plugins +// must treat credentials and provider secrets as transient request data and +// must not persist or log them. +service WatchSyncProvider { + rpc InitAuthorize(WatchSyncInitAuthorizeRequest) returns (WatchSyncInitAuthorizeResponse); + rpc ExchangeCode(WatchSyncExchangeCodeRequest) returns (WatchSyncCredentialResponse); + rpc ExchangeAPIKey(WatchSyncExchangeAPIKeyRequest) returns (WatchSyncCredentialResponse); + rpc RefreshCredentials(WatchSyncRefreshCredentialsRequest) returns (WatchSyncCredentialResponse); + rpc GetAccount(WatchSyncGetAccountRequest) returns (WatchSyncGetAccountResponse); + rpc ApplyEvents(WatchSyncApplyEventsRequest) returns (WatchSyncApplyEventsResponse); + rpc ListRemoteState(WatchSyncListRemoteStateRequest) returns (WatchSyncListRemoteStateResponse); +} + +enum WatchSyncAuthMethod { + WATCH_SYNC_AUTH_METHOD_UNSPECIFIED = 0; + WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE = 1; + WATCH_SYNC_AUTH_METHOD_API_KEY = 2; +} + +enum WatchSyncOperation { + WATCH_SYNC_OPERATION_UNSPECIFIED = 0; + WATCH_SYNC_OPERATION_MARK_WATCHED = 1; + WATCH_SYNC_OPERATION_MARK_UNWATCHED = 2; +} + +enum WatchSyncOrigin { + WATCH_SYNC_ORIGIN_UNSPECIFIED = 0; + WATCH_SYNC_ORIGIN_PLAYBACK_COMPLETION = 1; + WATCH_SYNC_ORIGIN_MANUAL = 2; + WATCH_SYNC_ORIGIN_RECONCILIATION = 3; +} + +enum WatchSyncApplyStatus { + WATCH_SYNC_APPLY_STATUS_UNSPECIFIED = 0; + WATCH_SYNC_APPLY_STATUS_APPLIED = 1; + WATCH_SYNC_APPLY_STATUS_NO_CHANGE = 2; + WATCH_SYNC_APPLY_STATUS_RETRY = 3; + WATCH_SYNC_APPLY_STATUS_REJECTED = 4; +} + +enum WatchSyncFaultCode { + WATCH_SYNC_FAULT_CODE_UNSPECIFIED = 0; + WATCH_SYNC_FAULT_CODE_TEMPORARY = 1; + WATCH_SYNC_FAULT_CODE_RATE_LIMITED = 2; + WATCH_SYNC_FAULT_CODE_INVALID_CREDENTIAL = 3; + WATCH_SYNC_FAULT_CODE_PERMISSION_DENIED = 4; + WATCH_SYNC_FAULT_CODE_INVALID_REQUEST = 5; + WATCH_SYNC_FAULT_CODE_PERMANENT = 6; +} + +message WatchSyncProviderDescriptor { + repeated WatchSyncAuthMethod auth_methods = 1; + bool export_watched = 2; + bool export_unwatched = 3; + bool import_watched = 4; + bool import_progress = 5; + repeated string supported_media_types = 6; + repeated string external_id_namespaces = 7; + int32 max_batch_size = 8; +} + +message WatchSyncProviderConfig { + map values = 1; + // The host encrypts these values at rest and includes them only in RPCs that + // need them. They are never Runtime.Configure entries. + map secret_values = 2; +} + +message WatchSyncCredentials { + string access_token = 1; + string refresh_token = 2; + google.protobuf.Timestamp expires_at = 3; + string token_type = 4; + repeated string scopes = 5; + // Provider-specific token fields. The host encrypts the credential as one + // opaque record and does not interpret these values. + map secret_attributes = 6; +} + +message WatchSyncAccount { + string external_subject = 1; + string username = 2; + string display_name = 3; + string avatar_url = 4; + string profile_url = 5; +} + +message WatchSyncFault { + WatchSyncFaultCode code = 1; + // Safe for persistence and operator display. Must not contain credentials, + // authorization codes, provider response bodies, or other secrets. + string safe_message = 2; + google.protobuf.Duration retry_after = 3; +} + +message WatchSyncInitAuthorizeRequest { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + string redirect_uri = 3; + // Generated, stored, and validated by the host. + string state = 4; +} + +message WatchSyncInitAuthorizeResponse { + string authorization_url = 1; + // Plugin-defined flow data. The host encrypts it until callback completion + // and never returns it through the public HTTP API. + bytes provider_state = 2; + WatchSyncFault fault = 3; +} + +message WatchSyncExchangeCodeRequest { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + string redirect_uri = 3; + string authorization_code = 4; + bytes provider_state = 5; +} + +message WatchSyncExchangeAPIKeyRequest { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + // API key or manually-issued bearer token. The host does not persist this + // request field unless the plugin validates and returns it as credentials. + string api_key = 3; +} + +message WatchSyncRefreshCredentialsRequest { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + WatchSyncCredentials credentials = 3; +} + +message WatchSyncCredentialResponse { + WatchSyncCredentials credentials = 1; + WatchSyncAccount account = 2; + WatchSyncFault fault = 3; +} + +message WatchSyncGetAccountRequest { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + WatchSyncCredentials credentials = 3; +} + +message WatchSyncGetAccountResponse { + WatchSyncAccount account = 1; + WatchSyncFault fault = 2; +} + +message WatchSyncMedia { + string media_item_id = 1; + string media_type = 2; + string title = 3; + string original_title = 4; + int32 year = 5; + map external_ids = 6; + string series_media_item_id = 7; + string series_title = 8; + int32 series_year = 9; + map series_external_ids = 10; + int32 season_number = 11; + int32 episode_number = 12; + int32 absolute_episode_number = 13; + int32 series_episode_count = 14; + google.protobuf.Struct metadata = 15; +} + +message WatchSyncEvent { + // Stable across retries and unique within one provider connection. + string event_id = 1; + WatchSyncOperation operation = 2; + WatchSyncOrigin origin = 3; + google.protobuf.Timestamp occurred_at = 4; + WatchSyncMedia media = 5; + string watch_history_id = 6; + string playback_session_id = 7; + double position_seconds = 8; + double duration_seconds = 9; + double completion_percent = 10; + int32 play_count = 11; +} + +message WatchSyncApplyEventsRequest { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + WatchSyncCredentials credentials = 3; + // Ordered desired-state events. Duplicate event IDs must not be interpreted + // as increments; ApplyEvents is an at-least-once delivery contract. + repeated WatchSyncEvent events = 4; +} + +message WatchSyncApplyResult { + string event_id = 1; + WatchSyncApplyStatus status = 2; + string safe_message = 3; + google.protobuf.Duration retry_after = 4; +} + +message WatchSyncApplyEventsResponse { + repeated WatchSyncApplyResult results = 1; + WatchSyncCredentials updated_credentials = 2; + WatchSyncFault fault = 3; +} + +message WatchSyncListRemoteStateRequest { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + WatchSyncCredentials credentials = 3; + string cursor = 4; + string page_token = 5; + int32 page_size = 6; +} + +message WatchSyncRemoteState { + string provider_item_key = 1; + WatchSyncMedia media = 2; + int32 progress = 3; + string status = 4; + google.protobuf.Timestamp updated_at = 5; +} + +message WatchSyncListRemoteStateResponse { + repeated WatchSyncRemoteState items = 1; + string next_page_token = 2; + string next_cursor = 3; + bool complete_snapshot = 4; + WatchSyncCredentials updated_credentials = 5; + WatchSyncFault fault = 6; +} From 1c9b623b31b845d713ee718b3f2ab36ad259ab80 Mon Sep 17 00:00:00 2001 From: tan Date: Sat, 18 Jul 2026 21:55:01 -0600 Subject: [PATCH 2/5] fix: tighten watch sync descriptor validation --- pkg/pluginsdk/manifest/manifest.go | 8 +++++ .../manifest/watch_sync_provider_test.go | 35 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/pkg/pluginsdk/manifest/manifest.go b/pkg/pluginsdk/manifest/manifest.go index 5053db6..a403525 100644 --- a/pkg/pluginsdk/manifest/manifest.go +++ b/pkg/pluginsdk/manifest/manifest.go @@ -151,12 +151,20 @@ func validateWatchSyncCapability(descriptor *pluginv1.CapabilityDescriptor) erro if len(watchSync.GetAuthMethods()) == 0 { return fmt.Errorf("plugin capability %q: at least one watch sync auth method is required", descriptor.GetId()) } + for _, method := range watchSync.GetAuthMethods() { + if method == pluginv1.WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_UNSPECIFIED { + return fmt.Errorf("plugin capability %q: watch sync auth method cannot be unspecified", descriptor.GetId()) + } + } if !watchSync.GetExportWatched() && !watchSync.GetExportUnwatched() && !watchSync.GetImportWatched() && !watchSync.GetImportProgress() { return fmt.Errorf("plugin capability %q: at least one watch sync operation is required", descriptor.GetId()) } if watchSync.GetMaxBatchSize() < 1 || watchSync.GetMaxBatchSize() > 100 { return fmt.Errorf("plugin capability %q: watch sync max_batch_size must be between 1 and 100", descriptor.GetId()) } + if len(watchSync.GetSupportedMediaTypes()) == 0 { + return fmt.Errorf("plugin capability %q: at least one supported media type is required", descriptor.GetId()) + } for _, mediaType := range watchSync.GetSupportedMediaTypes() { if mediaType != "movie" && mediaType != "episode" { return fmt.Errorf("plugin capability %q: unsupported watch sync media type %q", descriptor.GetId(), mediaType) diff --git a/pkg/pluginsdk/manifest/watch_sync_provider_test.go b/pkg/pluginsdk/manifest/watch_sync_provider_test.go index 36726a2..ec6eedc 100644 --- a/pkg/pluginsdk/manifest/watch_sync_provider_test.go +++ b/pkg/pluginsdk/manifest/watch_sync_provider_test.go @@ -41,6 +41,41 @@ func TestValidateWatchSyncProviderRejectsMissingDescriptor(t *testing.T) { } } +func TestValidateWatchSyncProviderRejectsUnspecifiedAuthMethod(t *testing.T) { + manifest := validWatchSyncManifest() + manifest.Capabilities[0].WatchSyncProvider.AuthMethods = []pluginv1.WatchSyncAuthMethod{ + pluginv1.WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_UNSPECIFIED, + } + if err := publicmanifest.Validate(manifest); err == nil { + t.Fatal("expected unspecified auth method to fail") + } +} + +func TestValidateWatchSyncProviderRejectsEmptyMediaTypes(t *testing.T) { + manifest := validWatchSyncManifest() + manifest.Capabilities[0].WatchSyncProvider.SupportedMediaTypes = nil + if err := publicmanifest.Validate(manifest); err == nil { + t.Fatal("expected empty supported media types to fail") + } +} + +func validWatchSyncManifest() *pluginv1.PluginManifest { + return &pluginv1.PluginManifest{ + PluginId: "silo.valid", Version: "1.0.0", + Capabilities: []*pluginv1.CapabilityDescriptor{{ + Type: "watch_sync_provider.v1", Id: "valid", + WatchSyncProvider: &pluginv1.WatchSyncProviderDescriptor{ + AuthMethods: []pluginv1.WatchSyncAuthMethod{ + pluginv1.WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_API_KEY, + }, + ExportWatched: true, + SupportedMediaTypes: []string{"movie"}, + MaxBatchSize: 1, + }, + }}, + } +} + func TestValidateWatchSyncProviderRejectsDescriptorOnOtherCapability(t *testing.T) { manifest := &pluginv1.PluginManifest{ PluginId: "silo.invalid", Version: "1.0.0", From b82a1ce1d553b378f9ec1ddb01c672ececa1f74b Mon Sep 17 00:00:00 2001 From: Quick <31828688+Quick104@users.noreply.github.com> Date: Fri, 24 Jul 2026 09:06:13 -0400 Subject: [PATCH 3/5] refine watch sync provider contract --- README.md | 18 + .../silo/plugin/v1/watch_sync_provider.pb.go | 862 +++++++++++------- .../plugin/v1/watch_sync_provider_test.go | 77 ++ .../convert/watch_sync_provider_test.go | 15 +- pkg/pluginsdk/manifest/manifest.go | 4 +- .../manifest/watch_sync_provider_test.go | 38 +- .../silo/plugin/v1/watch_sync_provider.proto | 91 +- 7 files changed, 728 insertions(+), 377 deletions(-) create mode 100644 pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go diff --git a/README.md b/README.md index 9699420..b9fbd45 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,24 @@ provider flow state, or secret configuration. `ApplyEvents` is an at-least-once contract; plugins must treat `event_id` as stable across retries and implement convergent desired-state updates rather than increments. +Authenticated RPCs receive the same host-owned capability, configuration, and +credential data through `WatchSyncAuthenticatedContext`. The context exists +only for one invocation and is never plugin configuration or plugin state. + +Descriptors and events use the shared `WatchSyncMediaType` enum so advertised +support and delivered media cannot drift between string conventions. Apply +results pair their delivery status with a typed fault: successful results omit +the fault, temporary retries use `TEMPORARY`, rate limits use `RATE_LIMITED` +with an optional delay, and rejected events use a non-retryable fault code. +Connection-wide faults such as invalid credentials belong on the RPC response. + +`ListRemoteState` returns provider-neutral typed subrecords. `watched` carries a +play count and last-watched time; `progress` carries a fractional percentage and +paused time. An item may contain either or both. The host keeps the request +`cursor` fixed while following ephemeral page tokens, commits every page, and +only then persists the final `next_cursor`. `complete_snapshot=true` means the +traversal is authoritative; when false, missing items are not deletions. + ## Scan sources The `scan_source.v1` capability is for Autoscan providers. The host owns the diff --git a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go index 65f25fb..4bfcec6 100644 --- a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go +++ b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go @@ -73,6 +73,55 @@ func (WatchSyncAuthMethod) EnumDescriptor() ([]byte, []int) { return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{0} } +type WatchSyncMediaType int32 + +const ( + WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED WatchSyncMediaType = 0 + WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_MOVIE WatchSyncMediaType = 1 + WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_EPISODE WatchSyncMediaType = 2 +) + +// Enum value maps for WatchSyncMediaType. +var ( + WatchSyncMediaType_name = map[int32]string{ + 0: "WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED", + 1: "WATCH_SYNC_MEDIA_TYPE_MOVIE", + 2: "WATCH_SYNC_MEDIA_TYPE_EPISODE", + } + WatchSyncMediaType_value = map[string]int32{ + "WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED": 0, + "WATCH_SYNC_MEDIA_TYPE_MOVIE": 1, + "WATCH_SYNC_MEDIA_TYPE_EPISODE": 2, + } +) + +func (x WatchSyncMediaType) Enum() *WatchSyncMediaType { + p := new(WatchSyncMediaType) + *p = x + return p +} + +func (x WatchSyncMediaType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WatchSyncMediaType) Descriptor() protoreflect.EnumDescriptor { + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[1].Descriptor() +} + +func (WatchSyncMediaType) Type() protoreflect.EnumType { + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[1] +} + +func (x WatchSyncMediaType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WatchSyncMediaType.Descriptor instead. +func (WatchSyncMediaType) EnumDescriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{1} +} + type WatchSyncOperation int32 const ( @@ -106,11 +155,11 @@ func (x WatchSyncOperation) String() string { } func (WatchSyncOperation) Descriptor() protoreflect.EnumDescriptor { - return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[1].Descriptor() + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[2].Descriptor() } func (WatchSyncOperation) Type() protoreflect.EnumType { - return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[1] + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[2] } func (x WatchSyncOperation) Number() protoreflect.EnumNumber { @@ -119,7 +168,7 @@ func (x WatchSyncOperation) Number() protoreflect.EnumNumber { // Deprecated: Use WatchSyncOperation.Descriptor instead. func (WatchSyncOperation) EnumDescriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{1} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{2} } type WatchSyncOrigin int32 @@ -158,11 +207,11 @@ func (x WatchSyncOrigin) String() string { } func (WatchSyncOrigin) Descriptor() protoreflect.EnumDescriptor { - return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[2].Descriptor() + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[3].Descriptor() } func (WatchSyncOrigin) Type() protoreflect.EnumType { - return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[2] + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[3] } func (x WatchSyncOrigin) Number() protoreflect.EnumNumber { @@ -171,7 +220,7 @@ func (x WatchSyncOrigin) Number() protoreflect.EnumNumber { // Deprecated: Use WatchSyncOrigin.Descriptor instead. func (WatchSyncOrigin) EnumDescriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{2} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{3} } type WatchSyncApplyStatus int32 @@ -213,11 +262,11 @@ func (x WatchSyncApplyStatus) String() string { } func (WatchSyncApplyStatus) Descriptor() protoreflect.EnumDescriptor { - return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[3].Descriptor() + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[4].Descriptor() } func (WatchSyncApplyStatus) Type() protoreflect.EnumType { - return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[3] + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[4] } func (x WatchSyncApplyStatus) Number() protoreflect.EnumNumber { @@ -226,7 +275,7 @@ func (x WatchSyncApplyStatus) Number() protoreflect.EnumNumber { // Deprecated: Use WatchSyncApplyStatus.Descriptor instead. func (WatchSyncApplyStatus) EnumDescriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{3} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{4} } type WatchSyncFaultCode int32 @@ -274,11 +323,11 @@ func (x WatchSyncFaultCode) String() string { } func (WatchSyncFaultCode) Descriptor() protoreflect.EnumDescriptor { - return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[4].Descriptor() + return file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[5].Descriptor() } func (WatchSyncFaultCode) Type() protoreflect.EnumType { - return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[4] + return &file_silo_plugin_v1_watch_sync_provider_proto_enumTypes[5] } func (x WatchSyncFaultCode) Number() protoreflect.EnumNumber { @@ -287,7 +336,7 @@ func (x WatchSyncFaultCode) Number() protoreflect.EnumNumber { // Deprecated: Use WatchSyncFaultCode.Descriptor instead. func (WatchSyncFaultCode) EnumDescriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{4} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{5} } type WatchSyncProviderDescriptor struct { @@ -297,7 +346,7 @@ type WatchSyncProviderDescriptor struct { ExportUnwatched bool `protobuf:"varint,3,opt,name=export_unwatched,json=exportUnwatched,proto3" json:"export_unwatched,omitempty"` ImportWatched bool `protobuf:"varint,4,opt,name=import_watched,json=importWatched,proto3" json:"import_watched,omitempty"` ImportProgress bool `protobuf:"varint,5,opt,name=import_progress,json=importProgress,proto3" json:"import_progress,omitempty"` - SupportedMediaTypes []string `protobuf:"bytes,6,rep,name=supported_media_types,json=supportedMediaTypes,proto3" json:"supported_media_types,omitempty"` + SupportedMediaTypes []WatchSyncMediaType `protobuf:"varint,6,rep,packed,name=supported_media_types,json=supportedMediaTypes,proto3,enum=silo.plugin.v1.WatchSyncMediaType" json:"supported_media_types,omitempty"` ExternalIdNamespaces []string `protobuf:"bytes,7,rep,name=external_id_namespaces,json=externalIdNamespaces,proto3" json:"external_id_namespaces,omitempty"` MaxBatchSize int32 `protobuf:"varint,8,opt,name=max_batch_size,json=maxBatchSize,proto3" json:"max_batch_size,omitempty"` unknownFields protoimpl.UnknownFields @@ -369,7 +418,7 @@ func (x *WatchSyncProviderDescriptor) GetImportProgress() bool { return false } -func (x *WatchSyncProviderDescriptor) GetSupportedMediaTypes() []string { +func (x *WatchSyncProviderDescriptor) GetSupportedMediaTypes() []WatchSyncMediaType { if x != nil { return x.SupportedMediaTypes } @@ -530,6 +579,68 @@ func (x *WatchSyncCredentials) GetSecretAttributes() map[string]string { return nil } +// Host-owned context shared by RPCs after a profile connection is established. +// Plugins must treat provider_config and credentials as transient request data. +type WatchSyncAuthenticatedContext struct { + state protoimpl.MessageState `protogen:"open.v1"` + CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` + ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` + Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncAuthenticatedContext) Reset() { + *x = WatchSyncAuthenticatedContext{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncAuthenticatedContext) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncAuthenticatedContext) ProtoMessage() {} + +func (x *WatchSyncAuthenticatedContext) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncAuthenticatedContext.ProtoReflect.Descriptor instead. +func (*WatchSyncAuthenticatedContext) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{3} +} + +func (x *WatchSyncAuthenticatedContext) GetCapabilityId() string { + if x != nil { + return x.CapabilityId + } + return "" +} + +func (x *WatchSyncAuthenticatedContext) GetProviderConfig() *WatchSyncProviderConfig { + if x != nil { + return x.ProviderConfig + } + return nil +} + +func (x *WatchSyncAuthenticatedContext) GetCredentials() *WatchSyncCredentials { + if x != nil { + return x.Credentials + } + return nil +} + type WatchSyncAccount struct { state protoimpl.MessageState `protogen:"open.v1"` ExternalSubject string `protobuf:"bytes,1,opt,name=external_subject,json=externalSubject,proto3" json:"external_subject,omitempty"` @@ -543,7 +654,7 @@ type WatchSyncAccount struct { func (x *WatchSyncAccount) Reset() { *x = WatchSyncAccount{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[3] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -555,7 +666,7 @@ func (x *WatchSyncAccount) String() string { func (*WatchSyncAccount) ProtoMessage() {} func (x *WatchSyncAccount) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[3] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -568,7 +679,7 @@ func (x *WatchSyncAccount) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncAccount.ProtoReflect.Descriptor instead. func (*WatchSyncAccount) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{3} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{4} } func (x *WatchSyncAccount) GetExternalSubject() string { @@ -611,7 +722,9 @@ type WatchSyncFault struct { Code WatchSyncFaultCode `protobuf:"varint,1,opt,name=code,proto3,enum=silo.plugin.v1.WatchSyncFaultCode" json:"code,omitempty"` // Safe for persistence and operator display. Must not contain credentials, // authorization codes, provider response bodies, or other secrets. - SafeMessage string `protobuf:"bytes,2,opt,name=safe_message,json=safeMessage,proto3" json:"safe_message,omitempty"` + SafeMessage string `protobuf:"bytes,2,opt,name=safe_message,json=safeMessage,proto3" json:"safe_message,omitempty"` + // Optional retry delay for TEMPORARY and RATE_LIMITED faults. Only + // RATE_LIMITED tells the host to defer the whole provider connection. RetryAfter *durationpb.Duration `protobuf:"bytes,3,opt,name=retry_after,json=retryAfter,proto3" json:"retry_after,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -619,7 +732,7 @@ type WatchSyncFault struct { func (x *WatchSyncFault) Reset() { *x = WatchSyncFault{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[4] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -631,7 +744,7 @@ func (x *WatchSyncFault) String() string { func (*WatchSyncFault) ProtoMessage() {} func (x *WatchSyncFault) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[4] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -644,7 +757,7 @@ func (x *WatchSyncFault) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncFault.ProtoReflect.Descriptor instead. func (*WatchSyncFault) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{4} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{5} } func (x *WatchSyncFault) GetCode() WatchSyncFaultCode { @@ -681,7 +794,7 @@ type WatchSyncInitAuthorizeRequest struct { func (x *WatchSyncInitAuthorizeRequest) Reset() { *x = WatchSyncInitAuthorizeRequest{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[5] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -693,7 +806,7 @@ func (x *WatchSyncInitAuthorizeRequest) String() string { func (*WatchSyncInitAuthorizeRequest) ProtoMessage() {} func (x *WatchSyncInitAuthorizeRequest) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[5] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -706,7 +819,7 @@ func (x *WatchSyncInitAuthorizeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncInitAuthorizeRequest.ProtoReflect.Descriptor instead. func (*WatchSyncInitAuthorizeRequest) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{5} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{6} } func (x *WatchSyncInitAuthorizeRequest) GetCapabilityId() string { @@ -750,7 +863,7 @@ type WatchSyncInitAuthorizeResponse struct { func (x *WatchSyncInitAuthorizeResponse) Reset() { *x = WatchSyncInitAuthorizeResponse{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[6] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -762,7 +875,7 @@ func (x *WatchSyncInitAuthorizeResponse) String() string { func (*WatchSyncInitAuthorizeResponse) ProtoMessage() {} func (x *WatchSyncInitAuthorizeResponse) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[6] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -775,7 +888,7 @@ func (x *WatchSyncInitAuthorizeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncInitAuthorizeResponse.ProtoReflect.Descriptor instead. func (*WatchSyncInitAuthorizeResponse) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{6} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{7} } func (x *WatchSyncInitAuthorizeResponse) GetAuthorizationUrl() string { @@ -812,7 +925,7 @@ type WatchSyncExchangeCodeRequest struct { func (x *WatchSyncExchangeCodeRequest) Reset() { *x = WatchSyncExchangeCodeRequest{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[7] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -824,7 +937,7 @@ func (x *WatchSyncExchangeCodeRequest) String() string { func (*WatchSyncExchangeCodeRequest) ProtoMessage() {} func (x *WatchSyncExchangeCodeRequest) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[7] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -837,7 +950,7 @@ func (x *WatchSyncExchangeCodeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncExchangeCodeRequest.ProtoReflect.Descriptor instead. func (*WatchSyncExchangeCodeRequest) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{7} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{8} } func (x *WatchSyncExchangeCodeRequest) GetCapabilityId() string { @@ -888,7 +1001,7 @@ type WatchSyncExchangeAPIKeyRequest struct { func (x *WatchSyncExchangeAPIKeyRequest) Reset() { *x = WatchSyncExchangeAPIKeyRequest{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[8] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -900,7 +1013,7 @@ func (x *WatchSyncExchangeAPIKeyRequest) String() string { func (*WatchSyncExchangeAPIKeyRequest) ProtoMessage() {} func (x *WatchSyncExchangeAPIKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[8] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -913,7 +1026,7 @@ func (x *WatchSyncExchangeAPIKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncExchangeAPIKeyRequest.ProtoReflect.Descriptor instead. func (*WatchSyncExchangeAPIKeyRequest) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{8} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{9} } func (x *WatchSyncExchangeAPIKeyRequest) GetCapabilityId() string { @@ -938,17 +1051,15 @@ func (x *WatchSyncExchangeAPIKeyRequest) GetApiKey() string { } type WatchSyncRefreshCredentialsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` - ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` - Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Context *WatchSyncAuthenticatedContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *WatchSyncRefreshCredentialsRequest) Reset() { *x = WatchSyncRefreshCredentialsRequest{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[9] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -960,7 +1071,7 @@ func (x *WatchSyncRefreshCredentialsRequest) String() string { func (*WatchSyncRefreshCredentialsRequest) ProtoMessage() {} func (x *WatchSyncRefreshCredentialsRequest) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[9] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -973,26 +1084,12 @@ func (x *WatchSyncRefreshCredentialsRequest) ProtoReflect() protoreflect.Message // Deprecated: Use WatchSyncRefreshCredentialsRequest.ProtoReflect.Descriptor instead. func (*WatchSyncRefreshCredentialsRequest) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{9} -} - -func (x *WatchSyncRefreshCredentialsRequest) GetCapabilityId() string { - if x != nil { - return x.CapabilityId - } - return "" -} - -func (x *WatchSyncRefreshCredentialsRequest) GetProviderConfig() *WatchSyncProviderConfig { - if x != nil { - return x.ProviderConfig - } - return nil + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{10} } -func (x *WatchSyncRefreshCredentialsRequest) GetCredentials() *WatchSyncCredentials { +func (x *WatchSyncRefreshCredentialsRequest) GetContext() *WatchSyncAuthenticatedContext { if x != nil { - return x.Credentials + return x.Context } return nil } @@ -1008,7 +1105,7 @@ type WatchSyncCredentialResponse struct { func (x *WatchSyncCredentialResponse) Reset() { *x = WatchSyncCredentialResponse{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[10] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1020,7 +1117,7 @@ func (x *WatchSyncCredentialResponse) String() string { func (*WatchSyncCredentialResponse) ProtoMessage() {} func (x *WatchSyncCredentialResponse) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[10] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1033,7 +1130,7 @@ func (x *WatchSyncCredentialResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncCredentialResponse.ProtoReflect.Descriptor instead. func (*WatchSyncCredentialResponse) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{10} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{11} } func (x *WatchSyncCredentialResponse) GetCredentials() *WatchSyncCredentials { @@ -1058,17 +1155,15 @@ func (x *WatchSyncCredentialResponse) GetFault() *WatchSyncFault { } type WatchSyncGetAccountRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` - ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` - Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Context *WatchSyncAuthenticatedContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *WatchSyncGetAccountRequest) Reset() { *x = WatchSyncGetAccountRequest{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[11] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1080,7 +1175,7 @@ func (x *WatchSyncGetAccountRequest) String() string { func (*WatchSyncGetAccountRequest) ProtoMessage() {} func (x *WatchSyncGetAccountRequest) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[11] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1093,26 +1188,12 @@ func (x *WatchSyncGetAccountRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncGetAccountRequest.ProtoReflect.Descriptor instead. func (*WatchSyncGetAccountRequest) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{11} -} - -func (x *WatchSyncGetAccountRequest) GetCapabilityId() string { - if x != nil { - return x.CapabilityId - } - return "" -} - -func (x *WatchSyncGetAccountRequest) GetProviderConfig() *WatchSyncProviderConfig { - if x != nil { - return x.ProviderConfig - } - return nil + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{12} } -func (x *WatchSyncGetAccountRequest) GetCredentials() *WatchSyncCredentials { +func (x *WatchSyncGetAccountRequest) GetContext() *WatchSyncAuthenticatedContext { if x != nil { - return x.Credentials + return x.Context } return nil } @@ -1127,7 +1208,7 @@ type WatchSyncGetAccountResponse struct { func (x *WatchSyncGetAccountResponse) Reset() { *x = WatchSyncGetAccountResponse{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[12] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1139,7 +1220,7 @@ func (x *WatchSyncGetAccountResponse) String() string { func (*WatchSyncGetAccountResponse) ProtoMessage() {} func (x *WatchSyncGetAccountResponse) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[12] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1152,7 +1233,7 @@ func (x *WatchSyncGetAccountResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncGetAccountResponse.ProtoReflect.Descriptor instead. func (*WatchSyncGetAccountResponse) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{12} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{13} } func (x *WatchSyncGetAccountResponse) GetAccount() *WatchSyncAccount { @@ -1172,7 +1253,7 @@ func (x *WatchSyncGetAccountResponse) GetFault() *WatchSyncFault { type WatchSyncMedia struct { state protoimpl.MessageState `protogen:"open.v1"` MediaItemId string `protobuf:"bytes,1,opt,name=media_item_id,json=mediaItemId,proto3" json:"media_item_id,omitempty"` - MediaType string `protobuf:"bytes,2,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` + MediaType WatchSyncMediaType `protobuf:"varint,2,opt,name=media_type,json=mediaType,proto3,enum=silo.plugin.v1.WatchSyncMediaType" json:"media_type,omitempty"` Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` OriginalTitle string `protobuf:"bytes,4,opt,name=original_title,json=originalTitle,proto3" json:"original_title,omitempty"` Year int32 `protobuf:"varint,5,opt,name=year,proto3" json:"year,omitempty"` @@ -1192,7 +1273,7 @@ type WatchSyncMedia struct { func (x *WatchSyncMedia) Reset() { *x = WatchSyncMedia{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[13] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1204,7 +1285,7 @@ func (x *WatchSyncMedia) String() string { func (*WatchSyncMedia) ProtoMessage() {} func (x *WatchSyncMedia) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[13] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1217,7 +1298,7 @@ func (x *WatchSyncMedia) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncMedia.ProtoReflect.Descriptor instead. func (*WatchSyncMedia) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{13} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{14} } func (x *WatchSyncMedia) GetMediaItemId() string { @@ -1227,11 +1308,11 @@ func (x *WatchSyncMedia) GetMediaItemId() string { return "" } -func (x *WatchSyncMedia) GetMediaType() string { +func (x *WatchSyncMedia) GetMediaType() WatchSyncMediaType { if x != nil { return x.MediaType } - return "" + return WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED } func (x *WatchSyncMedia) GetTitle() string { @@ -1345,7 +1426,7 @@ type WatchSyncEvent struct { func (x *WatchSyncEvent) Reset() { *x = WatchSyncEvent{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[14] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1357,7 +1438,7 @@ func (x *WatchSyncEvent) String() string { func (*WatchSyncEvent) ProtoMessage() {} func (x *WatchSyncEvent) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[14] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1370,7 +1451,7 @@ func (x *WatchSyncEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncEvent.ProtoReflect.Descriptor instead. func (*WatchSyncEvent) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{14} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{15} } func (x *WatchSyncEvent) GetEventId() string { @@ -1451,20 +1532,18 @@ func (x *WatchSyncEvent) GetPlayCount() int32 { } type WatchSyncApplyEventsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` - ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` - Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Context *WatchSyncAuthenticatedContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` // Ordered desired-state events. Duplicate event IDs must not be interpreted // as increments; ApplyEvents is an at-least-once delivery contract. - Events []*WatchSyncEvent `protobuf:"bytes,4,rep,name=events,proto3" json:"events,omitempty"` + Events []*WatchSyncEvent `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WatchSyncApplyEventsRequest) Reset() { *x = WatchSyncApplyEventsRequest{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[15] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1476,7 +1555,7 @@ func (x *WatchSyncApplyEventsRequest) String() string { func (*WatchSyncApplyEventsRequest) ProtoMessage() {} func (x *WatchSyncApplyEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[15] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1489,26 +1568,12 @@ func (x *WatchSyncApplyEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncApplyEventsRequest.ProtoReflect.Descriptor instead. func (*WatchSyncApplyEventsRequest) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{15} -} - -func (x *WatchSyncApplyEventsRequest) GetCapabilityId() string { - if x != nil { - return x.CapabilityId - } - return "" -} - -func (x *WatchSyncApplyEventsRequest) GetProviderConfig() *WatchSyncProviderConfig { - if x != nil { - return x.ProviderConfig - } - return nil + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{16} } -func (x *WatchSyncApplyEventsRequest) GetCredentials() *WatchSyncCredentials { +func (x *WatchSyncApplyEventsRequest) GetContext() *WatchSyncAuthenticatedContext { if x != nil { - return x.Credentials + return x.Context } return nil } @@ -1521,18 +1586,21 @@ func (x *WatchSyncApplyEventsRequest) GetEvents() []*WatchSyncEvent { } type WatchSyncApplyResult struct { - state protoimpl.MessageState `protogen:"open.v1"` - EventId string `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` - Status WatchSyncApplyStatus `protobuf:"varint,2,opt,name=status,proto3,enum=silo.plugin.v1.WatchSyncApplyStatus" json:"status,omitempty"` - SafeMessage string `protobuf:"bytes,3,opt,name=safe_message,json=safeMessage,proto3" json:"safe_message,omitempty"` - RetryAfter *durationpb.Duration `protobuf:"bytes,4,opt,name=retry_after,json=retryAfter,proto3" json:"retry_after,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + EventId string `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"` + Status WatchSyncApplyStatus `protobuf:"varint,2,opt,name=status,proto3,enum=silo.plugin.v1.WatchSyncApplyStatus" json:"status,omitempty"` + // APPLIED and NO_CHANGE omit fault. RETRY uses TEMPORARY or RATE_LIMITED; + // retry_after is an account-wide deferral only when the code is RATE_LIMITED. + // REJECTED uses a non-retryable code such as INVALID_REQUEST or PERMANENT. + // Connection-wide faults such as INVALID_CREDENTIAL belong on the response. + Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WatchSyncApplyResult) Reset() { *x = WatchSyncApplyResult{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[16] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1544,7 +1612,7 @@ func (x *WatchSyncApplyResult) String() string { func (*WatchSyncApplyResult) ProtoMessage() {} func (x *WatchSyncApplyResult) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[16] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1557,7 +1625,7 @@ func (x *WatchSyncApplyResult) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncApplyResult.ProtoReflect.Descriptor instead. func (*WatchSyncApplyResult) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{16} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{17} } func (x *WatchSyncApplyResult) GetEventId() string { @@ -1574,16 +1642,9 @@ func (x *WatchSyncApplyResult) GetStatus() WatchSyncApplyStatus { return WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_UNSPECIFIED } -func (x *WatchSyncApplyResult) GetSafeMessage() string { - if x != nil { - return x.SafeMessage - } - return "" -} - -func (x *WatchSyncApplyResult) GetRetryAfter() *durationpb.Duration { +func (x *WatchSyncApplyResult) GetFault() *WatchSyncFault { if x != nil { - return x.RetryAfter + return x.Fault } return nil } @@ -1592,14 +1653,16 @@ type WatchSyncApplyEventsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Results []*WatchSyncApplyResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,2,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` - Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Batch-level failure. When set, the host ignores results and applies the + // fault to the entire request. + Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *WatchSyncApplyEventsResponse) Reset() { *x = WatchSyncApplyEventsResponse{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[17] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1611,7 +1674,7 @@ func (x *WatchSyncApplyEventsResponse) String() string { func (*WatchSyncApplyEventsResponse) ProtoMessage() {} func (x *WatchSyncApplyEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[17] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1624,7 +1687,7 @@ func (x *WatchSyncApplyEventsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncApplyEventsResponse.ProtoReflect.Descriptor instead. func (*WatchSyncApplyEventsResponse) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{17} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{18} } func (x *WatchSyncApplyEventsResponse) GetResults() []*WatchSyncApplyResult { @@ -1649,20 +1712,25 @@ func (x *WatchSyncApplyEventsResponse) GetFault() *WatchSyncFault { } type WatchSyncListRemoteStateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - CapabilityId string `protobuf:"bytes,1,opt,name=capability_id,json=capabilityId,proto3" json:"capability_id,omitempty"` - ProviderConfig *WatchSyncProviderConfig `protobuf:"bytes,2,opt,name=provider_config,json=providerConfig,proto3" json:"provider_config,omitempty"` - Credentials *WatchSyncCredentials `protobuf:"bytes,3,opt,name=credentials,proto3" json:"credentials,omitempty"` - Cursor string `protobuf:"bytes,4,opt,name=cursor,proto3" json:"cursor,omitempty"` - PageToken string `protobuf:"bytes,5,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` - PageSize int32 `protobuf:"varint,6,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Context *WatchSyncAuthenticatedContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context,omitempty"` + // Opaque durable checkpoint from the last traversal whose items the host + // committed successfully. The host keeps this value unchanged across every + // page in one traversal and persists only next_cursor from the final page. + Cursor string `protobuf:"bytes,2,opt,name=cursor,proto3" json:"cursor,omitempty"` + // Opaque continuation for the traversal identified by cursor. It is used + // only for the immediately following page and is never persisted or reused + // after the traversal completes or fails. + PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"` + // Requested maximum number of items. Plugins may return fewer. + PageSize int32 `protobuf:"varint,4,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *WatchSyncListRemoteStateRequest) Reset() { *x = WatchSyncListRemoteStateRequest{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[18] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1674,7 +1742,7 @@ func (x *WatchSyncListRemoteStateRequest) String() string { func (*WatchSyncListRemoteStateRequest) ProtoMessage() {} func (x *WatchSyncListRemoteStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[18] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1687,26 +1755,12 @@ func (x *WatchSyncListRemoteStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncListRemoteStateRequest.ProtoReflect.Descriptor instead. func (*WatchSyncListRemoteStateRequest) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{18} -} - -func (x *WatchSyncListRemoteStateRequest) GetCapabilityId() string { - if x != nil { - return x.CapabilityId - } - return "" -} - -func (x *WatchSyncListRemoteStateRequest) GetProviderConfig() *WatchSyncProviderConfig { - if x != nil { - return x.ProviderConfig - } - return nil + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{19} } -func (x *WatchSyncListRemoteStateRequest) GetCredentials() *WatchSyncCredentials { +func (x *WatchSyncListRemoteStateRequest) GetContext() *WatchSyncAuthenticatedContext { if x != nil { - return x.Credentials + return x.Context } return nil } @@ -1732,20 +1786,130 @@ func (x *WatchSyncListRemoteStateRequest) GetPageSize() int32 { return 0 } +type WatchSyncRemoteWatchedState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Number of completed plays known to the provider. Must be at least one when + // this submessage is present. + PlayCount int32 `protobuf:"varint,1,opt,name=play_count,json=playCount,proto3" json:"play_count,omitempty"` + // Provider timestamp for the most recent completed play, when available. + LastWatchedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=last_watched_at,json=lastWatchedAt,proto3" json:"last_watched_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncRemoteWatchedState) Reset() { + *x = WatchSyncRemoteWatchedState{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncRemoteWatchedState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncRemoteWatchedState) ProtoMessage() {} + +func (x *WatchSyncRemoteWatchedState) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncRemoteWatchedState.ProtoReflect.Descriptor instead. +func (*WatchSyncRemoteWatchedState) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{20} +} + +func (x *WatchSyncRemoteWatchedState) GetPlayCount() int32 { + if x != nil { + return x.PlayCount + } + return 0 +} + +func (x *WatchSyncRemoteWatchedState) GetLastWatchedAt() *timestamppb.Timestamp { + if x != nil { + return x.LastWatchedAt + } + return nil +} + +type WatchSyncRemoteProgressState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Resume progress in the range [0, 100). Completed plays belong in watched. + ProgressPercent float64 `protobuf:"fixed64,1,opt,name=progress_percent,json=progressPercent,proto3" json:"progress_percent,omitempty"` + // Provider timestamp for this resume state. + PausedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=paused_at,json=pausedAt,proto3" json:"paused_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchSyncRemoteProgressState) Reset() { + *x = WatchSyncRemoteProgressState{} + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchSyncRemoteProgressState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchSyncRemoteProgressState) ProtoMessage() {} + +func (x *WatchSyncRemoteProgressState) ProtoReflect() protoreflect.Message { + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchSyncRemoteProgressState.ProtoReflect.Descriptor instead. +func (*WatchSyncRemoteProgressState) Descriptor() ([]byte, []int) { + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{21} +} + +func (x *WatchSyncRemoteProgressState) GetProgressPercent() float64 { + if x != nil { + return x.ProgressPercent + } + return 0 +} + +func (x *WatchSyncRemoteProgressState) GetPausedAt() *timestamppb.Timestamp { + if x != nil { + return x.PausedAt + } + return nil +} + type WatchSyncRemoteState struct { state protoimpl.MessageState `protogen:"open.v1"` ProviderItemKey string `protobuf:"bytes,1,opt,name=provider_item_key,json=providerItemKey,proto3" json:"provider_item_key,omitempty"` Media *WatchSyncMedia `protobuf:"bytes,2,opt,name=media,proto3" json:"media,omitempty"` - Progress int32 `protobuf:"varint,3,opt,name=progress,proto3" json:"progress,omitempty"` - Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` - UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // At least one typed state must be present. Both may be present when the + // provider reports a completed play and a separate resume point. + Watched *WatchSyncRemoteWatchedState `protobuf:"bytes,3,opt,name=watched,proto3" json:"watched,omitempty"` + Progress *WatchSyncRemoteProgressState `protobuf:"bytes,4,opt,name=progress,proto3" json:"progress,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *WatchSyncRemoteState) Reset() { *x = WatchSyncRemoteState{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[19] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1757,7 +1921,7 @@ func (x *WatchSyncRemoteState) String() string { func (*WatchSyncRemoteState) ProtoMessage() {} func (x *WatchSyncRemoteState) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[19] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1770,7 +1934,7 @@ func (x *WatchSyncRemoteState) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncRemoteState.ProtoReflect.Descriptor instead. func (*WatchSyncRemoteState) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{19} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{22} } func (x *WatchSyncRemoteState) GetProviderItemKey() string { @@ -1787,42 +1951,44 @@ func (x *WatchSyncRemoteState) GetMedia() *WatchSyncMedia { return nil } -func (x *WatchSyncRemoteState) GetProgress() int32 { +func (x *WatchSyncRemoteState) GetWatched() *WatchSyncRemoteWatchedState { if x != nil { - return x.Progress + return x.Watched } - return 0 -} - -func (x *WatchSyncRemoteState) GetStatus() string { - if x != nil { - return x.Status - } - return "" + return nil } -func (x *WatchSyncRemoteState) GetUpdatedAt() *timestamppb.Timestamp { +func (x *WatchSyncRemoteState) GetProgress() *WatchSyncRemoteProgressState { if x != nil { - return x.UpdatedAt + return x.Progress } return nil } type WatchSyncListRemoteStateResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Items []*WatchSyncRemoteState `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` - NextCursor string `protobuf:"bytes,3,opt,name=next_cursor,json=nextCursor,proto3" json:"next_cursor,omitempty"` - CompleteSnapshot bool `protobuf:"varint,4,opt,name=complete_snapshot,json=completeSnapshot,proto3" json:"complete_snapshot,omitempty"` - UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,5,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` - Fault *WatchSyncFault `protobuf:"bytes,6,opt,name=fault,proto3" json:"fault,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Items []*WatchSyncRemoteState `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + // Non-empty when more pages remain. The host requests the next page with the + // original cursor and this token before committing next_cursor. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + // Durable checkpoint for the completed traversal. Plugins set this only on + // the final page; the host persists it only after committing every item. + NextCursor string `protobuf:"bytes,3,opt,name=next_cursor,json=nextCursor,proto3" json:"next_cursor,omitempty"` + // True when this traversal is an authoritative full snapshot. False means an + // incremental delta, so an absent item must not be interpreted as deletion. + // The value must remain stable across every page in one traversal. + CompleteSnapshot bool `protobuf:"varint,4,opt,name=complete_snapshot,json=completeSnapshot,proto3" json:"complete_snapshot,omitempty"` + UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,5,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` + // Traversal-level failure. When set, the host discards this page and keeps + // its previously committed cursor. + Fault *WatchSyncFault `protobuf:"bytes,6,opt,name=fault,proto3" json:"fault,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *WatchSyncListRemoteStateResponse) Reset() { *x = WatchSyncListRemoteStateResponse{} - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[20] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1834,7 +2000,7 @@ func (x *WatchSyncListRemoteStateResponse) String() string { func (*WatchSyncListRemoteStateResponse) ProtoMessage() {} func (x *WatchSyncListRemoteStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[20] + mi := &file_silo_plugin_v1_watch_sync_provider_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1847,7 +2013,7 @@ func (x *WatchSyncListRemoteStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WatchSyncListRemoteStateResponse.ProtoReflect.Descriptor instead. func (*WatchSyncListRemoteStateResponse) Descriptor() ([]byte, []int) { - return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{20} + return file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP(), []int{23} } func (x *WatchSyncListRemoteStateResponse) GetItems() []*WatchSyncRemoteState { @@ -1896,14 +2062,14 @@ var File_silo_plugin_v1_watch_sync_provider_proto protoreflect.FileDescriptor const file_silo_plugin_v1_watch_sync_provider_proto_rawDesc = "" + "\n" + - "(silo/plugin/v1/watch_sync_provider.proto\x12\x0esilo.plugin.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x97\x03\n" + + "(silo/plugin/v1/watch_sync_provider.proto\x12\x0esilo.plugin.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xbb\x03\n" + "\x1bWatchSyncProviderDescriptor\x12F\n" + "\fauth_methods\x18\x01 \x03(\x0e2#.silo.plugin.v1.WatchSyncAuthMethodR\vauthMethods\x12%\n" + "\x0eexport_watched\x18\x02 \x01(\bR\rexportWatched\x12)\n" + "\x10export_unwatched\x18\x03 \x01(\bR\x0fexportUnwatched\x12%\n" + "\x0eimport_watched\x18\x04 \x01(\bR\rimportWatched\x12'\n" + - "\x0fimport_progress\x18\x05 \x01(\bR\x0eimportProgress\x122\n" + - "\x15supported_media_types\x18\x06 \x03(\tR\x13supportedMediaTypes\x124\n" + + "\x0fimport_progress\x18\x05 \x01(\bR\x0eimportProgress\x12V\n" + + "\x15supported_media_types\x18\x06 \x03(\x0e2\".silo.plugin.v1.WatchSyncMediaTypeR\x13supportedMediaTypes\x124\n" + "\x16external_id_namespaces\x18\a \x03(\tR\x14externalIdNamespaces\x12$\n" + "\x0emax_batch_size\x18\b \x01(\x05R\fmaxBatchSize\"\xc2\x02\n" + "\x17WatchSyncProviderConfig\x12K\n" + @@ -1926,7 +2092,11 @@ const file_silo_plugin_v1_watch_sync_provider_proto_rawDesc = "" + "\x11secret_attributes\x18\x06 \x03(\v2:.silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntryR\x10secretAttributes\x1aC\n" + "\x15SecretAttributesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xbc\x01\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xde\x01\n" + + "\x1dWatchSyncAuthenticatedContext\x12#\n" + + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + + "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\"\xbc\x01\n" + "\x10WatchSyncAccount\x12)\n" + "\x10external_subject\x18\x01 \x01(\tR\x0fexternalSubject\x12\x1a\n" + "\busername\x18\x02 \x01(\tR\busername\x12!\n" + @@ -1958,26 +2128,22 @@ const file_silo_plugin_v1_watch_sync_provider_proto_rawDesc = "" + "\x1eWatchSyncExchangeAPIKeyRequest\x12#\n" + "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12\x17\n" + - "\aapi_key\x18\x03 \x01(\tR\x06apiKey\"\xe3\x01\n" + - "\"WatchSyncRefreshCredentialsRequest\x12#\n" + - "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + - "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + - "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\"\xd7\x01\n" + + "\aapi_key\x18\x03 \x01(\tR\x06apiKey\"m\n" + + "\"WatchSyncRefreshCredentialsRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.silo.plugin.v1.WatchSyncAuthenticatedContextR\acontext\"\xd7\x01\n" + "\x1bWatchSyncCredentialResponse\x12F\n" + "\vcredentials\x18\x01 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\x12:\n" + "\aaccount\x18\x02 \x01(\v2 .silo.plugin.v1.WatchSyncAccountR\aaccount\x124\n" + - "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xdb\x01\n" + - "\x1aWatchSyncGetAccountRequest\x12#\n" + - "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + - "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + - "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\"\x8f\x01\n" + + "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"e\n" + + "\x1aWatchSyncGetAccountRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.silo.plugin.v1.WatchSyncAuthenticatedContextR\acontext\"\x8f\x01\n" + "\x1bWatchSyncGetAccountResponse\x12:\n" + "\aaccount\x18\x01 \x01(\v2 .silo.plugin.v1.WatchSyncAccountR\aaccount\x124\n" + - "\x05fault\x18\x02 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xc5\x06\n" + + "\x05fault\x18\x02 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xe9\x06\n" + "\x0eWatchSyncMedia\x12\"\n" + - "\rmedia_item_id\x18\x01 \x01(\tR\vmediaItemId\x12\x1d\n" + + "\rmedia_item_id\x18\x01 \x01(\tR\vmediaItemId\x12A\n" + "\n" + - "media_type\x18\x02 \x01(\tR\tmediaType\x12\x14\n" + + "media_type\x18\x02 \x01(\x0e2\".silo.plugin.v1.WatchSyncMediaTypeR\tmediaType\x12\x14\n" + "\x05title\x18\x03 \x01(\tR\x05title\x12%\n" + "\x0eoriginal_title\x18\x04 \x01(\tR\roriginalTitle\x12\x12\n" + "\x04year\x18\x05 \x01(\x05R\x04year\x12R\n" + @@ -2013,37 +2179,36 @@ const file_silo_plugin_v1_watch_sync_provider_proto_rawDesc = "" + "\x12completion_percent\x18\n" + " \x01(\x01R\x11completionPercent\x12\x1d\n" + "\n" + - "play_count\x18\v \x01(\x05R\tplayCount\"\x94\x02\n" + - "\x1bWatchSyncApplyEventsRequest\x12#\n" + - "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + - "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + - "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\x126\n" + - "\x06events\x18\x04 \x03(\v2\x1e.silo.plugin.v1.WatchSyncEventR\x06events\"\xce\x01\n" + + "play_count\x18\v \x01(\x05R\tplayCount\"\x9e\x01\n" + + "\x1bWatchSyncApplyEventsRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.silo.plugin.v1.WatchSyncAuthenticatedContextR\acontext\x126\n" + + "\x06events\x18\x02 \x03(\v2\x1e.silo.plugin.v1.WatchSyncEventR\x06events\"\xa5\x01\n" + "\x14WatchSyncApplyResult\x12\x19\n" + "\bevent_id\x18\x01 \x01(\tR\aeventId\x12<\n" + - "\x06status\x18\x02 \x01(\x0e2$.silo.plugin.v1.WatchSyncApplyStatusR\x06status\x12!\n" + - "\fsafe_message\x18\x03 \x01(\tR\vsafeMessage\x12:\n" + - "\vretry_after\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\n" + - "retryAfter\"\xeb\x01\n" + + "\x06status\x18\x02 \x01(\x0e2$.silo.plugin.v1.WatchSyncApplyStatusR\x06status\x124\n" + + "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xeb\x01\n" + "\x1cWatchSyncApplyEventsResponse\x12>\n" + "\aresults\x18\x01 \x03(\v2$.silo.plugin.v1.WatchSyncApplyResultR\aresults\x12U\n" + "\x13updated_credentials\x18\x02 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\x12updatedCredentials\x124\n" + - "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xb4\x02\n" + - "\x1fWatchSyncListRemoteStateRequest\x12#\n" + - "\rcapability_id\x18\x01 \x01(\tR\fcapabilityId\x12P\n" + - "\x0fprovider_config\x18\x02 \x01(\v2'.silo.plugin.v1.WatchSyncProviderConfigR\x0eproviderConfig\x12F\n" + - "\vcredentials\x18\x03 \x01(\v2$.silo.plugin.v1.WatchSyncCredentialsR\vcredentials\x12\x16\n" + - "\x06cursor\x18\x04 \x01(\tR\x06cursor\x12\x1d\n" + + "\x05fault\x18\x03 \x01(\v2\x1e.silo.plugin.v1.WatchSyncFaultR\x05fault\"\xbe\x01\n" + + "\x1fWatchSyncListRemoteStateRequest\x12G\n" + + "\acontext\x18\x01 \x01(\v2-.silo.plugin.v1.WatchSyncAuthenticatedContextR\acontext\x12\x16\n" + + "\x06cursor\x18\x02 \x01(\tR\x06cursor\x12\x1d\n" + + "\n" + + "page_token\x18\x03 \x01(\tR\tpageToken\x12\x1b\n" + + "\tpage_size\x18\x04 \x01(\x05R\bpageSize\"\x80\x01\n" + + "\x1bWatchSyncRemoteWatchedState\x12\x1d\n" + "\n" + - "page_token\x18\x05 \x01(\tR\tpageToken\x12\x1b\n" + - "\tpage_size\x18\x06 \x01(\x05R\bpageSize\"\xe7\x01\n" + + "play_count\x18\x01 \x01(\x05R\tplayCount\x12B\n" + + "\x0flast_watched_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\rlastWatchedAt\"\x82\x01\n" + + "\x1cWatchSyncRemoteProgressState\x12)\n" + + "\x10progress_percent\x18\x01 \x01(\x01R\x0fprogressPercent\x127\n" + + "\tpaused_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\bpausedAt\"\x89\x02\n" + "\x14WatchSyncRemoteState\x12*\n" + "\x11provider_item_key\x18\x01 \x01(\tR\x0fproviderItemKey\x124\n" + - "\x05media\x18\x02 \x01(\v2\x1e.silo.plugin.v1.WatchSyncMediaR\x05media\x12\x1a\n" + - "\bprogress\x18\x03 \x01(\x05R\bprogress\x12\x16\n" + - "\x06status\x18\x04 \x01(\tR\x06status\x129\n" + - "\n" + - "updated_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\"\xe1\x02\n" + + "\x05media\x18\x02 \x01(\v2\x1e.silo.plugin.v1.WatchSyncMediaR\x05media\x12E\n" + + "\awatched\x18\x03 \x01(\v2+.silo.plugin.v1.WatchSyncRemoteWatchedStateR\awatched\x12H\n" + + "\bprogress\x18\x04 \x01(\v2,.silo.plugin.v1.WatchSyncRemoteProgressStateR\bprogress\"\xe1\x02\n" + " WatchSyncListRemoteStateResponse\x12:\n" + "\x05items\x18\x01 \x03(\v2$.silo.plugin.v1.WatchSyncRemoteStateR\x05items\x12&\n" + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\x12\x1f\n" + @@ -2055,7 +2220,11 @@ const file_silo_plugin_v1_watch_sync_provider_proto_rawDesc = "" + "\x13WatchSyncAuthMethod\x12&\n" + "\"WATCH_SYNC_AUTH_METHOD_UNSPECIFIED\x10\x00\x12-\n" + ")WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE\x10\x01\x12\"\n" + - "\x1eWATCH_SYNC_AUTH_METHOD_API_KEY\x10\x02*\x8a\x01\n" + + "\x1eWATCH_SYNC_AUTH_METHOD_API_KEY\x10\x02*\x7f\n" + + "\x12WatchSyncMediaType\x12%\n" + + "!WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED\x10\x00\x12\x1f\n" + + "\x1bWATCH_SYNC_MEDIA_TYPE_MOVIE\x10\x01\x12!\n" + + "\x1dWATCH_SYNC_MEDIA_TYPE_EPISODE\x10\x02*\x8a\x01\n" + "\x12WatchSyncOperation\x12$\n" + " WATCH_SYNC_OPERATION_UNSPECIFIED\x10\x00\x12%\n" + "!WATCH_SYNC_OPERATION_MARK_WATCHED\x10\x01\x12'\n" + @@ -2101,106 +2270,113 @@ func file_silo_plugin_v1_watch_sync_provider_proto_rawDescGZIP() []byte { return file_silo_plugin_v1_watch_sync_provider_proto_rawDescData } -var file_silo_plugin_v1_watch_sync_provider_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_silo_plugin_v1_watch_sync_provider_proto_msgTypes = make([]protoimpl.MessageInfo, 26) +var file_silo_plugin_v1_watch_sync_provider_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_silo_plugin_v1_watch_sync_provider_proto_msgTypes = make([]protoimpl.MessageInfo, 29) var file_silo_plugin_v1_watch_sync_provider_proto_goTypes = []any{ (WatchSyncAuthMethod)(0), // 0: silo.plugin.v1.WatchSyncAuthMethod - (WatchSyncOperation)(0), // 1: silo.plugin.v1.WatchSyncOperation - (WatchSyncOrigin)(0), // 2: silo.plugin.v1.WatchSyncOrigin - (WatchSyncApplyStatus)(0), // 3: silo.plugin.v1.WatchSyncApplyStatus - (WatchSyncFaultCode)(0), // 4: silo.plugin.v1.WatchSyncFaultCode - (*WatchSyncProviderDescriptor)(nil), // 5: silo.plugin.v1.WatchSyncProviderDescriptor - (*WatchSyncProviderConfig)(nil), // 6: silo.plugin.v1.WatchSyncProviderConfig - (*WatchSyncCredentials)(nil), // 7: silo.plugin.v1.WatchSyncCredentials - (*WatchSyncAccount)(nil), // 8: silo.plugin.v1.WatchSyncAccount - (*WatchSyncFault)(nil), // 9: silo.plugin.v1.WatchSyncFault - (*WatchSyncInitAuthorizeRequest)(nil), // 10: silo.plugin.v1.WatchSyncInitAuthorizeRequest - (*WatchSyncInitAuthorizeResponse)(nil), // 11: silo.plugin.v1.WatchSyncInitAuthorizeResponse - (*WatchSyncExchangeCodeRequest)(nil), // 12: silo.plugin.v1.WatchSyncExchangeCodeRequest - (*WatchSyncExchangeAPIKeyRequest)(nil), // 13: silo.plugin.v1.WatchSyncExchangeAPIKeyRequest - (*WatchSyncRefreshCredentialsRequest)(nil), // 14: silo.plugin.v1.WatchSyncRefreshCredentialsRequest - (*WatchSyncCredentialResponse)(nil), // 15: silo.plugin.v1.WatchSyncCredentialResponse - (*WatchSyncGetAccountRequest)(nil), // 16: silo.plugin.v1.WatchSyncGetAccountRequest - (*WatchSyncGetAccountResponse)(nil), // 17: silo.plugin.v1.WatchSyncGetAccountResponse - (*WatchSyncMedia)(nil), // 18: silo.plugin.v1.WatchSyncMedia - (*WatchSyncEvent)(nil), // 19: silo.plugin.v1.WatchSyncEvent - (*WatchSyncApplyEventsRequest)(nil), // 20: silo.plugin.v1.WatchSyncApplyEventsRequest - (*WatchSyncApplyResult)(nil), // 21: silo.plugin.v1.WatchSyncApplyResult - (*WatchSyncApplyEventsResponse)(nil), // 22: silo.plugin.v1.WatchSyncApplyEventsResponse - (*WatchSyncListRemoteStateRequest)(nil), // 23: silo.plugin.v1.WatchSyncListRemoteStateRequest - (*WatchSyncRemoteState)(nil), // 24: silo.plugin.v1.WatchSyncRemoteState - (*WatchSyncListRemoteStateResponse)(nil), // 25: silo.plugin.v1.WatchSyncListRemoteStateResponse - nil, // 26: silo.plugin.v1.WatchSyncProviderConfig.ValuesEntry - nil, // 27: silo.plugin.v1.WatchSyncProviderConfig.SecretValuesEntry - nil, // 28: silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntry - nil, // 29: silo.plugin.v1.WatchSyncMedia.ExternalIdsEntry - nil, // 30: silo.plugin.v1.WatchSyncMedia.SeriesExternalIdsEntry - (*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 32: google.protobuf.Duration - (*structpb.Struct)(nil), // 33: google.protobuf.Struct + (WatchSyncMediaType)(0), // 1: silo.plugin.v1.WatchSyncMediaType + (WatchSyncOperation)(0), // 2: silo.plugin.v1.WatchSyncOperation + (WatchSyncOrigin)(0), // 3: silo.plugin.v1.WatchSyncOrigin + (WatchSyncApplyStatus)(0), // 4: silo.plugin.v1.WatchSyncApplyStatus + (WatchSyncFaultCode)(0), // 5: silo.plugin.v1.WatchSyncFaultCode + (*WatchSyncProviderDescriptor)(nil), // 6: silo.plugin.v1.WatchSyncProviderDescriptor + (*WatchSyncProviderConfig)(nil), // 7: silo.plugin.v1.WatchSyncProviderConfig + (*WatchSyncCredentials)(nil), // 8: silo.plugin.v1.WatchSyncCredentials + (*WatchSyncAuthenticatedContext)(nil), // 9: silo.plugin.v1.WatchSyncAuthenticatedContext + (*WatchSyncAccount)(nil), // 10: silo.plugin.v1.WatchSyncAccount + (*WatchSyncFault)(nil), // 11: silo.plugin.v1.WatchSyncFault + (*WatchSyncInitAuthorizeRequest)(nil), // 12: silo.plugin.v1.WatchSyncInitAuthorizeRequest + (*WatchSyncInitAuthorizeResponse)(nil), // 13: silo.plugin.v1.WatchSyncInitAuthorizeResponse + (*WatchSyncExchangeCodeRequest)(nil), // 14: silo.plugin.v1.WatchSyncExchangeCodeRequest + (*WatchSyncExchangeAPIKeyRequest)(nil), // 15: silo.plugin.v1.WatchSyncExchangeAPIKeyRequest + (*WatchSyncRefreshCredentialsRequest)(nil), // 16: silo.plugin.v1.WatchSyncRefreshCredentialsRequest + (*WatchSyncCredentialResponse)(nil), // 17: silo.plugin.v1.WatchSyncCredentialResponse + (*WatchSyncGetAccountRequest)(nil), // 18: silo.plugin.v1.WatchSyncGetAccountRequest + (*WatchSyncGetAccountResponse)(nil), // 19: silo.plugin.v1.WatchSyncGetAccountResponse + (*WatchSyncMedia)(nil), // 20: silo.plugin.v1.WatchSyncMedia + (*WatchSyncEvent)(nil), // 21: silo.plugin.v1.WatchSyncEvent + (*WatchSyncApplyEventsRequest)(nil), // 22: silo.plugin.v1.WatchSyncApplyEventsRequest + (*WatchSyncApplyResult)(nil), // 23: silo.plugin.v1.WatchSyncApplyResult + (*WatchSyncApplyEventsResponse)(nil), // 24: silo.plugin.v1.WatchSyncApplyEventsResponse + (*WatchSyncListRemoteStateRequest)(nil), // 25: silo.plugin.v1.WatchSyncListRemoteStateRequest + (*WatchSyncRemoteWatchedState)(nil), // 26: silo.plugin.v1.WatchSyncRemoteWatchedState + (*WatchSyncRemoteProgressState)(nil), // 27: silo.plugin.v1.WatchSyncRemoteProgressState + (*WatchSyncRemoteState)(nil), // 28: silo.plugin.v1.WatchSyncRemoteState + (*WatchSyncListRemoteStateResponse)(nil), // 29: silo.plugin.v1.WatchSyncListRemoteStateResponse + nil, // 30: silo.plugin.v1.WatchSyncProviderConfig.ValuesEntry + nil, // 31: silo.plugin.v1.WatchSyncProviderConfig.SecretValuesEntry + nil, // 32: silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntry + nil, // 33: silo.plugin.v1.WatchSyncMedia.ExternalIdsEntry + nil, // 34: silo.plugin.v1.WatchSyncMedia.SeriesExternalIdsEntry + (*timestamppb.Timestamp)(nil), // 35: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 36: google.protobuf.Duration + (*structpb.Struct)(nil), // 37: google.protobuf.Struct } var file_silo_plugin_v1_watch_sync_provider_proto_depIdxs = []int32{ 0, // 0: silo.plugin.v1.WatchSyncProviderDescriptor.auth_methods:type_name -> silo.plugin.v1.WatchSyncAuthMethod - 26, // 1: silo.plugin.v1.WatchSyncProviderConfig.values:type_name -> silo.plugin.v1.WatchSyncProviderConfig.ValuesEntry - 27, // 2: silo.plugin.v1.WatchSyncProviderConfig.secret_values:type_name -> silo.plugin.v1.WatchSyncProviderConfig.SecretValuesEntry - 31, // 3: silo.plugin.v1.WatchSyncCredentials.expires_at:type_name -> google.protobuf.Timestamp - 28, // 4: silo.plugin.v1.WatchSyncCredentials.secret_attributes:type_name -> silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntry - 4, // 5: silo.plugin.v1.WatchSyncFault.code:type_name -> silo.plugin.v1.WatchSyncFaultCode - 32, // 6: silo.plugin.v1.WatchSyncFault.retry_after:type_name -> google.protobuf.Duration - 6, // 7: silo.plugin.v1.WatchSyncInitAuthorizeRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig - 9, // 8: silo.plugin.v1.WatchSyncInitAuthorizeResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault - 6, // 9: silo.plugin.v1.WatchSyncExchangeCodeRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig - 6, // 10: silo.plugin.v1.WatchSyncExchangeAPIKeyRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig - 6, // 11: silo.plugin.v1.WatchSyncRefreshCredentialsRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig - 7, // 12: silo.plugin.v1.WatchSyncRefreshCredentialsRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials - 7, // 13: silo.plugin.v1.WatchSyncCredentialResponse.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials - 8, // 14: silo.plugin.v1.WatchSyncCredentialResponse.account:type_name -> silo.plugin.v1.WatchSyncAccount - 9, // 15: silo.plugin.v1.WatchSyncCredentialResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault - 6, // 16: silo.plugin.v1.WatchSyncGetAccountRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig - 7, // 17: silo.plugin.v1.WatchSyncGetAccountRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials - 8, // 18: silo.plugin.v1.WatchSyncGetAccountResponse.account:type_name -> silo.plugin.v1.WatchSyncAccount - 9, // 19: silo.plugin.v1.WatchSyncGetAccountResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault - 29, // 20: silo.plugin.v1.WatchSyncMedia.external_ids:type_name -> silo.plugin.v1.WatchSyncMedia.ExternalIdsEntry - 30, // 21: silo.plugin.v1.WatchSyncMedia.series_external_ids:type_name -> silo.plugin.v1.WatchSyncMedia.SeriesExternalIdsEntry - 33, // 22: silo.plugin.v1.WatchSyncMedia.metadata:type_name -> google.protobuf.Struct - 1, // 23: silo.plugin.v1.WatchSyncEvent.operation:type_name -> silo.plugin.v1.WatchSyncOperation - 2, // 24: silo.plugin.v1.WatchSyncEvent.origin:type_name -> silo.plugin.v1.WatchSyncOrigin - 31, // 25: silo.plugin.v1.WatchSyncEvent.occurred_at:type_name -> google.protobuf.Timestamp - 18, // 26: silo.plugin.v1.WatchSyncEvent.media:type_name -> silo.plugin.v1.WatchSyncMedia - 6, // 27: silo.plugin.v1.WatchSyncApplyEventsRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig - 7, // 28: silo.plugin.v1.WatchSyncApplyEventsRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials - 19, // 29: silo.plugin.v1.WatchSyncApplyEventsRequest.events:type_name -> silo.plugin.v1.WatchSyncEvent - 3, // 30: silo.plugin.v1.WatchSyncApplyResult.status:type_name -> silo.plugin.v1.WatchSyncApplyStatus - 32, // 31: silo.plugin.v1.WatchSyncApplyResult.retry_after:type_name -> google.protobuf.Duration - 21, // 32: silo.plugin.v1.WatchSyncApplyEventsResponse.results:type_name -> silo.plugin.v1.WatchSyncApplyResult - 7, // 33: silo.plugin.v1.WatchSyncApplyEventsResponse.updated_credentials:type_name -> silo.plugin.v1.WatchSyncCredentials - 9, // 34: silo.plugin.v1.WatchSyncApplyEventsResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault - 6, // 35: silo.plugin.v1.WatchSyncListRemoteStateRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig - 7, // 36: silo.plugin.v1.WatchSyncListRemoteStateRequest.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials - 18, // 37: silo.plugin.v1.WatchSyncRemoteState.media:type_name -> silo.plugin.v1.WatchSyncMedia - 31, // 38: silo.plugin.v1.WatchSyncRemoteState.updated_at:type_name -> google.protobuf.Timestamp - 24, // 39: silo.plugin.v1.WatchSyncListRemoteStateResponse.items:type_name -> silo.plugin.v1.WatchSyncRemoteState - 7, // 40: silo.plugin.v1.WatchSyncListRemoteStateResponse.updated_credentials:type_name -> silo.plugin.v1.WatchSyncCredentials - 9, // 41: silo.plugin.v1.WatchSyncListRemoteStateResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault - 10, // 42: silo.plugin.v1.WatchSyncProvider.InitAuthorize:input_type -> silo.plugin.v1.WatchSyncInitAuthorizeRequest - 12, // 43: silo.plugin.v1.WatchSyncProvider.ExchangeCode:input_type -> silo.plugin.v1.WatchSyncExchangeCodeRequest - 13, // 44: silo.plugin.v1.WatchSyncProvider.ExchangeAPIKey:input_type -> silo.plugin.v1.WatchSyncExchangeAPIKeyRequest - 14, // 45: silo.plugin.v1.WatchSyncProvider.RefreshCredentials:input_type -> silo.plugin.v1.WatchSyncRefreshCredentialsRequest - 16, // 46: silo.plugin.v1.WatchSyncProvider.GetAccount:input_type -> silo.plugin.v1.WatchSyncGetAccountRequest - 20, // 47: silo.plugin.v1.WatchSyncProvider.ApplyEvents:input_type -> silo.plugin.v1.WatchSyncApplyEventsRequest - 23, // 48: silo.plugin.v1.WatchSyncProvider.ListRemoteState:input_type -> silo.plugin.v1.WatchSyncListRemoteStateRequest - 11, // 49: silo.plugin.v1.WatchSyncProvider.InitAuthorize:output_type -> silo.plugin.v1.WatchSyncInitAuthorizeResponse - 15, // 50: silo.plugin.v1.WatchSyncProvider.ExchangeCode:output_type -> silo.plugin.v1.WatchSyncCredentialResponse - 15, // 51: silo.plugin.v1.WatchSyncProvider.ExchangeAPIKey:output_type -> silo.plugin.v1.WatchSyncCredentialResponse - 15, // 52: silo.plugin.v1.WatchSyncProvider.RefreshCredentials:output_type -> silo.plugin.v1.WatchSyncCredentialResponse - 17, // 53: silo.plugin.v1.WatchSyncProvider.GetAccount:output_type -> silo.plugin.v1.WatchSyncGetAccountResponse - 22, // 54: silo.plugin.v1.WatchSyncProvider.ApplyEvents:output_type -> silo.plugin.v1.WatchSyncApplyEventsResponse - 25, // 55: silo.plugin.v1.WatchSyncProvider.ListRemoteState:output_type -> silo.plugin.v1.WatchSyncListRemoteStateResponse - 49, // [49:56] is the sub-list for method output_type - 42, // [42:49] is the sub-list for method input_type - 42, // [42:42] is the sub-list for extension type_name - 42, // [42:42] is the sub-list for extension extendee - 0, // [0:42] is the sub-list for field type_name + 1, // 1: silo.plugin.v1.WatchSyncProviderDescriptor.supported_media_types:type_name -> silo.plugin.v1.WatchSyncMediaType + 30, // 2: silo.plugin.v1.WatchSyncProviderConfig.values:type_name -> silo.plugin.v1.WatchSyncProviderConfig.ValuesEntry + 31, // 3: silo.plugin.v1.WatchSyncProviderConfig.secret_values:type_name -> silo.plugin.v1.WatchSyncProviderConfig.SecretValuesEntry + 35, // 4: silo.plugin.v1.WatchSyncCredentials.expires_at:type_name -> google.protobuf.Timestamp + 32, // 5: silo.plugin.v1.WatchSyncCredentials.secret_attributes:type_name -> silo.plugin.v1.WatchSyncCredentials.SecretAttributesEntry + 7, // 6: silo.plugin.v1.WatchSyncAuthenticatedContext.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 8, // 7: silo.plugin.v1.WatchSyncAuthenticatedContext.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 5, // 8: silo.plugin.v1.WatchSyncFault.code:type_name -> silo.plugin.v1.WatchSyncFaultCode + 36, // 9: silo.plugin.v1.WatchSyncFault.retry_after:type_name -> google.protobuf.Duration + 7, // 10: silo.plugin.v1.WatchSyncInitAuthorizeRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 11, // 11: silo.plugin.v1.WatchSyncInitAuthorizeResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 7, // 12: silo.plugin.v1.WatchSyncExchangeCodeRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 7, // 13: silo.plugin.v1.WatchSyncExchangeAPIKeyRequest.provider_config:type_name -> silo.plugin.v1.WatchSyncProviderConfig + 9, // 14: silo.plugin.v1.WatchSyncRefreshCredentialsRequest.context:type_name -> silo.plugin.v1.WatchSyncAuthenticatedContext + 8, // 15: silo.plugin.v1.WatchSyncCredentialResponse.credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 10, // 16: silo.plugin.v1.WatchSyncCredentialResponse.account:type_name -> silo.plugin.v1.WatchSyncAccount + 11, // 17: silo.plugin.v1.WatchSyncCredentialResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 9, // 18: silo.plugin.v1.WatchSyncGetAccountRequest.context:type_name -> silo.plugin.v1.WatchSyncAuthenticatedContext + 10, // 19: silo.plugin.v1.WatchSyncGetAccountResponse.account:type_name -> silo.plugin.v1.WatchSyncAccount + 11, // 20: silo.plugin.v1.WatchSyncGetAccountResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 1, // 21: silo.plugin.v1.WatchSyncMedia.media_type:type_name -> silo.plugin.v1.WatchSyncMediaType + 33, // 22: silo.plugin.v1.WatchSyncMedia.external_ids:type_name -> silo.plugin.v1.WatchSyncMedia.ExternalIdsEntry + 34, // 23: silo.plugin.v1.WatchSyncMedia.series_external_ids:type_name -> silo.plugin.v1.WatchSyncMedia.SeriesExternalIdsEntry + 37, // 24: silo.plugin.v1.WatchSyncMedia.metadata:type_name -> google.protobuf.Struct + 2, // 25: silo.plugin.v1.WatchSyncEvent.operation:type_name -> silo.plugin.v1.WatchSyncOperation + 3, // 26: silo.plugin.v1.WatchSyncEvent.origin:type_name -> silo.plugin.v1.WatchSyncOrigin + 35, // 27: silo.plugin.v1.WatchSyncEvent.occurred_at:type_name -> google.protobuf.Timestamp + 20, // 28: silo.plugin.v1.WatchSyncEvent.media:type_name -> silo.plugin.v1.WatchSyncMedia + 9, // 29: silo.plugin.v1.WatchSyncApplyEventsRequest.context:type_name -> silo.plugin.v1.WatchSyncAuthenticatedContext + 21, // 30: silo.plugin.v1.WatchSyncApplyEventsRequest.events:type_name -> silo.plugin.v1.WatchSyncEvent + 4, // 31: silo.plugin.v1.WatchSyncApplyResult.status:type_name -> silo.plugin.v1.WatchSyncApplyStatus + 11, // 32: silo.plugin.v1.WatchSyncApplyResult.fault:type_name -> silo.plugin.v1.WatchSyncFault + 23, // 33: silo.plugin.v1.WatchSyncApplyEventsResponse.results:type_name -> silo.plugin.v1.WatchSyncApplyResult + 8, // 34: silo.plugin.v1.WatchSyncApplyEventsResponse.updated_credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 11, // 35: silo.plugin.v1.WatchSyncApplyEventsResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 9, // 36: silo.plugin.v1.WatchSyncListRemoteStateRequest.context:type_name -> silo.plugin.v1.WatchSyncAuthenticatedContext + 35, // 37: silo.plugin.v1.WatchSyncRemoteWatchedState.last_watched_at:type_name -> google.protobuf.Timestamp + 35, // 38: silo.plugin.v1.WatchSyncRemoteProgressState.paused_at:type_name -> google.protobuf.Timestamp + 20, // 39: silo.plugin.v1.WatchSyncRemoteState.media:type_name -> silo.plugin.v1.WatchSyncMedia + 26, // 40: silo.plugin.v1.WatchSyncRemoteState.watched:type_name -> silo.plugin.v1.WatchSyncRemoteWatchedState + 27, // 41: silo.plugin.v1.WatchSyncRemoteState.progress:type_name -> silo.plugin.v1.WatchSyncRemoteProgressState + 28, // 42: silo.plugin.v1.WatchSyncListRemoteStateResponse.items:type_name -> silo.plugin.v1.WatchSyncRemoteState + 8, // 43: silo.plugin.v1.WatchSyncListRemoteStateResponse.updated_credentials:type_name -> silo.plugin.v1.WatchSyncCredentials + 11, // 44: silo.plugin.v1.WatchSyncListRemoteStateResponse.fault:type_name -> silo.plugin.v1.WatchSyncFault + 12, // 45: silo.plugin.v1.WatchSyncProvider.InitAuthorize:input_type -> silo.plugin.v1.WatchSyncInitAuthorizeRequest + 14, // 46: silo.plugin.v1.WatchSyncProvider.ExchangeCode:input_type -> silo.plugin.v1.WatchSyncExchangeCodeRequest + 15, // 47: silo.plugin.v1.WatchSyncProvider.ExchangeAPIKey:input_type -> silo.plugin.v1.WatchSyncExchangeAPIKeyRequest + 16, // 48: silo.plugin.v1.WatchSyncProvider.RefreshCredentials:input_type -> silo.plugin.v1.WatchSyncRefreshCredentialsRequest + 18, // 49: silo.plugin.v1.WatchSyncProvider.GetAccount:input_type -> silo.plugin.v1.WatchSyncGetAccountRequest + 22, // 50: silo.plugin.v1.WatchSyncProvider.ApplyEvents:input_type -> silo.plugin.v1.WatchSyncApplyEventsRequest + 25, // 51: silo.plugin.v1.WatchSyncProvider.ListRemoteState:input_type -> silo.plugin.v1.WatchSyncListRemoteStateRequest + 13, // 52: silo.plugin.v1.WatchSyncProvider.InitAuthorize:output_type -> silo.plugin.v1.WatchSyncInitAuthorizeResponse + 17, // 53: silo.plugin.v1.WatchSyncProvider.ExchangeCode:output_type -> silo.plugin.v1.WatchSyncCredentialResponse + 17, // 54: silo.plugin.v1.WatchSyncProvider.ExchangeAPIKey:output_type -> silo.plugin.v1.WatchSyncCredentialResponse + 17, // 55: silo.plugin.v1.WatchSyncProvider.RefreshCredentials:output_type -> silo.plugin.v1.WatchSyncCredentialResponse + 19, // 56: silo.plugin.v1.WatchSyncProvider.GetAccount:output_type -> silo.plugin.v1.WatchSyncGetAccountResponse + 24, // 57: silo.plugin.v1.WatchSyncProvider.ApplyEvents:output_type -> silo.plugin.v1.WatchSyncApplyEventsResponse + 29, // 58: silo.plugin.v1.WatchSyncProvider.ListRemoteState:output_type -> silo.plugin.v1.WatchSyncListRemoteStateResponse + 52, // [52:59] is the sub-list for method output_type + 45, // [45:52] is the sub-list for method input_type + 45, // [45:45] is the sub-list for extension type_name + 45, // [45:45] is the sub-list for extension extendee + 0, // [0:45] is the sub-list for field type_name } func init() { file_silo_plugin_v1_watch_sync_provider_proto_init() } @@ -2213,8 +2389,8 @@ func file_silo_plugin_v1_watch_sync_provider_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_silo_plugin_v1_watch_sync_provider_proto_rawDesc), len(file_silo_plugin_v1_watch_sync_provider_proto_rawDesc)), - NumEnums: 5, - NumMessages: 26, + NumEnums: 6, + NumMessages: 29, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go new file mode 100644 index 0000000..8f06215 --- /dev/null +++ b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go @@ -0,0 +1,77 @@ +package pluginv1 + +import ( + "testing" + "time" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestWatchSyncTypedRemoteStateRoundTrip(t *testing.T) { + pausedAt := time.Unix(1_700_000_000, 0).UTC() + input := &WatchSyncListRemoteStateResponse{ + Items: []*WatchSyncRemoteState{{ + ProviderItemKey: "episode:42", + Media: &WatchSyncMedia{ + MediaItemId: "local-42", + MediaType: WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_EPISODE, + }, + Watched: &WatchSyncRemoteWatchedState{ + PlayCount: 2, + LastWatchedAt: timestamppb.New(pausedAt.Add(-time.Hour)), + }, + Progress: &WatchSyncRemoteProgressState{ + ProgressPercent: 42.5, + PausedAt: timestamppb.New(pausedAt), + }, + }}, + NextCursor: "checkpoint-2", + CompleteSnapshot: true, + } + + data, err := proto.Marshal(input) + if err != nil { + t.Fatal(err) + } + var output WatchSyncListRemoteStateResponse + if err := proto.Unmarshal(data, &output); err != nil { + t.Fatal(err) + } + + item := output.GetItems()[0] + if item.GetMedia().GetMediaType() != WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_EPISODE || + item.GetWatched().GetPlayCount() != 2 || + item.GetProgress().GetProgressPercent() != 42.5 || + !item.GetProgress().GetPausedAt().AsTime().Equal(pausedAt) { + t.Fatalf("remote state = %#v", item) + } +} + +func TestWatchSyncApplyResultCarriesTypedRateLimit(t *testing.T) { + retryAfter := 45 * time.Second + request := &WatchSyncApplyEventsRequest{ + Context: &WatchSyncAuthenticatedContext{ + CapabilityId: "anilist", + }, + Events: []*WatchSyncEvent{{ + EventId: "event-1", + }}, + } + result := &WatchSyncApplyResult{ + EventId: request.GetEvents()[0].GetEventId(), + Status: WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_RETRY, + Fault: &WatchSyncFault{ + Code: WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_RATE_LIMITED, + SafeMessage: "provider rate limit reached", + RetryAfter: durationpb.New(retryAfter), + }, + } + + if request.GetContext().GetCapabilityId() != "anilist" || + result.GetFault().GetCode() != WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_RATE_LIMITED || + result.GetFault().GetRetryAfter().AsDuration() != retryAfter { + t.Fatalf("request=%#v result=%#v", request, result) + } +} diff --git a/pkg/pluginsdk/convert/watch_sync_provider_test.go b/pkg/pluginsdk/convert/watch_sync_provider_test.go index 192ebdc..e554dc4 100644 --- a/pkg/pluginsdk/convert/watch_sync_provider_test.go +++ b/pkg/pluginsdk/convert/watch_sync_provider_test.go @@ -12,10 +12,13 @@ func TestWatchSyncProviderDescriptorRoundTrip(t *testing.T) { Type: "watch_sync_provider.v1", Id: "anilist", WatchSyncProvider: &pluginv1.WatchSyncProviderDescriptor{ - AuthMethods: []pluginv1.WatchSyncAuthMethod{pluginv1.WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE}, - ExportWatched: true, - SupportedMediaTypes: []string{"movie", "episode"}, - MaxBatchSize: 25, + AuthMethods: []pluginv1.WatchSyncAuthMethod{pluginv1.WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE}, + ExportWatched: true, + SupportedMediaTypes: []pluginv1.WatchSyncMediaType{ + pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_MOVIE, + pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_EPISODE, + }, + MaxBatchSize: 25, }, }}} records, err := convert.CapabilityRecordsFromManifest(manifest) @@ -27,7 +30,9 @@ func TestWatchSyncProviderDescriptorRoundTrip(t *testing.T) { t.Fatal(err) } got := decoded.GetWatchSyncProvider() - if got == nil || !got.GetExportWatched() || got.GetMaxBatchSize() != 25 || len(got.GetAuthMethods()) != 1 { + if got == nil || !got.GetExportWatched() || got.GetMaxBatchSize() != 25 || + len(got.GetAuthMethods()) != 1 || len(got.GetSupportedMediaTypes()) != 2 || + got.GetSupportedMediaTypes()[1] != pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_EPISODE { t.Fatalf("decoded descriptor = %#v", got) } } diff --git a/pkg/pluginsdk/manifest/manifest.go b/pkg/pluginsdk/manifest/manifest.go index a403525..a5bd670 100644 --- a/pkg/pluginsdk/manifest/manifest.go +++ b/pkg/pluginsdk/manifest/manifest.go @@ -166,8 +166,8 @@ func validateWatchSyncCapability(descriptor *pluginv1.CapabilityDescriptor) erro return fmt.Errorf("plugin capability %q: at least one supported media type is required", descriptor.GetId()) } for _, mediaType := range watchSync.GetSupportedMediaTypes() { - if mediaType != "movie" && mediaType != "episode" { - return fmt.Errorf("plugin capability %q: unsupported watch sync media type %q", descriptor.GetId(), mediaType) + if mediaType == pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED { + return fmt.Errorf("plugin capability %q: watch sync media type cannot be unspecified", descriptor.GetId()) } } for _, namespace := range watchSync.GetExternalIdNamespaces() { diff --git a/pkg/pluginsdk/manifest/watch_sync_provider_test.go b/pkg/pluginsdk/manifest/watch_sync_provider_test.go index ec6eedc..97f93b7 100644 --- a/pkg/pluginsdk/manifest/watch_sync_provider_test.go +++ b/pkg/pluginsdk/manifest/watch_sync_provider_test.go @@ -15,7 +15,7 @@ func TestLoadWatchSyncProvider(t *testing.T) { "watch_sync_provider":{ "auth_methods":["WATCH_SYNC_AUTH_METHOD_AUTHORIZATION_CODE"], "export_watched":true, - "supported_media_types":["movie","episode"], + "supported_media_types":["WATCH_SYNC_MEDIA_TYPE_MOVIE","WATCH_SYNC_MEDIA_TYPE_EPISODE"], "external_id_namespaces":["anilist","tmdb","tvdb"], "max_batch_size":25 } @@ -29,6 +29,11 @@ func TestLoadWatchSyncProvider(t *testing.T) { if descriptor == nil || !descriptor.GetExportWatched() || descriptor.GetMaxBatchSize() != 25 { t.Fatalf("watch sync descriptor = %#v", descriptor) } + if got := descriptor.GetSupportedMediaTypes(); len(got) != 2 || + got[0] != pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_MOVIE || + got[1] != pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_EPISODE { + t.Fatalf("supported media types = %v", got) + } } func TestValidateWatchSyncProviderRejectsMissingDescriptor(t *testing.T) { @@ -59,6 +64,29 @@ func TestValidateWatchSyncProviderRejectsEmptyMediaTypes(t *testing.T) { } } +func TestValidateWatchSyncProviderRejectsUnspecifiedMediaType(t *testing.T) { + manifest := validWatchSyncManifest() + manifest.Capabilities[0].WatchSyncProvider.SupportedMediaTypes = []pluginv1.WatchSyncMediaType{ + pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED, + } + if err := publicmanifest.Validate(manifest); err == nil { + t.Fatal("expected unspecified supported media type to fail") + } +} + +func TestValidateWatchSyncProviderAllowsUnknownFutureEnums(t *testing.T) { + manifest := validWatchSyncManifest() + manifest.Capabilities[0].WatchSyncProvider.AuthMethods = []pluginv1.WatchSyncAuthMethod{ + pluginv1.WatchSyncAuthMethod(99), + } + manifest.Capabilities[0].WatchSyncProvider.SupportedMediaTypes = []pluginv1.WatchSyncMediaType{ + pluginv1.WatchSyncMediaType(99), + } + if err := publicmanifest.Validate(manifest); err != nil { + t.Fatalf("future additive enum values should remain forward compatible: %v", err) + } +} + func validWatchSyncManifest() *pluginv1.PluginManifest { return &pluginv1.PluginManifest{ PluginId: "silo.valid", Version: "1.0.0", @@ -68,9 +96,11 @@ func validWatchSyncManifest() *pluginv1.PluginManifest { AuthMethods: []pluginv1.WatchSyncAuthMethod{ pluginv1.WatchSyncAuthMethod_WATCH_SYNC_AUTH_METHOD_API_KEY, }, - ExportWatched: true, - SupportedMediaTypes: []string{"movie"}, - MaxBatchSize: 1, + ExportWatched: true, + SupportedMediaTypes: []pluginv1.WatchSyncMediaType{ + pluginv1.WatchSyncMediaType_WATCH_SYNC_MEDIA_TYPE_MOVIE, + }, + MaxBatchSize: 1, }, }}, } diff --git a/proto/silo/plugin/v1/watch_sync_provider.proto b/proto/silo/plugin/v1/watch_sync_provider.proto index e362731..d526db7 100644 --- a/proto/silo/plugin/v1/watch_sync_provider.proto +++ b/proto/silo/plugin/v1/watch_sync_provider.proto @@ -28,6 +28,12 @@ enum WatchSyncAuthMethod { WATCH_SYNC_AUTH_METHOD_API_KEY = 2; } +enum WatchSyncMediaType { + WATCH_SYNC_MEDIA_TYPE_UNSPECIFIED = 0; + WATCH_SYNC_MEDIA_TYPE_MOVIE = 1; + WATCH_SYNC_MEDIA_TYPE_EPISODE = 2; +} + enum WatchSyncOperation { WATCH_SYNC_OPERATION_UNSPECIFIED = 0; WATCH_SYNC_OPERATION_MARK_WATCHED = 1; @@ -65,7 +71,7 @@ message WatchSyncProviderDescriptor { bool export_unwatched = 3; bool import_watched = 4; bool import_progress = 5; - repeated string supported_media_types = 6; + repeated WatchSyncMediaType supported_media_types = 6; repeated string external_id_namespaces = 7; int32 max_batch_size = 8; } @@ -88,6 +94,14 @@ message WatchSyncCredentials { map secret_attributes = 6; } +// Host-owned context shared by RPCs after a profile connection is established. +// Plugins must treat provider_config and credentials as transient request data. +message WatchSyncAuthenticatedContext { + string capability_id = 1; + WatchSyncProviderConfig provider_config = 2; + WatchSyncCredentials credentials = 3; +} + message WatchSyncAccount { string external_subject = 1; string username = 2; @@ -101,6 +115,8 @@ message WatchSyncFault { // Safe for persistence and operator display. Must not contain credentials, // authorization codes, provider response bodies, or other secrets. string safe_message = 2; + // Optional retry delay for TEMPORARY and RATE_LIMITED faults. Only + // RATE_LIMITED tells the host to defer the whole provider connection. google.protobuf.Duration retry_after = 3; } @@ -137,9 +153,7 @@ message WatchSyncExchangeAPIKeyRequest { } message WatchSyncRefreshCredentialsRequest { - string capability_id = 1; - WatchSyncProviderConfig provider_config = 2; - WatchSyncCredentials credentials = 3; + WatchSyncAuthenticatedContext context = 1; } message WatchSyncCredentialResponse { @@ -149,9 +163,7 @@ message WatchSyncCredentialResponse { } message WatchSyncGetAccountRequest { - string capability_id = 1; - WatchSyncProviderConfig provider_config = 2; - WatchSyncCredentials credentials = 3; + WatchSyncAuthenticatedContext context = 1; } message WatchSyncGetAccountResponse { @@ -161,7 +173,7 @@ message WatchSyncGetAccountResponse { message WatchSyncMedia { string media_item_id = 1; - string media_type = 2; + WatchSyncMediaType media_type = 2; string title = 3; string original_title = 4; int32 year = 5; @@ -193,49 +205,82 @@ message WatchSyncEvent { } message WatchSyncApplyEventsRequest { - string capability_id = 1; - WatchSyncProviderConfig provider_config = 2; - WatchSyncCredentials credentials = 3; + WatchSyncAuthenticatedContext context = 1; // Ordered desired-state events. Duplicate event IDs must not be interpreted // as increments; ApplyEvents is an at-least-once delivery contract. - repeated WatchSyncEvent events = 4; + repeated WatchSyncEvent events = 2; } message WatchSyncApplyResult { string event_id = 1; WatchSyncApplyStatus status = 2; - string safe_message = 3; - google.protobuf.Duration retry_after = 4; + // APPLIED and NO_CHANGE omit fault. RETRY uses TEMPORARY or RATE_LIMITED; + // retry_after is an account-wide deferral only when the code is RATE_LIMITED. + // REJECTED uses a non-retryable code such as INVALID_REQUEST or PERMANENT. + // Connection-wide faults such as INVALID_CREDENTIAL belong on the response. + WatchSyncFault fault = 3; } message WatchSyncApplyEventsResponse { repeated WatchSyncApplyResult results = 1; WatchSyncCredentials updated_credentials = 2; + // Batch-level failure. When set, the host ignores results and applies the + // fault to the entire request. WatchSyncFault fault = 3; } message WatchSyncListRemoteStateRequest { - string capability_id = 1; - WatchSyncProviderConfig provider_config = 2; - WatchSyncCredentials credentials = 3; - string cursor = 4; - string page_token = 5; - int32 page_size = 6; + WatchSyncAuthenticatedContext context = 1; + // Opaque durable checkpoint from the last traversal whose items the host + // committed successfully. The host keeps this value unchanged across every + // page in one traversal and persists only next_cursor from the final page. + string cursor = 2; + // Opaque continuation for the traversal identified by cursor. It is used + // only for the immediately following page and is never persisted or reused + // after the traversal completes or fails. + string page_token = 3; + // Requested maximum number of items. Plugins may return fewer. + int32 page_size = 4; +} + +message WatchSyncRemoteWatchedState { + // Number of completed plays known to the provider. Must be at least one when + // this submessage is present. + int32 play_count = 1; + // Provider timestamp for the most recent completed play, when available. + google.protobuf.Timestamp last_watched_at = 2; +} + +message WatchSyncRemoteProgressState { + // Resume progress in the range [0, 100). Completed plays belong in watched. + double progress_percent = 1; + // Provider timestamp for this resume state. + google.protobuf.Timestamp paused_at = 2; } message WatchSyncRemoteState { string provider_item_key = 1; WatchSyncMedia media = 2; - int32 progress = 3; - string status = 4; - google.protobuf.Timestamp updated_at = 5; + // At least one typed state must be present. Both may be present when the + // provider reports a completed play and a separate resume point. + WatchSyncRemoteWatchedState watched = 3; + WatchSyncRemoteProgressState progress = 4; } message WatchSyncListRemoteStateResponse { repeated WatchSyncRemoteState items = 1; + // Non-empty when more pages remain. The host requests the next page with the + // original cursor and this token before committing next_cursor. string next_page_token = 2; + // Durable checkpoint for the completed traversal. Plugins set this only on + // the final page; the host persists it only after committing every item. string next_cursor = 3; + // True when this traversal is an authoritative full snapshot. False means an + // incremental delta, so an absent item must not be interpreted as deletion. + // The value must remain stable across every page in one traversal. bool complete_snapshot = 4; WatchSyncCredentials updated_credentials = 5; + // Traversal-level failure. When set, the host discards this page and keeps + // its previously committed cursor. WatchSyncFault fault = 6; } From 4bf764b6b43de41e9fb83d1c1a82cad071a16766 Mon Sep 17 00:00:00 2001 From: Quick <31828688+Quick104@users.noreply.github.com> Date: Fri, 24 Jul 2026 09:18:57 -0400 Subject: [PATCH 4/5] test: tighten watch sync contract coverage --- README.md | 7 ++++--- .../silo/plugin/v1/watch_sync_provider_test.go | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b9fbd45..a009d4c 100644 --- a/README.md +++ b/README.md @@ -150,9 +150,10 @@ Connection-wide faults such as invalid credentials belong on the RPC response. `ListRemoteState` returns provider-neutral typed subrecords. `watched` carries a play count and last-watched time; `progress` carries a fractional percentage and paused time. An item may contain either or both. The host keeps the request -`cursor` fixed while following ephemeral page tokens, commits every page, and -only then persists the final `next_cursor`. `complete_snapshot=true` means the -traversal is authoritative; when false, missing items are not deletions. +`cursor` fixed while following ephemeral page tokens, commits each successful +page, and only then persists the final `next_cursor`. `complete_snapshot=true` +means the traversal is authoritative; when false, missing items are not +deletions. ## Scan sources diff --git a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go index 8f06215..dff73a3 100644 --- a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go +++ b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider_test.go @@ -69,9 +69,21 @@ func TestWatchSyncApplyResultCarriesTypedRateLimit(t *testing.T) { }, } + data, err := proto.Marshal(result) + if err != nil { + t.Fatal(err) + } + var output WatchSyncApplyResult + if err := proto.Unmarshal(data, &output); err != nil { + t.Fatal(err) + } + if request.GetContext().GetCapabilityId() != "anilist" || - result.GetFault().GetCode() != WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_RATE_LIMITED || - result.GetFault().GetRetryAfter().AsDuration() != retryAfter { - t.Fatalf("request=%#v result=%#v", request, result) + output.GetEventId() != "event-1" || + output.GetStatus() != WatchSyncApplyStatus_WATCH_SYNC_APPLY_STATUS_RETRY || + output.GetFault().GetCode() != WatchSyncFaultCode_WATCH_SYNC_FAULT_CODE_RATE_LIMITED || + output.GetFault().GetSafeMessage() != "provider rate limit reached" || + output.GetFault().GetRetryAfter().AsDuration() != retryAfter { + t.Fatalf("request=%#v result=%#v", request, &output) } } From cfabc3dc6fdf30088a9738919dec07a0e10b47ea Mon Sep 17 00:00:00 2001 From: Quick <31828688+Quick104@users.noreply.github.com> Date: Fri, 24 Jul 2026 09:21:14 -0400 Subject: [PATCH 5/5] docs: define watch sync credential commit order --- README.md | 4 ++ .../silo/plugin/v1/watch_sync_provider.pb.go | 38 +++++++++++++------ .../silo/plugin/v1/watch_sync_provider.proto | 22 +++++++++-- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a009d4c..b355010 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,10 @@ convergent desired-state updates rather than increments. Authenticated RPCs receive the same host-owned capability, configuration, and credential data through `WatchSyncAuthenticatedContext`. The context exists only for one invocation and is never plugin configuration or plugin state. +Credentials returned by any RPC are complete authoritative replacements, not +patches. The host validates and persists them before consuming results, pages, +or faults—even when the response contains a fault. If credential persistence +fails, the host commits no other response data. Descriptors and events use the shared `WatchSyncMediaType` enum so advertised support and delivered media cannot drift between string conventions. Apply diff --git a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go index 4bfcec6..7b21e9c 100644 --- a/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go +++ b/pkg/pluginproto/silo/plugin/v1/watch_sync_provider.pb.go @@ -493,6 +493,11 @@ func (x *WatchSyncProviderConfig) GetSecretValues() map[string]string { return nil } +// When returned by a plugin, this message is a complete authoritative +// replacement, not a patch. The plugin must omit it unless it is valid for the +// next RPC. The host validates and persists it before consuming any other +// response field, including a fault. If persistence fails, the host treats the +// RPC as failed and commits no other response data. type WatchSyncCredentials struct { state protoimpl.MessageState `protogen:"open.v1"` AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` @@ -1095,10 +1100,14 @@ func (x *WatchSyncRefreshCredentialsRequest) GetContext() *WatchSyncAuthenticate } type WatchSyncCredentialResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Credentials *WatchSyncCredentials `protobuf:"bytes,1,opt,name=credentials,proto3" json:"credentials,omitempty"` - Account *WatchSyncAccount `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"` - Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + // Follows the WatchSyncCredentials response commit rule, even when fault is + // also set. + Credentials *WatchSyncCredentials `protobuf:"bytes,1,opt,name=credentials,proto3" json:"credentials,omitempty"` + // Consumed only after credentials are persisted and fault is absent. + Account *WatchSyncAccount `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"` + // Surfaced after credentials are persisted. When set, account is ignored. + Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1650,11 +1659,13 @@ func (x *WatchSyncApplyResult) GetFault() *WatchSyncFault { } type WatchSyncApplyEventsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Results []*WatchSyncApplyResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` - UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,2,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` - // Batch-level failure. When set, the host ignores results and applies the - // fault to the entire request. + state protoimpl.MessageState `protogen:"open.v1"` + Results []*WatchSyncApplyResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` + // Complete authoritative replacement. The host persists it before consuming + // results or fault; a persistence failure leaves every event uncommitted. + UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,2,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` + // Batch-level failure. After successfully persisting updated_credentials, + // the host ignores results and applies the fault to the entire request. Fault *WatchSyncFault `protobuf:"bytes,3,opt,name=fault,proto3" json:"fault,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1977,10 +1988,13 @@ type WatchSyncListRemoteStateResponse struct { // True when this traversal is an authoritative full snapshot. False means an // incremental delta, so an absent item must not be interpreted as deletion. // The value must remain stable across every page in one traversal. - CompleteSnapshot bool `protobuf:"varint,4,opt,name=complete_snapshot,json=completeSnapshot,proto3" json:"complete_snapshot,omitempty"` + CompleteSnapshot bool `protobuf:"varint,4,opt,name=complete_snapshot,json=completeSnapshot,proto3" json:"complete_snapshot,omitempty"` + // Complete authoritative replacement. The host persists it before consuming + // this page or fault; a persistence failure discards the page and cursor. UpdatedCredentials *WatchSyncCredentials `protobuf:"bytes,5,opt,name=updated_credentials,json=updatedCredentials,proto3" json:"updated_credentials,omitempty"` - // Traversal-level failure. When set, the host discards this page and keeps - // its previously committed cursor. + // Traversal-level failure. After successfully persisting + // updated_credentials, the host discards this page and keeps its previously + // committed cursor. Fault *WatchSyncFault `protobuf:"bytes,6,opt,name=fault,proto3" json:"fault,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache diff --git a/proto/silo/plugin/v1/watch_sync_provider.proto b/proto/silo/plugin/v1/watch_sync_provider.proto index d526db7..eb959d2 100644 --- a/proto/silo/plugin/v1/watch_sync_provider.proto +++ b/proto/silo/plugin/v1/watch_sync_provider.proto @@ -83,6 +83,11 @@ message WatchSyncProviderConfig { map secret_values = 2; } +// When returned by a plugin, this message is a complete authoritative +// replacement, not a patch. The plugin must omit it unless it is valid for the +// next RPC. The host validates and persists it before consuming any other +// response field, including a fault. If persistence fails, the host treats the +// RPC as failed and commits no other response data. message WatchSyncCredentials { string access_token = 1; string refresh_token = 2; @@ -157,8 +162,12 @@ message WatchSyncRefreshCredentialsRequest { } message WatchSyncCredentialResponse { + // Follows the WatchSyncCredentials response commit rule, even when fault is + // also set. WatchSyncCredentials credentials = 1; + // Consumed only after credentials are persisted and fault is absent. WatchSyncAccount account = 2; + // Surfaced after credentials are persisted. When set, account is ignored. WatchSyncFault fault = 3; } @@ -223,9 +232,11 @@ message WatchSyncApplyResult { message WatchSyncApplyEventsResponse { repeated WatchSyncApplyResult results = 1; + // Complete authoritative replacement. The host persists it before consuming + // results or fault; a persistence failure leaves every event uncommitted. WatchSyncCredentials updated_credentials = 2; - // Batch-level failure. When set, the host ignores results and applies the - // fault to the entire request. + // Batch-level failure. After successfully persisting updated_credentials, + // the host ignores results and applies the fault to the entire request. WatchSyncFault fault = 3; } @@ -279,8 +290,11 @@ message WatchSyncListRemoteStateResponse { // incremental delta, so an absent item must not be interpreted as deletion. // The value must remain stable across every page in one traversal. bool complete_snapshot = 4; + // Complete authoritative replacement. The host persists it before consuming + // this page or fault; a persistence failure discards the page and cursor. WatchSyncCredentials updated_credentials = 5; - // Traversal-level failure. When set, the host discards this page and keeps - // its previously committed cursor. + // Traversal-level failure. After successfully persisting + // updated_credentials, the host discards this page and keeps its previously + // committed cursor. WatchSyncFault fault = 6; }