document generation for ops
This commit is contained in:
@@ -11,6 +11,9 @@ import (
|
||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
const tgsettleConnectorID = "tgsettle"
|
||||
@@ -152,12 +155,22 @@ func (s *Service) GetOperation(ctx context.Context, req *connectorv1.GetOperatio
|
||||
return nil, merrors.InvalidArgument("get_operation: operation_id is required")
|
||||
}
|
||||
operationID := strings.TrimSpace(req.GetOperationId())
|
||||
resp, err := s.GetTransfer(ctx, &chainv1.GetTransferRequest{TransferRef: operationID})
|
||||
|
||||
if s.repo == nil || s.repo.Payments() == nil {
|
||||
s.logger.Warn("Get operation storage unavailable", zap.String("operation_id", operationID))
|
||||
return nil, merrors.Internal("get_operation: storage is not configured")
|
||||
}
|
||||
|
||||
record, err := s.repo.Payments().FindByOperationRef(ctx, operationID)
|
||||
if err != nil {
|
||||
s.logger.Warn("Get operation failed", zap.String("operation_id", operationID), zap.Error(err))
|
||||
s.logger.Warn("Get operation lookup by operation_ref failed", zap.String("operation_id", operationID), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return &connectorv1.GetOperationResponse{Operation: transferToOperation(resp.GetTransfer())}, nil
|
||||
if record == nil {
|
||||
return nil, status.Error(codes.NotFound, "operation not found")
|
||||
}
|
||||
|
||||
return &connectorv1.GetOperationResponse{Operation: transferToOperation(transferFromPayment(record, nil))}, nil
|
||||
}
|
||||
|
||||
func (s *Service) ListOperations(_ context.Context, _ *connectorv1.ListOperationsRequest) (*connectorv1.ListOperationsResponse, error) {
|
||||
@@ -221,6 +234,19 @@ func transferToOperation(transfer *chainv1.Transfer) *connectorv1.Operation {
|
||||
CreatedAt: transfer.GetCreatedAt(),
|
||||
UpdatedAt: transfer.GetUpdatedAt(),
|
||||
}
|
||||
params := map[string]interface{}{}
|
||||
if paymentRef := strings.TrimSpace(transfer.GetPaymentRef()); paymentRef != "" {
|
||||
params["payment_ref"] = paymentRef
|
||||
}
|
||||
if organizationRef := strings.TrimSpace(transfer.GetOrganizationRef()); organizationRef != "" {
|
||||
params["organization_ref"] = organizationRef
|
||||
}
|
||||
if failureReason := strings.TrimSpace(transfer.GetFailureReason()); failureReason != "" {
|
||||
params["failure_reason"] = failureReason
|
||||
}
|
||||
if len(params) > 0 {
|
||||
op.Params = structFromMap(params)
|
||||
}
|
||||
if source := strings.TrimSpace(transfer.GetSourceWalletRef()); source != "" {
|
||||
op.From = &connectorv1.OperationParty{Ref: &connectorv1.OperationParty_Account{Account: &connectorv1.AccountRef{
|
||||
ConnectorId: tgsettleConnectorID,
|
||||
@@ -281,6 +307,17 @@ func operationAccountID(party *connectorv1.OperationParty) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func structFromMap(values map[string]interface{}) *structpb.Struct {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
result, err := structpb.NewStruct(values)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func operationLogFields(op *connectorv1.Operation) []zap.Field {
|
||||
if op == nil {
|
||||
return nil
|
||||
|
||||
@@ -675,6 +675,9 @@ func transferFromRequest(req *chainv1.SubmitTransferRequest) *chainv1.Transfer {
|
||||
SourceWalletRef: strings.TrimSpace(req.GetSourceWalletRef()),
|
||||
Destination: req.GetDestination(),
|
||||
RequestedAmount: req.GetAmount(),
|
||||
IntentRef: strings.TrimSpace(req.GetIntentRef()),
|
||||
OperationRef: strings.TrimSpace(req.GetOperationRef()),
|
||||
PaymentRef: strings.TrimSpace(req.GetPaymentRef()),
|
||||
Status: chainv1.TransferStatus_TRANSFER_CREATED,
|
||||
}
|
||||
}
|
||||
@@ -714,6 +717,10 @@ func transferFromPayment(record *storagemodel.PaymentRecord, req *chainv1.Submit
|
||||
IdempotencyKey: strings.TrimSpace(record.IdempotencyKey),
|
||||
RequestedAmount: requested,
|
||||
NetAmount: net,
|
||||
IntentRef: strings.TrimSpace(record.IntentRef),
|
||||
OperationRef: strings.TrimSpace(record.OperationRef),
|
||||
PaymentRef: strings.TrimSpace(record.PaymentRef),
|
||||
FailureReason: strings.TrimSpace(record.FailureReason),
|
||||
Status: status,
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,20 @@ func (f *fakePaymentsStore) FindByIdempotencyKey(_ context.Context, key string)
|
||||
return f.records[key], nil
|
||||
}
|
||||
|
||||
func (f *fakePaymentsStore) FindByOperationRef(_ context.Context, key string) (*storagemodel.PaymentRecord, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if f.records == nil {
|
||||
return nil, nil
|
||||
}
|
||||
for _, record := range f.records {
|
||||
if record != nil && record.OperationRef == key {
|
||||
return record, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *fakePaymentsStore) Upsert(_ context.Context, record *storagemodel.PaymentRecord) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
const (
|
||||
paymentsCollection = "payments"
|
||||
fieldIdempotencyKey = "idempotencyKey"
|
||||
fieldOperationRef = "operationRef"
|
||||
)
|
||||
|
||||
type Payments struct {
|
||||
@@ -44,6 +45,14 @@ func NewPayments(logger mlogger.Logger, db *mongo.Database) (*Payments, error) {
|
||||
logger.Error("Failed to create payments idempotency index", zap.Error(err), zap.String("index_field", fieldIdempotencyKey))
|
||||
return nil, err
|
||||
}
|
||||
if err := repo.CreateIndex(&ri.Definition{
|
||||
Keys: []ri.Key{{Field: fieldOperationRef, Sort: ri.Asc}},
|
||||
Unique: true,
|
||||
Sparse: true,
|
||||
}); err != nil {
|
||||
logger.Error("Failed to create payments operation index", zap.Error(err), zap.String("index_field", fieldOperationRef))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := &Payments{
|
||||
logger: logger,
|
||||
@@ -72,6 +81,25 @@ func (p *Payments) FindByIdempotencyKey(ctx context.Context, key string) (*model
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (p *Payments) FindByOperationRef(ctx context.Context, key string) (*model.PaymentRecord, error) {
|
||||
key = strings.TrimSpace(key)
|
||||
if key == "" {
|
||||
return nil, merrors.InvalidArgument("operation reference is required", "operation_ref")
|
||||
}
|
||||
var result model.PaymentRecord
|
||||
err := p.repo.FindOneByFilter(ctx, repository.Filter(fieldOperationRef, key), &result)
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {
|
||||
p.logger.Warn("Payment record lookup by operation ref failed", zap.String("operation_ref", key), zap.Error(err))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (p *Payments) Upsert(ctx context.Context, record *model.PaymentRecord) error {
|
||||
if record == nil {
|
||||
return merrors.InvalidArgument("payment record is nil", "record")
|
||||
@@ -82,6 +110,7 @@ func (p *Payments) Upsert(ctx context.Context, record *model.PaymentRecord) erro
|
||||
record.OutgoingLeg = strings.TrimSpace(record.OutgoingLeg)
|
||||
record.TargetChatID = strings.TrimSpace(record.TargetChatID)
|
||||
record.IntentRef = strings.TrimSpace(record.IntentRef)
|
||||
record.OperationRef = strings.TrimSpace(record.OperationRef)
|
||||
if record.PaymentIntentID == "" {
|
||||
return merrors.InvalidArgument("intention reference is required", "payment_intent_ref")
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type Repository interface {
|
||||
|
||||
type PaymentsStore interface {
|
||||
FindByIdempotencyKey(ctx context.Context, key string) (*model.PaymentRecord, error)
|
||||
FindByOperationRef(ctx context.Context, key string) (*model.PaymentRecord, error)
|
||||
Upsert(ctx context.Context, record *model.PaymentRecord) error
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user