diff --git a/testing/cmd/bankdemo/README.md b/testing/cmd/bankdemo/README.md new file mode 100644 index 0000000..d2502ea --- /dev/null +++ b/testing/cmd/bankdemo/README.md @@ -0,0 +1,9 @@ +# bankdemo + +The `bankdemo` program is an example gRPC server that was used to demo `grpcurl` at Gophercon 2018. + +It demonstrates interesting concepts for building a gRPC server, including chat functionality (that relies on full-duplex bidirectional streams). This code was written specifically to provide an interesting concrete demonstration and, as such, should not be considered in any way production-worthy. + +The demo app tracks user accounts, transactions, and balances completely in memory. Every few seconds, as well as on graceful shutdown (like when the server receives a SIGTERM or SIGINT signal), this state is saved to a file named `accounts.json`, so that the data can be restored if the process restarts. + +In addition to bank account data, the server also tracks "chat sessions", for demonstrating bidirectional streams in the form of an application where customers can chat with support agents. diff --git a/testing/cmd/bankdemo/auth.go b/testing/cmd/bankdemo/auth.go new file mode 100644 index 0000000..ce0bc07 --- /dev/null +++ b/testing/cmd/bankdemo/auth.go @@ -0,0 +1,49 @@ +package main + +import ( + "strings" + + "golang.org/x/net/context" + "google.golang.org/grpc/metadata" +) + +func getCustomer(ctx context.Context) string { + // we'll just treat the "auth token" as if it is a + // customer ID, but reject tokens that begin with "agent" + // (those are auth tokens for support agents, not customers) + cust := getAuthCode(ctx) + if strings.HasPrefix(cust, "agent") { + return "" + } + return cust +} + +func getAgent(ctx context.Context) string { + // we'll just treat the "auth token" as if it is an agent's + // user ID, but reject tokens that don't begin with "agent" + // (those are auth tokens for customers, not support agents) + agent := getAuthCode(ctx) + if !strings.HasPrefix(agent, "agent") { + return "" + } + return agent +} + +func getAuthCode(ctx context.Context) string { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return "" + } + vals := md.Get("authorization") + if len(vals) != 1 { + return "" + } + pieces := strings.SplitN(strings.ToLower(vals[0]), " ", 2) + if len(pieces) != 2 { + return "" + } + if pieces[0] != "token" { + return "" + } + return pieces[1] +} diff --git a/testing/cmd/bankdemo/bank.go b/testing/cmd/bankdemo/bank.go new file mode 100644 index 0000000..2971cc8 --- /dev/null +++ b/testing/cmd/bankdemo/bank.go @@ -0,0 +1,237 @@ +package main + +import ( + "fmt" + "time" + + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// bankServer implements the Bank gRPC service. +type bankServer struct { + allAccounts *accounts +} + +func (s *bankServer) OpenAccount(ctx context.Context, req *OpenAccountRequest) (*Account, error) { + cust := getCustomer(ctx) + if cust == "" { + return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + switch req.Type { + case Account_CHECKING, Account_SAVING, Account_MONEY_MARKET: + if req.InitialDepositCents < 0 { + return nil, status.Errorf(codes.InvalidArgument, "initial deposit amount cannot be negative: %s", dollars(req.InitialDepositCents)) + } + case Account_LINE_OF_CREDIT, Account_LOAN, Account_EQUITIES: + if req.InitialDepositCents != 0 { + return nil, status.Errorf(codes.InvalidArgument, "initial deposit amount must be zero for account type %v: %s", req.Type, dollars(req.InitialDepositCents)) + } + default: + return nil, status.Errorf(codes.InvalidArgument, "invalid account type: %v", req.Type) + } + + return s.allAccounts.openAccount(cust, req.Type, req.InitialDepositCents), nil +} + +func (s *bankServer) CloseAccount(ctx context.Context, req *CloseAccountRequest) (*empty.Empty, error) { + cust := getCustomer(ctx) + if cust == "" { + return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + if err := s.allAccounts.closeAccount(cust, req.AccountNumber); err != nil { + return nil, err + } + return &empty.Empty{}, nil +} + +func (s *bankServer) GetAccounts(ctx context.Context, _ *empty.Empty) (*GetAccountsResponse, error) { + cust := getCustomer(ctx) + if cust == "" { + return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + accounts := s.allAccounts.getAllAccounts(cust) + return &GetAccountsResponse{Accounts: accounts}, nil +} + +func (s *bankServer) GetTransactions(req *GetTransactionsRequest, stream Bank_GetTransactionsServer) error { + cust := getCustomer(stream.Context()) + if cust == "" { + return status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + acct, err := s.allAccounts.getAccount(cust, req.AccountNumber) + if err != nil { + return err + } + + var start, end time.Time + if req.Start != nil { + start, err = ptypes.Timestamp(req.Start) + if err != nil { + return err + } + } + if req.End != nil { + end, err = ptypes.Timestamp(req.End) + if err != nil { + return err + } + } else { + end = time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.Local) + } + + txns := acct.getTransactions() + for _, txn := range txns { + t, err := ptypes.Timestamp(txn.Date) + if err != nil { + return err + } + if (t.After(start) || t.Equal(start)) && + (t.Before(end) || t.Equal(end)) { + + if err := stream.Send(txn); err != nil { + return err + } + } + } + return nil +} + +func (s *bankServer) Deposit(ctx context.Context, req *DepositRequest) (*BalanceResponse, error) { + cust := getCustomer(ctx) + if cust == "" { + return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + switch req.Source { + case DepositRequest_ACH, DepositRequest_CASH, DepositRequest_CHECK, DepositRequest_WIRE: + // ok + default: + return nil, status.Errorf(codes.InvalidArgument, "unknown deposit source: %v", req.Source) + } + + if req.AmountCents <= 0 { + return nil, status.Errorf(codes.InvalidArgument, "deposit amount cannot be non-positive: %s", dollars(req.AmountCents)) + } + + desc := fmt.Sprintf("%v deposit", req.Source) + if req.Desc != "" { + desc = fmt.Sprintf("%s: %s", desc, req.Desc) + } + acct, err := s.allAccounts.getAccount(cust, req.AccountNumber) + if err != nil { + return nil, err + } + newBalance, err := acct.newTransaction(req.AmountCents, desc) + if err != nil { + return nil, err + } + return &BalanceResponse{ + AccountNumber: req.AccountNumber, + BalanceCents: newBalance, + }, nil +} + +func (s *bankServer) Withdraw(ctx context.Context, req *WithdrawRequest) (*BalanceResponse, error) { + cust := getCustomer(ctx) + if cust == "" { + return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + if req.AmountCents >= 0 { + return nil, status.Errorf(codes.InvalidArgument, "withdrawal amount cannot be non-negative: %s", dollars(req.AmountCents)) + } + + acct, err := s.allAccounts.getAccount(cust, req.AccountNumber) + if err != nil { + return nil, err + } + newBalance, err := acct.newTransaction(req.AmountCents, req.Desc) + if err != nil { + return nil, err + } + return &BalanceResponse{ + AccountNumber: req.AccountNumber, + BalanceCents: newBalance, + }, nil +} + +func (s *bankServer) Transfer(ctx context.Context, req *TransferRequest) (*TransferResponse, error) { + cust := getCustomer(ctx) + if cust == "" { + return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + if req.AmountCents <= 0 { + return nil, status.Errorf(codes.InvalidArgument, "transfer amount cannot be non-positive: %s", dollars(req.AmountCents)) + } + + var srcAcct *account + var srcDesc string + switch src := req.Source.(type) { + case *TransferRequest_ExternalSource: + srcDesc = fmt.Sprintf("ACH %09d:%06d", src.ExternalSource.AchRoutingNumber, src.ExternalSource.AchAccountNumber) + if src.ExternalSource.AchAccountNumber == 0 || src.ExternalSource.AchRoutingNumber == 0 { + return nil, status.Errorf(codes.InvalidArgument, "external source routing and account numbers cannot be zero: %s", srcDesc) + } + case *TransferRequest_SourceAccountNumber: + srcDesc = fmt.Sprintf("account %06d", src.SourceAccountNumber) + var err error + if srcAcct, err = s.allAccounts.getAccount(cust, src.SourceAccountNumber); err != nil { + return nil, err + } + } + + var destAcct *account + var destDesc string + switch dest := req.Dest.(type) { + case *TransferRequest_ExternalDest: + destDesc = fmt.Sprintf("ACH %09d:%06d", dest.ExternalDest.AchRoutingNumber, dest.ExternalDest.AchAccountNumber) + if dest.ExternalDest.AchAccountNumber == 0 || dest.ExternalDest.AchRoutingNumber == 0 { + return nil, status.Errorf(codes.InvalidArgument, "external source routing and account numbers cannot be zero: %s", destDesc) + } + case *TransferRequest_DestAccountNumber: + destDesc = fmt.Sprintf("account %06d", dest.DestAccountNumber) + var err error + if destAcct, err = s.allAccounts.getAccount(cust, dest.DestAccountNumber); err != nil { + return nil, err + } + } + + var srcBalance int32 + if srcAcct != nil { + desc := fmt.Sprintf("transfer to %s", destDesc) + if req.Desc != "" { + desc = fmt.Sprintf("%s: %s", desc, req.Desc) + } + var err error + if srcBalance, err = srcAcct.newTransaction(-req.AmountCents, desc); err != nil { + return nil, err + } + } + + var destBalance int32 + if destAcct != nil { + desc := fmt.Sprintf("transfer from %s", srcDesc) + if req.Desc != "" { + desc = fmt.Sprintf("%s: %s", desc, req.Desc) + } + var err error + if destBalance, err = destAcct.newTransaction(req.AmountCents, desc); err != nil { + return nil, err + } + } + + return &TransferResponse{ + SrcAccountNumber: req.GetSourceAccountNumber(), + SrcBalanceCents: srcBalance, + DestAccountNumber: req.GetDestAccountNumber(), + DestBalanceCents: destBalance, + }, nil +} diff --git a/testing/cmd/bankdemo/bank.pb.go b/testing/cmd/bankdemo/bank.pb.go new file mode 100644 index 0000000..c5fe017 --- /dev/null +++ b/testing/cmd/bankdemo/bank.pb.go @@ -0,0 +1,1307 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: bank.proto + +package main + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import empty "github.com/golang/protobuf/ptypes/empty" +import timestamp "github.com/golang/protobuf/ptypes/timestamp" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Account_Type int32 + +const ( + Account_UNKNOWN Account_Type = 0 + Account_CHECKING Account_Type = 1 + Account_SAVING Account_Type = 2 + Account_MONEY_MARKET Account_Type = 3 + Account_LINE_OF_CREDIT Account_Type = 4 + Account_LOAN Account_Type = 5 + Account_EQUITIES Account_Type = 6 +) + +var Account_Type_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CHECKING", + 2: "SAVING", + 3: "MONEY_MARKET", + 4: "LINE_OF_CREDIT", + 5: "LOAN", + 6: "EQUITIES", +} +var Account_Type_value = map[string]int32{ + "UNKNOWN": 0, + "CHECKING": 1, + "SAVING": 2, + "MONEY_MARKET": 3, + "LINE_OF_CREDIT": 4, + "LOAN": 5, + "EQUITIES": 6, +} + +func (x Account_Type) String() string { + return proto.EnumName(Account_Type_name, int32(x)) +} +func (Account_Type) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{3, 0} +} + +type DepositRequest_Source int32 + +const ( + DepositRequest_UNKNOWN DepositRequest_Source = 0 + DepositRequest_CASH DepositRequest_Source = 1 + DepositRequest_CHECK DepositRequest_Source = 2 + DepositRequest_ACH DepositRequest_Source = 3 + DepositRequest_WIRE DepositRequest_Source = 4 +) + +var DepositRequest_Source_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CASH", + 2: "CHECK", + 3: "ACH", + 4: "WIRE", +} +var DepositRequest_Source_value = map[string]int32{ + "UNKNOWN": 0, + "CASH": 1, + "CHECK": 2, + "ACH": 3, + "WIRE": 4, +} + +func (x DepositRequest_Source) String() string { + return proto.EnumName(DepositRequest_Source_name, int32(x)) +} +func (DepositRequest_Source) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{6, 0} +} + +type OpenAccountRequest struct { + InitialDepositCents int32 `protobuf:"varint,1,opt,name=initial_deposit_cents,json=initialDepositCents,proto3" json:"initial_deposit_cents,omitempty"` + Type Account_Type `protobuf:"varint,2,opt,name=type,proto3,enum=Account_Type" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OpenAccountRequest) Reset() { *m = OpenAccountRequest{} } +func (m *OpenAccountRequest) String() string { return proto.CompactTextString(m) } +func (*OpenAccountRequest) ProtoMessage() {} +func (*OpenAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{0} +} +func (m *OpenAccountRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OpenAccountRequest.Unmarshal(m, b) +} +func (m *OpenAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OpenAccountRequest.Marshal(b, m, deterministic) +} +func (dst *OpenAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_OpenAccountRequest.Merge(dst, src) +} +func (m *OpenAccountRequest) XXX_Size() int { + return xxx_messageInfo_OpenAccountRequest.Size(m) +} +func (m *OpenAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_OpenAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_OpenAccountRequest proto.InternalMessageInfo + +func (m *OpenAccountRequest) GetInitialDepositCents() int32 { + if m != nil { + return m.InitialDepositCents + } + return 0 +} + +func (m *OpenAccountRequest) GetType() Account_Type { + if m != nil { + return m.Type + } + return Account_UNKNOWN +} + +type CloseAccountRequest struct { + AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CloseAccountRequest) Reset() { *m = CloseAccountRequest{} } +func (m *CloseAccountRequest) String() string { return proto.CompactTextString(m) } +func (*CloseAccountRequest) ProtoMessage() {} +func (*CloseAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{1} +} +func (m *CloseAccountRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CloseAccountRequest.Unmarshal(m, b) +} +func (m *CloseAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CloseAccountRequest.Marshal(b, m, deterministic) +} +func (dst *CloseAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CloseAccountRequest.Merge(dst, src) +} +func (m *CloseAccountRequest) XXX_Size() int { + return xxx_messageInfo_CloseAccountRequest.Size(m) +} +func (m *CloseAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CloseAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CloseAccountRequest proto.InternalMessageInfo + +func (m *CloseAccountRequest) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +type GetAccountsResponse struct { + Accounts []*Account `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetAccountsResponse) Reset() { *m = GetAccountsResponse{} } +func (m *GetAccountsResponse) String() string { return proto.CompactTextString(m) } +func (*GetAccountsResponse) ProtoMessage() {} +func (*GetAccountsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{2} +} +func (m *GetAccountsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetAccountsResponse.Unmarshal(m, b) +} +func (m *GetAccountsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetAccountsResponse.Marshal(b, m, deterministic) +} +func (dst *GetAccountsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetAccountsResponse.Merge(dst, src) +} +func (m *GetAccountsResponse) XXX_Size() int { + return xxx_messageInfo_GetAccountsResponse.Size(m) +} +func (m *GetAccountsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetAccountsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetAccountsResponse proto.InternalMessageInfo + +func (m *GetAccountsResponse) GetAccounts() []*Account { + if m != nil { + return m.Accounts + } + return nil +} + +type Account struct { + AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + Type Account_Type `protobuf:"varint,2,opt,name=type,proto3,enum=Account_Type" json:"type,omitempty"` + BalanceCents int32 `protobuf:"varint,3,opt,name=balance_cents,json=balanceCents,proto3" json:"balance_cents,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Account) Reset() { *m = Account{} } +func (m *Account) String() string { return proto.CompactTextString(m) } +func (*Account) ProtoMessage() {} +func (*Account) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{3} +} +func (m *Account) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Account.Unmarshal(m, b) +} +func (m *Account) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Account.Marshal(b, m, deterministic) +} +func (dst *Account) XXX_Merge(src proto.Message) { + xxx_messageInfo_Account.Merge(dst, src) +} +func (m *Account) XXX_Size() int { + return xxx_messageInfo_Account.Size(m) +} +func (m *Account) XXX_DiscardUnknown() { + xxx_messageInfo_Account.DiscardUnknown(m) +} + +var xxx_messageInfo_Account proto.InternalMessageInfo + +func (m *Account) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +func (m *Account) GetType() Account_Type { + if m != nil { + return m.Type + } + return Account_UNKNOWN +} + +func (m *Account) GetBalanceCents() int32 { + if m != nil { + return m.BalanceCents + } + return 0 +} + +type GetTransactionsRequest struct { + AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + Start *timestamp.Timestamp `protobuf:"bytes,2,opt,name=start,proto3" json:"start,omitempty"` + End *timestamp.Timestamp `protobuf:"bytes,3,opt,name=end,proto3" json:"end,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetTransactionsRequest) Reset() { *m = GetTransactionsRequest{} } +func (m *GetTransactionsRequest) String() string { return proto.CompactTextString(m) } +func (*GetTransactionsRequest) ProtoMessage() {} +func (*GetTransactionsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{4} +} +func (m *GetTransactionsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTransactionsRequest.Unmarshal(m, b) +} +func (m *GetTransactionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTransactionsRequest.Marshal(b, m, deterministic) +} +func (dst *GetTransactionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTransactionsRequest.Merge(dst, src) +} +func (m *GetTransactionsRequest) XXX_Size() int { + return xxx_messageInfo_GetTransactionsRequest.Size(m) +} +func (m *GetTransactionsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTransactionsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTransactionsRequest proto.InternalMessageInfo + +func (m *GetTransactionsRequest) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +func (m *GetTransactionsRequest) GetStart() *timestamp.Timestamp { + if m != nil { + return m.Start + } + return nil +} + +func (m *GetTransactionsRequest) GetEnd() *timestamp.Timestamp { + if m != nil { + return m.End + } + return nil +} + +type Transaction struct { + AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + SeqNumber uint64 `protobuf:"varint,2,opt,name=seq_number,json=seqNumber,proto3" json:"seq_number,omitempty"` + Date *timestamp.Timestamp `protobuf:"bytes,3,opt,name=date,proto3" json:"date,omitempty"` + AmountCents int32 `protobuf:"varint,4,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"` + Desc string `protobuf:"bytes,5,opt,name=desc,proto3" json:"desc,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Transaction) Reset() { *m = Transaction{} } +func (m *Transaction) String() string { return proto.CompactTextString(m) } +func (*Transaction) ProtoMessage() {} +func (*Transaction) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{5} +} +func (m *Transaction) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Transaction.Unmarshal(m, b) +} +func (m *Transaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Transaction.Marshal(b, m, deterministic) +} +func (dst *Transaction) XXX_Merge(src proto.Message) { + xxx_messageInfo_Transaction.Merge(dst, src) +} +func (m *Transaction) XXX_Size() int { + return xxx_messageInfo_Transaction.Size(m) +} +func (m *Transaction) XXX_DiscardUnknown() { + xxx_messageInfo_Transaction.DiscardUnknown(m) +} + +var xxx_messageInfo_Transaction proto.InternalMessageInfo + +func (m *Transaction) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +func (m *Transaction) GetSeqNumber() uint64 { + if m != nil { + return m.SeqNumber + } + return 0 +} + +func (m *Transaction) GetDate() *timestamp.Timestamp { + if m != nil { + return m.Date + } + return nil +} + +func (m *Transaction) GetAmountCents() int32 { + if m != nil { + return m.AmountCents + } + return 0 +} + +func (m *Transaction) GetDesc() string { + if m != nil { + return m.Desc + } + return "" +} + +type DepositRequest struct { + AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + AmountCents int32 `protobuf:"varint,2,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"` + Source DepositRequest_Source `protobuf:"varint,3,opt,name=source,proto3,enum=DepositRequest_Source" json:"source,omitempty"` + Desc string `protobuf:"bytes,4,opt,name=desc,proto3" json:"desc,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DepositRequest) Reset() { *m = DepositRequest{} } +func (m *DepositRequest) String() string { return proto.CompactTextString(m) } +func (*DepositRequest) ProtoMessage() {} +func (*DepositRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{6} +} +func (m *DepositRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DepositRequest.Unmarshal(m, b) +} +func (m *DepositRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DepositRequest.Marshal(b, m, deterministic) +} +func (dst *DepositRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DepositRequest.Merge(dst, src) +} +func (m *DepositRequest) XXX_Size() int { + return xxx_messageInfo_DepositRequest.Size(m) +} +func (m *DepositRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DepositRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DepositRequest proto.InternalMessageInfo + +func (m *DepositRequest) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +func (m *DepositRequest) GetAmountCents() int32 { + if m != nil { + return m.AmountCents + } + return 0 +} + +func (m *DepositRequest) GetSource() DepositRequest_Source { + if m != nil { + return m.Source + } + return DepositRequest_UNKNOWN +} + +func (m *DepositRequest) GetDesc() string { + if m != nil { + return m.Desc + } + return "" +} + +type BalanceResponse struct { + AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + BalanceCents int32 `protobuf:"varint,2,opt,name=balance_cents,json=balanceCents,proto3" json:"balance_cents,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BalanceResponse) Reset() { *m = BalanceResponse{} } +func (m *BalanceResponse) String() string { return proto.CompactTextString(m) } +func (*BalanceResponse) ProtoMessage() {} +func (*BalanceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{7} +} +func (m *BalanceResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BalanceResponse.Unmarshal(m, b) +} +func (m *BalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BalanceResponse.Marshal(b, m, deterministic) +} +func (dst *BalanceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_BalanceResponse.Merge(dst, src) +} +func (m *BalanceResponse) XXX_Size() int { + return xxx_messageInfo_BalanceResponse.Size(m) +} +func (m *BalanceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_BalanceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_BalanceResponse proto.InternalMessageInfo + +func (m *BalanceResponse) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +func (m *BalanceResponse) GetBalanceCents() int32 { + if m != nil { + return m.BalanceCents + } + return 0 +} + +type WithdrawRequest struct { + AccountNumber uint64 `protobuf:"varint,1,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + AmountCents int32 `protobuf:"varint,2,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"` + Desc string `protobuf:"bytes,3,opt,name=desc,proto3" json:"desc,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WithdrawRequest) Reset() { *m = WithdrawRequest{} } +func (m *WithdrawRequest) String() string { return proto.CompactTextString(m) } +func (*WithdrawRequest) ProtoMessage() {} +func (*WithdrawRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{8} +} +func (m *WithdrawRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawRequest.Unmarshal(m, b) +} +func (m *WithdrawRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawRequest.Marshal(b, m, deterministic) +} +func (dst *WithdrawRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawRequest.Merge(dst, src) +} +func (m *WithdrawRequest) XXX_Size() int { + return xxx_messageInfo_WithdrawRequest.Size(m) +} +func (m *WithdrawRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawRequest proto.InternalMessageInfo + +func (m *WithdrawRequest) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +func (m *WithdrawRequest) GetAmountCents() int32 { + if m != nil { + return m.AmountCents + } + return 0 +} + +func (m *WithdrawRequest) GetDesc() string { + if m != nil { + return m.Desc + } + return "" +} + +type TransferRequest struct { + // Types that are valid to be assigned to Source: + // *TransferRequest_SourceAccountNumber + // *TransferRequest_ExternalSource + Source isTransferRequest_Source `protobuf_oneof:"source"` + // Types that are valid to be assigned to Dest: + // *TransferRequest_DestAccountNumber + // *TransferRequest_ExternalDest + Dest isTransferRequest_Dest `protobuf_oneof:"dest"` + AmountCents int32 `protobuf:"varint,5,opt,name=amount_cents,json=amountCents,proto3" json:"amount_cents,omitempty"` + Desc string `protobuf:"bytes,6,opt,name=desc,proto3" json:"desc,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TransferRequest) Reset() { *m = TransferRequest{} } +func (m *TransferRequest) String() string { return proto.CompactTextString(m) } +func (*TransferRequest) ProtoMessage() {} +func (*TransferRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{9} +} +func (m *TransferRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TransferRequest.Unmarshal(m, b) +} +func (m *TransferRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TransferRequest.Marshal(b, m, deterministic) +} +func (dst *TransferRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferRequest.Merge(dst, src) +} +func (m *TransferRequest) XXX_Size() int { + return xxx_messageInfo_TransferRequest.Size(m) +} +func (m *TransferRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TransferRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferRequest proto.InternalMessageInfo + +type isTransferRequest_Source interface { + isTransferRequest_Source() +} +type isTransferRequest_Dest interface { + isTransferRequest_Dest() +} + +type TransferRequest_SourceAccountNumber struct { + SourceAccountNumber uint64 `protobuf:"varint,1,opt,name=source_account_number,json=sourceAccountNumber,proto3,oneof"` +} +type TransferRequest_ExternalSource struct { + ExternalSource *TransferRequest_ExternalAccount `protobuf:"bytes,2,opt,name=external_source,json=externalSource,proto3,oneof"` +} +type TransferRequest_DestAccountNumber struct { + DestAccountNumber uint64 `protobuf:"varint,3,opt,name=dest_account_number,json=destAccountNumber,proto3,oneof"` +} +type TransferRequest_ExternalDest struct { + ExternalDest *TransferRequest_ExternalAccount `protobuf:"bytes,4,opt,name=external_dest,json=externalDest,proto3,oneof"` +} + +func (*TransferRequest_SourceAccountNumber) isTransferRequest_Source() {} +func (*TransferRequest_ExternalSource) isTransferRequest_Source() {} +func (*TransferRequest_DestAccountNumber) isTransferRequest_Dest() {} +func (*TransferRequest_ExternalDest) isTransferRequest_Dest() {} + +func (m *TransferRequest) GetSource() isTransferRequest_Source { + if m != nil { + return m.Source + } + return nil +} +func (m *TransferRequest) GetDest() isTransferRequest_Dest { + if m != nil { + return m.Dest + } + return nil +} + +func (m *TransferRequest) GetSourceAccountNumber() uint64 { + if x, ok := m.GetSource().(*TransferRequest_SourceAccountNumber); ok { + return x.SourceAccountNumber + } + return 0 +} + +func (m *TransferRequest) GetExternalSource() *TransferRequest_ExternalAccount { + if x, ok := m.GetSource().(*TransferRequest_ExternalSource); ok { + return x.ExternalSource + } + return nil +} + +func (m *TransferRequest) GetDestAccountNumber() uint64 { + if x, ok := m.GetDest().(*TransferRequest_DestAccountNumber); ok { + return x.DestAccountNumber + } + return 0 +} + +func (m *TransferRequest) GetExternalDest() *TransferRequest_ExternalAccount { + if x, ok := m.GetDest().(*TransferRequest_ExternalDest); ok { + return x.ExternalDest + } + return nil +} + +func (m *TransferRequest) GetAmountCents() int32 { + if m != nil { + return m.AmountCents + } + return 0 +} + +func (m *TransferRequest) GetDesc() string { + if m != nil { + return m.Desc + } + return "" +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*TransferRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _TransferRequest_OneofMarshaler, _TransferRequest_OneofUnmarshaler, _TransferRequest_OneofSizer, []interface{}{ + (*TransferRequest_SourceAccountNumber)(nil), + (*TransferRequest_ExternalSource)(nil), + (*TransferRequest_DestAccountNumber)(nil), + (*TransferRequest_ExternalDest)(nil), + } +} + +func _TransferRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*TransferRequest) + // source + switch x := m.Source.(type) { + case *TransferRequest_SourceAccountNumber: + b.EncodeVarint(1<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.SourceAccountNumber)) + case *TransferRequest_ExternalSource: + b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.ExternalSource); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("TransferRequest.Source has unexpected type %T", x) + } + // dest + switch x := m.Dest.(type) { + case *TransferRequest_DestAccountNumber: + b.EncodeVarint(3<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.DestAccountNumber)) + case *TransferRequest_ExternalDest: + b.EncodeVarint(4<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.ExternalDest); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("TransferRequest.Dest has unexpected type %T", x) + } + return nil +} + +func _TransferRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*TransferRequest) + switch tag { + case 1: // source.source_account_number + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Source = &TransferRequest_SourceAccountNumber{x} + return true, err + case 2: // source.external_source + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(TransferRequest_ExternalAccount) + err := b.DecodeMessage(msg) + m.Source = &TransferRequest_ExternalSource{msg} + return true, err + case 3: // dest.dest_account_number + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Dest = &TransferRequest_DestAccountNumber{x} + return true, err + case 4: // dest.external_dest + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(TransferRequest_ExternalAccount) + err := b.DecodeMessage(msg) + m.Dest = &TransferRequest_ExternalDest{msg} + return true, err + default: + return false, nil + } +} + +func _TransferRequest_OneofSizer(msg proto.Message) (n int) { + m := msg.(*TransferRequest) + // source + switch x := m.Source.(type) { + case *TransferRequest_SourceAccountNumber: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(x.SourceAccountNumber)) + case *TransferRequest_ExternalSource: + s := proto.Size(x.ExternalSource) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + // dest + switch x := m.Dest.(type) { + case *TransferRequest_DestAccountNumber: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(x.DestAccountNumber)) + case *TransferRequest_ExternalDest: + s := proto.Size(x.ExternalDest) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type TransferRequest_ExternalAccount struct { + AchRoutingNumber uint64 `protobuf:"varint,1,opt,name=ach_routing_number,json=achRoutingNumber,proto3" json:"ach_routing_number,omitempty"` + AchAccountNumber uint64 `protobuf:"varint,2,opt,name=ach_account_number,json=achAccountNumber,proto3" json:"ach_account_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TransferRequest_ExternalAccount) Reset() { *m = TransferRequest_ExternalAccount{} } +func (m *TransferRequest_ExternalAccount) String() string { return proto.CompactTextString(m) } +func (*TransferRequest_ExternalAccount) ProtoMessage() {} +func (*TransferRequest_ExternalAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{9, 0} +} +func (m *TransferRequest_ExternalAccount) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TransferRequest_ExternalAccount.Unmarshal(m, b) +} +func (m *TransferRequest_ExternalAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TransferRequest_ExternalAccount.Marshal(b, m, deterministic) +} +func (dst *TransferRequest_ExternalAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferRequest_ExternalAccount.Merge(dst, src) +} +func (m *TransferRequest_ExternalAccount) XXX_Size() int { + return xxx_messageInfo_TransferRequest_ExternalAccount.Size(m) +} +func (m *TransferRequest_ExternalAccount) XXX_DiscardUnknown() { + xxx_messageInfo_TransferRequest_ExternalAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferRequest_ExternalAccount proto.InternalMessageInfo + +func (m *TransferRequest_ExternalAccount) GetAchRoutingNumber() uint64 { + if m != nil { + return m.AchRoutingNumber + } + return 0 +} + +func (m *TransferRequest_ExternalAccount) GetAchAccountNumber() uint64 { + if m != nil { + return m.AchAccountNumber + } + return 0 +} + +type TransferResponse struct { + SrcAccountNumber uint64 `protobuf:"varint,1,opt,name=src_account_number,json=srcAccountNumber,proto3" json:"src_account_number,omitempty"` + SrcBalanceCents int32 `protobuf:"varint,2,opt,name=src_balance_cents,json=srcBalanceCents,proto3" json:"src_balance_cents,omitempty"` + DestAccountNumber uint64 `protobuf:"varint,3,opt,name=dest_account_number,json=destAccountNumber,proto3" json:"dest_account_number,omitempty"` + DestBalanceCents int32 `protobuf:"varint,4,opt,name=dest_balance_cents,json=destBalanceCents,proto3" json:"dest_balance_cents,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TransferResponse) Reset() { *m = TransferResponse{} } +func (m *TransferResponse) String() string { return proto.CompactTextString(m) } +func (*TransferResponse) ProtoMessage() {} +func (*TransferResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bank_485eb966c3eae5d1, []int{10} +} +func (m *TransferResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TransferResponse.Unmarshal(m, b) +} +func (m *TransferResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TransferResponse.Marshal(b, m, deterministic) +} +func (dst *TransferResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferResponse.Merge(dst, src) +} +func (m *TransferResponse) XXX_Size() int { + return xxx_messageInfo_TransferResponse.Size(m) +} +func (m *TransferResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TransferResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferResponse proto.InternalMessageInfo + +func (m *TransferResponse) GetSrcAccountNumber() uint64 { + if m != nil { + return m.SrcAccountNumber + } + return 0 +} + +func (m *TransferResponse) GetSrcBalanceCents() int32 { + if m != nil { + return m.SrcBalanceCents + } + return 0 +} + +func (m *TransferResponse) GetDestAccountNumber() uint64 { + if m != nil { + return m.DestAccountNumber + } + return 0 +} + +func (m *TransferResponse) GetDestBalanceCents() int32 { + if m != nil { + return m.DestBalanceCents + } + return 0 +} + +func init() { + proto.RegisterType((*OpenAccountRequest)(nil), "OpenAccountRequest") + proto.RegisterType((*CloseAccountRequest)(nil), "CloseAccountRequest") + proto.RegisterType((*GetAccountsResponse)(nil), "GetAccountsResponse") + proto.RegisterType((*Account)(nil), "Account") + proto.RegisterType((*GetTransactionsRequest)(nil), "GetTransactionsRequest") + proto.RegisterType((*Transaction)(nil), "Transaction") + proto.RegisterType((*DepositRequest)(nil), "DepositRequest") + proto.RegisterType((*BalanceResponse)(nil), "BalanceResponse") + proto.RegisterType((*WithdrawRequest)(nil), "WithdrawRequest") + proto.RegisterType((*TransferRequest)(nil), "TransferRequest") + proto.RegisterType((*TransferRequest_ExternalAccount)(nil), "TransferRequest.ExternalAccount") + proto.RegisterType((*TransferResponse)(nil), "TransferResponse") + proto.RegisterEnum("Account_Type", Account_Type_name, Account_Type_value) + proto.RegisterEnum("DepositRequest_Source", DepositRequest_Source_name, DepositRequest_Source_value) +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// BankClient is the client API for Bank service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type BankClient interface { + // OpenAccount creates an account with the type and given initial deposit + // as its balance. + OpenAccount(ctx context.Context, in *OpenAccountRequest, opts ...grpc.CallOption) (*Account, error) + // CloseAccount closes the indicated account. An account can only be + // closed if its balance is zero. + CloseAccount(ctx context.Context, in *CloseAccountRequest, opts ...grpc.CallOption) (*empty.Empty, error) + // GetAccounts lists all accounts for the current customer. + GetAccounts(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GetAccountsResponse, error) + // GetTransactions streams all transactions that match the given criteria. + // If the given start date is not specified, transactions since beginning + // of time are included. Similarly, if the given end date is not specified, + // transactions all the way to the presnet are included. + GetTransactions(ctx context.Context, in *GetTransactionsRequest, opts ...grpc.CallOption) (Bank_GetTransactionsClient, error) + // Deposit increases the balance of an account by depositing funds into it. + Deposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*BalanceResponse, error) + // Withdraw decreases the balance of an account by withdrawing funds from it. + Withdraw(ctx context.Context, in *WithdrawRequest, opts ...grpc.CallOption) (*BalanceResponse, error) + // Transfer moves money from one account to another. The source and destination + // accounts can be with this bank (e.g. "local" account numbers) or can be + // external accounts, identified by their ACH routing and account numbers. + Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error) +} + +type bankClient struct { + cc *grpc.ClientConn +} + +func NewBankClient(cc *grpc.ClientConn) BankClient { + return &bankClient{cc} +} + +func (c *bankClient) OpenAccount(ctx context.Context, in *OpenAccountRequest, opts ...grpc.CallOption) (*Account, error) { + out := new(Account) + err := c.cc.Invoke(ctx, "/Bank/OpenAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bankClient) CloseAccount(ctx context.Context, in *CloseAccountRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + out := new(empty.Empty) + err := c.cc.Invoke(ctx, "/Bank/CloseAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bankClient) GetAccounts(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GetAccountsResponse, error) { + out := new(GetAccountsResponse) + err := c.cc.Invoke(ctx, "/Bank/GetAccounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bankClient) GetTransactions(ctx context.Context, in *GetTransactionsRequest, opts ...grpc.CallOption) (Bank_GetTransactionsClient, error) { + stream, err := c.cc.NewStream(ctx, &_Bank_serviceDesc.Streams[0], "/Bank/GetTransactions", opts...) + if err != nil { + return nil, err + } + x := &bankGetTransactionsClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Bank_GetTransactionsClient interface { + Recv() (*Transaction, error) + grpc.ClientStream +} + +type bankGetTransactionsClient struct { + grpc.ClientStream +} + +func (x *bankGetTransactionsClient) Recv() (*Transaction, error) { + m := new(Transaction) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *bankClient) Deposit(ctx context.Context, in *DepositRequest, opts ...grpc.CallOption) (*BalanceResponse, error) { + out := new(BalanceResponse) + err := c.cc.Invoke(ctx, "/Bank/Deposit", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bankClient) Withdraw(ctx context.Context, in *WithdrawRequest, opts ...grpc.CallOption) (*BalanceResponse, error) { + out := new(BalanceResponse) + err := c.cc.Invoke(ctx, "/Bank/Withdraw", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *bankClient) Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*TransferResponse, error) { + out := new(TransferResponse) + err := c.cc.Invoke(ctx, "/Bank/Transfer", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BankServer is the server API for Bank service. +type BankServer interface { + // OpenAccount creates an account with the type and given initial deposit + // as its balance. + OpenAccount(context.Context, *OpenAccountRequest) (*Account, error) + // CloseAccount closes the indicated account. An account can only be + // closed if its balance is zero. + CloseAccount(context.Context, *CloseAccountRequest) (*empty.Empty, error) + // GetAccounts lists all accounts for the current customer. + GetAccounts(context.Context, *empty.Empty) (*GetAccountsResponse, error) + // GetTransactions streams all transactions that match the given criteria. + // If the given start date is not specified, transactions since beginning + // of time are included. Similarly, if the given end date is not specified, + // transactions all the way to the presnet are included. + GetTransactions(*GetTransactionsRequest, Bank_GetTransactionsServer) error + // Deposit increases the balance of an account by depositing funds into it. + Deposit(context.Context, *DepositRequest) (*BalanceResponse, error) + // Withdraw decreases the balance of an account by withdrawing funds from it. + Withdraw(context.Context, *WithdrawRequest) (*BalanceResponse, error) + // Transfer moves money from one account to another. The source and destination + // accounts can be with this bank (e.g. "local" account numbers) or can be + // external accounts, identified by their ACH routing and account numbers. + Transfer(context.Context, *TransferRequest) (*TransferResponse, error) +} + +func RegisterBankServer(s *grpc.Server, srv BankServer) { + s.RegisterService(&_Bank_serviceDesc, srv) +} + +func _Bank_OpenAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OpenAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BankServer).OpenAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/Bank/OpenAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BankServer).OpenAccount(ctx, req.(*OpenAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Bank_CloseAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CloseAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BankServer).CloseAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/Bank/CloseAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BankServer).CloseAccount(ctx, req.(*CloseAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Bank_GetAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(empty.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BankServer).GetAccounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/Bank/GetAccounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BankServer).GetAccounts(ctx, req.(*empty.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Bank_GetTransactions_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetTransactionsRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(BankServer).GetTransactions(m, &bankGetTransactionsServer{stream}) +} + +type Bank_GetTransactionsServer interface { + Send(*Transaction) error + grpc.ServerStream +} + +type bankGetTransactionsServer struct { + grpc.ServerStream +} + +func (x *bankGetTransactionsServer) Send(m *Transaction) error { + return x.ServerStream.SendMsg(m) +} + +func _Bank_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DepositRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BankServer).Deposit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/Bank/Deposit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BankServer).Deposit(ctx, req.(*DepositRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Bank_Withdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WithdrawRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BankServer).Withdraw(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/Bank/Withdraw", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BankServer).Withdraw(ctx, req.(*WithdrawRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Bank_Transfer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransferRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BankServer).Transfer(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/Bank/Transfer", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BankServer).Transfer(ctx, req.(*TransferRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Bank_serviceDesc = grpc.ServiceDesc{ + ServiceName: "Bank", + HandlerType: (*BankServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "OpenAccount", + Handler: _Bank_OpenAccount_Handler, + }, + { + MethodName: "CloseAccount", + Handler: _Bank_CloseAccount_Handler, + }, + { + MethodName: "GetAccounts", + Handler: _Bank_GetAccounts_Handler, + }, + { + MethodName: "Deposit", + Handler: _Bank_Deposit_Handler, + }, + { + MethodName: "Withdraw", + Handler: _Bank_Withdraw_Handler, + }, + { + MethodName: "Transfer", + Handler: _Bank_Transfer_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetTransactions", + Handler: _Bank_GetTransactions_Handler, + ServerStreams: true, + }, + }, + Metadata: "bank.proto", +} + +func init() { proto.RegisterFile("bank.proto", fileDescriptor_bank_485eb966c3eae5d1) } + +var fileDescriptor_bank_485eb966c3eae5d1 = []byte{ + // 913 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0xe2, 0x46, + 0x18, 0x8d, 0xb1, 0x21, 0xe4, 0x83, 0xc0, 0x64, 0xd8, 0x4d, 0x23, 0x57, 0x55, 0xb3, 0x6e, 0x2b, + 0x45, 0x15, 0x9a, 0x44, 0xb4, 0x57, 0xdd, 0xed, 0x05, 0x10, 0x37, 0xa0, 0xec, 0x82, 0x3a, 0x61, + 0x1b, 0xb5, 0x52, 0x65, 0x19, 0x33, 0x9b, 0x58, 0x01, 0x9b, 0x78, 0x06, 0xb5, 0x79, 0x9a, 0xbe, + 0x47, 0x5f, 0xa0, 0x52, 0xdf, 0xa1, 0x97, 0x7d, 0x89, 0x5e, 0x55, 0x1e, 0x8f, 0x59, 0xe3, 0x78, + 0x57, 0xac, 0xb4, 0x77, 0x66, 0xce, 0x99, 0x39, 0xdf, 0xcf, 0x99, 0xf9, 0x00, 0x98, 0xba, 0xc1, + 0x1d, 0x59, 0x46, 0xa1, 0x08, 0xcd, 0x4f, 0x6f, 0xc2, 0xf0, 0x66, 0xce, 0x4e, 0xe5, 0xaf, 0xe9, + 0xea, 0xcd, 0x29, 0x5b, 0x2c, 0xc5, 0x83, 0x02, 0x3f, 0xcf, 0x83, 0xc2, 0x5f, 0x30, 0x2e, 0xdc, + 0xc5, 0x32, 0x21, 0x58, 0x77, 0x80, 0xc7, 0x4b, 0x16, 0x74, 0x3d, 0x2f, 0x5c, 0x05, 0x82, 0xb2, + 0xfb, 0x15, 0xe3, 0x02, 0x77, 0xe0, 0xa9, 0x1f, 0xf8, 0xc2, 0x77, 0xe7, 0xce, 0x8c, 0x2d, 0x43, + 0xee, 0x0b, 0xc7, 0x63, 0x81, 0xe0, 0x47, 0xda, 0xb1, 0x76, 0x52, 0xa6, 0x2d, 0x05, 0x9e, 0x27, + 0x58, 0x3f, 0x86, 0xf0, 0x33, 0x30, 0xc4, 0xc3, 0x92, 0x1d, 0x95, 0x8e, 0xb5, 0x93, 0x46, 0x67, + 0x9f, 0xa8, 0x23, 0xc9, 0xe4, 0x61, 0xc9, 0xa8, 0x84, 0xac, 0x17, 0xd0, 0xea, 0xcf, 0x43, 0xce, + 0x72, 0x6a, 0x5f, 0x41, 0xc3, 0x4d, 0x56, 0x9c, 0x60, 0xb5, 0x98, 0xb2, 0x48, 0xca, 0x18, 0x74, + 0x5f, 0xad, 0x8e, 0xe4, 0xa2, 0xf5, 0x1c, 0x5a, 0x17, 0x4c, 0xa8, 0xbd, 0x9c, 0x32, 0xbe, 0x0c, + 0x03, 0xce, 0xf0, 0x97, 0x50, 0x55, 0xbc, 0x38, 0x3c, 0xfd, 0xa4, 0xd6, 0xa9, 0xa6, 0xda, 0x74, + 0x8d, 0x58, 0xff, 0x6a, 0xb0, 0xab, 0x56, 0xb7, 0xd4, 0xdb, 0x22, 0x21, 0xfc, 0x05, 0xec, 0x4f, + 0xdd, 0xb9, 0x1b, 0x78, 0x4c, 0xd5, 0x47, 0x97, 0xf5, 0xa9, 0xab, 0x45, 0x59, 0x18, 0xeb, 0x0e, + 0x8c, 0x78, 0x0b, 0xae, 0xc1, 0xee, 0xeb, 0xd1, 0xe5, 0x68, 0x7c, 0x3d, 0x42, 0x3b, 0xb8, 0x0e, + 0xd5, 0xfe, 0xc0, 0xee, 0x5f, 0x0e, 0x47, 0x17, 0x48, 0xc3, 0x00, 0x95, 0xab, 0xee, 0x4f, 0xf1, + 0x77, 0x09, 0x23, 0xa8, 0xbf, 0x1a, 0x8f, 0xec, 0x9f, 0x9d, 0x57, 0x5d, 0x7a, 0x69, 0x4f, 0x90, + 0x8e, 0x31, 0x34, 0x5e, 0x0e, 0x47, 0xb6, 0x33, 0xfe, 0xc1, 0xe9, 0x53, 0xfb, 0x7c, 0x38, 0x41, + 0x06, 0xae, 0x82, 0xf1, 0x72, 0xdc, 0x1d, 0xa1, 0x72, 0x7c, 0x92, 0xfd, 0xe3, 0xeb, 0xe1, 0x64, + 0x68, 0x5f, 0xa1, 0x8a, 0xf5, 0x87, 0x06, 0x87, 0x17, 0x4c, 0x4c, 0x22, 0x37, 0xe0, 0xae, 0x27, + 0xfc, 0x30, 0xe0, 0x1f, 0x56, 0x66, 0x7c, 0x06, 0x65, 0x2e, 0xdc, 0x48, 0xc8, 0xbc, 0x6b, 0x1d, + 0x93, 0x24, 0x16, 0x22, 0xa9, 0x85, 0xc8, 0x24, 0xb5, 0x10, 0x4d, 0x88, 0xb8, 0x0d, 0x3a, 0x0b, + 0x66, 0x32, 0xf7, 0xf7, 0xf3, 0x63, 0x9a, 0xf5, 0xa7, 0x06, 0xb5, 0x4c, 0x78, 0xdb, 0x86, 0xf5, + 0x19, 0x00, 0x67, 0xf7, 0x29, 0xa5, 0x24, 0x29, 0x7b, 0x9c, 0xdd, 0x2b, 0x98, 0x80, 0x31, 0x73, + 0x05, 0xdb, 0x22, 0x08, 0xc9, 0xc3, 0xcf, 0xa0, 0xee, 0x2e, 0xa4, 0x68, 0xd2, 0x38, 0x43, 0x36, + 0xae, 0x96, 0xac, 0x25, 0x86, 0xc6, 0x60, 0xcc, 0x18, 0xf7, 0x8e, 0xca, 0xc7, 0xda, 0xc9, 0x1e, + 0x95, 0xdf, 0xd6, 0x3f, 0x1a, 0x34, 0x94, 0xeb, 0x3f, 0xb0, 0xac, 0x79, 0xc1, 0xd2, 0x63, 0x41, + 0x02, 0x15, 0x1e, 0xae, 0x22, 0x2f, 0xc9, 0xa2, 0xd1, 0x39, 0x24, 0x9b, 0x52, 0xe4, 0x4a, 0xa2, + 0x54, 0xb1, 0xd6, 0x01, 0x1a, 0x99, 0x00, 0xbf, 0x87, 0x4a, 0xc2, 0xda, 0xb4, 0x5b, 0x15, 0x8c, + 0x7e, 0xf7, 0x6a, 0x80, 0x34, 0xbc, 0x07, 0x65, 0x69, 0x3c, 0x54, 0xc2, 0xbb, 0xa0, 0x77, 0xfb, + 0x03, 0xa4, 0xc7, 0xe8, 0xf5, 0x90, 0xda, 0xc8, 0xb0, 0x7e, 0x85, 0x66, 0x2f, 0xf1, 0xee, 0xfa, + 0x7e, 0x6d, 0x99, 0xdf, 0xa3, 0xab, 0x50, 0x2a, 0xb8, 0x0a, 0x21, 0x34, 0xaf, 0x7d, 0x71, 0x3b, + 0x8b, 0xdc, 0xdf, 0x3e, 0x7e, 0xf9, 0xd2, 0x72, 0xe8, 0x99, 0x72, 0xfc, 0xa5, 0x43, 0x53, 0x9a, + 0xed, 0x0d, 0x8b, 0x52, 0xc5, 0x6f, 0xe1, 0x69, 0x52, 0x40, 0xa7, 0x48, 0x78, 0xb0, 0x43, 0x5b, + 0x09, 0xdc, 0xdd, 0x08, 0xe0, 0x12, 0x9a, 0xec, 0x77, 0xc1, 0xa2, 0xc0, 0x9d, 0x3b, 0xaa, 0x4b, + 0xc9, 0x05, 0x39, 0x26, 0x39, 0x01, 0x62, 0x2b, 0x9e, 0x3a, 0x60, 0xb0, 0x43, 0x1b, 0xe9, 0x56, + 0xd5, 0x9b, 0x33, 0x68, 0xcd, 0x18, 0x17, 0xf9, 0x00, 0x74, 0x19, 0x80, 0x46, 0x0f, 0x62, 0x70, + 0x53, 0xfe, 0x02, 0xf6, 0xd7, 0xf2, 0x31, 0x2a, 0x9b, 0xbe, 0x8d, 0xb8, 0x46, 0xeb, 0xe9, 0xc6, + 0xf3, 0x38, 0xfb, 0x7c, 0x21, 0xcb, 0xef, 0x2e, 0x64, 0xe5, 0x6d, 0x21, 0xcd, 0x05, 0x34, 0x73, + 0x27, 0xe3, 0x36, 0x60, 0xd7, 0xbb, 0x75, 0xa2, 0x70, 0x25, 0xfc, 0xe0, 0x66, 0xb3, 0x7b, 0xc8, + 0xf5, 0x6e, 0x69, 0x02, 0xa8, 0x04, 0x14, 0x3b, 0x97, 0x71, 0x69, 0xcd, 0xde, 0x48, 0xb7, 0x57, + 0x4d, 0xaf, 0x42, 0xaf, 0x22, 0x83, 0x11, 0xd6, 0xdf, 0x1a, 0xa0, 0xb7, 0xb9, 0x2a, 0x6f, 0xb6, + 0x01, 0xf3, 0xc8, 0x2b, 0xec, 0x23, 0x45, 0x3c, 0xf2, 0x36, 0x6b, 0xf8, 0x35, 0x1c, 0xc4, 0xec, + 0x22, 0x9b, 0x36, 0x79, 0xe4, 0xf5, 0x32, 0x4e, 0xc5, 0xe4, 0x3d, 0x1d, 0x2a, 0xea, 0x4f, 0x1b, + 0xb0, 0xe4, 0x6f, 0x1e, 0x9e, 0xbc, 0x2a, 0x28, 0x46, 0xb2, 0xa7, 0x77, 0xfe, 0x2b, 0x81, 0xd1, + 0x73, 0x83, 0x3b, 0xdc, 0x86, 0x5a, 0x66, 0xfc, 0xe2, 0x16, 0x79, 0x3c, 0x8c, 0xcd, 0xf5, 0x38, + 0xc3, 0x2f, 0xa0, 0x9e, 0x9d, 0x9f, 0xf8, 0x09, 0x29, 0x18, 0xa7, 0xe6, 0xe1, 0xa3, 0xc7, 0xcf, + 0x8e, 0xff, 0x11, 0xe0, 0xe7, 0x50, 0xcb, 0xcc, 0x4f, 0xfc, 0x0e, 0x9a, 0xf9, 0x84, 0x14, 0x4d, + 0xd9, 0xef, 0xa0, 0x99, 0x1b, 0x2b, 0xf8, 0x13, 0x52, 0x3c, 0x68, 0xcc, 0x3a, 0xc9, 0xac, 0x9e, + 0x69, 0xb8, 0x0d, 0xbb, 0xea, 0x21, 0xc3, 0xcd, 0xdc, 0x93, 0x66, 0x22, 0x92, 0x7f, 0x6f, 0x08, + 0x54, 0xd3, 0x37, 0x02, 0x23, 0x92, 0x7b, 0x2e, 0x0a, 0xf8, 0xa7, 0x50, 0x4d, 0x7d, 0x81, 0x51, + 0xfe, 0x3a, 0x98, 0x07, 0x24, 0x6f, 0x9a, 0x5e, 0xe5, 0x17, 0x63, 0xe1, 0xfa, 0xc1, 0xb4, 0x22, + 0x13, 0xff, 0xe6, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xac, 0x47, 0x1e, 0xdd, 0x4d, 0x09, 0x00, + 0x00, +} diff --git a/testing/cmd/bankdemo/bank.proto b/testing/cmd/bankdemo/bank.proto new file mode 100644 index 0000000..8c7b4b9 --- /dev/null +++ b/testing/cmd/bankdemo/bank.proto @@ -0,0 +1,125 @@ +syntax = "proto3"; + +option go_package = "main"; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +// Bank provides operations for interacting with bank accounts. All +// operations operate for the authenticated user (identified via an +// "authorization" request header, where the type is "token" and the +// credential is the customer's ID). +service Bank { + // OpenAccount creates an account with the type and given initial deposit + // as its balance. + rpc OpenAccount(OpenAccountRequest) returns (Account); + // CloseAccount closes the indicated account. An account can only be + // closed if its balance is zero. + rpc CloseAccount(CloseAccountRequest) returns (google.protobuf.Empty); + // GetAccounts lists all accounts for the current customer. + rpc GetAccounts(google.protobuf.Empty) returns (GetAccountsResponse); + // GetTransactions streams all transactions that match the given criteria. + // If the given start date is not specified, transactions since beginning + // of time are included. Similarly, if the given end date is not specified, + // transactions all the way to the presnet are included. + rpc GetTransactions(GetTransactionsRequest) returns (stream Transaction); + // Deposit increases the balance of an account by depositing funds into it. + rpc Deposit(DepositRequest) returns (BalanceResponse); + // Withdraw decreases the balance of an account by withdrawing funds from it. + rpc Withdraw(WithdrawRequest) returns (BalanceResponse); + // Transfer moves money from one account to another. The source and destination + // accounts can be with this bank (e.g. "local" account numbers) or can be + // external accounts, identified by their ACH routing and account numbers. + rpc Transfer(TransferRequest) returns (TransferResponse); +} + +message OpenAccountRequest { + int32 initial_deposit_cents = 1; + Account.Type type = 2; +} + +message CloseAccountRequest { + uint64 account_number = 1; +} + +message GetAccountsResponse { + repeated Account accounts = 1; +} + +message Account { + uint64 account_number = 1; + enum Type { + UNKNOWN = 0; + CHECKING = 1; + SAVING = 2; + MONEY_MARKET = 3; + LINE_OF_CREDIT = 4; + LOAN = 5; + EQUITIES = 6; + } + Type type = 2; + int32 balance_cents = 3; +} + +message GetTransactionsRequest { + uint64 account_number = 1; + google.protobuf.Timestamp start = 2; + google.protobuf.Timestamp end = 3; +} + +message Transaction { + uint64 account_number = 1; + uint64 seq_number = 2; + google.protobuf.Timestamp date = 3; + int32 amount_cents = 4; + string desc = 5; +} + +message DepositRequest { + uint64 account_number = 1; + int32 amount_cents = 2; + enum Source { + UNKNOWN = 0; + CASH = 1; + CHECK = 2; + ACH = 3; + WIRE = 4; + } + Source source = 3; + string desc = 4; +} + +message BalanceResponse { + uint64 account_number = 1; + int32 balance_cents = 2; +} + +message WithdrawRequest { + uint64 account_number = 1; + int32 amount_cents = 2; + string desc = 3; +} + +message TransferRequest { + message ExternalAccount { + uint64 ach_routing_number = 1; + uint64 ach_account_number = 2; + } + oneof source { + uint64 source_account_number = 1; + ExternalAccount external_source = 2; + } + oneof dest { + uint64 dest_account_number = 3; + ExternalAccount external_dest = 4; + } + int32 amount_cents = 5; + string desc = 6; +} + +message TransferResponse { + uint64 src_account_number = 1; + int32 src_balance_cents = 2; + uint64 dest_account_number = 3; + int32 dest_balance_cents = 4; +} diff --git a/testing/cmd/bankdemo/chat.go b/testing/cmd/bankdemo/chat.go new file mode 100644 index 0000000..a5206d7 --- /dev/null +++ b/testing/cmd/bankdemo/chat.go @@ -0,0 +1,465 @@ +package main + +import ( + "fmt" + "io" + "sync" + + "github.com/golang/protobuf/ptypes" + "golang.org/x/net/context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// chatServer implements the Support gRPC service, for providing +// a capability to connect customers and support agents in real-time +// chat. +type chatServer struct { + chatsBySession map[string]*session + chatsAwaitingAgent []string + lastSession int32 + mu sync.RWMutex +} + +type session struct { + Session + active bool + cust *listener + agents map[string]*listener + mu sync.RWMutex +} + +type listener struct { + ch chan<- *ChatEntry + ctx context.Context +} + +func (l *listener) send(e *ChatEntry) { + select { + case l.ch <- e: + case <-l.ctx.Done(): + } +} + +func (s *session) copySession() *Session { + s.mu.RLock() + defer s.mu.RUnlock() + return &Session{ + SessionId: s.SessionId, + CustomerName: s.Session.CustomerName, + History: s.Session.History, + } +} + +func (s *chatServer) ChatCustomer(stream Support_ChatCustomerServer) error { + ctx, cancel := context.WithCancel(stream.Context()) + defer cancel() + + cust := getCustomer(ctx) + if cust == "" { + return status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + var sess *session + var ch chan *ChatEntry + var chCancel context.CancelFunc + cleanup := func() { + if sess != nil { + sess.mu.Lock() + sess.cust = nil + sess.mu.Unlock() + chCancel() + close(ch) + go func() { + // drain channel to prevent deadlock + for range ch { + } + }() + } + } + defer cleanup() + for { + req, err := stream.Recv() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + switch req := req.Req.(type) { + case *ChatCustomerRequest_Init: + if sess != nil { + return status.Errorf(codes.FailedPrecondition, "already called init, currently in chat session %q", sess.SessionId) + } + sessionID := req.Init.ResumeSessionId + if sessionID == "" { + sess, ch, chCancel = s.newSession(ctx, cust) + } else if sess, ch, chCancel = s.resumeSession(ctx, cust, sessionID); sess == nil { + return status.Errorf(codes.FailedPrecondition, "cannot resume session %q; it is not an open session", sessionID) + } + err := stream.Send(&ChatCustomerResponse{ + Resp: &ChatCustomerResponse_Session{ + Session: sess.copySession(), + }, + }) + + if err != nil { + return err + } + // monitor the returned channel, sending incoming agent messages down the pipe + go func() { + for { + select { + case entry, ok := <-ch: + if !ok { + return + } + if e, ok := entry.Entry.(*ChatEntry_AgentMsg); ok { + stream.Send(&ChatCustomerResponse{ + Resp: &ChatCustomerResponse_Msg{ + Msg: e.AgentMsg, + }, + }) + } + case <-ctx.Done(): + return + } + } + }() + + case *ChatCustomerRequest_Msg: + if sess == nil { + return status.Errorf(codes.FailedPrecondition, "never called init, no chat session for message") + } + + entry := &ChatEntry{ + Date: ptypes.TimestampNow(), + Entry: &ChatEntry_CustomerMsg{ + CustomerMsg: req.Msg, + }, + } + func() { + sess.mu.Lock() + sess.Session.History = append(sess.Session.History, entry) + sess.mu.Unlock() + + sess.mu.RLock() + defer sess.mu.RUnlock() + for _, l := range sess.agents { + l.send(entry) + } + }() + + case *ChatCustomerRequest_HangUp: + if sess == nil { + return status.Errorf(codes.FailedPrecondition, "never called init, no chat session to hang up") + } + s.closeSession(sess) + cleanup() + sess = nil + + default: + return status.Error(codes.InvalidArgument, "unknown request type") + } + } +} + +func (s *chatServer) ChatAgent(stream Support_ChatAgentServer) error { + ctx, cancel := context.WithCancel(stream.Context()) + defer cancel() + + agent := getAgent(ctx) + if agent == "" { + return status.Error(codes.Unauthenticated, codes.Unauthenticated.String()) + } + + var sess *session + var ch chan *ChatEntry + var chCancel context.CancelFunc + cleanup := func() { + if sess != nil { + sess.mu.Lock() + delete(sess.agents, agent) + if len(sess.agents) == 0 { + s.mu.Lock() + s.chatsAwaitingAgent = append(s.chatsAwaitingAgent, sess.SessionId) + s.mu.Unlock() + } + sess.mu.Unlock() + chCancel() + close(ch) + go func() { + // drain channel to prevent deadlock + for range ch { + } + }() + } + } + defer cleanup() + + checkSession := func() { + // see if session was concurrently closed + if sess != nil { + sess.mu.RLock() + active := sess.active + sess.mu.RUnlock() + if !active { + cleanup() + sess = nil + } + } + } + + for { + req, err := stream.Recv() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + checkSession() + + switch req := req.Req.(type) { + case *ChatAgentRequest_Accept: + if sess != nil { + return status.Errorf(codes.FailedPrecondition, "already called accept, currently in chat session %q", sess.SessionId) + } + sess, ch, chCancel = s.acceptSession(ctx, agent, req.Accept.SessionId) + if sess == nil { + return status.Errorf(codes.FailedPrecondition, "no session to accept") + } + err := stream.Send(&ChatAgentResponse{ + Resp: &ChatAgentResponse_AcceptedSession{ + AcceptedSession: sess.copySession(), + }, + }) + if err != nil { + return err + } + // monitor the returned channel, sending incoming agent messages down the pipe + go func() { + for { + select { + case entry, ok := <-ch: + if !ok { + return + } + + if entry == nil { + stream.Send(&ChatAgentResponse{ + Resp: &ChatAgentResponse_SessionEnded{ + SessionEnded: Void_VOID, + }, + }) + continue + } + + if agentMsg, ok := entry.Entry.(*ChatEntry_AgentMsg); ok { + if agentMsg.AgentMsg.AgentName == agent { + continue + } + } + stream.Send(&ChatAgentResponse{ + Resp: &ChatAgentResponse_Msg{ + Msg: entry, + }, + }) + case <-ctx.Done(): + return + } + } + }() + + case *ChatAgentRequest_Msg: + if sess == nil { + return status.Errorf(codes.FailedPrecondition, "never called accept, no chat session for message") + } + + entry := &ChatEntry{ + Date: ptypes.TimestampNow(), + Entry: &ChatEntry_AgentMsg{ + AgentMsg: &AgentMessage{ + AgentName: agent, + Msg: req.Msg, + }, + }, + } + active := true + func() { + sess.mu.Lock() + active = sess.active + if active { + sess.Session.History = append(sess.Session.History, entry) + } + sess.mu.Unlock() + + if !active { + return + } + + sess.mu.RLock() + defer sess.mu.RUnlock() + if sess.cust != nil { + sess.cust.send(entry) + } + for otherAgent, l := range sess.agents { + if otherAgent == agent { + continue + } + l.send(entry) + } + }() + if !active { + return status.Errorf(codes.FailedPrecondition, "customer hung up on chat session %s", sess.SessionId) + } + + case *ChatAgentRequest_LeaveSession: + if sess == nil { + return status.Errorf(codes.FailedPrecondition, "never called init, no chat session to hang up") + } + s.closeSession(sess) + cleanup() + sess = nil + + default: + return status.Error(codes.InvalidArgument, "unknown request type") + } + } +} + +func (s *chatServer) newSession(ctx context.Context, cust string) (*session, chan *ChatEntry, context.CancelFunc) { + s.mu.Lock() + defer s.mu.Unlock() + s.lastSession++ + id := fmt.Sprintf("%06d", s.lastSession) + s.chatsAwaitingAgent = append(s.chatsAwaitingAgent, id) + + ch := make(chan *ChatEntry, 1) + ctx, cancel := context.WithCancel(ctx) + l := &listener{ + ch: ch, + ctx: ctx, + } + sess := session{ + active: true, + Session: Session{ + SessionId: id, + CustomerName: cust, + }, + cust: l, + } + s.chatsBySession[id] = &sess + + return &sess, ch, cancel +} + +func (s *chatServer) resumeSession(ctx context.Context, cust, sessionID string) (*session, chan *ChatEntry, context.CancelFunc) { + s.mu.Lock() + defer s.mu.Unlock() + sess := s.chatsBySession[sessionID] + if sess.CustomerName != cust { + // customer cannot join chat that they did not start + return nil, nil, nil + } + if !sess.active { + // chat has been closed + return nil, nil, nil + } + if sess.cust != nil { + // customer is active in the chat in another stream! + return nil, nil, nil + } + + ch := make(chan *ChatEntry, 1) + ctx, cancel := context.WithCancel(ctx) + l := &listener{ + ch: ch, + ctx: ctx, + } + sess.cust = l + return sess, ch, cancel +} + +func (s *chatServer) closeSession(sess *session) { + active := true + func() { + sess.mu.Lock() + active = sess.active + sess.active = false + sess.mu.Unlock() + + if !active { + // already closed + return + } + + sess.mu.RLock() + defer sess.mu.RUnlock() + for _, l := range sess.agents { + l.send(nil) + } + }() + + if !active { + // already closed + return + } + + s.mu.Lock() + defer s.mu.Unlock() + delete(s.chatsBySession, sess.SessionId) + for i, id := range s.chatsAwaitingAgent { + if id == sess.SessionId { + s.chatsAwaitingAgent = append(s.chatsAwaitingAgent[:i], s.chatsAwaitingAgent[i+1:]...) + break + } + } +} + +func (s *chatServer) acceptSession(ctx context.Context, agent, sessionID string) (*session, chan *ChatEntry, context.CancelFunc) { + var sess *session + func() { + s.mu.Lock() + defer s.mu.Unlock() + + if len(s.chatsAwaitingAgent) == 0 { + return + } + if sessionID == "" { + sessionID = s.chatsAwaitingAgent[0] + s.chatsAwaitingAgent = s.chatsAwaitingAgent[1:] + } else { + found := false + for i, id := range s.chatsAwaitingAgent { + if id == sessionID { + found = true + s.chatsAwaitingAgent = append(s.chatsAwaitingAgent[:i], s.chatsAwaitingAgent[i+1:]...) + break + } + } + if !found { + return + } + } + sess = s.chatsBySession[sessionID] + }() + if sess == nil { + return nil, nil, nil + } + ch := make(chan *ChatEntry, 1) + ctx, cancel := context.WithCancel(ctx) + l := &listener{ + ch: ch, + ctx: ctx, + } + sess.mu.Lock() + if sess.agents == nil { + sess.agents = map[string]*listener{} + } + sess.agents[agent] = l + sess.mu.Unlock() + return sess, ch, cancel +} diff --git a/testing/cmd/bankdemo/db.go b/testing/cmd/bankdemo/db.go new file mode 100644 index 0000000..cae2b53 --- /dev/null +++ b/testing/cmd/bankdemo/db.go @@ -0,0 +1,189 @@ +package main + +import ( + "bytes" + "fmt" + "sync" + + "github.com/golang/protobuf/jsonpb" + "github.com/golang/protobuf/ptypes" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// In-memory database that is periodically saved to a JSON file. + +type accounts struct { + AccountNumbersByCustomer map[string][]uint64 + AccountsByNumber map[uint64]*account + AccountNumbers []uint64 + Customers []string + LastAccountNum uint64 + mu sync.RWMutex +} + +func (a *accounts) openAccount(customer string, accountType Account_Type, initialBalanceCents int32) *Account { + a.mu.Lock() + defer a.mu.Unlock() + + accountNums, ok := a.AccountNumbersByCustomer[customer] + if !ok { + // no accounts for this customer? it's a new customer + a.Customers = append(a.Customers, customer) + } + num := a.LastAccountNum + 1 + a.LastAccountNum = num + a.AccountNumbers = append(a.AccountNumbers, num) + accountNums = append(accountNums, num) + a.AccountNumbersByCustomer[customer] = accountNums + var acct account + acct.AccountNumber = num + acct.BalanceCents = initialBalanceCents + acct.Transactions = append(acct.Transactions, &Transaction{ + AccountNumber: num, + SeqNumber: 1, + Date: ptypes.TimestampNow(), + AmountCents: initialBalanceCents, + Desc: "initial deposit", + }) + a.AccountsByNumber[num] = &acct + return &acct.Account +} + +func (a *accounts) closeAccount(customer string, accountNumber uint64) error { + a.mu.Lock() + defer a.mu.Unlock() + + acctNums := a.AccountNumbersByCustomer[customer] + found := -1 + for i, num := range acctNums { + if num == accountNumber { + found = i + break + } + } + if found == -1 { + return status.Errorf(codes.NotFound, "you have no account numbered %d", accountNumber) + } + + acct := a.AccountsByNumber[accountNumber] + if acct.BalanceCents != 0 { + return status.Errorf(codes.FailedPrecondition, "account %d cannot be closed because it has a non-zero balance: %s", accountNumber, dollars(acct.BalanceCents)) + } + + for i, num := range a.AccountNumbers { + if num == accountNumber { + a.AccountNumbers = append(a.AccountNumbers[:i], a.AccountNumbers[i+1:]...) + break + } + } + + a.AccountNumbersByCustomer[customer] = append(acctNums[:found], acctNums[found+1:]...) + delete(a.AccountsByNumber, accountNumber) + return nil +} + +func (a *accounts) getAccount(customer string, accountNumber uint64) (*account, error) { + a.mu.RLock() + defer a.mu.RUnlock() + acctNums := a.AccountNumbersByCustomer[customer] + for _, num := range acctNums { + if num == accountNumber { + return a.AccountsByNumber[num], nil + } + } + return nil, status.Errorf(codes.NotFound, "you have no account numbered %d", accountNumber) +} + +func (a *accounts) getAllAccounts(customer string) []*Account { + a.mu.RLock() + defer a.mu.RUnlock() + + accountNums := a.AccountNumbersByCustomer[customer] + var accounts []*Account + for _, num := range accountNums { + accounts = append(accounts, &a.AccountsByNumber[num].Account) + } + return accounts +} + +type account struct { + Account + Transactions []*Transaction + mu sync.RWMutex +} + +func (a *account) getTransactions() []*Transaction { + a.mu.RLock() + defer a.mu.RUnlock() + return a.Transactions +} + +func (a *account) newTransaction(amountCents int32, desc string) (newBalance int32, err error) { + a.mu.Lock() + defer a.mu.Unlock() + bal := a.BalanceCents + amountCents + if bal < 0 { + return 0, status.Errorf(codes.FailedPrecondition, "insufficient funds: cannot withdraw %s when balance is %s", dollars(amountCents), dollars(a.BalanceCents)) + } + a.BalanceCents = bal + a.Transactions = append(a.Transactions, &Transaction{ + AccountNumber: a.AccountNumber, + Date: ptypes.TimestampNow(), + AmountCents: amountCents, + SeqNumber: uint64(len(a.Transactions) + 1), + Desc: desc, + }) + return bal, nil +} + +func (t *Transaction) MarshalJSON() ([]byte, error) { + var jsm jsonpb.Marshaler + var buf bytes.Buffer + if err := jsm.Marshal(&buf, t); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (t *Transaction) UnmarshalJSON(b []byte) error { + return jsonpb.Unmarshal(bytes.NewReader(b), t) +} + +func (a *accounts) clone() *accounts { + var clone accounts + clone.AccountNumbersByCustomer = map[string][]uint64{} + clone.AccountsByNumber = map[uint64]*account{} + + a.mu.RLock() + clone.Customers = a.Customers + a.mu.RUnlock() + + for _, cust := range clone.Customers { + var acctNums []uint64 + a.mu.RLock() + acctNums = a.AccountNumbersByCustomer[cust] + a.mu.RUnlock() + + clone.AccountNumbersByCustomer[cust] = acctNums + clone.AccountNumbers = append(clone.AccountNumbers, acctNums...) + + for _, acctNum := range acctNums { + a.mu.RLock() + acct := a.AccountsByNumber[acctNum] + a.mu.RUnlock() + + acct.mu.RLock() + txns := acct.Transactions + acct.mu.RUnlock() + + clone.AccountsByNumber[acctNum] = &account{Transactions: txns} + } + } + + return &clone +} + +func dollars(amountCents int32) string { + return fmt.Sprintf("$%02f", float64(amountCents)/100) +} diff --git a/testing/cmd/bankdemo/main.go b/testing/cmd/bankdemo/main.go new file mode 100644 index 0000000..77b5cf4 --- /dev/null +++ b/testing/cmd/bankdemo/main.go @@ -0,0 +1,168 @@ +package main + +//go:generate protoc --go_out=plugins=grpc:./ bank.proto support.proto + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "net" + "os" + "os/signal" + "sync/atomic" + "syscall" + "time" + + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/reflection" + "google.golang.org/grpc/status" +) + +func main() { + grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr)) + + port := flag.Int("port", 12345, "The port on which bankdemo gRPC server will listen.") + datafile := flag.String("datafile", "accounts.json", "The path and filename to which bank account data is saved and from which data will be loaded.") + flag.Parse() + + // create the server and load initial dataset + ctx, cancel := context.WithCancel(context.Background()) + s := &svr{ + datafile: *datafile, + ctx: ctx, + cancel: cancel, + } + if err := s.load(); err != nil { + panic(err) + } + + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port)) + if err != nil { + panic(err) + } + + grpcSvr := gRPCServer() + + // Register gRPC service implementations + bankSvc := bankServer{ + allAccounts: &s.allAccounts, + } + RegisterBankServer(grpcSvr, &bankSvc) + + chatSvc := chatServer{ + chatsBySession: map[string]*session{}, + } + RegisterSupportServer(grpcSvr, &chatSvc) + + go s.bgSaver() + + // don't forget to include server reflection support! + reflection.Register(grpcSvr) + + defer func() { + cancel() + s.flush() + }() + + // trap SIGINT / SIGTERM to exit cleanly + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT) + signal.Notify(c, syscall.SIGTERM) + go func() { + <-c + fmt.Println("Shutting down...") + grpcSvr.GracefulStop() + }() + + grpclog.Infof("server starting, listening on %v", l.Addr()) + if err := grpcSvr.Serve(l); err != nil { + panic(err) + } +} + +func gRPCServer() *grpc.Server { + var reqCounter uint64 + return grpc.NewServer( + grpc.UnaryInterceptor(func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + reqID := atomic.AddUint64(&reqCounter, 1) + var client string + if p, ok := peer.FromContext(ctx); ok { + client = p.Addr.String() + } else { + client = "?" + } + grpclog.Infof("request %d started for %s from %s", reqID, info.FullMethod, client) + + rsp, err := handler(ctx, req) + + stat, _ := status.FromError(err) + grpclog.Infof("request %d completed for %s from %s: %v %s", reqID, info.FullMethod, client, stat.Code(), stat.Message()) + return rsp, err + + }), + grpc.StreamInterceptor(func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + reqID := atomic.AddUint64(&reqCounter, 1) + var client string + if p, ok := peer.FromContext(ss.Context()); ok { + client = p.Addr.String() + } else { + client = "?" + } + grpclog.Infof("request %d started for %s from %s", reqID, info.FullMethod, client) + + err := handler(srv, ss) + + stat, _ := status.FromError(err) + grpclog.Infof("request %d completed for %s from %s: %v %s", reqID, info.FullMethod, client, stat.Code(), stat.Message()) + return err + })) +} + +type svr struct { + datafile string + ctx context.Context + cancel context.CancelFunc + allAccounts accounts +} + +func (s *svr) load() error { + accts, err := ioutil.ReadFile(s.datafile) + if err != nil && !os.IsNotExist(err) { + return err + } + if len(accts) == 0 { + s.allAccounts.AccountNumbersByCustomer = map[string][]uint64{} + s.allAccounts.AccountsByNumber = map[uint64]*account{} + } else if err := json.Unmarshal(accts, &s.allAccounts); err != nil { + return err + } + + return nil +} + +func (s *svr) bgSaver() { + ticker := time.NewTicker(5 * time.Second) + for { + select { + case <-ticker.C: + s.flush() + case <-s.ctx.Done(): + ticker.Stop() + return + } + } +} + +func (s *svr) flush() { + accounts := s.allAccounts.clone() + + if b, err := json.Marshal(accounts); err != nil { + grpclog.Errorf("failed to save data to %q", s.datafile) + } else if err := ioutil.WriteFile(s.datafile, b, 0666); err != nil { + grpclog.Errorf("failed to save data to %q", s.datafile) + } +} diff --git a/testing/cmd/bankdemo/support.pb.go b/testing/cmd/bankdemo/support.pb.go new file mode 100644 index 0000000..69a7c46 --- /dev/null +++ b/testing/cmd/bankdemo/support.pb.go @@ -0,0 +1,1250 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: support.proto + +package main + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import timestamp "github.com/golang/protobuf/ptypes/timestamp" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Void int32 + +const ( + Void_VOID Void = 0 +) + +var Void_name = map[int32]string{ + 0: "VOID", +} +var Void_value = map[string]int32{ + "VOID": 0, +} + +func (x Void) String() string { + return proto.EnumName(Void_name, int32(x)) +} +func (Void) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{0} +} + +type ChatCustomerRequest struct { + // Types that are valid to be assigned to Req: + // *ChatCustomerRequest_Init + // *ChatCustomerRequest_Msg + // *ChatCustomerRequest_HangUp + Req isChatCustomerRequest_Req `protobuf_oneof:"req"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChatCustomerRequest) Reset() { *m = ChatCustomerRequest{} } +func (m *ChatCustomerRequest) String() string { return proto.CompactTextString(m) } +func (*ChatCustomerRequest) ProtoMessage() {} +func (*ChatCustomerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{0} +} +func (m *ChatCustomerRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChatCustomerRequest.Unmarshal(m, b) +} +func (m *ChatCustomerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChatCustomerRequest.Marshal(b, m, deterministic) +} +func (dst *ChatCustomerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChatCustomerRequest.Merge(dst, src) +} +func (m *ChatCustomerRequest) XXX_Size() int { + return xxx_messageInfo_ChatCustomerRequest.Size(m) +} +func (m *ChatCustomerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ChatCustomerRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ChatCustomerRequest proto.InternalMessageInfo + +type isChatCustomerRequest_Req interface { + isChatCustomerRequest_Req() +} + +type ChatCustomerRequest_Init struct { + Init *InitiateChat `protobuf:"bytes,1,opt,name=init,proto3,oneof"` +} +type ChatCustomerRequest_Msg struct { + Msg string `protobuf:"bytes,2,opt,name=msg,proto3,oneof"` +} +type ChatCustomerRequest_HangUp struct { + HangUp Void `protobuf:"varint,3,opt,name=hang_up,json=hangUp,proto3,enum=Void,oneof"` +} + +func (*ChatCustomerRequest_Init) isChatCustomerRequest_Req() {} +func (*ChatCustomerRequest_Msg) isChatCustomerRequest_Req() {} +func (*ChatCustomerRequest_HangUp) isChatCustomerRequest_Req() {} + +func (m *ChatCustomerRequest) GetReq() isChatCustomerRequest_Req { + if m != nil { + return m.Req + } + return nil +} + +func (m *ChatCustomerRequest) GetInit() *InitiateChat { + if x, ok := m.GetReq().(*ChatCustomerRequest_Init); ok { + return x.Init + } + return nil +} + +func (m *ChatCustomerRequest) GetMsg() string { + if x, ok := m.GetReq().(*ChatCustomerRequest_Msg); ok { + return x.Msg + } + return "" +} + +func (m *ChatCustomerRequest) GetHangUp() Void { + if x, ok := m.GetReq().(*ChatCustomerRequest_HangUp); ok { + return x.HangUp + } + return Void_VOID +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*ChatCustomerRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _ChatCustomerRequest_OneofMarshaler, _ChatCustomerRequest_OneofUnmarshaler, _ChatCustomerRequest_OneofSizer, []interface{}{ + (*ChatCustomerRequest_Init)(nil), + (*ChatCustomerRequest_Msg)(nil), + (*ChatCustomerRequest_HangUp)(nil), + } +} + +func _ChatCustomerRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*ChatCustomerRequest) + // req + switch x := m.Req.(type) { + case *ChatCustomerRequest_Init: + b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Init); err != nil { + return err + } + case *ChatCustomerRequest_Msg: + b.EncodeVarint(2<<3 | proto.WireBytes) + b.EncodeStringBytes(x.Msg) + case *ChatCustomerRequest_HangUp: + b.EncodeVarint(3<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.HangUp)) + case nil: + default: + return fmt.Errorf("ChatCustomerRequest.Req has unexpected type %T", x) + } + return nil +} + +func _ChatCustomerRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*ChatCustomerRequest) + switch tag { + case 1: // req.init + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(InitiateChat) + err := b.DecodeMessage(msg) + m.Req = &ChatCustomerRequest_Init{msg} + return true, err + case 2: // req.msg + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeStringBytes() + m.Req = &ChatCustomerRequest_Msg{x} + return true, err + case 3: // req.hang_up + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Req = &ChatCustomerRequest_HangUp{Void(x)} + return true, err + default: + return false, nil + } +} + +func _ChatCustomerRequest_OneofSizer(msg proto.Message) (n int) { + m := msg.(*ChatCustomerRequest) + // req + switch x := m.Req.(type) { + case *ChatCustomerRequest_Init: + s := proto.Size(x.Init) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *ChatCustomerRequest_Msg: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(len(x.Msg))) + n += len(x.Msg) + case *ChatCustomerRequest_HangUp: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(x.HangUp)) + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type InitiateChat struct { + ResumeSessionId string `protobuf:"bytes,1,opt,name=resume_session_id,json=resumeSessionId,proto3" json:"resume_session_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InitiateChat) Reset() { *m = InitiateChat{} } +func (m *InitiateChat) String() string { return proto.CompactTextString(m) } +func (*InitiateChat) ProtoMessage() {} +func (*InitiateChat) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{1} +} +func (m *InitiateChat) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_InitiateChat.Unmarshal(m, b) +} +func (m *InitiateChat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_InitiateChat.Marshal(b, m, deterministic) +} +func (dst *InitiateChat) XXX_Merge(src proto.Message) { + xxx_messageInfo_InitiateChat.Merge(dst, src) +} +func (m *InitiateChat) XXX_Size() int { + return xxx_messageInfo_InitiateChat.Size(m) +} +func (m *InitiateChat) XXX_DiscardUnknown() { + xxx_messageInfo_InitiateChat.DiscardUnknown(m) +} + +var xxx_messageInfo_InitiateChat proto.InternalMessageInfo + +func (m *InitiateChat) GetResumeSessionId() string { + if m != nil { + return m.ResumeSessionId + } + return "" +} + +type AgentMessage struct { + AgentName string `protobuf:"bytes,1,opt,name=agent_name,json=agentName,proto3" json:"agent_name,omitempty"` + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AgentMessage) Reset() { *m = AgentMessage{} } +func (m *AgentMessage) String() string { return proto.CompactTextString(m) } +func (*AgentMessage) ProtoMessage() {} +func (*AgentMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{2} +} +func (m *AgentMessage) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AgentMessage.Unmarshal(m, b) +} +func (m *AgentMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AgentMessage.Marshal(b, m, deterministic) +} +func (dst *AgentMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_AgentMessage.Merge(dst, src) +} +func (m *AgentMessage) XXX_Size() int { + return xxx_messageInfo_AgentMessage.Size(m) +} +func (m *AgentMessage) XXX_DiscardUnknown() { + xxx_messageInfo_AgentMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_AgentMessage proto.InternalMessageInfo + +func (m *AgentMessage) GetAgentName() string { + if m != nil { + return m.AgentName + } + return "" +} + +func (m *AgentMessage) GetMsg() string { + if m != nil { + return m.Msg + } + return "" +} + +type ChatCustomerResponse struct { + // Types that are valid to be assigned to Resp: + // *ChatCustomerResponse_Session + // *ChatCustomerResponse_Msg + Resp isChatCustomerResponse_Resp `protobuf_oneof:"resp"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChatCustomerResponse) Reset() { *m = ChatCustomerResponse{} } +func (m *ChatCustomerResponse) String() string { return proto.CompactTextString(m) } +func (*ChatCustomerResponse) ProtoMessage() {} +func (*ChatCustomerResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{3} +} +func (m *ChatCustomerResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChatCustomerResponse.Unmarshal(m, b) +} +func (m *ChatCustomerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChatCustomerResponse.Marshal(b, m, deterministic) +} +func (dst *ChatCustomerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChatCustomerResponse.Merge(dst, src) +} +func (m *ChatCustomerResponse) XXX_Size() int { + return xxx_messageInfo_ChatCustomerResponse.Size(m) +} +func (m *ChatCustomerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ChatCustomerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ChatCustomerResponse proto.InternalMessageInfo + +type isChatCustomerResponse_Resp interface { + isChatCustomerResponse_Resp() +} + +type ChatCustomerResponse_Session struct { + Session *Session `protobuf:"bytes,1,opt,name=session,proto3,oneof"` +} +type ChatCustomerResponse_Msg struct { + Msg *AgentMessage `protobuf:"bytes,2,opt,name=msg,proto3,oneof"` +} + +func (*ChatCustomerResponse_Session) isChatCustomerResponse_Resp() {} +func (*ChatCustomerResponse_Msg) isChatCustomerResponse_Resp() {} + +func (m *ChatCustomerResponse) GetResp() isChatCustomerResponse_Resp { + if m != nil { + return m.Resp + } + return nil +} + +func (m *ChatCustomerResponse) GetSession() *Session { + if x, ok := m.GetResp().(*ChatCustomerResponse_Session); ok { + return x.Session + } + return nil +} + +func (m *ChatCustomerResponse) GetMsg() *AgentMessage { + if x, ok := m.GetResp().(*ChatCustomerResponse_Msg); ok { + return x.Msg + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*ChatCustomerResponse) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _ChatCustomerResponse_OneofMarshaler, _ChatCustomerResponse_OneofUnmarshaler, _ChatCustomerResponse_OneofSizer, []interface{}{ + (*ChatCustomerResponse_Session)(nil), + (*ChatCustomerResponse_Msg)(nil), + } +} + +func _ChatCustomerResponse_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*ChatCustomerResponse) + // resp + switch x := m.Resp.(type) { + case *ChatCustomerResponse_Session: + b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Session); err != nil { + return err + } + case *ChatCustomerResponse_Msg: + b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Msg); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("ChatCustomerResponse.Resp has unexpected type %T", x) + } + return nil +} + +func _ChatCustomerResponse_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*ChatCustomerResponse) + switch tag { + case 1: // resp.session + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Session) + err := b.DecodeMessage(msg) + m.Resp = &ChatCustomerResponse_Session{msg} + return true, err + case 2: // resp.msg + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(AgentMessage) + err := b.DecodeMessage(msg) + m.Resp = &ChatCustomerResponse_Msg{msg} + return true, err + default: + return false, nil + } +} + +func _ChatCustomerResponse_OneofSizer(msg proto.Message) (n int) { + m := msg.(*ChatCustomerResponse) + // resp + switch x := m.Resp.(type) { + case *ChatCustomerResponse_Session: + s := proto.Size(x.Session) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *ChatCustomerResponse_Msg: + s := proto.Size(x.Msg) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type ChatAgentRequest struct { + // Types that are valid to be assigned to Req: + // *ChatAgentRequest_Accept + // *ChatAgentRequest_Msg + // *ChatAgentRequest_LeaveSession + Req isChatAgentRequest_Req `protobuf_oneof:"req"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChatAgentRequest) Reset() { *m = ChatAgentRequest{} } +func (m *ChatAgentRequest) String() string { return proto.CompactTextString(m) } +func (*ChatAgentRequest) ProtoMessage() {} +func (*ChatAgentRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{4} +} +func (m *ChatAgentRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChatAgentRequest.Unmarshal(m, b) +} +func (m *ChatAgentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChatAgentRequest.Marshal(b, m, deterministic) +} +func (dst *ChatAgentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChatAgentRequest.Merge(dst, src) +} +func (m *ChatAgentRequest) XXX_Size() int { + return xxx_messageInfo_ChatAgentRequest.Size(m) +} +func (m *ChatAgentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ChatAgentRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ChatAgentRequest proto.InternalMessageInfo + +type isChatAgentRequest_Req interface { + isChatAgentRequest_Req() +} + +type ChatAgentRequest_Accept struct { + Accept *AcceptChat `protobuf:"bytes,1,opt,name=accept,proto3,oneof"` +} +type ChatAgentRequest_Msg struct { + Msg string `protobuf:"bytes,2,opt,name=msg,proto3,oneof"` +} +type ChatAgentRequest_LeaveSession struct { + LeaveSession Void `protobuf:"varint,3,opt,name=leave_session,json=leaveSession,proto3,enum=Void,oneof"` +} + +func (*ChatAgentRequest_Accept) isChatAgentRequest_Req() {} +func (*ChatAgentRequest_Msg) isChatAgentRequest_Req() {} +func (*ChatAgentRequest_LeaveSession) isChatAgentRequest_Req() {} + +func (m *ChatAgentRequest) GetReq() isChatAgentRequest_Req { + if m != nil { + return m.Req + } + return nil +} + +func (m *ChatAgentRequest) GetAccept() *AcceptChat { + if x, ok := m.GetReq().(*ChatAgentRequest_Accept); ok { + return x.Accept + } + return nil +} + +func (m *ChatAgentRequest) GetMsg() string { + if x, ok := m.GetReq().(*ChatAgentRequest_Msg); ok { + return x.Msg + } + return "" +} + +func (m *ChatAgentRequest) GetLeaveSession() Void { + if x, ok := m.GetReq().(*ChatAgentRequest_LeaveSession); ok { + return x.LeaveSession + } + return Void_VOID +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*ChatAgentRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _ChatAgentRequest_OneofMarshaler, _ChatAgentRequest_OneofUnmarshaler, _ChatAgentRequest_OneofSizer, []interface{}{ + (*ChatAgentRequest_Accept)(nil), + (*ChatAgentRequest_Msg)(nil), + (*ChatAgentRequest_LeaveSession)(nil), + } +} + +func _ChatAgentRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*ChatAgentRequest) + // req + switch x := m.Req.(type) { + case *ChatAgentRequest_Accept: + b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Accept); err != nil { + return err + } + case *ChatAgentRequest_Msg: + b.EncodeVarint(2<<3 | proto.WireBytes) + b.EncodeStringBytes(x.Msg) + case *ChatAgentRequest_LeaveSession: + b.EncodeVarint(3<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.LeaveSession)) + case nil: + default: + return fmt.Errorf("ChatAgentRequest.Req has unexpected type %T", x) + } + return nil +} + +func _ChatAgentRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*ChatAgentRequest) + switch tag { + case 1: // req.accept + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(AcceptChat) + err := b.DecodeMessage(msg) + m.Req = &ChatAgentRequest_Accept{msg} + return true, err + case 2: // req.msg + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeStringBytes() + m.Req = &ChatAgentRequest_Msg{x} + return true, err + case 3: // req.leave_session + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Req = &ChatAgentRequest_LeaveSession{Void(x)} + return true, err + default: + return false, nil + } +} + +func _ChatAgentRequest_OneofSizer(msg proto.Message) (n int) { + m := msg.(*ChatAgentRequest) + // req + switch x := m.Req.(type) { + case *ChatAgentRequest_Accept: + s := proto.Size(x.Accept) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *ChatAgentRequest_Msg: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(len(x.Msg))) + n += len(x.Msg) + case *ChatAgentRequest_LeaveSession: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(x.LeaveSession)) + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type AcceptChat struct { + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AcceptChat) Reset() { *m = AcceptChat{} } +func (m *AcceptChat) String() string { return proto.CompactTextString(m) } +func (*AcceptChat) ProtoMessage() {} +func (*AcceptChat) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{5} +} +func (m *AcceptChat) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AcceptChat.Unmarshal(m, b) +} +func (m *AcceptChat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AcceptChat.Marshal(b, m, deterministic) +} +func (dst *AcceptChat) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcceptChat.Merge(dst, src) +} +func (m *AcceptChat) XXX_Size() int { + return xxx_messageInfo_AcceptChat.Size(m) +} +func (m *AcceptChat) XXX_DiscardUnknown() { + xxx_messageInfo_AcceptChat.DiscardUnknown(m) +} + +var xxx_messageInfo_AcceptChat proto.InternalMessageInfo + +func (m *AcceptChat) GetSessionId() string { + if m != nil { + return m.SessionId + } + return "" +} + +type ChatEntry struct { + Date *timestamp.Timestamp `protobuf:"bytes,1,opt,name=date,proto3" json:"date,omitempty"` + // Types that are valid to be assigned to Entry: + // *ChatEntry_CustomerMsg + // *ChatEntry_AgentMsg + Entry isChatEntry_Entry `protobuf_oneof:"entry"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChatEntry) Reset() { *m = ChatEntry{} } +func (m *ChatEntry) String() string { return proto.CompactTextString(m) } +func (*ChatEntry) ProtoMessage() {} +func (*ChatEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{6} +} +func (m *ChatEntry) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChatEntry.Unmarshal(m, b) +} +func (m *ChatEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChatEntry.Marshal(b, m, deterministic) +} +func (dst *ChatEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChatEntry.Merge(dst, src) +} +func (m *ChatEntry) XXX_Size() int { + return xxx_messageInfo_ChatEntry.Size(m) +} +func (m *ChatEntry) XXX_DiscardUnknown() { + xxx_messageInfo_ChatEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_ChatEntry proto.InternalMessageInfo + +type isChatEntry_Entry interface { + isChatEntry_Entry() +} + +type ChatEntry_CustomerMsg struct { + CustomerMsg string `protobuf:"bytes,2,opt,name=customer_msg,json=customerMsg,proto3,oneof"` +} +type ChatEntry_AgentMsg struct { + AgentMsg *AgentMessage `protobuf:"bytes,3,opt,name=agent_msg,json=agentMsg,proto3,oneof"` +} + +func (*ChatEntry_CustomerMsg) isChatEntry_Entry() {} +func (*ChatEntry_AgentMsg) isChatEntry_Entry() {} + +func (m *ChatEntry) GetEntry() isChatEntry_Entry { + if m != nil { + return m.Entry + } + return nil +} + +func (m *ChatEntry) GetDate() *timestamp.Timestamp { + if m != nil { + return m.Date + } + return nil +} + +func (m *ChatEntry) GetCustomerMsg() string { + if x, ok := m.GetEntry().(*ChatEntry_CustomerMsg); ok { + return x.CustomerMsg + } + return "" +} + +func (m *ChatEntry) GetAgentMsg() *AgentMessage { + if x, ok := m.GetEntry().(*ChatEntry_AgentMsg); ok { + return x.AgentMsg + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*ChatEntry) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _ChatEntry_OneofMarshaler, _ChatEntry_OneofUnmarshaler, _ChatEntry_OneofSizer, []interface{}{ + (*ChatEntry_CustomerMsg)(nil), + (*ChatEntry_AgentMsg)(nil), + } +} + +func _ChatEntry_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*ChatEntry) + // entry + switch x := m.Entry.(type) { + case *ChatEntry_CustomerMsg: + b.EncodeVarint(2<<3 | proto.WireBytes) + b.EncodeStringBytes(x.CustomerMsg) + case *ChatEntry_AgentMsg: + b.EncodeVarint(3<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.AgentMsg); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("ChatEntry.Entry has unexpected type %T", x) + } + return nil +} + +func _ChatEntry_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*ChatEntry) + switch tag { + case 2: // entry.customer_msg + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeStringBytes() + m.Entry = &ChatEntry_CustomerMsg{x} + return true, err + case 3: // entry.agent_msg + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(AgentMessage) + err := b.DecodeMessage(msg) + m.Entry = &ChatEntry_AgentMsg{msg} + return true, err + default: + return false, nil + } +} + +func _ChatEntry_OneofSizer(msg proto.Message) (n int) { + m := msg.(*ChatEntry) + // entry + switch x := m.Entry.(type) { + case *ChatEntry_CustomerMsg: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(len(x.CustomerMsg))) + n += len(x.CustomerMsg) + case *ChatEntry_AgentMsg: + s := proto.Size(x.AgentMsg) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type ChatAgentResponse struct { + // Types that are valid to be assigned to Resp: + // *ChatAgentResponse_AcceptedSession + // *ChatAgentResponse_Msg + // *ChatAgentResponse_SessionEnded + Resp isChatAgentResponse_Resp `protobuf_oneof:"resp"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ChatAgentResponse) Reset() { *m = ChatAgentResponse{} } +func (m *ChatAgentResponse) String() string { return proto.CompactTextString(m) } +func (*ChatAgentResponse) ProtoMessage() {} +func (*ChatAgentResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{7} +} +func (m *ChatAgentResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ChatAgentResponse.Unmarshal(m, b) +} +func (m *ChatAgentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ChatAgentResponse.Marshal(b, m, deterministic) +} +func (dst *ChatAgentResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ChatAgentResponse.Merge(dst, src) +} +func (m *ChatAgentResponse) XXX_Size() int { + return xxx_messageInfo_ChatAgentResponse.Size(m) +} +func (m *ChatAgentResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ChatAgentResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ChatAgentResponse proto.InternalMessageInfo + +type isChatAgentResponse_Resp interface { + isChatAgentResponse_Resp() +} + +type ChatAgentResponse_AcceptedSession struct { + AcceptedSession *Session `protobuf:"bytes,1,opt,name=accepted_session,json=acceptedSession,proto3,oneof"` +} +type ChatAgentResponse_Msg struct { + Msg *ChatEntry `protobuf:"bytes,2,opt,name=msg,proto3,oneof"` +} +type ChatAgentResponse_SessionEnded struct { + SessionEnded Void `protobuf:"varint,3,opt,name=session_ended,json=sessionEnded,proto3,enum=Void,oneof"` +} + +func (*ChatAgentResponse_AcceptedSession) isChatAgentResponse_Resp() {} +func (*ChatAgentResponse_Msg) isChatAgentResponse_Resp() {} +func (*ChatAgentResponse_SessionEnded) isChatAgentResponse_Resp() {} + +func (m *ChatAgentResponse) GetResp() isChatAgentResponse_Resp { + if m != nil { + return m.Resp + } + return nil +} + +func (m *ChatAgentResponse) GetAcceptedSession() *Session { + if x, ok := m.GetResp().(*ChatAgentResponse_AcceptedSession); ok { + return x.AcceptedSession + } + return nil +} + +func (m *ChatAgentResponse) GetMsg() *ChatEntry { + if x, ok := m.GetResp().(*ChatAgentResponse_Msg); ok { + return x.Msg + } + return nil +} + +func (m *ChatAgentResponse) GetSessionEnded() Void { + if x, ok := m.GetResp().(*ChatAgentResponse_SessionEnded); ok { + return x.SessionEnded + } + return Void_VOID +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*ChatAgentResponse) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _ChatAgentResponse_OneofMarshaler, _ChatAgentResponse_OneofUnmarshaler, _ChatAgentResponse_OneofSizer, []interface{}{ + (*ChatAgentResponse_AcceptedSession)(nil), + (*ChatAgentResponse_Msg)(nil), + (*ChatAgentResponse_SessionEnded)(nil), + } +} + +func _ChatAgentResponse_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*ChatAgentResponse) + // resp + switch x := m.Resp.(type) { + case *ChatAgentResponse_AcceptedSession: + b.EncodeVarint(1<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.AcceptedSession); err != nil { + return err + } + case *ChatAgentResponse_Msg: + b.EncodeVarint(2<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Msg); err != nil { + return err + } + case *ChatAgentResponse_SessionEnded: + b.EncodeVarint(3<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.SessionEnded)) + case nil: + default: + return fmt.Errorf("ChatAgentResponse.Resp has unexpected type %T", x) + } + return nil +} + +func _ChatAgentResponse_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*ChatAgentResponse) + switch tag { + case 1: // resp.accepted_session + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Session) + err := b.DecodeMessage(msg) + m.Resp = &ChatAgentResponse_AcceptedSession{msg} + return true, err + case 2: // resp.msg + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ChatEntry) + err := b.DecodeMessage(msg) + m.Resp = &ChatAgentResponse_Msg{msg} + return true, err + case 3: // resp.session_ended + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Resp = &ChatAgentResponse_SessionEnded{Void(x)} + return true, err + default: + return false, nil + } +} + +func _ChatAgentResponse_OneofSizer(msg proto.Message) (n int) { + m := msg.(*ChatAgentResponse) + // resp + switch x := m.Resp.(type) { + case *ChatAgentResponse_AcceptedSession: + s := proto.Size(x.AcceptedSession) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *ChatAgentResponse_Msg: + s := proto.Size(x.Msg) + n += 1 // tag and wire + n += proto.SizeVarint(uint64(s)) + n += s + case *ChatAgentResponse_SessionEnded: + n += 1 // tag and wire + n += proto.SizeVarint(uint64(x.SessionEnded)) + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +type Session struct { + SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + CustomerName string `protobuf:"bytes,2,opt,name=customer_name,json=customerName,proto3" json:"customer_name,omitempty"` + History []*ChatEntry `protobuf:"bytes,3,rep,name=history,proto3" json:"history,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Session) Reset() { *m = Session{} } +func (m *Session) String() string { return proto.CompactTextString(m) } +func (*Session) ProtoMessage() {} +func (*Session) Descriptor() ([]byte, []int) { + return fileDescriptor_support_640cb5c436dbd37d, []int{8} +} +func (m *Session) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Session.Unmarshal(m, b) +} +func (m *Session) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Session.Marshal(b, m, deterministic) +} +func (dst *Session) XXX_Merge(src proto.Message) { + xxx_messageInfo_Session.Merge(dst, src) +} +func (m *Session) XXX_Size() int { + return xxx_messageInfo_Session.Size(m) +} +func (m *Session) XXX_DiscardUnknown() { + xxx_messageInfo_Session.DiscardUnknown(m) +} + +var xxx_messageInfo_Session proto.InternalMessageInfo + +func (m *Session) GetSessionId() string { + if m != nil { + return m.SessionId + } + return "" +} + +func (m *Session) GetCustomerName() string { + if m != nil { + return m.CustomerName + } + return "" +} + +func (m *Session) GetHistory() []*ChatEntry { + if m != nil { + return m.History + } + return nil +} + +func init() { + proto.RegisterType((*ChatCustomerRequest)(nil), "ChatCustomerRequest") + proto.RegisterType((*InitiateChat)(nil), "InitiateChat") + proto.RegisterType((*AgentMessage)(nil), "AgentMessage") + proto.RegisterType((*ChatCustomerResponse)(nil), "ChatCustomerResponse") + proto.RegisterType((*ChatAgentRequest)(nil), "ChatAgentRequest") + proto.RegisterType((*AcceptChat)(nil), "AcceptChat") + proto.RegisterType((*ChatEntry)(nil), "ChatEntry") + proto.RegisterType((*ChatAgentResponse)(nil), "ChatAgentResponse") + proto.RegisterType((*Session)(nil), "Session") + proto.RegisterEnum("Void", Void_name, Void_value) +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// SupportClient is the client API for Support service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type SupportClient interface { + // ChatCustomer is used by a customer-facing app to send the customer's messages + // to a chat session. The customer is how initiates and terminates (via "hangup") + // a chat session. Only customers may invoke this method (e.g. requests must + // include customer auth credentials). + ChatCustomer(ctx context.Context, opts ...grpc.CallOption) (Support_ChatCustomerClient, error) + // ChatAgent is used by an agent-facing app to allow an agent to reply to a + // customer's messages in a chat session. The agent may accept a chat session, + // which defaults to the session awaiting an agent for the longest period of time + // (FIFO queue). + ChatAgent(ctx context.Context, opts ...grpc.CallOption) (Support_ChatAgentClient, error) +} + +type supportClient struct { + cc *grpc.ClientConn +} + +func NewSupportClient(cc *grpc.ClientConn) SupportClient { + return &supportClient{cc} +} + +func (c *supportClient) ChatCustomer(ctx context.Context, opts ...grpc.CallOption) (Support_ChatCustomerClient, error) { + stream, err := c.cc.NewStream(ctx, &_Support_serviceDesc.Streams[0], "/Support/ChatCustomer", opts...) + if err != nil { + return nil, err + } + x := &supportChatCustomerClient{stream} + return x, nil +} + +type Support_ChatCustomerClient interface { + Send(*ChatCustomerRequest) error + Recv() (*ChatCustomerResponse, error) + grpc.ClientStream +} + +type supportChatCustomerClient struct { + grpc.ClientStream +} + +func (x *supportChatCustomerClient) Send(m *ChatCustomerRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *supportChatCustomerClient) Recv() (*ChatCustomerResponse, error) { + m := new(ChatCustomerResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *supportClient) ChatAgent(ctx context.Context, opts ...grpc.CallOption) (Support_ChatAgentClient, error) { + stream, err := c.cc.NewStream(ctx, &_Support_serviceDesc.Streams[1], "/Support/ChatAgent", opts...) + if err != nil { + return nil, err + } + x := &supportChatAgentClient{stream} + return x, nil +} + +type Support_ChatAgentClient interface { + Send(*ChatAgentRequest) error + Recv() (*ChatAgentResponse, error) + grpc.ClientStream +} + +type supportChatAgentClient struct { + grpc.ClientStream +} + +func (x *supportChatAgentClient) Send(m *ChatAgentRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *supportChatAgentClient) Recv() (*ChatAgentResponse, error) { + m := new(ChatAgentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// SupportServer is the server API for Support service. +type SupportServer interface { + // ChatCustomer is used by a customer-facing app to send the customer's messages + // to a chat session. The customer is how initiates and terminates (via "hangup") + // a chat session. Only customers may invoke this method (e.g. requests must + // include customer auth credentials). + ChatCustomer(Support_ChatCustomerServer) error + // ChatAgent is used by an agent-facing app to allow an agent to reply to a + // customer's messages in a chat session. The agent may accept a chat session, + // which defaults to the session awaiting an agent for the longest period of time + // (FIFO queue). + ChatAgent(Support_ChatAgentServer) error +} + +func RegisterSupportServer(s *grpc.Server, srv SupportServer) { + s.RegisterService(&_Support_serviceDesc, srv) +} + +func _Support_ChatCustomer_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(SupportServer).ChatCustomer(&supportChatCustomerServer{stream}) +} + +type Support_ChatCustomerServer interface { + Send(*ChatCustomerResponse) error + Recv() (*ChatCustomerRequest, error) + grpc.ServerStream +} + +type supportChatCustomerServer struct { + grpc.ServerStream +} + +func (x *supportChatCustomerServer) Send(m *ChatCustomerResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *supportChatCustomerServer) Recv() (*ChatCustomerRequest, error) { + m := new(ChatCustomerRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Support_ChatAgent_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(SupportServer).ChatAgent(&supportChatAgentServer{stream}) +} + +type Support_ChatAgentServer interface { + Send(*ChatAgentResponse) error + Recv() (*ChatAgentRequest, error) + grpc.ServerStream +} + +type supportChatAgentServer struct { + grpc.ServerStream +} + +func (x *supportChatAgentServer) Send(m *ChatAgentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *supportChatAgentServer) Recv() (*ChatAgentRequest, error) { + m := new(ChatAgentRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _Support_serviceDesc = grpc.ServiceDesc{ + ServiceName: "Support", + HandlerType: (*SupportServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "ChatCustomer", + Handler: _Support_ChatCustomer_Handler, + ServerStreams: true, + ClientStreams: true, + }, + { + StreamName: "ChatAgent", + Handler: _Support_ChatAgent_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "support.proto", +} + +func init() { proto.RegisterFile("support.proto", fileDescriptor_support_640cb5c436dbd37d) } + +var fileDescriptor_support_640cb5c436dbd37d = []byte{ + // 571 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x94, 0x4f, 0x6f, 0xd3, 0x40, + 0x10, 0xc5, 0x63, 0xec, 0xc6, 0xcd, 0x24, 0xa1, 0xc9, 0x52, 0xa4, 0x28, 0x12, 0x10, 0x9c, 0x22, + 0x45, 0x05, 0x6d, 0x51, 0x10, 0x1c, 0xb8, 0x54, 0x4d, 0xa9, 0x94, 0x1c, 0x02, 0x92, 0x0b, 0x3d, + 0x70, 0xb1, 0xb6, 0xf1, 0xe0, 0x58, 0xaa, 0xff, 0xd4, 0xbb, 0x06, 0xf5, 0xda, 0x2f, 0xc1, 0x95, + 0x8f, 0x8a, 0x76, 0xbd, 0x76, 0xdd, 0x26, 0x70, 0xdc, 0x99, 0x67, 0xcf, 0xdb, 0xdf, 0x3c, 0x1b, + 0xba, 0x3c, 0x4f, 0xd3, 0x24, 0x13, 0x34, 0xcd, 0x12, 0x91, 0x0c, 0x5f, 0x04, 0x49, 0x12, 0x5c, + 0xe1, 0x91, 0x3a, 0x5d, 0xe6, 0x3f, 0x8e, 0x44, 0x18, 0x21, 0x17, 0x2c, 0x4a, 0x0b, 0x81, 0xf3, + 0x0b, 0x9e, 0x9c, 0xae, 0x99, 0x38, 0xcd, 0xb9, 0x48, 0x22, 0xcc, 0x5c, 0xbc, 0xce, 0x91, 0x0b, + 0x32, 0x06, 0x2b, 0x8c, 0x43, 0x31, 0x30, 0x46, 0xc6, 0xa4, 0x3d, 0xed, 0xd2, 0x45, 0x1c, 0x8a, + 0x90, 0x09, 0x94, 0xda, 0x79, 0xc3, 0x55, 0x4d, 0x42, 0xc0, 0x8c, 0x78, 0x30, 0x78, 0x34, 0x32, + 0x26, 0xad, 0x79, 0xc3, 0x95, 0x07, 0x32, 0x02, 0x7b, 0xcd, 0xe2, 0xc0, 0xcb, 0xd3, 0x81, 0x39, + 0x32, 0x26, 0x8f, 0xa7, 0x3b, 0xf4, 0x22, 0x09, 0xfd, 0x79, 0xc3, 0x6d, 0xca, 0xfa, 0xb7, 0x74, + 0xb6, 0x03, 0x66, 0x86, 0xd7, 0xce, 0x47, 0xe8, 0xd4, 0x5f, 0x4a, 0x0e, 0xa1, 0x9f, 0x21, 0xcf, + 0x23, 0xf4, 0x38, 0x72, 0x1e, 0x26, 0xb1, 0x17, 0xfa, 0x6a, 0x7c, 0xcb, 0xdd, 0x2b, 0x1a, 0xe7, + 0x45, 0x7d, 0xe1, 0x3b, 0xc7, 0xd0, 0x39, 0x09, 0x30, 0x16, 0x4b, 0xe4, 0x9c, 0x05, 0x48, 0x9e, + 0x01, 0x30, 0x79, 0xf6, 0x62, 0x16, 0xa1, 0x7e, 0xa8, 0xa5, 0x2a, 0x9f, 0x59, 0x84, 0xa4, 0x57, + 0xf3, 0xa9, 0x5c, 0x3a, 0x01, 0xec, 0xdf, 0xbf, 0x35, 0x4f, 0x93, 0x98, 0x23, 0x39, 0x00, 0x5b, + 0x4f, 0xd7, 0x37, 0xdf, 0xa5, 0x7a, 0xea, 0xbc, 0xe1, 0x96, 0x2d, 0xf2, 0xf2, 0xee, 0x7d, 0x92, + 0x4d, 0xdd, 0x8a, 0xc6, 0x30, 0x6b, 0x82, 0x95, 0x21, 0x4f, 0x9d, 0x5b, 0x03, 0x7a, 0x72, 0x92, + 0xd2, 0x94, 0x70, 0x5f, 0x41, 0x93, 0xad, 0x56, 0x98, 0x96, 0x78, 0xdb, 0xf4, 0x44, 0x1d, 0x35, + 0x5c, 0xdd, 0xdc, 0x8a, 0xf7, 0x0d, 0x74, 0xaf, 0x90, 0xfd, 0xac, 0x20, 0x3d, 0x84, 0xdc, 0x51, + 0x5d, 0xed, 0xb9, 0x44, 0xfd, 0x1a, 0xe0, 0x6e, 0x80, 0x84, 0xb5, 0x41, 0xb8, 0xc5, 0x2b, 0xb6, + 0xbf, 0x0d, 0x68, 0x49, 0xdd, 0x59, 0x2c, 0xb2, 0x1b, 0x42, 0xc1, 0xf2, 0x99, 0x40, 0x6d, 0x74, + 0x48, 0x8b, 0x38, 0xd1, 0x32, 0x4e, 0xf4, 0x6b, 0x19, 0x27, 0x57, 0xe9, 0xc8, 0x18, 0x3a, 0x2b, + 0x0d, 0xd5, 0xab, 0x9b, 0x6f, 0x97, 0xd5, 0xa5, 0xba, 0x44, 0xb1, 0x1c, 0xa5, 0x30, 0xb7, 0x53, + 0xdc, 0x55, 0x8a, 0x25, 0x0f, 0x66, 0x36, 0xec, 0xa0, 0xf4, 0xe2, 0xfc, 0x31, 0xa0, 0x5f, 0x63, + 0xa9, 0x57, 0xf6, 0x1e, 0x7a, 0x05, 0x2f, 0xf4, 0xbd, 0x7f, 0xef, 0x6e, 0xaf, 0xd4, 0xe8, 0x12, + 0x79, 0x5e, 0xdf, 0x21, 0xd0, 0xea, 0xc6, 0x35, 0xd0, 0x25, 0x25, 0x8c, 0x7d, 0xf4, 0x37, 0x40, + 0xeb, 0xee, 0x99, 0x6c, 0x56, 0xeb, 0xe6, 0x60, 0x97, 0x03, 0xfe, 0x8f, 0x99, 0x8c, 0xa1, 0x5b, + 0x81, 0x52, 0xa9, 0x2d, 0xd2, 0x59, 0xd1, 0x53, 0xc1, 0x3d, 0x00, 0x7b, 0x1d, 0x72, 0x91, 0x64, + 0x37, 0x03, 0x73, 0x64, 0xde, 0x37, 0xea, 0x96, 0xad, 0xc3, 0x1e, 0x58, 0xd2, 0x14, 0xd9, 0x05, + 0xeb, 0xe2, 0xcb, 0xe2, 0x53, 0xaf, 0x31, 0xbd, 0x35, 0xc0, 0x3e, 0x2f, 0xfe, 0x03, 0xe4, 0x18, + 0x3a, 0xf5, 0xa8, 0x93, 0x7d, 0xba, 0xe5, 0x7b, 0x1f, 0x3e, 0xa5, 0xdb, 0xbe, 0x87, 0x89, 0xf1, + 0xd6, 0x20, 0x1f, 0x8a, 0x3c, 0x28, 0xea, 0xa4, 0x4f, 0x1f, 0xa6, 0x79, 0x48, 0xe8, 0xc6, 0x52, + 0xe4, 0x73, 0xb3, 0xe6, 0x77, 0x2b, 0x62, 0x61, 0x7c, 0xd9, 0x54, 0x61, 0x79, 0xf7, 0x37, 0x00, + 0x00, 0xff, 0xff, 0x00, 0xf3, 0x29, 0x6f, 0x9a, 0x04, 0x00, 0x00, +} diff --git a/testing/cmd/bankdemo/support.proto b/testing/cmd/bankdemo/support.proto new file mode 100644 index 0000000..567b165 --- /dev/null +++ b/testing/cmd/bankdemo/support.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; + +option go_package = "main"; + +import "google/protobuf/timestamp.proto"; + +// Support provides an interactive chat service, for customers to interact with +// the bank's support agents. A single stream, for either of the two methods, is +// a stateful connection to a single "chat session". Streams are initially disconnected +// (not part of any session). A stream must be disconnected from a session (via customer +// hang up or via agent leaving a session) before it can be connected to a new one. +service Support { + // ChatCustomer is used by a customer-facing app to send the customer's messages + // to a chat session. The customer is how initiates and terminates (via "hangup") + // a chat session. Only customers may invoke this method (e.g. requests must + // include customer auth credentials). + rpc ChatCustomer(stream ChatCustomerRequest) returns (stream ChatCustomerResponse); + // ChatAgent is used by an agent-facing app to allow an agent to reply to a + // customer's messages in a chat session. The agent may accept a chat session, + // which defaults to the session awaiting an agent for the longest period of time + // (FIFO queue). + rpc ChatAgent(stream ChatAgentRequest) returns (stream ChatAgentResponse); +} + +enum Void { + VOID = 0; +} + +message ChatCustomerRequest { + oneof req { + // init is used when a chat stream is not part of a + // chat session. This is a stream's initial state, as well as + // the state after a "hang_up" request is sent. This creates + // a new state session or resumes an existing one. + InitiateChat init = 1; + // msg is used to send the customer's messages to support + // agents. + string msg = 2; + // hang_up is used to terminate a chat session. If a stream + // is broken, but the session was not terminated, the client + // may initiate a new stream and use init to resume that + // session. Sessions are not terminated unless done so + // explicitly via sending this kind of request on the stream. + Void hang_up = 3; + } +} + +message InitiateChat { + string resume_session_id = 1; +} + +message AgentMessage { + string agent_name = 1; + string msg = 2; +} + +message ChatCustomerResponse { + oneof resp { + // session is sent from the server when the stream is connected + // to a chat session. This happens after an init request is sent + // and the stream is connected to either a new or resumed session. + Session session = 1; + // msg is sent from the server to convey agents' messages to the + // customer. + AgentMessage msg = 2; + } +} + +message ChatAgentRequest { + oneof req { + // accept is used when an agent wants to join a customer chat + // session. It can be used to connect to a specific session (by + // ID), or to just accept the session for which the customer has + // been waiting the longest (e.g. poll a FIFO queue of sessions + // awaiting a support agent). It is possible for multiple agents + // to be connected to the same chat session. + AcceptChat accept = 1; + // msg is used to send a message to the customer. It will also be + // delivered to any other connected support agents. + string msg = 2; + // leave_session allows an agent to exit a chat session. They can + // always re-enter later by sending an accept message for that + // session ID. + Void leave_session = 3; + } +} + +message AcceptChat { + string session_id = 1; +} + +message ChatEntry { + google.protobuf.Timestamp date = 1; + oneof entry { + string customer_msg = 2; + AgentMessage agent_msg = 3; + } +} + +message ChatAgentResponse { + oneof resp { + // accepted_session provides the detail of a chat session. The server + // sends this message after the agent has accepted a chat session. + Session accepted_session = 1; + // msg is sent by the server when the customer, or another support + // agent, sends a message in stream's current session. + ChatEntry msg = 2; + // session_ended notifies the support agent that their currently + // connected chat session has been terminated by the customer. + Void session_ended = 3; + } +} + +message Session { + string session_id = 1; + string customer_name = 2; + repeated ChatEntry history = 3; +} diff --git a/testing/cmd/testserver/README.md b/testing/cmd/testserver/README.md new file mode 100644 index 0000000..cb22c60 --- /dev/null +++ b/testing/cmd/testserver/README.md @@ -0,0 +1,6 @@ +# testserver + +The `testserver` program is a simple server that can be used for testing RPC clients such +as `grpcurl`. It implements an RPC interface that is defined in `grpcurl`'s [testing package](https://github.com/fullstorydev/grpcurl/blob/master/testing/example.proto) and also exposes [the implementation](https://godoc.org/github.com/fullstorydev/grpcurl/testing#TestServer) that is defined in that same package. This is the same test interface and implementation that is used in unit tests for `grpcurl`. + +For a possibly more interesting test server, take a look at `bankdemo`, which is a demo gRPC app that provides a more concrete RPC interface, including full-duplex bidirectional streaming methods, plus an example implementation. \ No newline at end of file diff --git a/cmd/testserver/testserver.go b/testing/cmd/testserver/testserver.go similarity index 100% rename from cmd/testserver/testserver.go rename to testing/cmd/testserver/testserver.go diff --git a/cmd/testserver/unix.go b/testing/cmd/testserver/unix.go similarity index 100% rename from cmd/testserver/unix.go rename to testing/cmd/testserver/unix.go