diff --git a/components/usage-api/go/v1/billing.pb.go b/components/usage-api/go/v1/billing.pb.go new file mode 100644 index 00000000000000..0293031c9fceda --- /dev/null +++ b/components/usage-api/go/v1/billing.pb.go @@ -0,0 +1,246 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.20.1 +// source: usage/v1/billing.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +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 UpdateInvoicesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StartTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Sessions []*BilledSession `protobuf:"bytes,3,rep,name=sessions,proto3" json:"sessions,omitempty"` +} + +func (x *UpdateInvoicesRequest) Reset() { + *x = UpdateInvoicesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_usage_v1_billing_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateInvoicesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateInvoicesRequest) ProtoMessage() {} + +func (x *UpdateInvoicesRequest) ProtoReflect() protoreflect.Message { + mi := &file_usage_v1_billing_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 UpdateInvoicesRequest.ProtoReflect.Descriptor instead. +func (*UpdateInvoicesRequest) Descriptor() ([]byte, []int) { + return file_usage_v1_billing_proto_rawDescGZIP(), []int{0} +} + +func (x *UpdateInvoicesRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *UpdateInvoicesRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *UpdateInvoicesRequest) GetSessions() []*BilledSession { + if x != nil { + return x.Sessions + } + return nil +} + +type UpdateInvoicesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UpdateInvoicesResponse) Reset() { + *x = UpdateInvoicesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_usage_v1_billing_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateInvoicesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateInvoicesResponse) ProtoMessage() {} + +func (x *UpdateInvoicesResponse) ProtoReflect() protoreflect.Message { + mi := &file_usage_v1_billing_proto_msgTypes[1] + 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 UpdateInvoicesResponse.ProtoReflect.Descriptor instead. +func (*UpdateInvoicesResponse) Descriptor() ([]byte, []int) { + return file_usage_v1_billing_proto_rawDescGZIP(), []int{1} +} + +var File_usage_v1_billing_proto protoreflect.FileDescriptor + +var file_usage_v1_billing_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x6c, 0x6c, 0x69, + 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, + 0x76, 0x31, 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, 0x14, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, + 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbe, 0x01, 0x0a, 0x15, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x01, 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, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 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, 0x07, 0x65, 0x6e, + 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x67, 0x0a, 0x0e, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2a, 0x5a, + 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, + 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x75, 0x73, 0x61, + 0x67, 0x65, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_usage_v1_billing_proto_rawDescOnce sync.Once + file_usage_v1_billing_proto_rawDescData = file_usage_v1_billing_proto_rawDesc +) + +func file_usage_v1_billing_proto_rawDescGZIP() []byte { + file_usage_v1_billing_proto_rawDescOnce.Do(func() { + file_usage_v1_billing_proto_rawDescData = protoimpl.X.CompressGZIP(file_usage_v1_billing_proto_rawDescData) + }) + return file_usage_v1_billing_proto_rawDescData +} + +var file_usage_v1_billing_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_usage_v1_billing_proto_goTypes = []interface{}{ + (*UpdateInvoicesRequest)(nil), // 0: usage.v1.UpdateInvoicesRequest + (*UpdateInvoicesResponse)(nil), // 1: usage.v1.UpdateInvoicesResponse + (*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp + (*BilledSession)(nil), // 3: usage.v1.BilledSession +} +var file_usage_v1_billing_proto_depIdxs = []int32{ + 2, // 0: usage.v1.UpdateInvoicesRequest.start_time:type_name -> google.protobuf.Timestamp + 2, // 1: usage.v1.UpdateInvoicesRequest.end_time:type_name -> google.protobuf.Timestamp + 3, // 2: usage.v1.UpdateInvoicesRequest.sessions:type_name -> usage.v1.BilledSession + 0, // 3: usage.v1.BillingService.UpdateInvoices:input_type -> usage.v1.UpdateInvoicesRequest + 1, // 4: usage.v1.BillingService.UpdateInvoices:output_type -> usage.v1.UpdateInvoicesResponse + 4, // [4:5] is the sub-list for method output_type + 3, // [3:4] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_usage_v1_billing_proto_init() } +func file_usage_v1_billing_proto_init() { + if File_usage_v1_billing_proto != nil { + return + } + file_usage_v1_usage_proto_init() + if !protoimpl.UnsafeEnabled { + file_usage_v1_billing_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateInvoicesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_usage_v1_billing_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateInvoicesResponse); 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_usage_v1_billing_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_usage_v1_billing_proto_goTypes, + DependencyIndexes: file_usage_v1_billing_proto_depIdxs, + MessageInfos: file_usage_v1_billing_proto_msgTypes, + }.Build() + File_usage_v1_billing_proto = out.File + file_usage_v1_billing_proto_rawDesc = nil + file_usage_v1_billing_proto_goTypes = nil + file_usage_v1_billing_proto_depIdxs = nil +} diff --git a/components/usage-api/go/v1/billing_grpc.pb.go b/components/usage-api/go/v1/billing_grpc.pb.go new file mode 100644 index 00000000000000..b663ea01fb8200 --- /dev/null +++ b/components/usage-api/go/v1/billing_grpc.pb.go @@ -0,0 +1,109 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.20.1 +// source: usage/v1/billing.proto + +package v1 + +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.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// BillingServiceClient is the client API for BillingService 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. +type BillingServiceClient interface { + UpdateInvoices(ctx context.Context, in *UpdateInvoicesRequest, opts ...grpc.CallOption) (*UpdateInvoicesResponse, error) +} + +type billingServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewBillingServiceClient(cc grpc.ClientConnInterface) BillingServiceClient { + return &billingServiceClient{cc} +} + +func (c *billingServiceClient) UpdateInvoices(ctx context.Context, in *UpdateInvoicesRequest, opts ...grpc.CallOption) (*UpdateInvoicesResponse, error) { + out := new(UpdateInvoicesResponse) + err := c.cc.Invoke(ctx, "/usage.v1.BillingService/UpdateInvoices", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BillingServiceServer is the server API for BillingService service. +// All implementations must embed UnimplementedBillingServiceServer +// for forward compatibility +type BillingServiceServer interface { + UpdateInvoices(context.Context, *UpdateInvoicesRequest) (*UpdateInvoicesResponse, error) + mustEmbedUnimplementedBillingServiceServer() +} + +// UnimplementedBillingServiceServer must be embedded to have forward compatible implementations. +type UnimplementedBillingServiceServer struct { +} + +func (UnimplementedBillingServiceServer) UpdateInvoices(context.Context, *UpdateInvoicesRequest) (*UpdateInvoicesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateInvoices not implemented") +} +func (UnimplementedBillingServiceServer) mustEmbedUnimplementedBillingServiceServer() {} + +// UnsafeBillingServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BillingServiceServer will +// result in compilation errors. +type UnsafeBillingServiceServer interface { + mustEmbedUnimplementedBillingServiceServer() +} + +func RegisterBillingServiceServer(s grpc.ServiceRegistrar, srv BillingServiceServer) { + s.RegisterService(&BillingService_ServiceDesc, srv) +} + +func _BillingService_UpdateInvoices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateInvoicesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BillingServiceServer).UpdateInvoices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/usage.v1.BillingService/UpdateInvoices", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BillingServiceServer).UpdateInvoices(ctx, req.(*UpdateInvoicesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// BillingService_ServiceDesc is the grpc.ServiceDesc for BillingService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BillingService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "usage.v1.BillingService", + HandlerType: (*BillingServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdateInvoices", + Handler: _BillingService_UpdateInvoices_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "usage/v1/billing.proto", +} diff --git a/components/usage-api/go/v1/usage.pb.go b/components/usage-api/go/v1/usage.pb.go index 22c2228b12a152..b1a2651f822eb1 100644 --- a/components/usage-api/go/v1/usage.pb.go +++ b/components/usage-api/go/v1/usage.pb.go @@ -320,6 +320,108 @@ func (x *BilledSession) GetCredits() int64 { return 0 } +type CollectUsageRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StartTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` +} + +func (x *CollectUsageRequest) Reset() { + *x = CollectUsageRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_usage_v1_usage_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CollectUsageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CollectUsageRequest) ProtoMessage() {} + +func (x *CollectUsageRequest) ProtoReflect() protoreflect.Message { + mi := &file_usage_v1_usage_proto_msgTypes[3] + 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 CollectUsageRequest.ProtoReflect.Descriptor instead. +func (*CollectUsageRequest) Descriptor() ([]byte, []int) { + return file_usage_v1_usage_proto_rawDescGZIP(), []int{3} +} + +func (x *CollectUsageRequest) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +func (x *CollectUsageRequest) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +type CollectUsageResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sessions []*BilledSession `protobuf:"bytes,1,rep,name=sessions,proto3" json:"sessions,omitempty"` +} + +func (x *CollectUsageResponse) Reset() { + *x = CollectUsageResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_usage_v1_usage_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CollectUsageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CollectUsageResponse) ProtoMessage() {} + +func (x *CollectUsageResponse) ProtoReflect() protoreflect.Message { + mi := &file_usage_v1_usage_proto_msgTypes[4] + 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 CollectUsageResponse.ProtoReflect.Descriptor instead. +func (*CollectUsageResponse) Descriptor() ([]byte, []int) { + return file_usage_v1_usage_proto_rawDescGZIP(), []int{4} +} + +func (x *CollectUsageResponse) GetSessions() []*BilledSession { + if x != nil { + return x.Sessions + } + return nil +} + var File_usage_v1_usage_proto protoreflect.FileDescriptor var file_usage_v1_usage_proto_rawDesc = []byte{ @@ -376,17 +478,35 @@ var file_usage_v1_usage_proto_rawDesc = []byte{ 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, - 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x32, 0x68, 0x0a, 0x0c, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x42, - 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x2e, 0x75, 0x73, 0x61, - 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, - 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x69, 0x6c, 0x6c, - 0x65, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, - 0x2f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x22, 0x87, 0x01, 0x0a, 0x13, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 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, + 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 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, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x22, 0x4b, 0x0a, 0x14, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x75, 0x73, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x32, 0xb9, + 0x01, 0x0a, 0x0c, 0x55, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x58, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x20, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0c, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, + 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2d, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -402,28 +522,35 @@ func file_usage_v1_usage_proto_rawDescGZIP() []byte { } var file_usage_v1_usage_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_usage_v1_usage_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_usage_v1_usage_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_usage_v1_usage_proto_goTypes = []interface{}{ (ListBilledUsageRequest_Ordering)(0), // 0: usage.v1.ListBilledUsageRequest.Ordering (*ListBilledUsageRequest)(nil), // 1: usage.v1.ListBilledUsageRequest (*ListBilledUsageResponse)(nil), // 2: usage.v1.ListBilledUsageResponse (*BilledSession)(nil), // 3: usage.v1.BilledSession - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*CollectUsageRequest)(nil), // 4: usage.v1.CollectUsageRequest + (*CollectUsageResponse)(nil), // 5: usage.v1.CollectUsageResponse + (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp } var file_usage_v1_usage_proto_depIdxs = []int32{ - 4, // 0: usage.v1.ListBilledUsageRequest.from:type_name -> google.protobuf.Timestamp - 4, // 1: usage.v1.ListBilledUsageRequest.to:type_name -> google.protobuf.Timestamp - 0, // 2: usage.v1.ListBilledUsageRequest.order:type_name -> usage.v1.ListBilledUsageRequest.Ordering - 3, // 3: usage.v1.ListBilledUsageResponse.sessions:type_name -> usage.v1.BilledSession - 4, // 4: usage.v1.BilledSession.start_time:type_name -> google.protobuf.Timestamp - 4, // 5: usage.v1.BilledSession.end_time:type_name -> google.protobuf.Timestamp - 1, // 6: usage.v1.UsageService.ListBilledUsage:input_type -> usage.v1.ListBilledUsageRequest - 2, // 7: usage.v1.UsageService.ListBilledUsage:output_type -> usage.v1.ListBilledUsageResponse - 7, // [7:8] is the sub-list for method output_type - 6, // [6:7] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 6, // 0: usage.v1.ListBilledUsageRequest.from:type_name -> google.protobuf.Timestamp + 6, // 1: usage.v1.ListBilledUsageRequest.to:type_name -> google.protobuf.Timestamp + 0, // 2: usage.v1.ListBilledUsageRequest.order:type_name -> usage.v1.ListBilledUsageRequest.Ordering + 3, // 3: usage.v1.ListBilledUsageResponse.sessions:type_name -> usage.v1.BilledSession + 6, // 4: usage.v1.BilledSession.start_time:type_name -> google.protobuf.Timestamp + 6, // 5: usage.v1.BilledSession.end_time:type_name -> google.protobuf.Timestamp + 6, // 6: usage.v1.CollectUsageRequest.start_time:type_name -> google.protobuf.Timestamp + 6, // 7: usage.v1.CollectUsageRequest.end_time:type_name -> google.protobuf.Timestamp + 3, // 8: usage.v1.CollectUsageResponse.sessions:type_name -> usage.v1.BilledSession + 1, // 9: usage.v1.UsageService.ListBilledUsage:input_type -> usage.v1.ListBilledUsageRequest + 4, // 10: usage.v1.UsageService.CollectUsage:input_type -> usage.v1.CollectUsageRequest + 2, // 11: usage.v1.UsageService.ListBilledUsage:output_type -> usage.v1.ListBilledUsageResponse + 5, // 12: usage.v1.UsageService.CollectUsage:output_type -> usage.v1.CollectUsageResponse + 11, // [11:13] is the sub-list for method output_type + 9, // [9:11] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_usage_v1_usage_proto_init() } @@ -468,6 +595,30 @@ func file_usage_v1_usage_proto_init() { return nil } } + file_usage_v1_usage_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CollectUsageRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_usage_v1_usage_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CollectUsageResponse); 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{ @@ -475,7 +626,7 @@ func file_usage_v1_usage_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_usage_v1_usage_proto_rawDesc, NumEnums: 1, - NumMessages: 3, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/components/usage-api/go/v1/usage_grpc.pb.go b/components/usage-api/go/v1/usage_grpc.pb.go index 9c53069ac7d8ba..60ba4b5d73894f 100644 --- a/components/usage-api/go/v1/usage_grpc.pb.go +++ b/components/usage-api/go/v1/usage_grpc.pb.go @@ -28,6 +28,8 @@ const _ = grpc.SupportPackageIsVersion7 type UsageServiceClient interface { // ListBilledUsage retrieves all usage for the specified attributionId ListBilledUsage(ctx context.Context, in *ListBilledUsageRequest, opts ...grpc.CallOption) (*ListBilledUsageResponse, error) + // CollectUsage collects usage for the specified time range, for all users and teams. + CollectUsage(ctx context.Context, in *CollectUsageRequest, opts ...grpc.CallOption) (*CollectUsageResponse, error) } type usageServiceClient struct { @@ -47,12 +49,23 @@ func (c *usageServiceClient) ListBilledUsage(ctx context.Context, in *ListBilled return out, nil } +func (c *usageServiceClient) CollectUsage(ctx context.Context, in *CollectUsageRequest, opts ...grpc.CallOption) (*CollectUsageResponse, error) { + out := new(CollectUsageResponse) + err := c.cc.Invoke(ctx, "/usage.v1.UsageService/CollectUsage", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // UsageServiceServer is the server API for UsageService service. // All implementations must embed UnimplementedUsageServiceServer // for forward compatibility type UsageServiceServer interface { // ListBilledUsage retrieves all usage for the specified attributionId ListBilledUsage(context.Context, *ListBilledUsageRequest) (*ListBilledUsageResponse, error) + // CollectUsage collects usage for the specified time range, for all users and teams. + CollectUsage(context.Context, *CollectUsageRequest) (*CollectUsageResponse, error) mustEmbedUnimplementedUsageServiceServer() } @@ -63,6 +76,9 @@ type UnimplementedUsageServiceServer struct { func (UnimplementedUsageServiceServer) ListBilledUsage(context.Context, *ListBilledUsageRequest) (*ListBilledUsageResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListBilledUsage not implemented") } +func (UnimplementedUsageServiceServer) CollectUsage(context.Context, *CollectUsageRequest) (*CollectUsageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CollectUsage not implemented") +} func (UnimplementedUsageServiceServer) mustEmbedUnimplementedUsageServiceServer() {} // UnsafeUsageServiceServer may be embedded to opt out of forward compatibility for this service. @@ -94,6 +110,24 @@ func _UsageService_ListBilledUsage_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _UsageService_CollectUsage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CollectUsageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UsageServiceServer).CollectUsage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/usage.v1.UsageService/CollectUsage", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UsageServiceServer).CollectUsage(ctx, req.(*CollectUsageRequest)) + } + return interceptor(ctx, in, info, handler) +} + // UsageService_ServiceDesc is the grpc.ServiceDesc for UsageService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -105,6 +139,10 @@ var UsageService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListBilledUsage", Handler: _UsageService_ListBilledUsage_Handler, }, + { + MethodName: "CollectUsage", + Handler: _UsageService_CollectUsage_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "usage/v1/usage.proto", diff --git a/components/usage-api/typescript/src/usage/v1/billing_grpc_pb.d.ts b/components/usage-api/typescript/src/usage/v1/billing_grpc_pb.d.ts new file mode 100644 index 00000000000000..933119960ba27f --- /dev/null +++ b/components/usage-api/typescript/src/usage/v1/billing_grpc_pb.d.ts @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2022 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + +// package: usage.v1 +// file: usage/v1/billing.proto + +/* tslint:disable */ +/* eslint-disable */ + +import * as grpc from "@grpc/grpc-js"; +import * as usage_v1_billing_pb from "../../usage/v1/billing_pb"; +import * as google_protobuf_timestamp_pb from "google-protobuf/google/protobuf/timestamp_pb"; +import * as usage_v1_usage_pb from "../../usage/v1/usage_pb"; + +interface IBillingServiceService extends grpc.ServiceDefinition { + updateInvoices: IBillingServiceService_IUpdateInvoices; +} + +interface IBillingServiceService_IUpdateInvoices extends grpc.MethodDefinition { + path: "/usage.v1.BillingService/UpdateInvoices"; + requestStream: false; + responseStream: false; + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} + +export const BillingServiceService: IBillingServiceService; + +export interface IBillingServiceServer extends grpc.UntypedServiceImplementation { + updateInvoices: grpc.handleUnaryCall; +} + +export interface IBillingServiceClient { + updateInvoices(request: usage_v1_billing_pb.UpdateInvoicesRequest, callback: (error: grpc.ServiceError | null, response: usage_v1_billing_pb.UpdateInvoicesResponse) => void): grpc.ClientUnaryCall; + updateInvoices(request: usage_v1_billing_pb.UpdateInvoicesRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: usage_v1_billing_pb.UpdateInvoicesResponse) => void): grpc.ClientUnaryCall; + updateInvoices(request: usage_v1_billing_pb.UpdateInvoicesRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: usage_v1_billing_pb.UpdateInvoicesResponse) => void): grpc.ClientUnaryCall; +} + +export class BillingServiceClient extends grpc.Client implements IBillingServiceClient { + constructor(address: string, credentials: grpc.ChannelCredentials, options?: Partial); + public updateInvoices(request: usage_v1_billing_pb.UpdateInvoicesRequest, callback: (error: grpc.ServiceError | null, response: usage_v1_billing_pb.UpdateInvoicesResponse) => void): grpc.ClientUnaryCall; + public updateInvoices(request: usage_v1_billing_pb.UpdateInvoicesRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: usage_v1_billing_pb.UpdateInvoicesResponse) => void): grpc.ClientUnaryCall; + public updateInvoices(request: usage_v1_billing_pb.UpdateInvoicesRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: usage_v1_billing_pb.UpdateInvoicesResponse) => void): grpc.ClientUnaryCall; +} diff --git a/components/usage-api/typescript/src/usage/v1/billing_grpc_pb.js b/components/usage-api/typescript/src/usage/v1/billing_grpc_pb.js new file mode 100644 index 00000000000000..4118eb68ceeb92 --- /dev/null +++ b/components/usage-api/typescript/src/usage/v1/billing_grpc_pb.js @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2022 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + +// GENERATED CODE -- DO NOT EDIT! + +'use strict'; +var grpc = require('@grpc/grpc-js'); +var usage_v1_billing_pb = require('../../usage/v1/billing_pb.js'); +var google_protobuf_timestamp_pb = require('google-protobuf/google/protobuf/timestamp_pb.js'); +var usage_v1_usage_pb = require('../../usage/v1/usage_pb.js'); + +function serialize_usage_v1_UpdateInvoicesRequest(arg) { + if (!(arg instanceof usage_v1_billing_pb.UpdateInvoicesRequest)) { + throw new Error('Expected argument of type usage.v1.UpdateInvoicesRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_usage_v1_UpdateInvoicesRequest(buffer_arg) { + return usage_v1_billing_pb.UpdateInvoicesRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_usage_v1_UpdateInvoicesResponse(arg) { + if (!(arg instanceof usage_v1_billing_pb.UpdateInvoicesResponse)) { + throw new Error('Expected argument of type usage.v1.UpdateInvoicesResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_usage_v1_UpdateInvoicesResponse(buffer_arg) { + return usage_v1_billing_pb.UpdateInvoicesResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + + +var BillingServiceService = exports.BillingServiceService = { + updateInvoices: { + path: '/usage.v1.BillingService/UpdateInvoices', + requestStream: false, + responseStream: false, + requestType: usage_v1_billing_pb.UpdateInvoicesRequest, + responseType: usage_v1_billing_pb.UpdateInvoicesResponse, + requestSerialize: serialize_usage_v1_UpdateInvoicesRequest, + requestDeserialize: deserialize_usage_v1_UpdateInvoicesRequest, + responseSerialize: serialize_usage_v1_UpdateInvoicesResponse, + responseDeserialize: deserialize_usage_v1_UpdateInvoicesResponse, + }, +}; + +exports.BillingServiceClient = grpc.makeGenericClientConstructor(BillingServiceService); diff --git a/components/usage-api/typescript/src/usage/v1/billing_pb.d.ts b/components/usage-api/typescript/src/usage/v1/billing_pb.d.ts new file mode 100644 index 00000000000000..fae61cbe2604e0 --- /dev/null +++ b/components/usage-api/typescript/src/usage/v1/billing_pb.d.ts @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2022 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + +// package: usage.v1 +// file: usage/v1/billing.proto + +/* tslint:disable */ +/* eslint-disable */ + +import * as jspb from "google-protobuf"; +import * as google_protobuf_timestamp_pb from "google-protobuf/google/protobuf/timestamp_pb"; +import * as usage_v1_usage_pb from "../../usage/v1/usage_pb"; + +export class UpdateInvoicesRequest extends jspb.Message { + + hasStartTime(): boolean; + clearStartTime(): void; + getStartTime(): google_protobuf_timestamp_pb.Timestamp | undefined; + setStartTime(value?: google_protobuf_timestamp_pb.Timestamp): UpdateInvoicesRequest; + + hasEndTime(): boolean; + clearEndTime(): void; + getEndTime(): google_protobuf_timestamp_pb.Timestamp | undefined; + setEndTime(value?: google_protobuf_timestamp_pb.Timestamp): UpdateInvoicesRequest; + clearSessionsList(): void; + getSessionsList(): Array; + setSessionsList(value: Array): UpdateInvoicesRequest; + addSessions(value?: usage_v1_usage_pb.BilledSession, index?: number): usage_v1_usage_pb.BilledSession; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UpdateInvoicesRequest.AsObject; + static toObject(includeInstance: boolean, msg: UpdateInvoicesRequest): UpdateInvoicesRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UpdateInvoicesRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UpdateInvoicesRequest; + static deserializeBinaryFromReader(message: UpdateInvoicesRequest, reader: jspb.BinaryReader): UpdateInvoicesRequest; +} + +export namespace UpdateInvoicesRequest { + export type AsObject = { + startTime?: google_protobuf_timestamp_pb.Timestamp.AsObject, + endTime?: google_protobuf_timestamp_pb.Timestamp.AsObject, + sessionsList: Array, + } +} + +export class UpdateInvoicesResponse extends jspb.Message { + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): UpdateInvoicesResponse.AsObject; + static toObject(includeInstance: boolean, msg: UpdateInvoicesResponse): UpdateInvoicesResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: UpdateInvoicesResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): UpdateInvoicesResponse; + static deserializeBinaryFromReader(message: UpdateInvoicesResponse, reader: jspb.BinaryReader): UpdateInvoicesResponse; +} + +export namespace UpdateInvoicesResponse { + export type AsObject = { + } +} diff --git a/components/usage-api/typescript/src/usage/v1/billing_pb.js b/components/usage-api/typescript/src/usage/v1/billing_pb.js new file mode 100644 index 00000000000000..c4ba9ef41a66b5 --- /dev/null +++ b/components/usage-api/typescript/src/usage/v1/billing_pb.js @@ -0,0 +1,435 @@ +/** + * Copyright (c) 2022 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + +// source: usage/v1/billing.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { return this || window || global || self || Function('return this')(); }).call(null); + +var google_protobuf_timestamp_pb = require('google-protobuf/google/protobuf/timestamp_pb.js'); +goog.object.extend(proto, google_protobuf_timestamp_pb); +var usage_v1_usage_pb = require('../../usage/v1/usage_pb.js'); +goog.object.extend(proto, usage_v1_usage_pb); +goog.exportSymbol('proto.usage.v1.UpdateInvoicesRequest', null, global); +goog.exportSymbol('proto.usage.v1.UpdateInvoicesResponse', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.usage.v1.UpdateInvoicesRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.usage.v1.UpdateInvoicesRequest.repeatedFields_, null); +}; +goog.inherits(proto.usage.v1.UpdateInvoicesRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.usage.v1.UpdateInvoicesRequest.displayName = 'proto.usage.v1.UpdateInvoicesRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.usage.v1.UpdateInvoicesResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.usage.v1.UpdateInvoicesResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.usage.v1.UpdateInvoicesResponse.displayName = 'proto.usage.v1.UpdateInvoicesResponse'; +} + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.usage.v1.UpdateInvoicesRequest.repeatedFields_ = [3]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.toObject = function(opt_includeInstance) { + return proto.usage.v1.UpdateInvoicesRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.usage.v1.UpdateInvoicesRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.UpdateInvoicesRequest.toObject = function(includeInstance, msg) { + var f, obj = { + startTime: (f = msg.getStartTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), + endTime: (f = msg.getEndTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), + sessionsList: jspb.Message.toObjectList(msg.getSessionsList(), + usage_v1_usage_pb.BilledSession.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.usage.v1.UpdateInvoicesRequest} + */ +proto.usage.v1.UpdateInvoicesRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.usage.v1.UpdateInvoicesRequest; + return proto.usage.v1.UpdateInvoicesRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.usage.v1.UpdateInvoicesRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.usage.v1.UpdateInvoicesRequest} + */ +proto.usage.v1.UpdateInvoicesRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new google_protobuf_timestamp_pb.Timestamp; + reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader); + msg.setStartTime(value); + break; + case 2: + var value = new google_protobuf_timestamp_pb.Timestamp; + reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader); + msg.setEndTime(value); + break; + case 3: + var value = new usage_v1_usage_pb.BilledSession; + reader.readMessage(value,usage_v1_usage_pb.BilledSession.deserializeBinaryFromReader); + msg.addSessions(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.usage.v1.UpdateInvoicesRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.usage.v1.UpdateInvoicesRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.UpdateInvoicesRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStartTime(); + if (f != null) { + writer.writeMessage( + 1, + f, + google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter + ); + } + f = message.getEndTime(); + if (f != null) { + writer.writeMessage( + 2, + f, + google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter + ); + } + f = message.getSessionsList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 3, + f, + usage_v1_usage_pb.BilledSession.serializeBinaryToWriter + ); + } +}; + + +/** + * optional google.protobuf.Timestamp start_time = 1; + * @return {?proto.google.protobuf.Timestamp} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.getStartTime = function() { + return /** @type{?proto.google.protobuf.Timestamp} */ ( + jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 1)); +}; + + +/** + * @param {?proto.google.protobuf.Timestamp|undefined} value + * @return {!proto.usage.v1.UpdateInvoicesRequest} returns this +*/ +proto.usage.v1.UpdateInvoicesRequest.prototype.setStartTime = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.usage.v1.UpdateInvoicesRequest} returns this + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.clearStartTime = function() { + return this.setStartTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.hasStartTime = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional google.protobuf.Timestamp end_time = 2; + * @return {?proto.google.protobuf.Timestamp} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.getEndTime = function() { + return /** @type{?proto.google.protobuf.Timestamp} */ ( + jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 2)); +}; + + +/** + * @param {?proto.google.protobuf.Timestamp|undefined} value + * @return {!proto.usage.v1.UpdateInvoicesRequest} returns this +*/ +proto.usage.v1.UpdateInvoicesRequest.prototype.setEndTime = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.usage.v1.UpdateInvoicesRequest} returns this + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.clearEndTime = function() { + return this.setEndTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.hasEndTime = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * repeated BilledSession sessions = 3; + * @return {!Array} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.getSessionsList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, usage_v1_usage_pb.BilledSession, 3)); +}; + + +/** + * @param {!Array} value + * @return {!proto.usage.v1.UpdateInvoicesRequest} returns this +*/ +proto.usage.v1.UpdateInvoicesRequest.prototype.setSessionsList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 3, value); +}; + + +/** + * @param {!proto.usage.v1.BilledSession=} opt_value + * @param {number=} opt_index + * @return {!proto.usage.v1.BilledSession} + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.addSessions = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 3, opt_value, proto.usage.v1.BilledSession, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.usage.v1.UpdateInvoicesRequest} returns this + */ +proto.usage.v1.UpdateInvoicesRequest.prototype.clearSessionsList = function() { + return this.setSessionsList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.usage.v1.UpdateInvoicesResponse.prototype.toObject = function(opt_includeInstance) { + return proto.usage.v1.UpdateInvoicesResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.usage.v1.UpdateInvoicesResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.UpdateInvoicesResponse.toObject = function(includeInstance, msg) { + var f, obj = { + + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.usage.v1.UpdateInvoicesResponse} + */ +proto.usage.v1.UpdateInvoicesResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.usage.v1.UpdateInvoicesResponse; + return proto.usage.v1.UpdateInvoicesResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.usage.v1.UpdateInvoicesResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.usage.v1.UpdateInvoicesResponse} + */ +proto.usage.v1.UpdateInvoicesResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.usage.v1.UpdateInvoicesResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.usage.v1.UpdateInvoicesResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.usage.v1.UpdateInvoicesResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.UpdateInvoicesResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; +}; + + +goog.object.extend(exports, proto.usage.v1); diff --git a/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.d.ts b/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.d.ts index 613d8320273d33..960d698dc40f6f 100644 --- a/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.d.ts +++ b/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.d.ts @@ -16,6 +16,7 @@ import * as google_protobuf_timestamp_pb from "google-protobuf/google/protobuf/t interface IUsageServiceService extends grpc.ServiceDefinition { listBilledUsage: IUsageServiceService_IListBilledUsage; + collectUsage: IUsageServiceService_ICollectUsage; } interface IUsageServiceService_IListBilledUsage extends grpc.MethodDefinition { @@ -27,17 +28,30 @@ interface IUsageServiceService_IListBilledUsage extends grpc.MethodDefinition; responseDeserialize: grpc.deserialize; } +interface IUsageServiceService_ICollectUsage extends grpc.MethodDefinition { + path: "/usage.v1.UsageService/CollectUsage"; + requestStream: false; + responseStream: false; + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} export const UsageServiceService: IUsageServiceService; export interface IUsageServiceServer extends grpc.UntypedServiceImplementation { listBilledUsage: grpc.handleUnaryCall; + collectUsage: grpc.handleUnaryCall; } export interface IUsageServiceClient { listBilledUsage(request: usage_v1_usage_pb.ListBilledUsageRequest, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.ListBilledUsageResponse) => void): grpc.ClientUnaryCall; listBilledUsage(request: usage_v1_usage_pb.ListBilledUsageRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.ListBilledUsageResponse) => void): grpc.ClientUnaryCall; listBilledUsage(request: usage_v1_usage_pb.ListBilledUsageRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.ListBilledUsageResponse) => void): grpc.ClientUnaryCall; + collectUsage(request: usage_v1_usage_pb.CollectUsageRequest, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.CollectUsageResponse) => void): grpc.ClientUnaryCall; + collectUsage(request: usage_v1_usage_pb.CollectUsageRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.CollectUsageResponse) => void): grpc.ClientUnaryCall; + collectUsage(request: usage_v1_usage_pb.CollectUsageRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.CollectUsageResponse) => void): grpc.ClientUnaryCall; } export class UsageServiceClient extends grpc.Client implements IUsageServiceClient { @@ -45,4 +59,7 @@ export class UsageServiceClient extends grpc.Client implements IUsageServiceClie public listBilledUsage(request: usage_v1_usage_pb.ListBilledUsageRequest, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.ListBilledUsageResponse) => void): grpc.ClientUnaryCall; public listBilledUsage(request: usage_v1_usage_pb.ListBilledUsageRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.ListBilledUsageResponse) => void): grpc.ClientUnaryCall; public listBilledUsage(request: usage_v1_usage_pb.ListBilledUsageRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.ListBilledUsageResponse) => void): grpc.ClientUnaryCall; + public collectUsage(request: usage_v1_usage_pb.CollectUsageRequest, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.CollectUsageResponse) => void): grpc.ClientUnaryCall; + public collectUsage(request: usage_v1_usage_pb.CollectUsageRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.CollectUsageResponse) => void): grpc.ClientUnaryCall; + public collectUsage(request: usage_v1_usage_pb.CollectUsageRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: usage_v1_usage_pb.CollectUsageResponse) => void): grpc.ClientUnaryCall; } diff --git a/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.js b/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.js index f72af7f5c45034..8b66ae817d5993 100644 --- a/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.js +++ b/components/usage-api/typescript/src/usage/v1/usage_grpc_pb.js @@ -11,6 +11,28 @@ var grpc = require('@grpc/grpc-js'); var usage_v1_usage_pb = require('../../usage/v1/usage_pb.js'); var google_protobuf_timestamp_pb = require('google-protobuf/google/protobuf/timestamp_pb.js'); +function serialize_usage_v1_CollectUsageRequest(arg) { + if (!(arg instanceof usage_v1_usage_pb.CollectUsageRequest)) { + throw new Error('Expected argument of type usage.v1.CollectUsageRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_usage_v1_CollectUsageRequest(buffer_arg) { + return usage_v1_usage_pb.CollectUsageRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_usage_v1_CollectUsageResponse(arg) { + if (!(arg instanceof usage_v1_usage_pb.CollectUsageResponse)) { + throw new Error('Expected argument of type usage.v1.CollectUsageResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_usage_v1_CollectUsageResponse(buffer_arg) { + return usage_v1_usage_pb.CollectUsageResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + function serialize_usage_v1_ListBilledUsageRequest(arg) { if (!(arg instanceof usage_v1_usage_pb.ListBilledUsageRequest)) { throw new Error('Expected argument of type usage.v1.ListBilledUsageRequest'); @@ -47,6 +69,18 @@ listBilledUsage: { responseSerialize: serialize_usage_v1_ListBilledUsageResponse, responseDeserialize: deserialize_usage_v1_ListBilledUsageResponse, }, + // CollectUsage collects usage for the specified time range, for all users and teams. +collectUsage: { + path: '/usage.v1.UsageService/CollectUsage', + requestStream: false, + responseStream: false, + requestType: usage_v1_usage_pb.CollectUsageRequest, + responseType: usage_v1_usage_pb.CollectUsageResponse, + requestSerialize: serialize_usage_v1_CollectUsageRequest, + requestDeserialize: deserialize_usage_v1_CollectUsageRequest, + responseSerialize: serialize_usage_v1_CollectUsageResponse, + responseDeserialize: deserialize_usage_v1_CollectUsageResponse, + }, }; exports.UsageServiceClient = grpc.makeGenericClientConstructor(UsageServiceService); diff --git a/components/usage-api/typescript/src/usage/v1/usage_pb.d.ts b/components/usage-api/typescript/src/usage/v1/usage_pb.d.ts index 7da2527a6c652a..eaf5d4b7b8a2da 100644 --- a/components/usage-api/typescript/src/usage/v1/usage_pb.d.ts +++ b/components/usage-api/typescript/src/usage/v1/usage_pb.d.ts @@ -131,3 +131,54 @@ export namespace BilledSession { credits: number, } } + +export class CollectUsageRequest extends jspb.Message { + + hasStartTime(): boolean; + clearStartTime(): void; + getStartTime(): google_protobuf_timestamp_pb.Timestamp | undefined; + setStartTime(value?: google_protobuf_timestamp_pb.Timestamp): CollectUsageRequest; + + hasEndTime(): boolean; + clearEndTime(): void; + getEndTime(): google_protobuf_timestamp_pb.Timestamp | undefined; + setEndTime(value?: google_protobuf_timestamp_pb.Timestamp): CollectUsageRequest; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): CollectUsageRequest.AsObject; + static toObject(includeInstance: boolean, msg: CollectUsageRequest): CollectUsageRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: CollectUsageRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): CollectUsageRequest; + static deserializeBinaryFromReader(message: CollectUsageRequest, reader: jspb.BinaryReader): CollectUsageRequest; +} + +export namespace CollectUsageRequest { + export type AsObject = { + startTime?: google_protobuf_timestamp_pb.Timestamp.AsObject, + endTime?: google_protobuf_timestamp_pb.Timestamp.AsObject, + } +} + +export class CollectUsageResponse extends jspb.Message { + clearSessionsList(): void; + getSessionsList(): Array; + setSessionsList(value: Array): CollectUsageResponse; + addSessions(value?: BilledSession, index?: number): BilledSession; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): CollectUsageResponse.AsObject; + static toObject(includeInstance: boolean, msg: CollectUsageResponse): CollectUsageResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: CollectUsageResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): CollectUsageResponse; + static deserializeBinaryFromReader(message: CollectUsageResponse, reader: jspb.BinaryReader): CollectUsageResponse; +} + +export namespace CollectUsageResponse { + export type AsObject = { + sessionsList: Array, + } +} diff --git a/components/usage-api/typescript/src/usage/v1/usage_pb.js b/components/usage-api/typescript/src/usage/v1/usage_pb.js index 648b9445ea1f45..b148f2f762c427 100644 --- a/components/usage-api/typescript/src/usage/v1/usage_pb.js +++ b/components/usage-api/typescript/src/usage/v1/usage_pb.js @@ -24,6 +24,8 @@ var global = (function() { return this || window || global || self || Function(' var google_protobuf_timestamp_pb = require('google-protobuf/google/protobuf/timestamp_pb.js'); goog.object.extend(proto, google_protobuf_timestamp_pb); goog.exportSymbol('proto.usage.v1.BilledSession', null, global); +goog.exportSymbol('proto.usage.v1.CollectUsageRequest', null, global); +goog.exportSymbol('proto.usage.v1.CollectUsageResponse', null, global); goog.exportSymbol('proto.usage.v1.ListBilledUsageRequest', null, global); goog.exportSymbol('proto.usage.v1.ListBilledUsageRequest.Ordering', null, global); goog.exportSymbol('proto.usage.v1.ListBilledUsageResponse', null, global); @@ -90,6 +92,48 @@ if (goog.DEBUG && !COMPILED) { */ proto.usage.v1.BilledSession.displayName = 'proto.usage.v1.BilledSession'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.usage.v1.CollectUsageRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.usage.v1.CollectUsageRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.usage.v1.CollectUsageRequest.displayName = 'proto.usage.v1.CollectUsageRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.usage.v1.CollectUsageResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.usage.v1.CollectUsageResponse.repeatedFields_, null); +}; +goog.inherits(proto.usage.v1.CollectUsageResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.usage.v1.CollectUsageResponse.displayName = 'proto.usage.v1.CollectUsageResponse'; +} @@ -992,4 +1036,366 @@ proto.usage.v1.BilledSession.prototype.setCredits = function(value) { }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.usage.v1.CollectUsageRequest.prototype.toObject = function(opt_includeInstance) { + return proto.usage.v1.CollectUsageRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.usage.v1.CollectUsageRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.CollectUsageRequest.toObject = function(includeInstance, msg) { + var f, obj = { + startTime: (f = msg.getStartTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f), + endTime: (f = msg.getEndTime()) && google_protobuf_timestamp_pb.Timestamp.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.usage.v1.CollectUsageRequest} + */ +proto.usage.v1.CollectUsageRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.usage.v1.CollectUsageRequest; + return proto.usage.v1.CollectUsageRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.usage.v1.CollectUsageRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.usage.v1.CollectUsageRequest} + */ +proto.usage.v1.CollectUsageRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new google_protobuf_timestamp_pb.Timestamp; + reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader); + msg.setStartTime(value); + break; + case 2: + var value = new google_protobuf_timestamp_pb.Timestamp; + reader.readMessage(value,google_protobuf_timestamp_pb.Timestamp.deserializeBinaryFromReader); + msg.setEndTime(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.usage.v1.CollectUsageRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.usage.v1.CollectUsageRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.usage.v1.CollectUsageRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.CollectUsageRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStartTime(); + if (f != null) { + writer.writeMessage( + 1, + f, + google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter + ); + } + f = message.getEndTime(); + if (f != null) { + writer.writeMessage( + 2, + f, + google_protobuf_timestamp_pb.Timestamp.serializeBinaryToWriter + ); + } +}; + + +/** + * optional google.protobuf.Timestamp start_time = 1; + * @return {?proto.google.protobuf.Timestamp} + */ +proto.usage.v1.CollectUsageRequest.prototype.getStartTime = function() { + return /** @type{?proto.google.protobuf.Timestamp} */ ( + jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 1)); +}; + + +/** + * @param {?proto.google.protobuf.Timestamp|undefined} value + * @return {!proto.usage.v1.CollectUsageRequest} returns this +*/ +proto.usage.v1.CollectUsageRequest.prototype.setStartTime = function(value) { + return jspb.Message.setWrapperField(this, 1, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.usage.v1.CollectUsageRequest} returns this + */ +proto.usage.v1.CollectUsageRequest.prototype.clearStartTime = function() { + return this.setStartTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.usage.v1.CollectUsageRequest.prototype.hasStartTime = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional google.protobuf.Timestamp end_time = 2; + * @return {?proto.google.protobuf.Timestamp} + */ +proto.usage.v1.CollectUsageRequest.prototype.getEndTime = function() { + return /** @type{?proto.google.protobuf.Timestamp} */ ( + jspb.Message.getWrapperField(this, google_protobuf_timestamp_pb.Timestamp, 2)); +}; + + +/** + * @param {?proto.google.protobuf.Timestamp|undefined} value + * @return {!proto.usage.v1.CollectUsageRequest} returns this +*/ +proto.usage.v1.CollectUsageRequest.prototype.setEndTime = function(value) { + return jspb.Message.setWrapperField(this, 2, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.usage.v1.CollectUsageRequest} returns this + */ +proto.usage.v1.CollectUsageRequest.prototype.clearEndTime = function() { + return this.setEndTime(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.usage.v1.CollectUsageRequest.prototype.hasEndTime = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.usage.v1.CollectUsageResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.usage.v1.CollectUsageResponse.prototype.toObject = function(opt_includeInstance) { + return proto.usage.v1.CollectUsageResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.usage.v1.CollectUsageResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.CollectUsageResponse.toObject = function(includeInstance, msg) { + var f, obj = { + sessionsList: jspb.Message.toObjectList(msg.getSessionsList(), + proto.usage.v1.BilledSession.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.usage.v1.CollectUsageResponse} + */ +proto.usage.v1.CollectUsageResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.usage.v1.CollectUsageResponse; + return proto.usage.v1.CollectUsageResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.usage.v1.CollectUsageResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.usage.v1.CollectUsageResponse} + */ +proto.usage.v1.CollectUsageResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.usage.v1.BilledSession; + reader.readMessage(value,proto.usage.v1.BilledSession.deserializeBinaryFromReader); + msg.addSessions(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.usage.v1.CollectUsageResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.usage.v1.CollectUsageResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.usage.v1.CollectUsageResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.usage.v1.CollectUsageResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getSessionsList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + proto.usage.v1.BilledSession.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated BilledSession sessions = 1; + * @return {!Array} + */ +proto.usage.v1.CollectUsageResponse.prototype.getSessionsList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.usage.v1.BilledSession, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.usage.v1.CollectUsageResponse} returns this +*/ +proto.usage.v1.CollectUsageResponse.prototype.setSessionsList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!proto.usage.v1.BilledSession=} opt_value + * @param {number=} opt_index + * @return {!proto.usage.v1.BilledSession} + */ +proto.usage.v1.CollectUsageResponse.prototype.addSessions = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.usage.v1.BilledSession, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.usage.v1.CollectUsageResponse} returns this + */ +proto.usage.v1.CollectUsageResponse.prototype.clearSessionsList = function() { + return this.setSessionsList([]); +}; + + goog.object.extend(exports, proto.usage.v1); diff --git a/components/usage-api/usage/v1/billing.proto b/components/usage-api/usage/v1/billing.proto new file mode 100644 index 00000000000000..64195436a56962 --- /dev/null +++ b/components/usage-api/usage/v1/billing.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package usage.v1; + +option go_package = "github.com/gitpod-io/gitpod/usage-api/v1"; + +import "google/protobuf/timestamp.proto"; +import "usage/v1/usage.proto"; + +service BillingService { + rpc UpdateInvoices(UpdateInvoicesRequest) returns (UpdateInvoicesResponse) {}; +} + +message UpdateInvoicesRequest { + google.protobuf.Timestamp start_time = 1; + google.protobuf.Timestamp end_time = 2; + + repeated BilledSession sessions = 3; +} + +message UpdateInvoicesResponse { +} diff --git a/components/usage-api/usage/v1/usage.proto b/components/usage-api/usage/v1/usage.proto index a9bdcdebbbc034..91f0f0b9be6c82 100644 --- a/components/usage-api/usage/v1/usage.proto +++ b/components/usage-api/usage/v1/usage.proto @@ -9,6 +9,9 @@ import "google/protobuf/timestamp.proto"; service UsageService { // ListBilledUsage retrieves all usage for the specified attributionId rpc ListBilledUsage(ListBilledUsageRequest) returns (ListBilledUsageResponse) {} + + // CollectUsage collects usage for the specified time range, for all users and teams. + rpc CollectUsage(CollectUsageRequest) returns (CollectUsageResponse) {} } message ListBilledUsageRequest { @@ -51,3 +54,12 @@ message BilledSession { int64 credits = 11; } + +message CollectUsageRequest { + google.protobuf.Timestamp start_time = 1; + google.protobuf.Timestamp end_time = 2; +} + +message CollectUsageResponse { + repeated BilledSession sessions = 1; +} diff --git a/components/usage/go.sum b/components/usage/go.sum index 40e47726038412..f63492d75bb309 100644 --- a/components/usage/go.sum +++ b/components/usage/go.sum @@ -517,7 +517,6 @@ golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/components/usage/pkg/apiv1/billing.go b/components/usage/pkg/apiv1/billing.go new file mode 100644 index 00000000000000..ee967b00b94d76 --- /dev/null +++ b/components/usage/pkg/apiv1/billing.go @@ -0,0 +1,76 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +package apiv1 + +import ( + "context" + "fmt" + "github.com/gitpod-io/gitpod/common-go/log" + v1 "github.com/gitpod-io/gitpod/usage-api/v1" + "github.com/gitpod-io/gitpod/usage/pkg/db" + "github.com/gitpod-io/gitpod/usage/pkg/stripe" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "math" +) + +func NewBillingService(sc *stripe.Client) *BillingService { + return &BillingService{ + sc: sc, + } +} + +type BillingService struct { + sc *stripe.Client + + v1.UnimplementedBillingServiceServer +} + +func (s *BillingService) UpdateInvoices(ctx context.Context, req *v1.UpdateInvoicesRequest) (*v1.UpdateInvoicesResponse, error) { + creditsPerTeam, err := sessionsToCreditReportForTeams(req.GetSessions()) + if err != nil { + log.Log.WithError(err).Error("Failed to generate credit report for teams.") + return nil, status.Errorf(codes.InvalidArgument, "Failed to generate credit report") + } + + err = s.sc.UpdateUsage(creditsPerTeam) + if err != nil { + log.Log.WithError(err).Errorf("Failed to update stripe usage") + return nil, status.Errorf(codes.Internal, "failed to update stripe usage") + } + + return &v1.UpdateInvoicesResponse{}, nil +} + +func sessionsToCreditReportForTeams(sessions []*v1.BilledSession) (map[string]int64, error) { + creditsPerTeamID := map[string]int64{} + + for _, instance := range sessions { + attribution, err := db.ParseAttributionID(instance.AttributionId) + if err != nil { + return nil, fmt.Errorf("invalid attribution: %w", err) + } + + entity, id := attribution.Values() + if entity != db.AttributionEntity_Team { + continue + } + + if _, ok := creditsPerTeamID[id]; !ok { + creditsPerTeamID[id] = 0 + } + creditsPerTeamID[id] += instance.Credits + } + + // Round credits up once we've accumulated all of them + for team, credits := range creditsPerTeamID { + creditsPerTeamID[team] = int64(math.Ceil(float64(credits))) + } + return creditsPerTeamID, nil +} + +type UnimplementedBillingService struct { + v1.UnimplementedBillingServiceServer +} diff --git a/components/usage/pkg/apiv1/usage.go b/components/usage/pkg/apiv1/usage.go index e5b2ed92196421..acb93e00b55a7f 100644 --- a/components/usage/pkg/apiv1/usage.go +++ b/components/usage/pkg/apiv1/usage.go @@ -7,6 +7,7 @@ package apiv1 import ( context "context" "github.com/gitpod-io/gitpod/common-go/log" + "github.com/gitpod-io/gitpod/usage/pkg/controller" "time" v1 "github.com/gitpod-io/gitpod/usage-api/v1" @@ -21,6 +22,9 @@ var _ v1.UsageServiceServer = (*UsageService)(nil) type UsageService struct { conn *gorm.DB + + ctrl *controller.UsageReconciler + v1.UnimplementedUsageServiceServer } @@ -64,23 +68,7 @@ func (us *UsageService) ListBilledUsage(ctx context.Context, in *v1.ListBilledUs var billedSessions []*v1.BilledSession for _, usageRecord := range usageRecords { - var endTime *timestamppb.Timestamp - if usageRecord.StoppedAt.Valid { - endTime = timestamppb.New(usageRecord.StoppedAt.Time) - } - billedSession := &v1.BilledSession{ - AttributionId: string(usageRecord.AttributionID), - UserId: usageRecord.UserID.String(), - WorkspaceId: usageRecord.WorkspaceID, - WorkspaceType: string(usageRecord.WorkspaceType), - ProjectId: usageRecord.ProjectID, - InstanceId: usageRecord.InstanceID.String(), - WorkspaceClass: usageRecord.WorkspaceClass, - StartTime: timestamppb.New(usageRecord.StartedAt), - EndTime: endTime, - Credits: int64(usageRecord.CreditsUsed), - } - billedSessions = append(billedSessions, billedSession) + billedSessions = append(billedSessions, usageRecordToBilledUsageProto(usageRecord)) } return &v1.ListBilledUsageResponse{ @@ -88,6 +76,52 @@ func (us *UsageService) ListBilledUsage(ctx context.Context, in *v1.ListBilledUs }, nil } -func NewUsageService(conn *gorm.DB) *UsageService { - return &UsageService{conn: conn} +func (s *UsageService) CollectUsage(ctx context.Context, req *v1.CollectUsageRequest) (*v1.CollectUsageResponse, error) { + from := req.GetStartTime().AsTime() + to := req.GetEndTime().AsTime() + + if from.Before(to) { + return nil, status.Errorf(codes.InvalidArgument, "End time must be after start time") + } + + _, report, err := s.ctrl.ReconcileTimeRange(ctx, from, to) + if err != nil { + log.Log.WithError(err).Error("Failed to reconcile time range.") + return nil, status.Error(codes.Internal, "failed to reconcile time range") + } + + var sessions []*v1.BilledSession + for _, instance := range report { + sessions = append(sessions, usageRecordToBilledUsageProto(instance)) + } + + return &v1.CollectUsageResponse{ + Sessions: sessions, + }, nil +} + +func NewUsageService(conn *gorm.DB, ctrl *controller.UsageReconciler) *UsageService { + return &UsageService{ + conn: conn, + ctrl: ctrl, + } +} + +func usageRecordToBilledUsageProto(usageRecord db.WorkspaceInstanceUsage) *v1.BilledSession { + var endTime *timestamppb.Timestamp + if usageRecord.StoppedAt.Valid { + endTime = timestamppb.New(usageRecord.StoppedAt.Time) + } + return &v1.BilledSession{ + AttributionId: string(usageRecord.AttributionID), + UserId: usageRecord.UserID.String(), + WorkspaceId: usageRecord.WorkspaceID, + WorkspaceType: string(usageRecord.WorkspaceType), + ProjectId: usageRecord.ProjectID, + InstanceId: usageRecord.InstanceID.String(), + WorkspaceClass: usageRecord.WorkspaceClass, + StartTime: timestamppb.New(usageRecord.StartedAt), + EndTime: endTime, + Credits: int64(usageRecord.CreditsUsed), + } } diff --git a/components/usage/pkg/controller/billing.go b/components/usage/pkg/controller/billing.go index 88333ca9a1afdc..bdaf907b01784f 100644 --- a/components/usage/pkg/controller/billing.go +++ b/components/usage/pkg/controller/billing.go @@ -7,6 +7,8 @@ package controller import ( "context" "fmt" + v1 "github.com/gitpod-io/gitpod/usage-api/v1" + "math" "time" "github.com/gitpod-io/gitpod/usage/pkg/db" @@ -14,31 +16,51 @@ import ( ) type BillingController interface { - Reconcile(ctx context.Context, now time.Time, report UsageReport) error + Reconcile(ctx context.Context, sessions []*v1.BilledSession) error } type NoOpBillingController struct{} -func (b *NoOpBillingController) Reconcile(_ context.Context, _ time.Time, _ UsageReport) error { +func (b *NoOpBillingController) Reconcile(_ context.Context, _ []*v1.BilledSession) error { return nil } type StripeBillingController struct { - pricer *WorkspacePricer - sc *stripe.Client + sc *stripe.Client } -func NewStripeBillingController(sc *stripe.Client, pricer *WorkspacePricer) *StripeBillingController { +func NewStripeBillingController(sc *stripe.Client) *StripeBillingController { return &StripeBillingController{ - sc: sc, - pricer: pricer, + sc: sc, } } -func (b *StripeBillingController) Reconcile(_ context.Context, now time.Time, report UsageReport) error { - runtimeReport := report.CreditSummaryForTeams(b.pricer, now) +func (b *StripeBillingController) Reconcile(_ context.Context, sessions []*v1.BilledSession) error { + creditsPerTeamID := map[string]int64{} - err := b.sc.UpdateUsage(runtimeReport) + for _, instance := range sessions { + attribution, err := db.ParseAttributionID(instance.AttributionId) + if err != nil { + return fmt.Errorf("invalid attribution: %w", err) + } + + entity, id := attribution.Values() + if entity != db.AttributionEntity_Team { + continue + } + + if _, ok := creditsPerTeamID[id]; !ok { + creditsPerTeamID[id] = 0 + } + creditsPerTeamID[id] += instance.Credits + } + + // Round credits up once we've accumulated all of them + for team, credits := range creditsPerTeamID { + creditsPerTeamID[team] = int64(math.Ceil(float64(credits))) + } + + err := b.sc.UpdateUsage(creditsPerTeamID) if err != nil { return fmt.Errorf("failed to update usage: %w", err) } diff --git a/components/usage/pkg/controller/controller.go b/components/usage/pkg/controller/controller.go index 6ecbcffba41dcb..b258b589fdb681 100644 --- a/components/usage/pkg/controller/controller.go +++ b/components/usage/pkg/controller/controller.go @@ -5,9 +5,12 @@ package controller import ( + "context" "fmt" "github.com/gitpod-io/gitpod/common-go/log" + v1 "github.com/gitpod-io/gitpod/usage-api/v1" "github.com/robfig/cron" + "google.golang.org/protobuf/types/known/timestamppb" "sync" "time" ) @@ -82,3 +85,43 @@ func (c *Controller) Stop() { c.runningJobs.Wait() } + +type DelegatingReconciler struct { + usageService v1.UsageServiceClient + billingService v1.BillingServiceClient +} + +func NewDelegatingReconciler(usageSvc v1.UsageServiceClient, billingSvc v1.BillingServiceClient) *DelegatingReconciler { + return &DelegatingReconciler{ + usageService: usageSvc, + billingService: billingSvc, + } +} + +func (r *DelegatingReconciler) Reconcile() error { + ctx := context.Background() + now := time.Now().UTC() + + start := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC) + startOfCurrentMonth := timestamppb.New(start) + startOfNextMonth := timestamppb.New(start.AddDate(0, 1, 0)) + + usage, err := r.usageService.CollectUsage(ctx, &v1.CollectUsageRequest{ + StartTime: startOfCurrentMonth, + EndTime: startOfNextMonth, + }) + if err != nil { + return fmt.Errorf("failed to collect usage: %w", err) + } + + _, err = r.billingService.UpdateInvoices(ctx, &v1.UpdateInvoicesRequest{ + StartTime: startOfCurrentMonth, + EndTime: startOfNextMonth, + Sessions: usage.GetSessions(), + }) + if err != nil { + return fmt.Errorf("failed to update invoices: %w", err) + } + + return nil +} diff --git a/components/usage/pkg/controller/reconciler.go b/components/usage/pkg/controller/reconciler.go index 4d3a0cca030e6f..923736e6f152fc 100644 --- a/components/usage/pkg/controller/reconciler.go +++ b/components/usage/pkg/controller/reconciler.go @@ -29,20 +29,18 @@ func (f ReconcilerFunc) Reconcile() error { } type UsageReconciler struct { - nowFunc func() time.Time - conn *gorm.DB - pricer *WorkspacePricer - billingController BillingController - contentService contentservice.Interface + nowFunc func() time.Time + conn *gorm.DB + pricer *WorkspacePricer + contentService contentservice.Interface } -func NewUsageReconciler(conn *gorm.DB, pricer *WorkspacePricer, billingController BillingController, contentService contentservice.Interface) *UsageReconciler { +func NewUsageReconciler(conn *gorm.DB, pricer *WorkspacePricer, contentService contentservice.Interface) *UsageReconciler { return &UsageReconciler{ - conn: conn, - pricer: pricer, - billingController: billingController, - contentService: contentService, - nowFunc: time.Now, + conn: conn, + pricer: pricer, + contentService: contentService, + nowFunc: time.Now, } } @@ -72,11 +70,6 @@ func (u *UsageReconciler) Reconcile() (err error) { } log.WithField("usage_reconcile_status", status).Info("Reconcile completed.") - err = db.CreateUsageRecords(ctx, u.conn, usageReportToUsageRecords(report, u.pricer, u.nowFunc().UTC())) - if err != nil { - return fmt.Errorf("failed to write usage records to database: %s", err) - } - filename := fmt.Sprintf("%s.gz", now.Format(time.RFC3339)) err = u.contentService.UploadUsageReport(ctx, filename, report) if err != nil { @@ -105,17 +98,53 @@ func (u *UsageReconciler) ReconcileTimeRange(ctx context.Context, from, to time. } log.WithField("workspace_instances", instances).Debug("Successfully loaded workspace instances.") - instancesByAttributionID := groupInstancesByAttributionID(instances) + //instancesByAttributionID := groupInstancesByAttributionID(instances) + + usageRecords := convertInstancesToUsageRecords(instances, u.pricer, now) - err = u.billingController.Reconcile(ctx, now, instancesByAttributionID) + err = db.CreateUsageRecords(ctx, u.conn, usageRecords) if err != nil { - return nil, nil, fmt.Errorf("failed to reconcile billing: %w", err) + return nil, nil, fmt.Errorf("failed to write usage records to database: %s", err) } - return status, instancesByAttributionID, nil + //err = u.billingController.Reconcile(ctx, now, instancesByAttributionID) + //if err != nil { + // return nil, nil, fmt.Errorf("failed to reconcile billing: %w", err) + //} + + return status, usageRecords, nil +} + +func convertInstancesToUsageRecords(instances []db.WorkspaceInstanceForUsage, pricer *WorkspacePricer, now time.Time) []db.WorkspaceInstanceUsage { + var usageRecords []db.WorkspaceInstanceUsage + for _, instance := range instances { + var stoppedAt sql.NullTime + if instance.StoppedTime.IsSet() { + stoppedAt = sql.NullTime{Time: instance.StoppedTime.Time(), Valid: true} + } + + projectID := "" + if instance.ProjectID.Valid { + projectID = instance.ProjectID.String + } + + usageRecords = append(usageRecords, db.WorkspaceInstanceUsage{ + InstanceID: instance.ID, + AttributionID: instance.UsageAttributionID, + WorkspaceID: instance.WorkspaceID, + ProjectID: projectID, + UserID: instance.OwnerID, + WorkspaceType: instance.Type, + WorkspaceClass: instance.WorkspaceClass, + StartedAt: instance.CreationTime.Time(), + StoppedAt: stoppedAt, + CreditsUsed: pricer.CreditsUsedByInstance(&instance, now), + GenerationID: 0, + }) + } } -type UsageReport map[db.AttributionID][]db.WorkspaceInstanceForUsage +type UsageReport []db.WorkspaceInstanceUsage func (u UsageReport) CreditSummaryForTeams(pricer *WorkspacePricer, maxStopTime time.Time) map[string]int64 { creditsPerTeamID := map[string]int64{} diff --git a/components/usage/pkg/db/workspace_instance.go b/components/usage/pkg/db/workspace_instance.go index fb6a54232c0ec6..9b9e7d21168c83 100644 --- a/components/usage/pkg/db/workspace_instance.go +++ b/components/usage/pkg/db/workspace_instance.go @@ -120,6 +120,22 @@ func (a AttributionID) Values() (entity string, identifier string) { return tokens[0], tokens[1] } +func ParseAttributionID(s string) (AttributionID, error) { + tokens := strings.Split(s, ":") + if len(tokens) != 2 { + return "", fmt.Errorf("invalid attribution ID: %s", s) + } + + switch tokens[0] { + case AttributionEntity_Team: + return NewTeamAttributionID(tokens[1]), nil + case AttributionEntity_User: + return NewUserAttributionID(tokens[1]), nil + default: + return "", fmt.Errorf("invalid attribution ID entity: %s", s) + } +} + const ( WorkspaceClass_Default = "default" ) diff --git a/components/usage/pkg/server/server.go b/components/usage/pkg/server/server.go index cdd7cbdbeb4f99..6287d02acbb4ef 100644 --- a/components/usage/pkg/server/server.go +++ b/components/usage/pkg/server/server.go @@ -6,6 +6,8 @@ package server import ( "fmt" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "net" "os" "time" @@ -47,25 +49,31 @@ func Start(cfg Config) error { return fmt.Errorf("failed to establish database connection: %w", err) } + var serverOpts []baseserver.Option + if cfg.Server != nil { + serverOpts = append(serverOpts, baseserver.WithConfig(cfg.Server)) + } + srv, err := baseserver.New("usage", serverOpts...) + if err != nil { + return fmt.Errorf("failed to initialize usage server: %w", err) + } + pricer, err := controller.NewWorkspacePricer(cfg.CreditsPerMinuteByWorkspaceClass) if err != nil { return fmt.Errorf("failed to create workspace pricer: %w", err) } - var billingController controller.BillingController = &controller.NoOpBillingController{} - + var sc *stripe.Client if cfg.StripeCredentialsFile != "" { config, err := stripe.ReadConfigFromFile(cfg.StripeCredentialsFile) if err != nil { return fmt.Errorf("failed to load stripe credentials: %w", err) } - c, err := stripe.New(config) + sc, err = stripe.New(config) if err != nil { return fmt.Errorf("failed to initialize stripe client: %w", err) } - - billingController = controller.NewStripeBillingController(c, pricer) } schedule, err := time.ParseDuration(cfg.ControllerSchedule) @@ -78,7 +86,16 @@ func Start(cfg Config) error { contentService = contentservice.New(cfg.ContentServiceAddress) } - ctrl, err := controller.New(schedule, controller.NewUsageReconciler(conn, pricer, billingController, contentService)) + usageController := controller.NewUsageReconciler(conn, pricer, contentService) + + selfConn, err := grpc.Dial(srv.GRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return fmt.Errorf("failed to create self grpc connection: %w", err) + } + usageServiceClient := v1.NewUsageServiceClient(selfConn) + billingServiceClient := v1.NewBillingServiceClient(selfConn) + delegating := controller.NewDelegatingReconciler(usageServiceClient, billingServiceClient) + ctrl, err := controller.New(schedule, delegating) if err != nil { return fmt.Errorf("failed to initialize usage controller: %w", err) } @@ -89,19 +106,13 @@ func Start(cfg Config) error { } defer ctrl.Stop() - var serverOpts []baseserver.Option - if cfg.Server != nil { - serverOpts = append(serverOpts, baseserver.WithConfig(cfg.Server)) - } - srv, err := baseserver.New("usage", serverOpts...) - if err != nil { - return fmt.Errorf("failed to initialize usage server: %w", err) - } - err = registerGRPCServices(srv, conn) + err = registerGRPCServices(srv, conn, sc, usageController) if err != nil { return fmt.Errorf("failed to register gRPC services: %w", err) } + srv.GRPCAddress() + err = controller.RegisterMetrics(srv.MetricsRegistry()) if err != nil { return fmt.Errorf("failed to register controller metrics: %w", err) @@ -115,7 +126,14 @@ func Start(cfg Config) error { return nil } -func registerGRPCServices(srv *baseserver.Server, conn *gorm.DB) error { - v1.RegisterUsageServiceServer(srv.GRPC(), apiv1.NewUsageService(conn)) +func registerGRPCServices(srv *baseserver.Server, conn *gorm.DB, sc *stripe.Client, usageReconciler *controller.UsageReconciler) error { + v1.RegisterUsageServiceServer(srv.GRPC(), apiv1.NewUsageService(conn, usageReconciler)) + + if sc == nil { + v1.RegisterBillingServiceServer(srv.GRPC(), &apiv1.UnimplementedBillingService{}) + } else { + v1.RegisterBillingServiceServer(srv.GRPC(), apiv1.NewBillingService(sc)) + } + return nil }