Skip to content

Commit

Permalink
Merge pull request #211 from bitcoin-sv/feat-639-contact-api
Browse files Browse the repository at this point in the history
feat(BUX-639): handle contact api
  • Loading branch information
arkadiuszos4chain authored Apr 4, 2024
2 parents 735d91a + 24d3e40 commit c4a680f
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 1 deletion.
38 changes: 38 additions & 0 deletions contacts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package walletclient

import (
"context"

"github.com/bitcoin-sv/spv-wallet-go-client/transports"
"github.com/bitcoin-sv/spv-wallet/models"
)

// UpsertContact add or update contact. When adding a new contact, the system utilizes Paymail's PIKE capability to dispatch an invitation request, asking the counterparty to include the current user in their contacts.
func (b *WalletClient) UpsertContact(ctx context.Context, paymail, fullName string, metadata *models.Metadata) (*models.Contact, transports.ResponseError) {
return b.transport.UpsertContact(ctx, paymail, fullName, metadata, "")
}

// UpsertContactForPaymail add or update contact. When adding a new contact, the system utilizes Paymail's PIKE capability to dispatch an invitation request, asking the counterparty to include the current user specified paymail in their contacts.
func (b *WalletClient) UpsertContactForPaymail(ctx context.Context, paymail, fullName string, metadata *models.Metadata, requesterPaymail string) (*models.Contact, transports.ResponseError) {
return b.transport.UpsertContact(ctx, paymail, fullName, metadata, requesterPaymail)
}

// AcceptContact will accept the contact associated with the paymail
func (b *WalletClient) AcceptContact(ctx context.Context, paymail string) transports.ResponseError {
return b.transport.AcceptContact(ctx, paymail)
}

// RejectContact will reject the contact associated with the paymail
func (b *WalletClient) RejectContact(ctx context.Context, paymail string) transports.ResponseError {
return b.transport.RejectContact(ctx, paymail)
}

// ConfirmContact will confirm the contact associated with the paymail
func (b *WalletClient) ConfirmContact(ctx context.Context, paymail string) transports.ResponseError {
return b.transport.ConfirmContact(ctx, paymail)
}

// GetContacts will get contacts by conditions
func (b *WalletClient) GetContacts(ctx context.Context, conditions map[string]interface{}, metadata *models.Metadata, queryParams *transports.QueryParams) ([]*models.Contact, transports.ResponseError) {
return b.transport.GetContacts(ctx, conditions, metadata, queryParams)
}
87 changes: 87 additions & 0 deletions contacts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package walletclient

import (
"context"
"testing"

"github.com/bitcoin-sv/spv-wallet-go-client/fixtures"
"github.com/stretchr/testify/assert"
)

// TestRejectContact will test the RejectContact method
func TestContactActionsRouting(t *testing.T) {
tcs := []struct {
name string
route string
responsePayload string
f func(c *WalletClient) error
}{
{
name: "RejectContact",
route: "/contact/rejected/",
responsePayload: "{}",
f: func(c *WalletClient) error { return c.RejectContact(context.Background(), fixtures.PaymailAddress) },
},
{
name: "AcceptContact",
route: "/contact/accepted/",
responsePayload: "{}",
f: func(c *WalletClient) error { return c.AcceptContact(context.Background(), fixtures.PaymailAddress) },
},
{
name: "ConfirmContact",
route: "/contact/confirmed/",
responsePayload: "{}",
f: func(c *WalletClient) error { return c.ConfirmContact(context.Background(), fixtures.PaymailAddress) },
},
{
name: "GetContacts",
route: "/contact/search/",
responsePayload: "[]",
f: func(c *WalletClient) error {
_, err := c.GetContacts(context.Background(), nil, nil, nil)
return err
},
},
{
name: "UpsertContact",
route: "/contact/",
responsePayload: "{}",
f: func(c *WalletClient) error {
_, err := c.UpsertContact(context.Background(), "", "", nil)
return err
},
},
{
name: "UpsertContactForPaymail",
route: "/contact/",
responsePayload: "{}",
f: func(c *WalletClient) error {
_, err := c.UpsertContactForPaymail(context.Background(), "", "", nil, "")
return err
},
},
}

for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
// given
tmq := testTransportHandler{
Type: fixtures.RequestType,
Path: tc.route,
Result: tc.responsePayload,
ClientURL: fixtures.ServerURL,
Client: WithHTTPClient,
}

client := getTestWalletClient(tmq, true)

// when
err := tc.f(client)

// then
assert.NoError(t, err)
})
}

}
82 changes: 81 additions & 1 deletion transports/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ func (h *TransportHTTP) GetTransaction(ctx context.Context, txID string) (*model
return &transaction, nil
}

// GetTransactions will get a transactions by conditions
// GetTransactions will get transactions by conditions
func (h *TransportHTTP) GetTransactions(ctx context.Context, conditions map[string]interface{},
metadataConditions *models.Metadata, queryParams *QueryParams,
) ([]*models.Transaction, ResponseError) {
Expand Down Expand Up @@ -639,3 +639,83 @@ func (h *TransportHTTP) authenticateWithXpriv(sign bool, req *http.Request, xPri
func (h *TransportHTTP) authenticateWithAccessKey(req *http.Request, rawJSON []byte) ResponseError {
return SetSignatureFromAccessKey(&req.Header, hex.EncodeToString(h.accessKey.Serialise()), string(rawJSON))
}

// AcceptContact will accept the contact associated with the paymail
func (h *TransportHTTP) AcceptContact(ctx context.Context, paymail string) ResponseError {
if err := h.doHTTPRequest(
ctx, http.MethodPatch, "/contact/accepted/"+paymail, nil, h.xPriv, h.signRequest, nil,
); err != nil {
return err
}

return nil
}

// RejectContact will reject the contact associated with the paymail
func (h *TransportHTTP) RejectContact(ctx context.Context, paymail string) ResponseError {
if err := h.doHTTPRequest(
ctx, http.MethodPatch, "/contact/rejected/"+paymail, nil, h.xPriv, h.signRequest, nil,
); err != nil {
return err
}

return nil
}

// ConfirmContact will confirm the contact associated with the paymail
func (h *TransportHTTP) ConfirmContact(ctx context.Context, paymail string) ResponseError {
if err := h.doHTTPRequest(
ctx, http.MethodPatch, "/contact/confirmed/"+paymail, nil, h.xPriv, h.signRequest, nil,
); err != nil {
return err
}

return nil
}

// GetContacts will get contacts by conditions
func (h *TransportHTTP) GetContacts(ctx context.Context, conditions map[string]interface{}, metadata *models.Metadata, queryParams *QueryParams) ([]*models.Contact, ResponseError) {
jsonStr, err := json.Marshal(map[string]interface{}{
FieldConditions: conditions,
FieldMetadata: processMetadata(metadata),
FieldQueryParams: queryParams,
})
if err != nil {
return nil, WrapError(err)
}

var result []*models.Contact
if err := h.doHTTPRequest(
ctx, http.MethodPost, "/contact/search", jsonStr, h.xPriv, h.signRequest, &result,
); err != nil {
return nil, err
}

return result, nil
}

// UpsertContact add or update contact. When adding a new contact, the system utilizes Paymail's PIKE capability to dispatch an invitation request, asking the counterparty to include the current user in their contacts.
func (h *TransportHTTP) UpsertContact(ctx context.Context, paymail, fullName string, metadata *models.Metadata, requesterPaymail string) (*models.Contact, ResponseError) {
payload := map[string]interface{}{
"fullName": fullName,
FieldMetadata: processMetadata(metadata),
}

if requesterPaymail != "" {
payload["requesterPaymail"] = requesterPaymail
}

jsonStr, err := json.Marshal(payload)
if err != nil {
return nil, WrapError(err)
}

var result models.Contact
if err := h.doHTTPRequest(
ctx, http.MethodPut, "/contact/"+paymail, jsonStr, h.xPriv, h.signRequest, &result,
); err != nil {
return nil, err
}

return &result, nil
}
10 changes: 10 additions & 0 deletions transports/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ type TransactionService interface {
GetUtxosCount(ctx context.Context, conditions map[string]interface{}, metadata *models.Metadata) (int64, ResponseError)
}

// ContactService is the contact related requests
type ContactService interface {
AcceptContact(ctx context.Context, paymail string) ResponseError
RejectContact(ctx context.Context, paymail string) ResponseError
ConfirmContact(ctx context.Context, paymail string) ResponseError
GetContacts(ctx context.Context, conditions map[string]interface{}, metadata *models.Metadata, queryParams *QueryParams) ([]*models.Contact, ResponseError)
UpsertContact(ctx context.Context, paymail, fullName string, metadata *models.Metadata, requesterPaymail string) (*models.Contact, ResponseError)
}

// AdminService is the admin related requests
type AdminService interface {
AdminGetStatus(ctx context.Context) (bool, ResponseError)
Expand Down Expand Up @@ -79,6 +88,7 @@ type AdminService interface {
type TransportService interface {
AccessKeyService
AdminService
ContactService
DestinationService
TransactionService
XpubService
Expand Down

0 comments on commit c4a680f

Please sign in to comment.