diff options
| author | Xe Iaso <me@xeiaso.net> | 2024-02-12 04:32:43 -0800 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2024-02-12 04:32:43 -0800 |
| commit | e02b9ef3ee4394db1a387b146aa5faeea95efa3a (patch) | |
| tree | e5b6f2c7c21dda6a5e4ea1617e10e3437c93979c /internal/adminpb | |
| parent | 0eb26108f65b68aa4e99786f7f7a0b28cd356f72 (diff) | |
| download | xesite-e02b9ef3ee4394db1a387b146aa5faeea95efa3a.tar.xz xesite-e02b9ef3ee4394db1a387b146aa5faeea95efa3a.zip | |
cmd/patreon-saasproxy: use twirp/protobuf instead of yolo json
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'internal/adminpb')
| -rw-r--r-- | internal/adminpb/generate.go | 5 | ||||
| -rw-r--r-- | internal/adminpb/internal.pb.go | 201 | ||||
| -rw-r--r-- | internal/adminpb/internal.proto | 22 | ||||
| -rw-r--r-- | internal/adminpb/internal.twirp.go | 1607 |
4 files changed, 1835 insertions, 0 deletions
diff --git a/internal/adminpb/generate.go b/internal/adminpb/generate.go new file mode 100644 index 0000000..29a5216 --- /dev/null +++ b/internal/adminpb/generate.go @@ -0,0 +1,5 @@ +package adminpb + +func init() {} + +//go:generate protoc --proto_path=. --proto_path=../../pb --go_out=. --go_opt=paths=source_relative --twirp_out=. --twirp_opt=paths=source_relative internal.proto diff --git a/internal/adminpb/internal.pb.go b/internal/adminpb/internal.pb.go new file mode 100644 index 0000000..fc47e2b --- /dev/null +++ b/internal/adminpb/internal.pb.go @@ -0,0 +1,201 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc v4.24.4 +// source: internal.proto + +package adminpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + pb "xeiaso.net/v4/pb" +) + +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 PatreonToken struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + TokenType string `protobuf:"bytes,2,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` + RefreshToken string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"` + Expiry *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expiry,proto3" json:"expiry,omitempty"` +} + +func (x *PatreonToken) Reset() { + *x = PatreonToken{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PatreonToken) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PatreonToken) ProtoMessage() {} + +func (x *PatreonToken) ProtoReflect() protoreflect.Message { + mi := &file_internal_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PatreonToken.ProtoReflect.Descriptor instead. +func (*PatreonToken) Descriptor() ([]byte, []int) { + return file_internal_proto_rawDescGZIP(), []int{0} +} + +func (x *PatreonToken) GetAccessToken() string { + if x != nil { + return x.AccessToken + } + return "" +} + +func (x *PatreonToken) GetTokenType() string { + if x != nil { + return x.TokenType + } + return "" +} + +func (x *PatreonToken) GetRefreshToken() string { + if x != nil { + return x.RefreshToken + } + return "" +} + +func (x *PatreonToken) GetExpiry() *timestamppb.Timestamp { + if x != nil { + return x.Expiry + } + return nil +} + +var File_internal_proto protoreflect.FileDescriptor + +var file_internal_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x13, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x78, 0x65, 0x73, 0x69, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xa9, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x72, 0x65, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x32, 0x50, 0x0a, + 0x07, 0x50, 0x61, 0x74, 0x72, 0x65, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x78, + 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2e, 0x50, 0x61, 0x74, 0x72, 0x65, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, + 0x41, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x38, 0x0a, 0x07, 0x52, 0x65, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x78, 0x65, + 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, + 0x66, 0x6f, 0x42, 0x20, 0x5a, 0x1e, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, + 0x2f, 0x76, 0x34, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_internal_proto_rawDescOnce sync.Once + file_internal_proto_rawDescData = file_internal_proto_rawDesc +) + +func file_internal_proto_rawDescGZIP() []byte { + file_internal_proto_rawDescOnce.Do(func() { + file_internal_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_proto_rawDescData) + }) + return file_internal_proto_rawDescData +} + +var file_internal_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_internal_proto_goTypes = []interface{}{ + (*PatreonToken)(nil), // 0: xeiaso.net.internal.PatreonToken + (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp + (*emptypb.Empty)(nil), // 2: google.protobuf.Empty + (*pb.BuildInfo)(nil), // 3: xeiaso.net.BuildInfo +} +var file_internal_proto_depIdxs = []int32{ + 1, // 0: xeiaso.net.internal.PatreonToken.expiry:type_name -> google.protobuf.Timestamp + 2, // 1: xeiaso.net.internal.Patreon.GetToken:input_type -> google.protobuf.Empty + 2, // 2: xeiaso.net.internal.Admin.Rebuild:input_type -> google.protobuf.Empty + 0, // 3: xeiaso.net.internal.Patreon.GetToken:output_type -> xeiaso.net.internal.PatreonToken + 3, // 4: xeiaso.net.internal.Admin.Rebuild:output_type -> xeiaso.net.BuildInfo + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_internal_proto_init() } +func file_internal_proto_init() { + if File_internal_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_internal_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PatreonToken); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_internal_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_internal_proto_goTypes, + DependencyIndexes: file_internal_proto_depIdxs, + MessageInfos: file_internal_proto_msgTypes, + }.Build() + File_internal_proto = out.File + file_internal_proto_rawDesc = nil + file_internal_proto_goTypes = nil + file_internal_proto_depIdxs = nil +} diff --git a/internal/adminpb/internal.proto b/internal/adminpb/internal.proto new file mode 100644 index 0000000..a0755ff --- /dev/null +++ b/internal/adminpb/internal.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package xeiaso.net.internal.admin; +option go_package = "xeiaso.net/v4/internal/adminpb"; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "xesite.proto"; + +service Patreon { + rpc GetToken (google.protobuf.Empty) returns (PatreonToken); +} + +message PatreonToken { + string access_token = 1; + string token_type = 2; + string refresh_token = 3; + google.protobuf.Timestamp expiry = 4; +} + +service Admin { + rpc Rebuild(google.protobuf.Empty) returns (xeiaso.net.BuildInfo); +}
\ No newline at end of file diff --git a/internal/adminpb/internal.twirp.go b/internal/adminpb/internal.twirp.go new file mode 100644 index 0000000..3ad3281 --- /dev/null +++ b/internal/adminpb/internal.twirp.go @@ -0,0 +1,1607 @@ +// Code generated by protoc-gen-twirp v8.1.3, DO NOT EDIT. +// source: internal.proto + +package adminpb + +import context "context" +import fmt "fmt" +import http "net/http" +import io "io" +import json "encoding/json" +import strconv "strconv" +import strings "strings" + +import protojson "google.golang.org/protobuf/encoding/protojson" +import proto "google.golang.org/protobuf/proto" +import twirp "github.com/twitchtv/twirp" +import ctxsetters "github.com/twitchtv/twirp/ctxsetters" + +import google_protobuf "google.golang.org/protobuf/types/known/emptypb" +import xeiaso_net "xeiaso.net/v4/pb" + +import bytes "bytes" +import errors "errors" +import path "path" +import url "net/url" + +// Version compatibility assertion. +// If the constant is not defined in the package, that likely means +// the package needs to be updated to work with this generated code. +// See https://twitchtv.github.io/twirp/docs/version_matrix.html +const _ = twirp.TwirpPackageMinVersion_8_1_0 + +// ================= +// Patreon Interface +// ================= + +type Patreon interface { + GetToken(context.Context, *google_protobuf.Empty) (*PatreonToken, error) +} + +// ======================= +// Patreon Protobuf Client +// ======================= + +type patreonProtobufClient struct { + client HTTPClient + urls [1]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewPatreonProtobufClient creates a Protobuf client that implements the Patreon interface. +// It communicates using Protobuf and can be configured with a custom HTTPClient. +func NewPatreonProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Patreon { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + literalURLs := false + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) + var pathPrefix string + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(pathPrefix, "xeiaso.net.internal", "Patreon") + urls := [1]string{ + serviceURL + "GetToken", + } + + return &patreonProtobufClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *patreonProtobufClient) GetToken(ctx context.Context, in *google_protobuf.Empty) (*PatreonToken, error) { + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net.internal") + ctx = ctxsetters.WithServiceName(ctx, "Patreon") + ctx = ctxsetters.WithMethodName(ctx, "GetToken") + caller := c.callGetToken + if c.interceptor != nil { + caller = func(ctx context.Context, req *google_protobuf.Empty) (*PatreonToken, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*google_protobuf.Empty) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") + } + return c.callGetToken(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*PatreonToken) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*PatreonToken) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *patreonProtobufClient) callGetToken(ctx context.Context, in *google_protobuf.Empty) (*PatreonToken, error) { + out := new(PatreonToken) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// =================== +// Patreon JSON Client +// =================== + +type patreonJSONClient struct { + client HTTPClient + urls [1]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewPatreonJSONClient creates a JSON client that implements the Patreon interface. +// It communicates using JSON and can be configured with a custom HTTPClient. +func NewPatreonJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Patreon { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + literalURLs := false + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) + var pathPrefix string + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(pathPrefix, "xeiaso.net.internal", "Patreon") + urls := [1]string{ + serviceURL + "GetToken", + } + + return &patreonJSONClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *patreonJSONClient) GetToken(ctx context.Context, in *google_protobuf.Empty) (*PatreonToken, error) { + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net.internal") + ctx = ctxsetters.WithServiceName(ctx, "Patreon") + ctx = ctxsetters.WithMethodName(ctx, "GetToken") + caller := c.callGetToken + if c.interceptor != nil { + caller = func(ctx context.Context, req *google_protobuf.Empty) (*PatreonToken, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*google_protobuf.Empty) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") + } + return c.callGetToken(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*PatreonToken) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*PatreonToken) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *patreonJSONClient) callGetToken(ctx context.Context, in *google_protobuf.Empty) (*PatreonToken, error) { + out := new(PatreonToken) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ====================== +// Patreon Server Handler +// ====================== + +type patreonServer struct { + Patreon + interceptor twirp.Interceptor + hooks *twirp.ServerHooks + pathPrefix string // prefix for routing + jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response + jsonCamelCase bool // JSON fields are serialized as lowerCamelCase rather than keeping the original proto names +} + +// NewPatreonServer builds a TwirpServer that can be used as an http.Handler to handle +// HTTP requests that are routed to the right method in the provided svc implementation. +// The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). +func NewPatreonServer(svc Patreon, opts ...interface{}) TwirpServer { + serverOpts := newServerOpts(opts) + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + jsonSkipDefaults := false + _ = serverOpts.ReadOpt("jsonSkipDefaults", &jsonSkipDefaults) + jsonCamelCase := false + _ = serverOpts.ReadOpt("jsonCamelCase", &jsonCamelCase) + var pathPrefix string + if ok := serverOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + return &patreonServer{ + Patreon: svc, + hooks: serverOpts.Hooks, + interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), + pathPrefix: pathPrefix, + jsonSkipDefaults: jsonSkipDefaults, + jsonCamelCase: jsonCamelCase, + } +} + +// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func (s *patreonServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// handleRequestBodyError is used to handle error when the twirp server cannot read request +func (s *patreonServer) handleRequestBodyError(ctx context.Context, resp http.ResponseWriter, msg string, err error) { + if context.Canceled == ctx.Err() { + s.writeError(ctx, resp, twirp.NewError(twirp.Canceled, "failed to read request: context canceled")) + return + } + if context.DeadlineExceeded == ctx.Err() { + s.writeError(ctx, resp, twirp.NewError(twirp.DeadlineExceeded, "failed to read request: deadline exceeded")) + return + } + s.writeError(ctx, resp, twirp.WrapError(malformedRequestError(msg), err)) +} + +// PatreonPathPrefix is a convenience constant that may identify URL paths. +// Should be used with caution, it only matches routes generated by Twirp Go clients, +// with the default "/twirp" prefix and default CamelCase service and method names. +// More info: https://twitchtv.github.io/twirp/docs/routing.html +const PatreonPathPrefix = "/twirp/xeiaso.net.internal.Patreon/" + +func (s *patreonServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net.internal") + ctx = ctxsetters.WithServiceName(ctx, "Patreon") + ctx = ctxsetters.WithResponseWriter(ctx, resp) + + var err error + ctx, err = callRequestReceived(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + if req.Method != "POST" { + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + // Verify path format: [<prefix>]/<package>.<Service>/<Method> + prefix, pkgService, method := parseTwirpPath(req.URL.Path) + if pkgService != "xeiaso.net.internal.Patreon" { + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + if prefix != s.pathPrefix { + msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } + + switch method { + case "GetToken": + s.serveGetToken(ctx, resp, req) + return + default: + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) + return + } +} + +func (s *patreonServer) serveGetToken(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveGetTokenJSON(ctx, resp, req) + case "application/protobuf": + s.serveGetTokenProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *patreonServer) serveGetTokenJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "GetToken") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + d := json.NewDecoder(req.Body) + rawReqBody := json.RawMessage{} + if err := d.Decode(&rawReqBody); err != nil { + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) + return + } + reqContent := new(google_protobuf.Empty) + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} + if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) + return + } + + handler := s.Patreon.GetToken + if s.interceptor != nil { + handler = func(ctx context.Context, req *google_protobuf.Empty) (*PatreonToken, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*google_protobuf.Empty) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") + } + return s.Patreon.GetToken(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*PatreonToken) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*PatreonToken) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *PatreonToken + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *PatreonToken and nil error while calling GetToken. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} + respBytes, err := marshaler.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *patreonServer) serveGetTokenProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "GetToken") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := io.ReadAll(req.Body) + if err != nil { + s.handleRequestBodyError(ctx, resp, "failed to read request body", err) + return + } + reqContent := new(google_protobuf.Empty) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.Patreon.GetToken + if s.interceptor != nil { + handler = func(ctx context.Context, req *google_protobuf.Empty) (*PatreonToken, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*google_protobuf.Empty) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") + } + return s.Patreon.GetToken(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*PatreonToken) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*PatreonToken) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *PatreonToken + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *PatreonToken and nil error while calling GetToken. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *patreonServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 0 +} + +func (s *patreonServer) ProtocGenTwirpVersion() string { + return "v8.1.3" +} + +// PathPrefix returns the base service path, in the form: "/<prefix>/<package>.<Service>/" +// that is everything in a Twirp route except for the <Method>. This can be used for routing, +// for example to identify the requests that are targeted to this service in a mux. +func (s *patreonServer) PathPrefix() string { + return baseServicePath(s.pathPrefix, "xeiaso.net.internal", "Patreon") +} + +// =============== +// Admin Interface +// =============== + +type Admin interface { + Rebuild(context.Context, *google_protobuf.Empty) (*xeiaso_net.BuildInfo, error) +} + +// ===================== +// Admin Protobuf Client +// ===================== + +type adminProtobufClient struct { + client HTTPClient + urls [1]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewAdminProtobufClient creates a Protobuf client that implements the Admin interface. +// It communicates using Protobuf and can be configured with a custom HTTPClient. +func NewAdminProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Admin { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + literalURLs := false + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) + var pathPrefix string + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(pathPrefix, "xeiaso.net.internal", "Admin") + urls := [1]string{ + serviceURL + "Rebuild", + } + + return &adminProtobufClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *adminProtobufClient) Rebuild(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net.BuildInfo, error) { + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net.internal") + ctx = ctxsetters.WithServiceName(ctx, "Admin") + ctx = ctxsetters.WithMethodName(ctx, "Rebuild") + caller := c.callRebuild + if c.interceptor != nil { + caller = func(ctx context.Context, req *google_protobuf.Empty) (*xeiaso_net.BuildInfo, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*google_protobuf.Empty) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") + } + return c.callRebuild(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*xeiaso_net.BuildInfo) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*xeiaso_net.BuildInfo) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *adminProtobufClient) callRebuild(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net.BuildInfo, error) { + out := new(xeiaso_net.BuildInfo) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ================= +// Admin JSON Client +// ================= + +type adminJSONClient struct { + client HTTPClient + urls [1]string + interceptor twirp.Interceptor + opts twirp.ClientOptions +} + +// NewAdminJSONClient creates a JSON client that implements the Admin interface. +// It communicates using JSON and can be configured with a custom HTTPClient. +func NewAdminJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Admin { + if c, ok := client.(*http.Client); ok { + client = withoutRedirects(c) + } + + clientOpts := twirp.ClientOptions{} + for _, o := range opts { + o(&clientOpts) + } + + // Using ReadOpt allows backwards and forwards compatibility with new options in the future + literalURLs := false + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) + var pathPrefix string + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { + pathPrefix = "/twirp" // default prefix + } + + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> + serviceURL := sanitizeBaseURL(baseURL) + serviceURL += baseServicePath(pathPrefix, "xeiaso.net.internal", "Admin") + urls := [1]string{ + serviceURL + "Rebuild", + } + + return &adminJSONClient{ + client: client, + urls: urls, + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), + opts: clientOpts, + } +} + +func (c *adminJSONClient) Rebuild(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net.BuildInfo, error) { + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net.internal") + ctx = ctxsetters.WithServiceName(ctx, "Admin") + ctx = ctxsetters.WithMethodName(ctx, "Rebuild") + caller := c.callRebuild + if c.interceptor != nil { + caller = func(ctx context.Context, req *google_protobuf.Empty) (*xeiaso_net.BuildInfo, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*google_protobuf.Empty) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") + } + return c.callRebuild(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*xeiaso_net.BuildInfo) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*xeiaso_net.BuildInfo) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *adminJSONClient) callRebuild(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net.BuildInfo, error) { + out := new(xeiaso_net.BuildInfo) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + +// ==================== +// Admin Server Handler +// ==================== + +type adminServer struct { |
