feat(gl): scaffold general ledger grpc service
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
.PHONY: build generate test
|
||||||
|
|
||||||
|
generate:
|
||||||
|
buf generate ../proto --template ./buf.gen.yaml --path ../proto/base/v1 --path ../proto/ledger/v1
|
||||||
|
|
||||||
|
build: generate
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test ./...
|
||||||
@@ -5,3 +5,13 @@ committed wallet value movement so financial state can be reconstructed when a
|
|||||||
blockchain or provider is unavailable.
|
blockchain or provider is unavailable.
|
||||||
|
|
||||||
The implementation contract and invariants are defined in [DESIGN.md](DESIGN.md).
|
The implementation contract and invariants are defined in [DESIGN.md](DESIGN.md).
|
||||||
|
|
||||||
|
Generate protobufs, test, and build with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make generate
|
||||||
|
make test
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run locally with `go run ./cmd/gl -conf ./gl.cfg.toml`.
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Package health provides the service readiness use case.
|
||||||
|
package health
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type Database interface {
|
||||||
|
Ping(context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Serving bool
|
||||||
|
DatabaseReady bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
database Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(database Database) *Service {
|
||||||
|
return &Service{database: database}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Check(ctx context.Context) Status {
|
||||||
|
status := Status{Serving: true}
|
||||||
|
if s.database != nil {
|
||||||
|
status.DatabaseReady = s.database.Ping(ctx) == nil
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package health
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type databaseStub struct{ err error }
|
||||||
|
|
||||||
|
func (s databaseStub) Ping(context.Context) error { return s.err }
|
||||||
|
|
||||||
|
func TestCheckReportsDatabaseReadiness(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
db Database
|
||||||
|
ready bool
|
||||||
|
}{
|
||||||
|
{name: "not connected", db: nil, ready: false},
|
||||||
|
{name: "ready", db: databaseStub{}, ready: true},
|
||||||
|
{name: "unavailable", db: databaseStub{err: errors.New("down")}, ready: false},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got := NewService(tc.db).Check(context.Background())
|
||||||
|
if !got.Serving || got.DatabaseReady != tc.ready {
|
||||||
|
t.Fatalf("unexpected status: %+v", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
version: v2
|
||||||
|
clean: true
|
||||||
|
managed:
|
||||||
|
enabled: true
|
||||||
|
override:
|
||||||
|
- file_option: go_package_prefix
|
||||||
|
value: gl/gen
|
||||||
|
plugins:
|
||||||
|
- local: protoc-gen-go
|
||||||
|
out: gen
|
||||||
|
opt: paths=source_relative
|
||||||
|
- local: protoc-gen-go-grpc
|
||||||
|
out: gen
|
||||||
|
opt:
|
||||||
|
- paths=source_relative
|
||||||
|
- require_unimplemented_servers=false
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gl/application/health"
|
||||||
|
"gl/infrastructure/config"
|
||||||
|
grpcadapter "gl/interface/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
configPath := flag.String("conf", "./gl.cfg.toml", "path to the TOML configuration file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
cfg, err := config.Load(*configPath)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("load configuration", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
handler := grpcadapter.NewHealthHandler(health.NewService(nil))
|
||||||
|
slog.Info("starting GL gRPC service", "host", cfg.GRPC.Host, "port", cfg.GRPC.Port)
|
||||||
|
serverConfig := grpcadapter.ServerConfig{
|
||||||
|
Host: cfg.GRPC.Host,
|
||||||
|
Port: cfg.GRPC.Port,
|
||||||
|
ShutdownTimeout: cfg.GRPC.ShutdownTimeout,
|
||||||
|
}
|
||||||
|
if err := grpcadapter.Run(ctx, serverConfig, handler); err != nil {
|
||||||
|
slog.Error("GL service stopped", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.11
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: base/v1/msg.proto
|
||||||
|
|
||||||
|
package basev1
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Empty struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Empty) Reset() {
|
||||||
|
*x = Empty{}
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Empty) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Empty) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Empty) Descriptor() ([]byte, []int) {
|
||||||
|
return file_base_v1_msg_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusRes struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StatusRes) Reset() {
|
||||||
|
*x = StatusRes{}
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StatusRes) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*StatusRes) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *StatusRes) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[1]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use StatusRes.ProtoReflect.Descriptor instead.
|
||||||
|
func (*StatusRes) Descriptor() ([]byte, []int) {
|
||||||
|
return file_base_v1_msg_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StatusRes) GetSuccess() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Success
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdRes struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdRes) Reset() {
|
||||||
|
*x = IdRes{}
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdRes) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*IdRes) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *IdRes) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[2]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use IdRes.ProtoReflect.Descriptor instead.
|
||||||
|
func (*IdRes) Descriptor() ([]byte, []int) {
|
||||||
|
return file_base_v1_msg_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdRes) GetId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdReq struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdReq) Reset() {
|
||||||
|
*x = IdReq{}
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdReq) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*IdReq) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *IdReq) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[3]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use IdReq.ProtoReflect.Descriptor instead.
|
||||||
|
func (*IdReq) Descriptor() ([]byte, []int) {
|
||||||
|
return file_base_v1_msg_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IdReq) GetId() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type YesNoRes struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Yes bool `protobuf:"varint,1,opt,name=yes,proto3" json:"yes,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *YesNoRes) Reset() {
|
||||||
|
*x = YesNoRes{}
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *YesNoRes) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*YesNoRes) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *YesNoRes) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[4]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use YesNoRes.ProtoReflect.Descriptor instead.
|
||||||
|
func (*YesNoRes) Descriptor() ([]byte, []int) {
|
||||||
|
return file_base_v1_msg_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *YesNoRes) GetYes() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Yes
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaginationRespSample struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
PageNo uint32 `protobuf:"varint,2,opt,name=page_no,json=pageNo,proto3" json:"page_no,omitempty"`
|
||||||
|
PageSize uint32 `protobuf:"varint,3,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
|
||||||
|
TotalCount uint32 `protobuf:"varint,4,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PaginationRespSample) Reset() {
|
||||||
|
*x = PaginationRespSample{}
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PaginationRespSample) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PaginationRespSample) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PaginationRespSample) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_base_v1_msg_proto_msgTypes[5]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PaginationRespSample.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PaginationRespSample) Descriptor() ([]byte, []int) {
|
||||||
|
return file_base_v1_msg_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PaginationRespSample) GetPageNo() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.PageNo
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PaginationRespSample) GetPageSize() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.PageSize
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PaginationRespSample) GetTotalCount() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.TotalCount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_base_v1_msg_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_base_v1_msg_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"\x11base/v1/msg.proto\x12\abase.v1\"\a\n" +
|
||||||
|
"\x05Empty\"%\n" +
|
||||||
|
"\tStatusRes\x12\x18\n" +
|
||||||
|
"\asuccess\x18\x01 \x01(\bR\asuccess\"\x17\n" +
|
||||||
|
"\x05IdRes\x12\x0e\n" +
|
||||||
|
"\x02id\x18\x01 \x01(\x03R\x02id\"\x17\n" +
|
||||||
|
"\x05IdReq\x12\x0e\n" +
|
||||||
|
"\x02id\x18\x01 \x01(\x03R\x02id\"\x1c\n" +
|
||||||
|
"\bYesNoRes\x12\x10\n" +
|
||||||
|
"\x03yes\x18\x01 \x01(\bR\x03yes\"m\n" +
|
||||||
|
"\x14PaginationRespSample\x12\x17\n" +
|
||||||
|
"\apage_no\x18\x02 \x01(\rR\x06pageNo\x12\x1b\n" +
|
||||||
|
"\tpage_size\x18\x03 \x01(\rR\bpageSize\x12\x1f\n" +
|
||||||
|
"\vtotal_count\x18\x04 \x01(\rR\n" +
|
||||||
|
"totalCountBk\n" +
|
||||||
|
"\vcom.base.v1B\bMsgProtoP\x01Z\x15gl/gen/base/v1;basev1\xa2\x02\x03BXX\xaa\x02\aBase.V1\xca\x02\aBase\\V1\xe2\x02\x13Base\\V1\\GPBMetadata\xea\x02\bBase::V1b\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_base_v1_msg_proto_rawDescOnce sync.Once
|
||||||
|
file_base_v1_msg_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_base_v1_msg_proto_rawDescGZIP() []byte {
|
||||||
|
file_base_v1_msg_proto_rawDescOnce.Do(func() {
|
||||||
|
file_base_v1_msg_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_base_v1_msg_proto_rawDesc), len(file_base_v1_msg_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_base_v1_msg_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_base_v1_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||||
|
var file_base_v1_msg_proto_goTypes = []any{
|
||||||
|
(*Empty)(nil), // 0: base.v1.Empty
|
||||||
|
(*StatusRes)(nil), // 1: base.v1.StatusRes
|
||||||
|
(*IdRes)(nil), // 2: base.v1.IdRes
|
||||||
|
(*IdReq)(nil), // 3: base.v1.IdReq
|
||||||
|
(*YesNoRes)(nil), // 4: base.v1.YesNoRes
|
||||||
|
(*PaginationRespSample)(nil), // 5: base.v1.PaginationRespSample
|
||||||
|
}
|
||||||
|
var file_base_v1_msg_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_base_v1_msg_proto_init() }
|
||||||
|
func file_base_v1_msg_proto_init() {
|
||||||
|
if File_base_v1_msg_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_base_v1_msg_proto_rawDesc), len(file_base_v1_msg_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 6,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_base_v1_msg_proto_goTypes,
|
||||||
|
DependencyIndexes: file_base_v1_msg_proto_depIdxs,
|
||||||
|
MessageInfos: file_base_v1_msg_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_base_v1_msg_proto = out.File
|
||||||
|
file_base_v1_msg_proto_goTypes = nil
|
||||||
|
file_base_v1_msg_proto_depIdxs = nil
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.11
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: ledger/v1/srv.proto
|
||||||
|
|
||||||
|
package ledgerv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "gl/gen/base/v1"
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
var File_ledger_v1_srv_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_ledger_v1_srv_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"\x13ledger/v1/srv.proto\x12\tledger.v1\x1a\x11base/v1/msg.proto\x1a\x13ledger/v1/msg.proto2\xbe\x04\n" +
|
||||||
|
"\x14GeneralLedgerService\x123\n" +
|
||||||
|
"\x06Health\x12\x0e.base.v1.Empty\x1a\x19.ledger.v1.HealthResponse\x12R\n" +
|
||||||
|
"\rAppendJournal\x12\x1f.ledger.v1.AppendJournalRequest\x1a .ledger.v1.AppendJournalResponse\x12m\n" +
|
||||||
|
"\x16AppendTransactionEvent\x12(.ledger.v1.AppendTransactionEventRequest\x1a).ledger.v1.AppendTransactionEventResponse\x12>\n" +
|
||||||
|
"\n" +
|
||||||
|
"GetJournal\x12\x1c.ledger.v1.GetJournalRequest\x1a\x12.ledger.v1.Journal\x12L\n" +
|
||||||
|
"\vListEntries\x12\x1d.ledger.v1.ListEntriesRequest\x1a\x1e.ledger.v1.ListEntriesResponse\x12I\n" +
|
||||||
|
"\n" +
|
||||||
|
"GetBalance\x12\x1c.ledger.v1.GetBalanceRequest\x1a\x1d.ledger.v1.GetBalanceResponse\x12U\n" +
|
||||||
|
"\x0eReplayJournals\x12 .ledger.v1.ReplayJournalsRequest\x1a!.ledger.v1.ReplayJournalsResponseBy\n" +
|
||||||
|
"\rcom.ledger.v1B\bSrvProtoP\x01Z\x19gl/gen/ledger/v1;ledgerv1\xa2\x02\x03LXX\xaa\x02\tLedger.V1\xca\x02\tLedger\\V1\xe2\x02\x15Ledger\\V1\\GPBMetadata\xea\x02\n" +
|
||||||
|
"Ledger::V1b\x06proto3"
|
||||||
|
|
||||||
|
var file_ledger_v1_srv_proto_goTypes = []any{
|
||||||
|
(*v1.Empty)(nil), // 0: base.v1.Empty
|
||||||
|
(*AppendJournalRequest)(nil), // 1: ledger.v1.AppendJournalRequest
|
||||||
|
(*AppendTransactionEventRequest)(nil), // 2: ledger.v1.AppendTransactionEventRequest
|
||||||
|
(*GetJournalRequest)(nil), // 3: ledger.v1.GetJournalRequest
|
||||||
|
(*ListEntriesRequest)(nil), // 4: ledger.v1.ListEntriesRequest
|
||||||
|
(*GetBalanceRequest)(nil), // 5: ledger.v1.GetBalanceRequest
|
||||||
|
(*ReplayJournalsRequest)(nil), // 6: ledger.v1.ReplayJournalsRequest
|
||||||
|
(*HealthResponse)(nil), // 7: ledger.v1.HealthResponse
|
||||||
|
(*AppendJournalResponse)(nil), // 8: ledger.v1.AppendJournalResponse
|
||||||
|
(*AppendTransactionEventResponse)(nil), // 9: ledger.v1.AppendTransactionEventResponse
|
||||||
|
(*Journal)(nil), // 10: ledger.v1.Journal
|
||||||
|
(*ListEntriesResponse)(nil), // 11: ledger.v1.ListEntriesResponse
|
||||||
|
(*GetBalanceResponse)(nil), // 12: ledger.v1.GetBalanceResponse
|
||||||
|
(*ReplayJournalsResponse)(nil), // 13: ledger.v1.ReplayJournalsResponse
|
||||||
|
}
|
||||||
|
var file_ledger_v1_srv_proto_depIdxs = []int32{
|
||||||
|
0, // 0: ledger.v1.GeneralLedgerService.Health:input_type -> base.v1.Empty
|
||||||
|
1, // 1: ledger.v1.GeneralLedgerService.AppendJournal:input_type -> ledger.v1.AppendJournalRequest
|
||||||
|
2, // 2: ledger.v1.GeneralLedgerService.AppendTransactionEvent:input_type -> ledger.v1.AppendTransactionEventRequest
|
||||||
|
3, // 3: ledger.v1.GeneralLedgerService.GetJournal:input_type -> ledger.v1.GetJournalRequest
|
||||||
|
4, // 4: ledger.v1.GeneralLedgerService.ListEntries:input_type -> ledger.v1.ListEntriesRequest
|
||||||
|
5, // 5: ledger.v1.GeneralLedgerService.GetBalance:input_type -> ledger.v1.GetBalanceRequest
|
||||||
|
6, // 6: ledger.v1.GeneralLedgerService.ReplayJournals:input_type -> ledger.v1.ReplayJournalsRequest
|
||||||
|
7, // 7: ledger.v1.GeneralLedgerService.Health:output_type -> ledger.v1.HealthResponse
|
||||||
|
8, // 8: ledger.v1.GeneralLedgerService.AppendJournal:output_type -> ledger.v1.AppendJournalResponse
|
||||||
|
9, // 9: ledger.v1.GeneralLedgerService.AppendTransactionEvent:output_type -> ledger.v1.AppendTransactionEventResponse
|
||||||
|
10, // 10: ledger.v1.GeneralLedgerService.GetJournal:output_type -> ledger.v1.Journal
|
||||||
|
11, // 11: ledger.v1.GeneralLedgerService.ListEntries:output_type -> ledger.v1.ListEntriesResponse
|
||||||
|
12, // 12: ledger.v1.GeneralLedgerService.GetBalance:output_type -> ledger.v1.GetBalanceResponse
|
||||||
|
13, // 13: ledger.v1.GeneralLedgerService.ReplayJournals:output_type -> ledger.v1.ReplayJournalsResponse
|
||||||
|
7, // [7:14] is the sub-list for method output_type
|
||||||
|
0, // [0:7] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_ledger_v1_srv_proto_init() }
|
||||||
|
func file_ledger_v1_srv_proto_init() {
|
||||||
|
if File_ledger_v1_srv_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file_ledger_v1_msg_proto_init()
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_ledger_v1_srv_proto_rawDesc), len(file_ledger_v1_srv_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 0,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_ledger_v1_srv_proto_goTypes,
|
||||||
|
DependencyIndexes: file_ledger_v1_srv_proto_depIdxs,
|
||||||
|
}.Build()
|
||||||
|
File_ledger_v1_srv_proto = out.File
|
||||||
|
file_ledger_v1_srv_proto_goTypes = nil
|
||||||
|
file_ledger_v1_srv_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.6.2
|
||||||
|
// - protoc (unknown)
|
||||||
|
// source: ledger/v1/srv.proto
|
||||||
|
|
||||||
|
package ledgerv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
v1 "gl/gen/base/v1"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.64.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion9
|
||||||
|
|
||||||
|
const (
|
||||||
|
GeneralLedgerService_Health_FullMethodName = "/ledger.v1.GeneralLedgerService/Health"
|
||||||
|
GeneralLedgerService_AppendJournal_FullMethodName = "/ledger.v1.GeneralLedgerService/AppendJournal"
|
||||||
|
GeneralLedgerService_AppendTransactionEvent_FullMethodName = "/ledger.v1.GeneralLedgerService/AppendTransactionEvent"
|
||||||
|
GeneralLedgerService_GetJournal_FullMethodName = "/ledger.v1.GeneralLedgerService/GetJournal"
|
||||||
|
GeneralLedgerService_ListEntries_FullMethodName = "/ledger.v1.GeneralLedgerService/ListEntries"
|
||||||
|
GeneralLedgerService_GetBalance_FullMethodName = "/ledger.v1.GeneralLedgerService/GetBalance"
|
||||||
|
GeneralLedgerService_ReplayJournals_FullMethodName = "/ledger.v1.GeneralLedgerService/ReplayJournals"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GeneralLedgerServiceClient is the client API for GeneralLedgerService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type GeneralLedgerServiceClient interface {
|
||||||
|
Health(ctx context.Context, in *v1.Empty, opts ...grpc.CallOption) (*HealthResponse, error)
|
||||||
|
AppendJournal(ctx context.Context, in *AppendJournalRequest, opts ...grpc.CallOption) (*AppendJournalResponse, error)
|
||||||
|
AppendTransactionEvent(ctx context.Context, in *AppendTransactionEventRequest, opts ...grpc.CallOption) (*AppendTransactionEventResponse, error)
|
||||||
|
GetJournal(ctx context.Context, in *GetJournalRequest, opts ...grpc.CallOption) (*Journal, error)
|
||||||
|
ListEntries(ctx context.Context, in *ListEntriesRequest, opts ...grpc.CallOption) (*ListEntriesResponse, error)
|
||||||
|
GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error)
|
||||||
|
ReplayJournals(ctx context.Context, in *ReplayJournalsRequest, opts ...grpc.CallOption) (*ReplayJournalsResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type generalLedgerServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGeneralLedgerServiceClient(cc grpc.ClientConnInterface) GeneralLedgerServiceClient {
|
||||||
|
return &generalLedgerServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *generalLedgerServiceClient) Health(ctx context.Context, in *v1.Empty, opts ...grpc.CallOption) (*HealthResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(HealthResponse)
|
||||||
|
err := c.cc.Invoke(ctx, GeneralLedgerService_Health_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *generalLedgerServiceClient) AppendJournal(ctx context.Context, in *AppendJournalRequest, opts ...grpc.CallOption) (*AppendJournalResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(AppendJournalResponse)
|
||||||
|
err := c.cc.Invoke(ctx, GeneralLedgerService_AppendJournal_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *generalLedgerServiceClient) AppendTransactionEvent(ctx context.Context, in *AppendTransactionEventRequest, opts ...grpc.CallOption) (*AppendTransactionEventResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(AppendTransactionEventResponse)
|
||||||
|
err := c.cc.Invoke(ctx, GeneralLedgerService_AppendTransactionEvent_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *generalLedgerServiceClient) GetJournal(ctx context.Context, in *GetJournalRequest, opts ...grpc.CallOption) (*Journal, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(Journal)
|
||||||
|
err := c.cc.Invoke(ctx, GeneralLedgerService_GetJournal_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *generalLedgerServiceClient) ListEntries(ctx context.Context, in *ListEntriesRequest, opts ...grpc.CallOption) (*ListEntriesResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(ListEntriesResponse)
|
||||||
|
err := c.cc.Invoke(ctx, GeneralLedgerService_ListEntries_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *generalLedgerServiceClient) GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(GetBalanceResponse)
|
||||||
|
err := c.cc.Invoke(ctx, GeneralLedgerService_GetBalance_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *generalLedgerServiceClient) ReplayJournals(ctx context.Context, in *ReplayJournalsRequest, opts ...grpc.CallOption) (*ReplayJournalsResponse, error) {
|
||||||
|
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||||
|
out := new(ReplayJournalsResponse)
|
||||||
|
err := c.cc.Invoke(ctx, GeneralLedgerService_ReplayJournals_FullMethodName, in, out, cOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeneralLedgerServiceServer is the server API for GeneralLedgerService service.
|
||||||
|
// All implementations should embed UnimplementedGeneralLedgerServiceServer
|
||||||
|
// for forward compatibility.
|
||||||
|
type GeneralLedgerServiceServer interface {
|
||||||
|
Health(context.Context, *v1.Empty) (*HealthResponse, error)
|
||||||
|
AppendJournal(context.Context, *AppendJournalRequest) (*AppendJournalResponse, error)
|
||||||
|
AppendTransactionEvent(context.Context, *AppendTransactionEventRequest) (*AppendTransactionEventResponse, error)
|
||||||
|
GetJournal(context.Context, *GetJournalRequest) (*Journal, error)
|
||||||
|
ListEntries(context.Context, *ListEntriesRequest) (*ListEntriesResponse, error)
|
||||||
|
GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error)
|
||||||
|
ReplayJournals(context.Context, *ReplayJournalsRequest) (*ReplayJournalsResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedGeneralLedgerServiceServer should be embedded to have
|
||||||
|
// forward compatible implementations.
|
||||||
|
//
|
||||||
|
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||||
|
// pointer dereference when methods are called.
|
||||||
|
type UnimplementedGeneralLedgerServiceServer struct{}
|
||||||
|
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) Health(context.Context, *v1.Empty) (*HealthResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method Health not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) AppendJournal(context.Context, *AppendJournalRequest) (*AppendJournalResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method AppendJournal not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) AppendTransactionEvent(context.Context, *AppendTransactionEventRequest) (*AppendTransactionEventResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method AppendTransactionEvent not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) GetJournal(context.Context, *GetJournalRequest) (*Journal, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method GetJournal not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) ListEntries(context.Context, *ListEntriesRequest) (*ListEntriesResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method ListEntries not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method GetBalance not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) ReplayJournals(context.Context, *ReplayJournalsRequest) (*ReplayJournalsResponse, error) {
|
||||||
|
return nil, status.Error(codes.Unimplemented, "method ReplayJournals not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedGeneralLedgerServiceServer) testEmbeddedByValue() {}
|
||||||
|
|
||||||
|
// UnsafeGeneralLedgerServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to GeneralLedgerServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeGeneralLedgerServiceServer interface {
|
||||||
|
mustEmbedUnimplementedGeneralLedgerServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterGeneralLedgerServiceServer(s grpc.ServiceRegistrar, srv GeneralLedgerServiceServer) {
|
||||||
|
// If the following call panics, it indicates UnimplementedGeneralLedgerServiceServer was
|
||||||
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||||
|
t.testEmbeddedByValue()
|
||||||
|
}
|
||||||
|
s.RegisterService(&GeneralLedgerService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GeneralLedgerService_Health_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(v1.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeneralLedgerServiceServer).Health(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: GeneralLedgerService_Health_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeneralLedgerServiceServer).Health(ctx, req.(*v1.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GeneralLedgerService_AppendJournal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(AppendJournalRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeneralLedgerServiceServer).AppendJournal(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: GeneralLedgerService_AppendJournal_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeneralLedgerServiceServer).AppendJournal(ctx, req.(*AppendJournalRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GeneralLedgerService_AppendTransactionEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(AppendTransactionEventRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeneralLedgerServiceServer).AppendTransactionEvent(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: GeneralLedgerService_AppendTransactionEvent_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeneralLedgerServiceServer).AppendTransactionEvent(ctx, req.(*AppendTransactionEventRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GeneralLedgerService_GetJournal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetJournalRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeneralLedgerServiceServer).GetJournal(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: GeneralLedgerService_GetJournal_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeneralLedgerServiceServer).GetJournal(ctx, req.(*GetJournalRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GeneralLedgerService_ListEntries_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ListEntriesRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeneralLedgerServiceServer).ListEntries(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: GeneralLedgerService_ListEntries_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeneralLedgerServiceServer).ListEntries(ctx, req.(*ListEntriesRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GeneralLedgerService_GetBalance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetBalanceRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeneralLedgerServiceServer).GetBalance(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: GeneralLedgerService_GetBalance_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeneralLedgerServiceServer).GetBalance(ctx, req.(*GetBalanceRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _GeneralLedgerService_ReplayJournals_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ReplayJournalsRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(GeneralLedgerServiceServer).ReplayJournals(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: GeneralLedgerService_ReplayJournals_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(GeneralLedgerServiceServer).ReplayJournals(ctx, req.(*ReplayJournalsRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeneralLedgerService_ServiceDesc is the grpc.ServiceDesc for GeneralLedgerService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var GeneralLedgerService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "ledger.v1.GeneralLedgerService",
|
||||||
|
HandlerType: (*GeneralLedgerServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "Health",
|
||||||
|
Handler: _GeneralLedgerService_Health_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "AppendJournal",
|
||||||
|
Handler: _GeneralLedgerService_AppendJournal_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "AppendTransactionEvent",
|
||||||
|
Handler: _GeneralLedgerService_AppendTransactionEvent_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetJournal",
|
||||||
|
Handler: _GeneralLedgerService_GetJournal_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ListEntries",
|
||||||
|
Handler: _GeneralLedgerService_ListEntries_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetBalance",
|
||||||
|
Handler: _GeneralLedgerService_GetBalance_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ReplayJournals",
|
||||||
|
Handler: _GeneralLedgerService_ReplayJournals_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "ledger/v1/srv.proto",
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
environment = "local"
|
||||||
|
|
||||||
|
[grpc]
|
||||||
|
host = "0.0.0.0"
|
||||||
|
port = 8500
|
||||||
|
shutdown-timeout = "10s"
|
||||||
|
|
||||||
|
[database]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 5432
|
||||||
|
name = "gl_db"
|
||||||
|
user = "postgres"
|
||||||
|
password = ""
|
||||||
|
ssl-mode = "disable"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
module gl
|
||||||
|
|
||||||
|
go 1.24
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/knadh/koanf/parsers/toml v0.1.0
|
||||||
|
github.com/knadh/koanf/providers/file v1.2.1
|
||||||
|
github.com/knadh/koanf/v2 v2.3.4
|
||||||
|
google.golang.org/grpc v1.67.1
|
||||||
|
google.golang.org/protobuf v1.36.6
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
|
github.com/knadh/koanf/maps v0.1.2 // indirect
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
|
golang.org/x/net v0.28.0 // indirect
|
||||||
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
|
golang.org/x/text v0.17.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
|
||||||
|
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
|
||||||
|
github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=
|
||||||
|
github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=
|
||||||
|
github.com/knadh/koanf/providers/file v1.2.1 h1:bEWbtQwYrA+W2DtdBrQWyXqJaJSG3KrP3AESOJYp9wM=
|
||||||
|
github.com/knadh/koanf/providers/file v1.2.1/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
|
||||||
|
github.com/knadh/koanf/v2 v2.3.4 h1:fnynNSDlujWE+v83hAp8wKr/cdoxHLO0629SN+U8Urc=
|
||||||
|
github.com/knadh/koanf/v2 v2.3.4/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||||
|
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||||
|
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||||
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// Package config owns GL configuration loading and validation.
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/knadh/koanf/parsers/toml"
|
||||||
|
"github.com/knadh/koanf/providers/file"
|
||||||
|
"github.com/knadh/koanf/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Environment string `koanf:"environment"`
|
||||||
|
GRPC GRPCConfig `koanf:"grpc"`
|
||||||
|
Database DatabaseConfig `koanf:"database"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GRPCConfig struct {
|
||||||
|
Host string `koanf:"host"`
|
||||||
|
Port int `koanf:"port"`
|
||||||
|
ShutdownTimeout time.Duration `koanf:"shutdown-timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Host string `koanf:"host"`
|
||||||
|
Port int `koanf:"port"`
|
||||||
|
Name string `koanf:"name"`
|
||||||
|
User string `koanf:"user"`
|
||||||
|
Password string `koanf:"password"`
|
||||||
|
SSLMode string `koanf:"ssl-mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load(path string) (*Config, error) {
|
||||||
|
cfg := &Config{
|
||||||
|
Environment: "local",
|
||||||
|
GRPC: GRPCConfig{
|
||||||
|
Host: "0.0.0.0",
|
||||||
|
Port: 8500,
|
||||||
|
ShutdownTimeout: 10 * time.Second,
|
||||||
|
},
|
||||||
|
Database: DatabaseConfig{
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
Port: 5432,
|
||||||
|
Name: "gl_db",
|
||||||
|
User: "postgres",
|
||||||
|
SSLMode: "disable",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
k := koanf.New(".")
|
||||||
|
if err := k.Load(file.Provider(path), toml.Parser()); err != nil {
|
||||||
|
return nil, fmt.Errorf("load config: %w", err)
|
||||||
|
}
|
||||||
|
if err := k.Unmarshal("", cfg); err != nil {
|
||||||
|
return nil, fmt.Errorf("decode config: %w", err)
|
||||||
|
}
|
||||||
|
if err := cfg.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Validate() error {
|
||||||
|
if c.GRPC.Host == "" {
|
||||||
|
return fmt.Errorf("grpc host is required")
|
||||||
|
}
|
||||||
|
if c.GRPC.Port < 0 || c.GRPC.Port > 65535 {
|
||||||
|
return fmt.Errorf("grpc port must be between 0 and 65535")
|
||||||
|
}
|
||||||
|
if c.GRPC.ShutdownTimeout <= 0 {
|
||||||
|
return fmt.Errorf("grpc shutdown timeout must be positive")
|
||||||
|
}
|
||||||
|
if c.Database.Host == "" || c.Database.Name == "" || c.Database.User == "" {
|
||||||
|
return fmt.Errorf("database host, name, and user are required")
|
||||||
|
}
|
||||||
|
if c.Database.Port < 1 || c.Database.Port > 65535 {
|
||||||
|
return fmt.Errorf("database port must be between 1 and 65535")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadUsesDefaultsAndOverrides(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
path := filepath.Join(dir, "gl.toml")
|
||||||
|
contents := []byte("[grpc]\nport = 0\nshutdown-timeout = \"3s\"\n")
|
||||||
|
if err := os.WriteFile(path, contents, 0o600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := Load(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if cfg.GRPC.Port != 0 || cfg.GRPC.ShutdownTimeout != 3*time.Second {
|
||||||
|
t.Fatalf("unexpected grpc config: %+v", cfg.GRPC)
|
||||||
|
}
|
||||||
|
if cfg.Database.Name != "gl_db" || cfg.Database.Port != 5432 {
|
||||||
|
t.Fatalf("unexpected database defaults: %+v", cfg.Database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadRejectsInvalidConfiguration(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
path := filepath.Join(dir, "gl.toml")
|
||||||
|
if err := os.WriteFile(path, []byte("[grpc]\nshutdown-timeout = \"0s\"\n"), 0o600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Load(path); err == nil {
|
||||||
|
t.Fatal("expected validation error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadReturnsMissingFileError(t *testing.T) {
|
||||||
|
if _, err := Load(filepath.Join(t.TempDir(), "missing.toml")); err == nil {
|
||||||
|
t.Fatal("expected missing file error")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package grpcadapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gl/application/health"
|
||||||
|
basev1 "gl/gen/base/v1"
|
||||||
|
ledgerv1 "gl/gen/ledger/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HealthHandler struct {
|
||||||
|
ledgerv1.UnimplementedGeneralLedgerServiceServer
|
||||||
|
service *health.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHealthHandler(service *health.Service) *HealthHandler {
|
||||||
|
return &HealthHandler{service: service}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HealthHandler) Health(ctx context.Context, _ *basev1.Empty) (*ledgerv1.HealthResponse, error) {
|
||||||
|
result := h.service.Check(ctx)
|
||||||
|
return &ledgerv1.HealthResponse{
|
||||||
|
Serving: result.Serving,
|
||||||
|
DatabaseReady: result.DatabaseReady,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package grpcadapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gl/application/health"
|
||||||
|
basev1 "gl/gen/base/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHealth(t *testing.T) {
|
||||||
|
response, err := NewHealthHandler(health.NewService(nil)).Health(context.Background(), &basev1.Empty{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !response.Serving || response.DatabaseReady {
|
||||||
|
t.Fatalf("unexpected response: %+v", response)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package grpcadapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
ledgerv1 "gl/gen/ledger/v1"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/reflection"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
ShutdownTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(ctx context.Context, cfg ServerConfig, handler ledgerv1.GeneralLedgerServiceServer) error {
|
||||||
|
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("listen for grpc: %w", err)
|
||||||
|
}
|
||||||
|
return runWithListener(ctx, cfg.ShutdownTimeout, listener, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWithListener(ctx context.Context, shutdownTimeout time.Duration, listener net.Listener, handler ledgerv1.GeneralLedgerServiceServer) error {
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
server := grpc.NewServer()
|
||||||
|
ledgerv1.RegisterGeneralLedgerServiceServer(server, handler)
|
||||||
|
reflection.Register(server)
|
||||||
|
|
||||||
|
serveErr := make(chan error, 1)
|
||||||
|
go func() { serveErr <- server.Serve(listener) }()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-serveErr:
|
||||||
|
if errors.Is(err, grpc.ErrServerStopped) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("serve grpc: %w", err)
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
|
||||||
|
stopped := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
server.GracefulStop()
|
||||||
|
close(stopped)
|
||||||
|
}()
|
||||||
|
|
||||||
|
timer := time.NewTimer(shutdownTimeout)
|
||||||
|
defer timer.Stop()
|
||||||
|
select {
|
||||||
|
case <-stopped:
|
||||||
|
case <-timer.C:
|
||||||
|
server.Stop()
|
||||||
|
<-stopped
|
||||||
|
}
|
||||||
|
|
||||||
|
err := <-serveErr
|
||||||
|
if err != nil && !errors.Is(err, grpc.ErrServerStopped) {
|
||||||
|
return fmt.Errorf("serve grpc: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package grpcadapter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gl/application/health"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/test/bufconn"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunStopsWhenContextIsCancelled(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
err := runWithListener(
|
||||||
|
ctx,
|
||||||
|
time.Second,
|
||||||
|
bufconn.Listen(1024),
|
||||||
|
NewHealthHandler(health.NewService(nil)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user