Documentation
¶
Overview ¶
Package subscriptionv1 is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
Index ¶
- Constants
- Variables
- func RegisterInternalSubscriptionServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error
- func RegisterInternalSubscriptionServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, ...) error
- func RegisterInternalSubscriptionServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, ...) (err error)
- func RegisterInternalSubscriptionServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, ...) error
- func RegisterInternalSubscriptionServiceServer(s grpc.ServiceRegistrar, srv InternalSubscriptionServiceServer)
- type InternalSubscriptionServiceClient
- type InternalSubscriptionServiceServer
- type InternalSubscriptionService_SubscribeClient
- type InternalSubscriptionService_SubscribeServer
- type SubscriptionAnchor
- func (SubscriptionAnchor) Descriptor() protoreflect.EnumDescriptor
- func (x SubscriptionAnchor) Enum() *SubscriptionAnchor
- func (SubscriptionAnchor) EnumDescriptor() ([]byte, []int)deprecated
- func (x SubscriptionAnchor) Number() protoreflect.EnumNumber
- func (x SubscriptionAnchor) String() string
- func (SubscriptionAnchor) Type() protoreflect.EnumType
- type SubscriptionEvent
- func (*SubscriptionEvent) Descriptor() ([]byte, []int)deprecated
- func (x *SubscriptionEvent) GetCarrier() isSubscriptionEvent_Carrier
- func (x *SubscriptionEvent) GetChange() SubscriptionEvent_Change
- func (x *SubscriptionEvent) GetKind() string
- func (x *SubscriptionEvent) GetPosition() string
- func (x *SubscriptionEvent) GetProjectId() string
- func (x *SubscriptionEvent) GetResourceId() string
- func (x *SubscriptionEvent) GetState() *anypb.Any
- func (x *SubscriptionEvent) GetStateUnavailable() *SubscriptionEvent_StateUnavailable
- func (*SubscriptionEvent) ProtoMessage()
- func (x *SubscriptionEvent) ProtoReflect() protoreflect.Message
- func (x *SubscriptionEvent) Reset()
- func (x *SubscriptionEvent) String() string
- type SubscriptionEvent_Change
- func (SubscriptionEvent_Change) Descriptor() protoreflect.EnumDescriptor
- func (x SubscriptionEvent_Change) Enum() *SubscriptionEvent_Change
- func (SubscriptionEvent_Change) EnumDescriptor() ([]byte, []int)deprecated
- func (x SubscriptionEvent_Change) Number() protoreflect.EnumNumber
- func (x SubscriptionEvent_Change) String() string
- func (SubscriptionEvent_Change) Type() protoreflect.EnumType
- type SubscriptionEvent_State
- type SubscriptionEvent_StateUnavailable
- func (*SubscriptionEvent_StateUnavailable) Descriptor() ([]byte, []int)deprecated
- func (x *SubscriptionEvent_StateUnavailable) GetReason() SubscriptionEvent_StateUnavailable_Reason
- func (*SubscriptionEvent_StateUnavailable) ProtoMessage()
- func (x *SubscriptionEvent_StateUnavailable) ProtoReflect() protoreflect.Message
- func (x *SubscriptionEvent_StateUnavailable) Reset()
- func (x *SubscriptionEvent_StateUnavailable) String() string
- type SubscriptionEvent_StateUnavailable_
- type SubscriptionEvent_StateUnavailable_Reason
- func (SubscriptionEvent_StateUnavailable_Reason) Descriptor() protoreflect.EnumDescriptor
- func (x SubscriptionEvent_StateUnavailable_Reason) Enum() *SubscriptionEvent_StateUnavailable_Reason
- func (SubscriptionEvent_StateUnavailable_Reason) EnumDescriptor() ([]byte, []int)deprecated
- func (x SubscriptionEvent_StateUnavailable_Reason) Number() protoreflect.EnumNumber
- func (x SubscriptionEvent_StateUnavailable_Reason) String() string
- func (SubscriptionEvent_StateUnavailable_Reason) Type() protoreflect.EnumType
- type SubscriptionMessage
- func (*SubscriptionMessage) Descriptor() ([]byte, []int)deprecated
- func (x *SubscriptionMessage) GetEvent() *SubscriptionEvent
- func (x *SubscriptionMessage) GetMessage() isSubscriptionMessage_Message
- func (x *SubscriptionMessage) GetOpened() *SubscriptionOpened
- func (*SubscriptionMessage) ProtoMessage()
- func (x *SubscriptionMessage) ProtoReflect() protoreflect.Message
- func (x *SubscriptionMessage) Reset()
- func (x *SubscriptionMessage) String() string
- type SubscriptionMessage_Event
- type SubscriptionMessage_Opened
- type SubscriptionOpened
- func (*SubscriptionOpened) Descriptor() ([]byte, []int)deprecated
- func (x *SubscriptionOpened) GetCaughtUp() bool
- func (x *SubscriptionOpened) GetEarliestResumablePosition() string
- func (x *SubscriptionOpened) GetHonoredFilters() []string
- func (x *SubscriptionOpened) GetKnownKinds() []string
- func (x *SubscriptionOpened) GetPosition() string
- func (x *SubscriptionOpened) GetRetainsEverything() bool
- func (*SubscriptionOpened) ProtoMessage()
- func (x *SubscriptionOpened) ProtoReflect() protoreflect.Message
- func (x *SubscriptionOpened) Reset()
- func (x *SubscriptionOpened) String() string
- type SubscriptionRequest
- func (*SubscriptionRequest) Descriptor() ([]byte, []int)deprecated
- func (x *SubscriptionRequest) GetAnchor() SubscriptionAnchor
- func (x *SubscriptionRequest) GetIds() []string
- func (x *SubscriptionRequest) GetKinds() []string
- func (x *SubscriptionRequest) GetPosition() string
- func (x *SubscriptionRequest) GetProjectId() string
- func (x *SubscriptionRequest) GetStart() isSubscriptionRequest_Start
- func (*SubscriptionRequest) ProtoMessage()
- func (x *SubscriptionRequest) ProtoReflect() protoreflect.Message
- func (x *SubscriptionRequest) Reset()
- func (x *SubscriptionRequest) String() string
- type SubscriptionRequest_Anchor
- type SubscriptionRequest_Position
- type UnimplementedInternalSubscriptionServiceServer
- type UnsafeInternalSubscriptionServiceServer
Constants ¶
const (
InternalSubscriptionService_Subscribe_FullMethodName = "/kacho.cloud.subscription.InternalSubscriptionService/Subscribe"
)
Variables ¶
var ( SubscriptionAnchor_name = map[int32]string{ 0: "SUBSCRIPTION_ANCHOR_UNSPECIFIED", 1: "BEGINNING", 2: "CURRENT_END", } SubscriptionAnchor_value = map[string]int32{ "SUBSCRIPTION_ANCHOR_UNSPECIFIED": 0, "BEGINNING": 1, "CURRENT_END": 2, } )
Enum value maps for SubscriptionAnchor.
var ( SubscriptionEvent_Change_name = map[int32]string{ 0: "CHANGE_UNSPECIFIED", 1: "CREATED", 2: "UPDATED", 3: "DELETED", } SubscriptionEvent_Change_value = map[string]int32{ "CHANGE_UNSPECIFIED": 0, "CREATED": 1, "UPDATED": 2, "DELETED": 3, } )
Enum value maps for SubscriptionEvent_Change.
var ( 0: "REASON_UNSPECIFIED", 1: "NOT_SERIALIZABLE", 2: "NOT_RETAINED", 3: "WITHHELD", 4: "NOT_PRODUCED", } "REASON_UNSPECIFIED": 0, "NOT_SERIALIZABLE": 1, "NOT_RETAINED": 2, "WITHHELD": 3, "NOT_PRODUCED": 4, } )
Enum value maps for SubscriptionEvent_StateUnavailable_Reason.
var File_kacho_cloud_subscription_subscription_proto protoreflect.FileDescriptor
var File_kacho_cloud_subscription_subscription_service_proto protoreflect.FileDescriptor
var InternalSubscriptionService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "kacho.cloud.subscription.InternalSubscriptionService", HandlerType: (*InternalSubscriptionServiceServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "Subscribe", Handler: _InternalSubscriptionService_Subscribe_Handler, ServerStreams: true, }, }, Metadata: "kacho/cloud/subscription/subscription_service.proto", }
InternalSubscriptionService_ServiceDesc is the grpc.ServiceDesc for InternalSubscriptionService service. It's only intended for direct use with grpc.RegisterService, and not to be introspected or modified (even as a copy)
Functions ¶
func RegisterInternalSubscriptionServiceHandler ¶
func RegisterInternalSubscriptionServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error
RegisterInternalSubscriptionServiceHandler registers the http handlers for service InternalSubscriptionService to "mux". The handlers forward requests to the grpc endpoint over "conn".
func RegisterInternalSubscriptionServiceHandlerClient ¶
func RegisterInternalSubscriptionServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client InternalSubscriptionServiceClient) error
RegisterInternalSubscriptionServiceHandlerClient registers the http handlers for service InternalSubscriptionService to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "InternalSubscriptionServiceClient". Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "InternalSubscriptionServiceClient" doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in "InternalSubscriptionServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterInternalSubscriptionServiceHandlerFromEndpoint ¶
func RegisterInternalSubscriptionServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error)
RegisterInternalSubscriptionServiceHandlerFromEndpoint is same as RegisterInternalSubscriptionServiceHandler but automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterInternalSubscriptionServiceHandlerServer ¶
func RegisterInternalSubscriptionServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server InternalSubscriptionServiceServer) error
RegisterInternalSubscriptionServiceHandlerServer registers the http handlers for service InternalSubscriptionService to "mux". UnaryRPC :call InternalSubscriptionServiceServer directly. StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterInternalSubscriptionServiceHandlerFromEndpoint instead. GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterInternalSubscriptionServiceServer ¶
func RegisterInternalSubscriptionServiceServer(s grpc.ServiceRegistrar, srv InternalSubscriptionServiceServer)
Types ¶
type InternalSubscriptionServiceClient ¶
type InternalSubscriptionServiceClient interface {
// Subscribe — открыть поток изменений журнала владельца.
//
// # Решение о показе принимается НА КАЖДОЙ ОТДАВАЕМОЙ СТРОКЕ
//
// Единого объекта, о котором можно было бы задать ОДИН вопрос при открытии, у
// этого метода не существует: поток несёт события разных предметов разных
// проектов, и какие именно — заранее не известно. Поэтому метод объявлен
// `scope_filtered`: край пообъектного вопроса не задаёт, а владелец сужает
// поток по правам вызывающего на каждой строке, партиями, тем же сужателем,
// что и списки.
//
// Следствие, названное прямо: у метода НЕТ полосы, на которую можно было бы
// откатиться при неработающем сужателе. Сервер отказывает в открытии потока,
// если сужатель отсутствует либо не сужает, а боевая стража отказывает в
// старте процессу, объявившему `scope_filtered` без работающего фильтра.
//
// # Что этот метод НЕ делает
//
// Он не заменяет полл операции: событие пишется в той же транзакции, что и
// ресурсная строка, поэтому при отказе операции события нет ВОВСЕ, и
// наблюдатель ресурсов не отличит «ещё делается» от «упало».
Subscribe(ctx context.Context, in *SubscriptionRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SubscriptionMessage], error)
}
InternalSubscriptionServiceClient is the client API for InternalSubscriptionService 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.
InternalSubscriptionService — ЕДИНСТВЕННЫЙ глагол подписки на платформе.
Почему имя начинается с `Internal` ¶
Это не украшение: префикс имени службы есть ДЕЙСТВУЮЩИЙ дискриминатор запрета #6 на всей популяции методов дерева (`gateway/internal/allowlist` `HasInternalSuffix`). Метод с таким именем не попадает во внешний маршрутизатор by construction, и проверка этого — не обещание, а обход дескрипторов.
Подписку открывает наш же модуль по проверенному сертификату. Проекция потока в браузер (kacho#1020) идёт ЧЕРЕЗ КРАЙ, который сам становится потребителем этого метода, — а не публикацией метода наружу.
Где этот глагол доступен КЛИЕНТУ ¶
Недостижимым он не является, и сказать это здесь обязательно: читатель одного контракта видит слово `Internal` и заключает «не для меня» — заключение неверное, и стоило оно имитации клиента трёх часов.
Клиент открывает поток у КРАЯ: `GET /subscription/v1/events` (Server-Sent Events), где владелец журнала называется параметром `owner`, а оси фильтра — параметрами `kinds`, `projectId`, `ids` и `start`. Позиция возвращается заголовком `Last-Event-ID`. Адрес, словари и кадры описаны страницей «Подписка на изменения» документации края; здесь они не воспроизводятся, чтобы не завести второго места об одном предмете.
Один сервис на всех владельцев, а не по сервису на домен ¶
Полное имя метода одно (`/kacho.cloud.subscription.InternalSubscriptionService/Subscribe`), и каждый владелец журнала регистрирует ЭТОТ сервис на своём внутреннем слушателе. Имена gRPC-сервисов уникальны в пределах процесса, а процессы у владельцев разные — поэтому одно объявление обслуживает всех, и запись каталога прав тоже одна.
Доменного объявления рядом не заводится, и это держит гейт дерева (`internal/repohygiene/subscriptionformsingularity.go`), а не эта фраза.
Только внутренний слушатель ¶
Внешнего пути у метода нет: `google.api.http` не объявлен, а имя службы выводит её из внешнего маршрутизатора механически (см. выше).
func NewInternalSubscriptionServiceClient ¶
func NewInternalSubscriptionServiceClient(cc grpc.ClientConnInterface) InternalSubscriptionServiceClient
type InternalSubscriptionServiceServer ¶
type InternalSubscriptionServiceServer interface {
// Subscribe — открыть поток изменений журнала владельца.
//
// # Решение о показе принимается НА КАЖДОЙ ОТДАВАЕМОЙ СТРОКЕ
//
// Единого объекта, о котором можно было бы задать ОДИН вопрос при открытии, у
// этого метода не существует: поток несёт события разных предметов разных
// проектов, и какие именно — заранее не известно. Поэтому метод объявлен
// `scope_filtered`: край пообъектного вопроса не задаёт, а владелец сужает
// поток по правам вызывающего на каждой строке, партиями, тем же сужателем,
// что и списки.
//
// Следствие, названное прямо: у метода НЕТ полосы, на которую можно было бы
// откатиться при неработающем сужателе. Сервер отказывает в открытии потока,
// если сужатель отсутствует либо не сужает, а боевая стража отказывает в
// старте процессу, объявившему `scope_filtered` без работающего фильтра.
//
// # Что этот метод НЕ делает
//
// Он не заменяет полл операции: событие пишется в той же транзакции, что и
// ресурсная строка, поэтому при отказе операции события нет ВОВСЕ, и
// наблюдатель ресурсов не отличит «ещё делается» от «упало».
Subscribe(*SubscriptionRequest, grpc.ServerStreamingServer[SubscriptionMessage]) error
// contains filtered or unexported methods
}
InternalSubscriptionServiceServer is the server API for InternalSubscriptionService service. All implementations must embed UnimplementedInternalSubscriptionServiceServer for forward compatibility.
InternalSubscriptionService — ЕДИНСТВЕННЫЙ глагол подписки на платформе.
Почему имя начинается с `Internal` ¶
Это не украшение: префикс имени службы есть ДЕЙСТВУЮЩИЙ дискриминатор запрета #6 на всей популяции методов дерева (`gateway/internal/allowlist` `HasInternalSuffix`). Метод с таким именем не попадает во внешний маршрутизатор by construction, и проверка этого — не обещание, а обход дескрипторов.
Подписку открывает наш же модуль по проверенному сертификату. Проекция потока в браузер (kacho#1020) идёт ЧЕРЕЗ КРАЙ, который сам становится потребителем этого метода, — а не публикацией метода наружу.
Где этот глагол доступен КЛИЕНТУ ¶
Недостижимым он не является, и сказать это здесь обязательно: читатель одного контракта видит слово `Internal` и заключает «не для меня» — заключение неверное, и стоило оно имитации клиента трёх часов.
Клиент открывает поток у КРАЯ: `GET /subscription/v1/events` (Server-Sent Events), где владелец журнала называется параметром `owner`, а оси фильтра — параметрами `kinds`, `projectId`, `ids` и `start`. Позиция возвращается заголовком `Last-Event-ID`. Адрес, словари и кадры описаны страницей «Подписка на изменения» документации края; здесь они не воспроизводятся, чтобы не завести второго места об одном предмете.
Один сервис на всех владельцев, а не по сервису на домен ¶
Полное имя метода одно (`/kacho.cloud.subscription.InternalSubscriptionService/Subscribe`), и каждый владелец журнала регистрирует ЭТОТ сервис на своём внутреннем слушателе. Имена gRPC-сервисов уникальны в пределах процесса, а процессы у владельцев разные — поэтому одно объявление обслуживает всех, и запись каталога прав тоже одна.
Доменного объявления рядом не заводится, и это держит гейт дерева (`internal/repohygiene/subscriptionformsingularity.go`), а не эта фраза.
Только внутренний слушатель ¶
Внешнего пути у метода нет: `google.api.http` не объявлен, а имя службы выводит её из внешнего маршрутизатора механически (см. выше).
type InternalSubscriptionService_SubscribeClient ¶
type InternalSubscriptionService_SubscribeClient = grpc.ServerStreamingClient[SubscriptionMessage]
This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type InternalSubscriptionService_SubscribeServer ¶
type InternalSubscriptionService_SubscribeServer = grpc.ServerStreamingServer[SubscriptionMessage]
This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type SubscriptionAnchor ¶
type SubscriptionAnchor int32
SubscriptionAnchor — якорь начала, когда клиент называет его словом, а не позицией.
const ( // Не назван. В ветви `SubscriptionRequest.start` отвергается // `INVALID_ARGUMENT`: выбрав ветвь якоря, вызывающий обязан назвать якорь. // Умолчание живёт у НЕЗАДАННОЙ ветви, а не у незаданного значения внутри // выбранной — иначе одно и то же намерение выражалось бы двумя способами. SubscriptionAnchor_SUBSCRIPTION_ANCHOR_UNSPECIFIED SubscriptionAnchor = 0 // С начала журнала — всё, что владелец ещё удерживает. SubscriptionAnchor_BEGINNING SubscriptionAnchor = 1 // С текущего конца — только то, что записано после открытия подписки. SubscriptionAnchor_CURRENT_END SubscriptionAnchor = 2 )
func (SubscriptionAnchor) Descriptor ¶
func (SubscriptionAnchor) Descriptor() protoreflect.EnumDescriptor
func (SubscriptionAnchor) Enum ¶
func (x SubscriptionAnchor) Enum() *SubscriptionAnchor
func (SubscriptionAnchor) EnumDescriptor
deprecated
func (SubscriptionAnchor) EnumDescriptor() ([]byte, []int)
Deprecated: Use SubscriptionAnchor.Descriptor instead.
func (SubscriptionAnchor) Number ¶
func (x SubscriptionAnchor) Number() protoreflect.EnumNumber
func (SubscriptionAnchor) String ¶
func (x SubscriptionAnchor) String() string
func (SubscriptionAnchor) Type ¶
func (SubscriptionAnchor) Type() protoreflect.EnumType
type SubscriptionEvent ¶
type SubscriptionEvent struct {
// position — позиция ЭТОГО события. Непрозрачна; клиент запоминает её и
// возвращает дословно, чтобы возобновиться отсюда.
Position string `protobuf:"bytes,1,opt,name=position,proto3" json:"position,omitempty"`
// kind — вид предмета из закрытого словаря ВЛАДЕЛЬЦА, в том же написании, в
// каком его принимает ось `SubscriptionRequest.kinds` и в каком его
// перечисляет `SubscriptionOpened.known_kinds`.
//
// Написание одно на всё дерево — тип объекта модели прав; чем оно держится и
// почему выбрано именно оно, сказано у поля `kinds`. Слово, которым владелец
// записал строку в своём журнале, сюда не попадает: оно частное.
Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"`
// resource_id — неизменяемый идентификатор предмета. По нему сужает ось
// `SubscriptionRequest.ids`.
//
// Может быть пуст: не всякое событие относится к ресурсу (предметом бывает
// субъект прав), и непустой идентификатор ресурса не является условием ни
// открытия подписки, ни доставки события.
ResourceId string `protobuf:"bytes,3,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
// project_id — АВТОРИЗУЕМЫЙ ЯКОРЬ, и он стоит полем ОБОЛОЧКИ, а не внутри
// нагрузки.
//
// Решение «кому это показать» принимается по нему БЕЗ обращения к предмету.
// Для события удаления это несущее: обратиться не к чему — предмета больше
// нет, а нагрузка у удаления беднее всего. Якорь внутри нагрузки означал бы
// выбор из двух негодных: спрашивать модель прав про несуществующий объект
// либо не показывать удаления вовсе. Второе наступает ТИХО, и цена его — та
// самая, ради которой заводится подписка: потребитель, снявший поллинг,
// никогда не узнает об удалении и будет держать удалённые строки вечно.
//
// Пусто означает «событие не принадлежит проекту» — предмет уровня аккаунта
// или кластера. Это законное состояние, а не пропуск.
ProjectId string `protobuf:"bytes,4,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"`
// change — род изменения. Словарь платформенный, в отличие от `kind`: действие
// потребителя зависит именно от него, и различать снос от правки обязан уметь
// всякий подписчик, не зная домена.
Change SubscriptionEvent_Change `protobuf:"varint,5,opt,name=change,proto3,enum=kacho.cloud.subscription.SubscriptionEvent_Change" json:"change,omitempty"`
// Носитель нагрузки — ВЫБОР ИЗ ДВУХ ВЕТВЕЙ: состояние ЛИБО признак, что
// состояния нет.
//
// Ветвление здесь потому, что запретить прозой значение, которое допускает
// собственный тип, невозможно. Пустая нагрузка НИКОГДА не означает «у предмета
// не осталось полей»: подставить пустой объект вместо признака недоступности
// в этой форме просто негде.
//
// Подписчик, получивший непустую нагрузку, вправе читать её как ПОЛНОЕ
// состояние предмета — и это единственный случай, когда он вправе так делать.
//
// Types that are valid to be assigned to Carrier:
//
// *SubscriptionEvent_State
// *SubscriptionEvent_StateUnavailable_
Carrier isSubscriptionEvent_Carrier `protobuf_oneof:"carrier"`
// contains filtered or unexported fields
}
SubscriptionEvent — оболочка события: то, что приходит в потоке после `SubscriptionOpened`.
Что в оболочке, а что в нагрузке ¶
В оболочке лежит всё, по чему сервер принимает решения и по чему клиент ведёт своё состояние: позиция, вид, идентификатор, ЯКОРЬ АВТОРИЗАЦИИ и род изменения. В нагрузке — состояние предмета, специфичное для владельца.
Причины остановки потока здесь НЕТ, и это намеренно ¶
Ничто в этой форме не обязывает сервер сообщать исход остановки полезной нагрузкой: закрытие потока (в том числе по отозванному доступу) остаётся статусом вызова, который следующая фаза вправе выбрать отличимым. Обязательное поле «причина остановки» внутри события загнало бы этот исход в контракт данных.
func (*SubscriptionEvent) Descriptor
deprecated
func (*SubscriptionEvent) Descriptor() ([]byte, []int)
Deprecated: Use SubscriptionEvent.ProtoReflect.Descriptor instead.
func (*SubscriptionEvent) GetCarrier ¶
func (x *SubscriptionEvent) GetCarrier() isSubscriptionEvent_Carrier
func (*SubscriptionEvent) GetChange ¶
func (x *SubscriptionEvent) GetChange() SubscriptionEvent_Change
func (*SubscriptionEvent) GetKind ¶
func (x *SubscriptionEvent) GetKind() string
func (*SubscriptionEvent) GetPosition ¶
func (x *SubscriptionEvent) GetPosition() string
func (*SubscriptionEvent) GetProjectId ¶
func (x *SubscriptionEvent) GetProjectId() string
func (*SubscriptionEvent) GetResourceId ¶
func (x *SubscriptionEvent) GetResourceId() string
func (*SubscriptionEvent) GetState ¶
func (x *SubscriptionEvent) GetState() *anypb.Any
func (*SubscriptionEvent) GetStateUnavailable ¶
func (x *SubscriptionEvent) GetStateUnavailable() *SubscriptionEvent_StateUnavailable
func (*SubscriptionEvent) ProtoMessage ¶
func (*SubscriptionEvent) ProtoMessage()
func (*SubscriptionEvent) ProtoReflect ¶
func (x *SubscriptionEvent) ProtoReflect() protoreflect.Message
func (*SubscriptionEvent) Reset ¶
func (x *SubscriptionEvent) Reset()
func (*SubscriptionEvent) String ¶
func (x *SubscriptionEvent) String() string
type SubscriptionEvent_Change ¶
type SubscriptionEvent_Change int32
Change — род изменения предмета.
const ( // Не назван. Владелец обязан назвать род изменения: подписчик, ведущий своё // состояние, иначе не знает, добавить строку, обновить её или снять. SubscriptionEvent_CHANGE_UNSPECIFIED SubscriptionEvent_Change = 0 SubscriptionEvent_CREATED SubscriptionEvent_Change = 1 SubscriptionEvent_UPDATED SubscriptionEvent_Change = 2 SubscriptionEvent_DELETED SubscriptionEvent_Change = 3 )
func (SubscriptionEvent_Change) Descriptor ¶
func (SubscriptionEvent_Change) Descriptor() protoreflect.EnumDescriptor
func (SubscriptionEvent_Change) Enum ¶
func (x SubscriptionEvent_Change) Enum() *SubscriptionEvent_Change
func (SubscriptionEvent_Change) EnumDescriptor
deprecated
func (SubscriptionEvent_Change) EnumDescriptor() ([]byte, []int)
Deprecated: Use SubscriptionEvent_Change.Descriptor instead.
func (SubscriptionEvent_Change) Number ¶
func (x SubscriptionEvent_Change) Number() protoreflect.EnumNumber
func (SubscriptionEvent_Change) String ¶
func (x SubscriptionEvent_Change) String() string
func (SubscriptionEvent_Change) Type ¶
func (SubscriptionEvent_Change) Type() protoreflect.EnumType
type SubscriptionEvent_State ¶
type SubscriptionEvent_State struct {
// state — состояние предмета. Тип НАЗВАН: `Any` несёт имя типа на проводе,
// и ключи нагрузки суть поля этого типа, объявленные контрактом владельца.
//
// Свободной структуры здесь нет намеренно. У неё ключи на проводе
// производятся от имён идентификаторов Go, `buf breaking` этого не видит
// by construction (в контракте написано «объект»), и обычный внутренний
// рефактор молча ломает публичную нагрузку.
State *anypb.Any `protobuf:"bytes,10,opt,name=state,proto3,oneof"`
}
type SubscriptionEvent_StateUnavailable ¶
type SubscriptionEvent_StateUnavailable struct {
// contains filtered or unexported fields
}
StateUnavailable — вторая ветвь носителя: состояние предмета недоступно.
Это НЕ причина остановки потока и не отказ вызова: поток продолжается, а подписчик узнаёт, что по этому конкретному событию состояния не будет.
func (*SubscriptionEvent_StateUnavailable) Descriptor
deprecated
func (*SubscriptionEvent_StateUnavailable) Descriptor() ([]byte, []int)
Deprecated: Use SubscriptionEvent_StateUnavailable.ProtoReflect.Descriptor instead.
func (*SubscriptionEvent_StateUnavailable) GetReason ¶
func (x *SubscriptionEvent_StateUnavailable) GetReason() SubscriptionEvent_StateUnavailable_Reason
func (*SubscriptionEvent_StateUnavailable) ProtoMessage ¶
func (*SubscriptionEvent_StateUnavailable) ProtoMessage()
func (*SubscriptionEvent_StateUnavailable) ProtoReflect ¶
func (x *SubscriptionEvent_StateUnavailable) ProtoReflect() protoreflect.Message
func (*SubscriptionEvent_StateUnavailable) Reset ¶
func (x *SubscriptionEvent_StateUnavailable) Reset()
func (*SubscriptionEvent_StateUnavailable) String ¶
func (x *SubscriptionEvent_StateUnavailable) String() string
type SubscriptionEvent_StateUnavailable_ ¶
type SubscriptionEvent_StateUnavailable_ struct {
StateUnavailable *SubscriptionEvent_StateUnavailable `protobuf:"bytes,11,opt,name=state_unavailable,json=stateUnavailable,proto3,oneof"`
}
type SubscriptionEvent_StateUnavailable_Reason ¶
type SubscriptionEvent_StateUnavailable_Reason int32
Reason — почему состояния нет. Словарь закрыт: «прочее» здесь не заводится, потому что корзина «прочее» перестаёт различать через месяц.
const ( // ради совместимости разбора, а не как законный исход. SubscriptionEvent_StateUnavailable_REASON_UNSPECIFIED SubscriptionEvent_StateUnavailable_Reason = 0 SubscriptionEvent_StateUnavailable_NOT_SERIALIZABLE SubscriptionEvent_StateUnavailable_Reason = 1 SubscriptionEvent_StateUnavailable_NOT_RETAINED SubscriptionEvent_StateUnavailable_Reason = 2 SubscriptionEvent_StateUnavailable_WITHHELD SubscriptionEvent_StateUnavailable_Reason = 3 // // Это СВОЙСТВО ЖУРНАЛА, а не сбой: собирать было нечего, попытки не было, // и повтор ничего не изменит. Тем оно и отличается от `NOT_SERIALIZABLE`, // где состояние есть, а собрать его не удалось: там разумное действие — // перечитать, здесь — сразу идти за предметом по `resource_id` (либо снять // его из своего состояния, если род изменения `DELETED`). // // Случая два, и оба живые: владелец, чей журнал не несёт состояния ни у // одного вида, и событие снятия — предмета больше нет, собирать нечего // by construction. // // Значение ДОБАВЛЕНО к закрытому словарю, а не заведено корзиной «прочее»: // до него владелец, у которого состояния не бывает, был вынужден называть // причиной соседнюю запись — то есть утверждать неудавшуюся попытку там, // где попытки не было. Подписчик, не знающий этого значения, читает его // как «состояния не будет» — ровно то, что означает ветвь. SubscriptionEvent_StateUnavailable_NOT_PRODUCED SubscriptionEvent_StateUnavailable_Reason = 4 )
func (SubscriptionEvent_StateUnavailable_Reason) Descriptor ¶
func (SubscriptionEvent_StateUnavailable_Reason) Descriptor() protoreflect.EnumDescriptor
func (SubscriptionEvent_StateUnavailable_Reason) EnumDescriptor
deprecated
func (SubscriptionEvent_StateUnavailable_Reason) EnumDescriptor() ([]byte, []int)
Deprecated: Use SubscriptionEvent_StateUnavailable_Reason.Descriptor instead.
func (SubscriptionEvent_StateUnavailable_Reason) Number ¶
func (x SubscriptionEvent_StateUnavailable_Reason) Number() protoreflect.EnumNumber
func (SubscriptionEvent_StateUnavailable_Reason) String ¶
func (x SubscriptionEvent_StateUnavailable_Reason) String() string
func (SubscriptionEvent_StateUnavailable_Reason) Type ¶
func (SubscriptionEvent_StateUnavailable_Reason) Type() protoreflect.EnumType
type SubscriptionMessage ¶
type SubscriptionMessage struct {
// Types that are valid to be assigned to Message:
//
// *SubscriptionMessage_Opened
// *SubscriptionMessage_Event
Message isSubscriptionMessage_Message `protobuf_oneof:"message"`
// contains filtered or unexported fields
}
Глагол подписки платформы и носитель его потока.
Почему ОТДЕЛЬНЫЙ файл от формы ¶
Форма (`subscription.proto`) обязана импортироваться кем угодно и потому не зависит ни от одного контракта дерева. Глагол несёт аннотации каталога прав — зависимость от контракта другого домена, — и, лежи он рядом с формой, форма перестала бы быть общей.
Разделение не косметическое: домены импортируют ФОРМУ (чтобы говорить на одном языке подписки) и НЕ обязаны импортировать глагол — его регистрирует общий сервер `corelib/subscription`, один на платформу. SubscriptionMessage — то, что едет по потоку: ЛИБО служебное сообщение открытия, ЛИБО событие.
Зачем носитель, а не «поток событий, а служебное — заголовком» ¶
`SubscriptionOpened` обязано прийти ПЕРВЫМ и ВСЕГДА, в том числе когда событий нет вовсе. Поток типа `SubscriptionEvent` этого выразить не может: пришлось бы либо возить служебные поля в каждом событии, либо вынести их в метаданные вызова — то есть в место, где у них нет ни типа, ни разбора, ни `buf breaking`.
Ветвление, а не два необязательных поля: сообщение, у которого пусты обе ветви, в этой форме просто негде построить, и подписчику не приходится решать, что значит «оба пусты» или «оба заполнены».
func (*SubscriptionMessage) Descriptor
deprecated
func (*SubscriptionMessage) Descriptor() ([]byte, []int)
Deprecated: Use SubscriptionMessage.ProtoReflect.Descriptor instead.
func (*SubscriptionMessage) GetEvent ¶
func (x *SubscriptionMessage) GetEvent() *SubscriptionEvent
func (*SubscriptionMessage) GetMessage ¶
func (x *SubscriptionMessage) GetMessage() isSubscriptionMessage_Message
func (*SubscriptionMessage) GetOpened ¶
func (x *SubscriptionMessage) GetOpened() *SubscriptionOpened
func (*SubscriptionMessage) ProtoMessage ¶
func (*SubscriptionMessage) ProtoMessage()
func (*SubscriptionMessage) ProtoReflect ¶
func (x *SubscriptionMessage) ProtoReflect() protoreflect.Message
func (*SubscriptionMessage) Reset ¶
func (x *SubscriptionMessage) Reset()
func (*SubscriptionMessage) String ¶
func (x *SubscriptionMessage) String() string
type SubscriptionMessage_Event ¶
type SubscriptionMessage_Event struct {
// event — событие. Приходит только после `opened`.
Event *SubscriptionEvent `protobuf:"bytes,2,opt,name=event,proto3,oneof"`
}
type SubscriptionMessage_Opened ¶
type SubscriptionMessage_Opened struct {
// opened — служебное сообщение открытия. Приходит первым и ровно один раз за
// поток.
Opened *SubscriptionOpened `protobuf:"bytes,1,opt,name=opened,proto3,oneof"`
}
type SubscriptionOpened ¶
type SubscriptionOpened struct {
// position — позиция, с которой сервер начал отдавать. Непрозрачна ровно так
// же, как `SubscriptionRequest.position`, и возвращается в неё дословно.
Position string `protobuf:"bytes,1,opt,name=position,proto3" json:"position,omitempty"`
// caught_up — исчерпан ли журнал на момент открытия.
//
// Этим «событий не было» отличается от «я подключился позже», и отличается ПО
// СЛОВУ СЕРВЕРА, а не арифметикой клиента: над непрозрачным токеном сравнение
// двух позиций невыразимо by construction, и непрозрачность именно этот ход и
// запрещает.
//
// `true` + молчание = изменений не было. `false` + молчание = я ещё догоняю.
CaughtUp bool `protobuf:"varint,2,opt,name=caught_up,json=caughtUp,proto3" json:"caught_up,omitempty"`
// honored_filters — оси, которые этот владелец отобрал ЧЕСТНО.
//
// Значения — ИМЕНА ПОЛЕЙ `SubscriptionRequest`, как они объявлены в этом
// контракте: `kinds`, `project_id`, `ids`. Имя поля контракта и есть провод,
// поэтому свой второй словарь осей здесь не заводится — он разошёлся бы с
// полями молча.
//
// Зачем перечень. Журналы владельцев устроены по-разному, и ось, выразимая
// контрактом, не у каждого отбираема. Исходов у такой оси три: молча
// проигнорировать — ЗАПРЕЩЕНО (вызывающий получит успех и решит, что его
// параметр применён); отвергнуть — лишает подписки половину владельцев;
// НАЗВАТЬ — то, что выбрано здесь. Цена названа: по неотобранной оси клиент
// доотбирает сам.
//
// Исключение — ЯКОРНАЯ ось: её в этом перечне не бывает отсутствующей при
// заданном значении, потому что владелец, не умеющий по ней отобрать,
// подписку отвергает. Решение о показе не перекладывается на клиента.
//
// Владелец, отобравший все заданные оси, перечисляет их все, и перечень
// совпадает с заданными вызывающим.
HonoredFilters []string `protobuf:"bytes,3,rep,name=honored_filters,json=honoredFilters,proto3" json:"honored_filters,omitempty"`
// earliest_resumable_position — самая ранняя позиция, с которой возобновление
// ещё возможно.
//
// Владелец, переставший удерживать часть журнала, обязан уметь сказать
// «твоя позиция больше не возобновима» ЯВНО и назвать место, с которого
// подписка возможна, — а не начать молча с ближайшего удержанного. Молчаливое
// начало клиент запишет как «изменений не было», и дописать этот исход потом
// будет ломающим изменением.
//
// Отказ на невозобновимой позиции называет ЭТУ ЖЕ величину, поэтому она
// объявлена одним полем, а не двумя похожими в двух местах.
EarliestResumablePosition string `` /* 138-byte string literal not displayed */
// retains_everything — владелец удерживает журнал целиком.
//
// Тогда отказ «позиция утрачена» не наступает никогда, и это СВОЙСТВО
// ВЛАДЕЛЬЦА, объявленное им, а не умолчание формы: при `true` поле
// `earliest_resumable_position` не несёт горизонта, потому что горизонта нет.
RetainsEverything bool `protobuf:"varint,5,opt,name=retains_everything,json=retainsEverything,proto3" json:"retains_everything,omitempty"`
// known_kinds — ЗАКРЫТЫЙ СЛОВАРЬ ВИДОВ этого владельца, целиком.
//
// Это ответ на вопрос «что вообще можно назвать в `SubscriptionRequest.kinds`».
// До него ответа не было НИ ОДНОГО: словарь принадлежит владельцу, объявлен
// закрытым, а прочесть его было неоткуда — оставался перебор против отказа на
// живой ручке. Перебор не воспроизводится при добавлении домена: новый
// владелец заводит свои виды, и узнать о них снова стало бы неоткуда.
//
// # ПОЧЕМУ ЗДЕСЬ, А НЕ ПЕРЕЧИСЛЕНИЕМ В КОНТРАКТЕ И НЕ ОТДЕЛЬНЫМ ГЛАГОЛОМ
//
// Перечислением НЕЛЬЗЯ: словарь принадлежит владельцу, а эта форма — общая.
// Перечисление в ней означало бы, что виды всех владельцев объявлены здесь, и
// тогда владелец, чьи события не про ресурс, а про субъект прав, в форму не
// поместился бы вовсе — ровно то, что оговорено у поля `kinds`. Плюс каждый
// новый вид становился бы правкой ОБЩЕГО контракта, то есть словарь пополнялся
// бы правкой документа, а не по построению.
//
// Отдельным глаголом МОЖНО, и цена измерена, а не предположена: глагол — это
// вторая запись каталога прав (обе встроенные копии обязаны остаться
// байт-идентичными), регистрация у КАЖДОГО владельца, второй путь на крае и
// второе место, где словарь может разойтись с тем, чем сервер на самом деле
// судит. Здесь же он не может разойтись by construction: сервер отдаёт ровно
// тот перечень, которым отвергает неизвестный вид.
//
// Служебное сообщение приходит ПЕРВЫМ и ВСЕГДА и уже везётся по проводу — то
// есть поверхность не расширяется вовсе, а до браузера словарь доезжает через
// существующую проекцию края без единой её правки.
//
// # КАК ЕГО ПРОЧИТАТЬ, НЕ ЗНАЯ НИ ОДНОГО ВИДА
//
// Открыть подписку, НЕ называя `kinds`: незаданная ось не сужает, отказа не
// будет, а первое же сообщение принесёт словарь целиком. Названный неизвестный
// вид отвергается ДО открытия — поэтому узнать словарь отказом нельзя, и
// отказ по этой причине называет его сам.
//
// # ПОРЯДОК ЗНАЧИМ: перечень отсортирован
//
// Он приходит в возрастающем лексикографическом порядке, и это ЧАСТЬ
// КОНТРАКТА, а не «как получилось»: словарь живёт отображением, обход
// отображения в Go случаен by construction, и клиент, ведущий своё состояние
// по индексу, читал бы каждое открытие как смену словаря.
//
// # ЧТО ЭТО НЕ ЗНАЧИТ
//
// Не значит «по каждому из них тебе придут события»: словарь есть объявление
// ВЛАДЕЛЬЦА, а не выборка по правам вызывающего. Показ решается построчно, и
// вид, на который у вызывающего нет прав, остаётся в словаре — иначе перечень
// стал бы способом узнать, что кому доступно.
//
// Пустым не бывает: владелец с пустым словарём не поднимается вовсе — поток,
// из которого не доставляется ничего, объявлением подписки не является.
KnownKinds []string `protobuf:"bytes,6,rep,name=known_kinds,json=knownKinds,proto3" json:"known_kinds,omitempty"`
// contains filtered or unexported fields
}
SubscriptionOpened — служебное сообщение, которое приходит ПЕРВЫМ и всегда.
Оно приходит и тогда, когда событий нет вовсе: поток остаётся открытым, а клиент уже знает, с чего сервер начал и что именно он для него сделал. Только после него идут события.
Тип, а не соглашение о порядке: соглашение нельзя ни импортировать, ни проверить, а всё, что сервер обязан сказать о принятой подписке, держится ровно на нём.
func (*SubscriptionOpened) Descriptor
deprecated
func (*SubscriptionOpened) Descriptor() ([]byte, []int)
Deprecated: Use SubscriptionOpened.ProtoReflect.Descriptor instead.
func (*SubscriptionOpened) GetCaughtUp ¶
func (x *SubscriptionOpened) GetCaughtUp() bool
func (*SubscriptionOpened) GetEarliestResumablePosition ¶
func (x *SubscriptionOpened) GetEarliestResumablePosition() string
func (*SubscriptionOpened) GetHonoredFilters ¶
func (x *SubscriptionOpened) GetHonoredFilters() []string
func (*SubscriptionOpened) GetKnownKinds ¶
func (x *SubscriptionOpened) GetKnownKinds() []string
func (*SubscriptionOpened) GetPosition ¶
func (x *SubscriptionOpened) GetPosition() string
func (*SubscriptionOpened) GetRetainsEverything ¶
func (x *SubscriptionOpened) GetRetainsEverything() bool
func (*SubscriptionOpened) ProtoMessage ¶
func (*SubscriptionOpened) ProtoMessage()
func (*SubscriptionOpened) ProtoReflect ¶
func (x *SubscriptionOpened) ProtoReflect() protoreflect.Message
func (*SubscriptionOpened) Reset ¶
func (x *SubscriptionOpened) Reset()
func (*SubscriptionOpened) String ¶
func (x *SubscriptionOpened) String() string
type SubscriptionRequest ¶
type SubscriptionRequest struct {
// kinds — виды предметов, о которых слать события. Категория: УДОБСТВА.
//
// Словарь видов принадлежит ВЛАДЕЛЬЦУ и им закрыт; фиксированного перечня
// «ресурсы такого-то домена» здесь нет — иначе владелец, чьи события не про
// ресурс, а про субъект прав, в эту форму не поместился бы вовсе.
//
// ГДЕ ЕГО ПРОЧИТАТЬ: `SubscriptionOpened.known_kinds` — служебное сообщение
// открытия несёт словарь целиком и приходит первым. Подписка, не назвавшая ни
// одного вида, законна и отказа не даёт, поэтому словарь читается ОДНИМ
// открытием, а не перебором против отказа.
//
// # НАПИСАНИЕ ВИДА ОДНО НА ВСЁ ДЕРЕВО: это ТИП ОБЪЕКТА МОДЕЛИ ПРАВ
//
// Вид называется тем же словом, каким этот предмет зовёт модель прав
// (`compute_instance`, `nlb_network_load_balancer`) — тем самым, что стоит в
// `scope_extractor.object_type` контрактов домена. Слово НЕ придумывается
// подпиской и не берётся из хранилища владельца: как строка журнала записана,
// есть частное дело владельца, и наружу это не выходит.
//
// Причина ровно одна и она про клиента: словарь, у которого нет единственного
// производителя, пишется столькими способами, сколько у него авторов, и
// клиент, взявший написание у одного, не может знать, то ли оно. Здесь
// производитель один — тот же, которым сервер решает вопрос о видимости
// строки, — поэтому второго написания не существует by construction, а не по
// договорённости.
//
// Форма слова: строчные латинские буквы, цифры и подчёркивание, первый знак —
// буква. Владелец, объявивший вид иначе, НЕ ПОДНИМАЕТСЯ.
//
// Вид вне словаря владельца — `INVALID_ARGUMENT` с именем поля, названным
// значением И перечнем известных видов, а НЕ открытый поток, в который никогда
// ничего не придёт. Пустой поток есть утверждение «событий нет», которого
// сервер не вправе делать про вход, которого он не понял; отказ, не называющий
// годных значений, оставляет вызывающего ровно там, откуда он пришёл.
Kinds []string `protobuf:"bytes,1,rep,name=kinds,proto3" json:"kinds,omitempty"`
// project_id — проект, к которому относятся события. Категория: ЯКОРНАЯ.
//
// По ней принимается решение о показе, поэтому владелец, не умеющий отобрать
// по ней, ОТВЕРГАЕТ подписку, а не открывает её с оговоркой: «доотбор на
// клиенте» означало бы, что границу доступа держит клиент.
//
// Незаданная ось проектом не сужает. Событие, не принадлежащее проекту вовсе
// (предмет уровня аккаунта или кластера), выразимо — см.
// `SubscriptionEvent.project_id`.
ProjectId string `protobuf:"bytes,2,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"`
// ids — неизменяемые идентификаторы предметов. Категория: УДОБСТВА.
//
// Это единственная ось, принимающая произвольную строку, и потому единственный
// путь протащить мутабельную адресацию через иммутабельную. Имя ресурса,
// поданное сюда, обязано быть отвергнуто `INVALID_ARGUMENT` — иначе подписка
// откроется и замолчит навсегда, потому что имени не отвечает ни один
// идентификатор.
//
// Идентификатор правильной формы, которому не отвечает ни один предмет, —
// НЕ отказ: подписка открывается и просто не приносит по нему событий.
// «Негодная форма» и «такого нет» — разные полосы, и подписка их не смешивает.
Ids []string `protobuf:"bytes,3,rep,name=ids,proto3" json:"ids,omitempty"`
// start — с какого места отдавать. Состояний ТРИ, и они различимы:
// «с начала журнала», «с этой позиции», «с текущего конца».
//
// ИСХОД НЕЗАДАННОГО НАЗВАН, а не подразумевается: `start` не задан означает
// ровно `CURRENT_END` — подписка на то, что произойдёт дальше. Выбор
// осознанный: молчаливая полная выдача журнала («с начала») превращает
// подписку в выгрузку и тем дороже, чем дольше журнал не чистили, а узнать об
// этом вызывающий может только по счёту за неё.
//
// Types that are valid to be assigned to Start:
//
// *SubscriptionRequest_Anchor
// *SubscriptionRequest_Position
Start isSubscriptionRequest_Start `protobuf_oneof:"start"`
// contains filtered or unexported fields
}
SubscriptionRequest — запрос подписки: чем сузить поток и с какого места его начать.
Оси фильтра: три, все необязательны, соединяются конъюнкцией ¶
Заданные оси сужают ВМЕСТЕ: событие приходит, только если удовлетворяет всем заданным сразу. Незаданная ось не сужает ничем — и это её ЕДИНСТВЕННОЕ значение: подписка без единой оси даёт то же, что подписка, где каждая ось перечислила все свои значения (в пределах прав вызывающего).
У каждой оси проставлен ПРИЗНАК КАТЕГОРИИ — «якорная» либо «удобства», — и различие не косметическое. По якорной оси принимается решение о показе; исхода «ось названа, но не применена» у неё НЕ СУЩЕСТВУЕТ (см. `SubscriptionOpened.honored_filters`). По оси удобства доотбор на клиенте законен: он получит лишнее и отбросит.
Имени ресурса и меток здесь НЕТ, и вот почему ¶
ИМЯ — мутабельный косметический ярлык, а адресуется ресурс неизменяемым `id`. Подписка по имени МОЛЧА перестаёт совпадать после переименования: ни события, ни ошибки, ни следа — поток просто замолкает навсегда. Адресный отбор делает ось `ids`.
МЕТКИ мутабельны, и ресурс входит в выборку и выходит из неё по их правке. Без предыдущего состояния выход из выборки неотличим от удаления ресурса, то есть подписчик принял бы правку метки за снос. Отбор по меткам делает КЛИЕНТ.
ЦЕНА ЭТОГО РЕШЕНИЯ НАЗВАНА, а не подразумевается: клиент отбирает по меткам ровно там, где событие принесло состояние. Владелец, чей журнал состояния НЕ ПРОИЗВОДИТ, источника для такого отбора не даёт — по его видам отбор по меткам НЕИСПОЛНИМ, и подписчик, которому метки нужны, читает предмет по `resource_id`. Это не пропуск и не временное состояние: производит владелец состояние или нет, объявляется в потоке — причиной `SubscriptionEvent.StateUnavailable.NOT_PRODUCED`, отличимой машинно от сбоя сборки. Соврать здесь было бы дороже отказа: подписчик, поверивший обещанию, молча отобрал бы по меткам, которых не получал.
Отсутствие этих полей — решение, а не пропуск. Поле без записанной причины отсутствия заводит следующий «для удобства», и он будет прав по-своему.
func (*SubscriptionRequest) Descriptor
deprecated
func (*SubscriptionRequest) Descriptor() ([]byte, []int)
Deprecated: Use SubscriptionRequest.ProtoReflect.Descriptor instead.
func (*SubscriptionRequest) GetAnchor ¶
func (x *SubscriptionRequest) GetAnchor() SubscriptionAnchor
func (*SubscriptionRequest) GetIds ¶
func (x *SubscriptionRequest) GetIds() []string
func (*SubscriptionRequest) GetKinds ¶
func (x *SubscriptionRequest) GetKinds() []string
func (*SubscriptionRequest) GetPosition ¶
func (x *SubscriptionRequest) GetPosition() string
func (*SubscriptionRequest) GetProjectId ¶
func (x *SubscriptionRequest) GetProjectId() string
func (*SubscriptionRequest) GetStart ¶
func (x *SubscriptionRequest) GetStart() isSubscriptionRequest_Start
func (*SubscriptionRequest) ProtoMessage ¶
func (*SubscriptionRequest) ProtoMessage()
func (*SubscriptionRequest) ProtoReflect ¶
func (x *SubscriptionRequest) ProtoReflect() protoreflect.Message
func (*SubscriptionRequest) Reset ¶
func (x *SubscriptionRequest) Reset()
func (*SubscriptionRequest) String ¶
func (x *SubscriptionRequest) String() string
type SubscriptionRequest_Anchor ¶
type SubscriptionRequest_Anchor struct {
// anchor — назвать место словом.
Anchor SubscriptionAnchor `protobuf:"varint,10,opt,name=anchor,proto3,enum=kacho.cloud.subscription.SubscriptionAnchor,oneof"`
}
type SubscriptionRequest_Position ¶
type SubscriptionRequest_Position struct {
// position — НЕПРОЗРАЧНЫЙ ТОКЕН, выданный сервером, и ничего больше.
//
// Клиент хранит его и возвращает ДОСЛОВНО: не разбирает, не сравнивает два
// токена между собой и не конструирует свой. Позиция, сконструированная или
// изменённая клиентом, отвергается `INVALID_ARGUMENT` — а не принимается
// «как похожая».
//
// ОБЯЗАТЕЛЬСТВО, РАДИ КОТОРОГО ПОЗИЦИЯ НЕПРОЗРАЧНА:
//
// позиция, выданная сервером, обязана быть такой, что возобновление с неё
// не пропускает ни одной строки, закоммиченной ПОСЛЕ её выдачи.
//
// Оно записано здесь дословно, потому что непрозрачность его НЕ ОБЕСПЕЧИВАЕТ.
// Непрозрачность покупает право починить внутреннюю форму позже, не ломая
// контракт; она не чинит. Токен, кодирующий границу по МАКСИМУМУ ВИДИМОГО
// номера, непрозрачен и всё равно теряет строку: номер присваивается на
// вставке, видимость наступает на фиксации, поэтому писатель, закоммитивший
// позже с меньшим номером, оказывается за границей — навсегда, молча и без
// пропуска в нумерации, видимого клиенту. Скалярная граница законна;
// граница по максимуму видимого — нет.
//
// Кодек позиции живёт там же, где объявлены прочие формы курсора этого
// дерева, — в пакете `corelib/pagetoken`, и объявляется СВОИМ типом. Уже
// объявленный там `Cursor` для неё НЕ ГОДИТСЯ: его семантика — «последняя
// ОТДАННАЯ строка», а это дословно та граница, которую обязательство выше и
// запрещает.
//
// Позиция принадлежит КЛИЕНТУ, а не подписке: сервер её не помнит и остаётся
// stateless. Иначе возобновление привязывается к реплике, на которой
// подписка была открыта, и переподключение к другой перестаёт работать.
Position string `protobuf:"bytes,11,opt,name=position,proto3,oneof"`
}
type UnimplementedInternalSubscriptionServiceServer ¶
type UnimplementedInternalSubscriptionServiceServer struct{}
UnimplementedInternalSubscriptionServiceServer must 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 UnsafeInternalSubscriptionServiceServer ¶
type UnsafeInternalSubscriptionServiceServer interface {
// contains filtered or unexported methods
}
UnsafeInternalSubscriptionServiceServer may be embedded to opt out of forward compatibility for this service. Use of this interface is not recommended, as added methods to InternalSubscriptionServiceServer will result in compilation errors.