generated from mrz1836/go-template
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(SPV-1249) paymail list & assert with JSON template (#793)
- Loading branch information
1 parent
c8bd64f
commit 492beda
Showing
18 changed files
with
1,100 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package paymails | ||
|
||
import ( | ||
"github.com/bitcoin-sv/spv-wallet/server/handlers" | ||
routes "github.com/bitcoin-sv/spv-wallet/server/handlers" | ||
) | ||
|
||
// RegisterRoutes creates the specific package routes in RESTful style | ||
func RegisterRoutes(handlersManager *routes.Manager) { | ||
group := handlersManager.Group(routes.GroupAPI, "/paymails") | ||
group.GET("", handlers.AsUser(paymailAddressesSearch)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package paymails | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/bitcoin-sv/spv-wallet/actions/common" | ||
"github.com/bitcoin-sv/spv-wallet/engine/spverrors" | ||
"github.com/bitcoin-sv/spv-wallet/internal/query" | ||
"github.com/bitcoin-sv/spv-wallet/mappings" | ||
"github.com/bitcoin-sv/spv-wallet/models/filter" | ||
"github.com/bitcoin-sv/spv-wallet/models/response" | ||
"github.com/bitcoin-sv/spv-wallet/server/reqctx" | ||
"github.com/gin-gonic/gin" | ||
) | ||
|
||
// paymailAddressesSearch will fetch a list of paymail addresses filtered by metadata | ||
// Paymail addresses search by metadata | ||
// @Summary Paymail addresses search | ||
// @Description Paymail addresses search | ||
// @Tags Users | ||
// @Produce json | ||
// @Param SearchPaymails body filter.PaymailFilter false "Supports targeted resource searches with filters and metadata, plus options for pagination and sorting to streamline data exploration and analysis" | ||
// @Success 200 {object} []response.PaymailAddress "List of paymail addresses" | ||
// @Failure 400 "Bad request - Error while parsing SearchPaymails from request body" | ||
// @Failure 500 "Internal server error - Error while searching for paymail addresses" | ||
// @Router /api/v1/paymails [get] | ||
// @Security x-auth-xpub | ||
func paymailAddressesSearch(c *gin.Context, userContext *reqctx.UserContext) { | ||
logger := reqctx.Logger(c) | ||
|
||
searchParams, err := query.ParseSearchParams[filter.PaymailFilter](c) | ||
if err != nil { | ||
spverrors.ErrorResponse(c, spverrors.ErrCannotParseQueryParams.WithTrace(err), logger) | ||
return | ||
} | ||
|
||
conditions := searchParams.Conditions.ToDbConditions() | ||
metadata := mappings.MapToMetadata(searchParams.Metadata) | ||
pageOptions := mappings.MapToDbQueryParams(&searchParams.Page) | ||
|
||
paymailAddresses, err := reqctx.Engine(c).GetPaymailAddressesByXPubID( | ||
c, | ||
userContext.GetXPubID(), | ||
metadata, | ||
conditions, | ||
pageOptions, | ||
) | ||
if err != nil { | ||
spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindPaymail.WithTrace(err), logger) | ||
return | ||
} | ||
|
||
count, err := reqctx.Engine(c).GetPaymailAddressesCount(c.Request.Context(), metadata, conditions) | ||
if err != nil { | ||
spverrors.ErrorResponse(c, spverrors.ErrCouldNotFindPaymail.WithTrace(err), logger) | ||
return | ||
} | ||
|
||
paymailAddressContracts := common.MapToTypeContracts(paymailAddresses, mappings.MapToPaymailContract) | ||
|
||
result := response.PageModel[response.PaymailAddress]{ | ||
Content: paymailAddressContracts, | ||
Page: common.GetPageDescriptionFromSearchParams(pageOptions, count), | ||
} | ||
c.JSON(http.StatusOK, result) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package paymails_test | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"github.com/bitcoin-sv/spv-wallet/actions/testabilities" | ||
"github.com/bitcoin-sv/spv-wallet/engine/tester/fixtures" | ||
) | ||
|
||
func TestCurrentUserPaymails(t *testing.T) { | ||
t.Run("return paymails info for user (single paymail)", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.New(t) | ||
cleanup := given.StartedSPVWallet() | ||
defer cleanup() | ||
client := given.HttpClient().ForGivenUser(fixtures.Sender) | ||
|
||
// when: | ||
res, _ := client.R().Get("/api/v1/paymails") | ||
|
||
// then: | ||
then.Response(res). | ||
IsOK(). | ||
WithJSONMatching(`{ | ||
"content": [ | ||
{ | ||
"address": "{{.Address}}", | ||
"alias": "{{.Alias}}", | ||
"avatar": "{{ matchURL | orEmpty }}", | ||
"createdAt": "{{ matchTimestamp }}", | ||
"deletedAt": null, | ||
"domain": "{{.Domain}}", | ||
"id": "{{ matchID64 }}", | ||
"metadata": "*", | ||
"publicName": "{{.PublicName}}", | ||
"updatedAt": "{{ matchTimestamp }}", | ||
"xpubId": "{{.XPubID}}" | ||
} | ||
], | ||
"page": { | ||
"number": 1, | ||
"size": 50, | ||
"totalElements": 1, | ||
"totalPages": 1 | ||
} | ||
}`, map[string]any{ | ||
"Address": strings.ToLower(fixtures.Sender.Paymails[0]), | ||
"PublicName": fixtures.Sender.Paymails[0], | ||
"Alias": getAliasFromPaymail(t, fixtures.Sender.Paymails[0]), | ||
"XPubID": fixtures.Sender.XPubID(), | ||
"Domain": fixtures.PaymailDomain, | ||
}) | ||
}) | ||
|
||
t.Run("return paymails info for user (multiple paymails)", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.New(t) | ||
cleanup := given.StartedSPVWallet() | ||
defer cleanup() | ||
client := given.HttpClient().ForGivenUser(fixtures.UserWithMorePaymails) | ||
|
||
// when: | ||
res, _ := client.R().Get("/api/v1/paymails") | ||
|
||
// then: | ||
then.Response(res). | ||
IsOK(). | ||
WithJSONMatching(`{ | ||
"content": [ | ||
{ | ||
"address": "{{.SecondPaymail.Address}}", | ||
"alias": "{{.SecondPaymail.Alias}}", | ||
"avatar": "{{ matchURL | orEmpty }}", | ||
"createdAt": "{{ matchTimestamp }}", | ||
"deletedAt": null, | ||
"domain": "{{.Domain}}", | ||
"id": "{{ matchID64 }}", | ||
"metadata": "*", | ||
"publicName": "{{.SecondPaymail.PublicName}}", | ||
"updatedAt": "{{ matchTimestamp }}", | ||
"xpubId": "{{.XPubID}}" | ||
}, | ||
{ | ||
"address": "{{.FirstPaymail.Address}}", | ||
"alias": "{{.FirstPaymail.Alias}}", | ||
"avatar": "{{ matchURL | orEmpty }}", | ||
"createdAt": "{{ matchTimestamp }}", | ||
"deletedAt": null, | ||
"domain": "{{.Domain}}", | ||
"id": "{{ matchID64 }}", | ||
"metadata": "*", | ||
"publicName": "{{.FirstPaymail.PublicName}}", | ||
"updatedAt": "{{ matchTimestamp }}", | ||
"xpubId": "{{.XPubID}}" | ||
} | ||
], | ||
"page": { | ||
"number": 1, | ||
"size": 50, | ||
"totalElements": 2, | ||
"totalPages": 1 | ||
} | ||
}`, map[string]any{ | ||
"FirstPaymail": map[string]any{ | ||
"Address": strings.ToLower(fixtures.UserWithMorePaymails.Paymails[0]), | ||
"PublicName": fixtures.UserWithMorePaymails.Paymails[0], | ||
"Alias": getAliasFromPaymail(t, fixtures.UserWithMorePaymails.Paymails[0]), | ||
}, | ||
"SecondPaymail": map[string]any{ | ||
"Address": strings.ToLower(fixtures.UserWithMorePaymails.Paymails[1]), | ||
"PublicName": fixtures.UserWithMorePaymails.Paymails[1], | ||
"Alias": getAliasFromPaymail(t, fixtures.UserWithMorePaymails.Paymails[1]), | ||
}, | ||
"XPubID": fixtures.UserWithMorePaymails.XPubID(), | ||
"Domain": fixtures.PaymailDomain, | ||
}) | ||
}) | ||
|
||
t.Run("try to return paymails info for admin", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.New(t) | ||
cleanup := given.StartedSPVWallet() | ||
defer cleanup() | ||
client := given.HttpClient().ForAdmin() | ||
|
||
// when: | ||
res, _ := client.R().Get("/api/v1/paymails") | ||
|
||
// then: | ||
then.Response(res).IsUnauthorizedForAdmin() | ||
}) | ||
|
||
t.Run("return xpub info for anonymous", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.New(t) | ||
cleanup := given.StartedSPVWallet() | ||
defer cleanup() | ||
client := given.HttpClient().ForAnonymous() | ||
|
||
// when: | ||
res, _ := client.R().Get("/api/v1/paymails") | ||
|
||
// then: | ||
then.Response(res).IsUnauthorized() | ||
}) | ||
} | ||
|
||
func getAliasFromPaymail(t testing.TB, paymail string) (alias string) { | ||
parts := strings.SplitN(paymail, "@", 2) | ||
if len(parts) == 0 { | ||
t.Fatalf("Failed to parse paymail: %s", paymail) | ||
} | ||
alias = strings.ToLower(parts[0]) | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package users_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/bitcoin-sv/spv-wallet/actions/testabilities" | ||
"github.com/bitcoin-sv/spv-wallet/engine/tester/fixtures" | ||
) | ||
|
||
func TestCurrentUserGet(t *testing.T) { | ||
givenForAllTests := testabilities.Given(t) | ||
cleanup := givenForAllTests.StartedSPVWallet() | ||
defer cleanup() | ||
|
||
t.Run("return xpub info for user", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.NewOf(givenForAllTests, t) | ||
client := given.HttpClient().ForUser() | ||
|
||
// when: | ||
res, _ := client.R().Get("/api/v1/users/current") | ||
|
||
// then: | ||
then.Response(res). | ||
IsOK(). | ||
WithJSONMatching(`{ | ||
"id": "{{.ID}}", | ||
"createdAt": "{{ matchTimestamp }}", | ||
"updatedAt": "{{ matchTimestamp }}", | ||
"currentBalance": 0, | ||
"deletedAt": null, | ||
"metadata": "*", | ||
"nextExternalNum": 1, | ||
"nextInternalNum": 0 | ||
}`, map[string]any{ | ||
"ID": fixtures.Sender.XPubID(), | ||
}) | ||
}) | ||
|
||
t.Run("return xpub info for user (old api)", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.NewOf(givenForAllTests, t) | ||
client := given.HttpClient().ForUser() | ||
|
||
// when: | ||
res, _ := client.R().Get("/v1/xpub") | ||
|
||
// then: | ||
then.Response(res). | ||
IsOK(). | ||
WithJSONMatching(`{ | ||
"id": "{{.ID}}", | ||
"created_at": "/.*/", | ||
"updated_at": "/.*/", | ||
"current_balance": 0, | ||
"deleted_at": null, | ||
"metadata": "*", | ||
"next_external_num": 1, | ||
"next_internal_num": 0 | ||
}`, map[string]any{ | ||
"ID": fixtures.Sender.XPubID(), | ||
}) | ||
}) | ||
|
||
t.Run("return xpub info for admin", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.NewOf(givenForAllTests, t) | ||
client := given.HttpClient().ForAdmin() | ||
|
||
// when: | ||
res, _ := client.R().Get("/api/v1/users/current") | ||
|
||
// then: | ||
then.Response(res).IsUnauthorizedForAdmin() | ||
}) | ||
|
||
t.Run("return xpub info for anonymous", func(t *testing.T) { | ||
// given: | ||
given, then := testabilities.NewOf(givenForAllTests, t) | ||
client := given.HttpClient().ForAnonymous() | ||
|
||
// when: | ||
res, _ := client.R().Get("/api/v1/users/current") | ||
|
||
// then: | ||
then.Response(res).IsUnauthorized() | ||
}) | ||
} |
Oops, something went wrong.