api-structs #117

Merged
lash merged 35 commits from api-structs into master 2024-10-24 15:53:46 +02:00
Showing only changes of commit 9bc9d04a49 - Show all commits

View File

@ -2,6 +2,7 @@ package server
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -12,16 +13,17 @@ import (
"github.com/grassrootseconomics/eth-custodial/pkg/api" "github.com/grassrootseconomics/eth-custodial/pkg/api"
) )
Outdated
Review

why is this needed?

why is this needed?

Well,at the point of making the api call and receiving a response back, we don't really know the response that will be sent back,it can either be an ErrResponse or an OKResponse but we are certain that the ok and description field will be present.So the OK field in the ApiResponse will be used to decide which type to Unmarshal on.

if apiResponse.Ok {
		err = json.Unmarshal([]byte(body), &okResponse)
		if err != nil {
			errResponse.Description = err.Error()
			return nil, &errResponse
		}
		return &okResponse, nil
	} else {
		err := json.Unmarshal([]byte(body), &errResponse)
		if err != nil {
			errResponse.Description = err.Error()
			return nil, &errResponse
		}
		return nil, &errResponse
	}
Well,at the point of making the api call and receiving a response back, we don't really know the response that will be sent back,it can either be an ErrResponse or an OKResponse but we are certain that the ok and description field will be present.So the OK field in the ApiResponse will be used to decide which type to Unmarshal on. ``` if apiResponse.Ok { err = json.Unmarshal([]byte(body), &okResponse) if err != nil { errResponse.Description = err.Error() return nil, &errResponse } return &okResponse, nil } else { err := json.Unmarshal([]byte(body), &errResponse) if err != nil { errResponse.Description = err.Error() return nil, &errResponse } return nil, &errResponse } ```
type ApiResponse struct { var (
Ok bool `json:"ok"` okResponse api.OKResponse
Description string `json:"description"` errResponse api.ErrResponse
} EMPTY_RESPONSE = 0
)
type AccountServiceInterface interface { type AccountServiceInterface interface {
CheckBalance(publicKey string) (*models.BalanceResponse, error) CheckBalance(publicKey string) (*models.BalanceResponse, error)
CreateAccount() (*api.OKResponse, *api.ErrResponse) CreateAccount() (*api.OKResponse, error)
CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error)
TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) TrackAccountStatus(publicKey string) (*api.OKResponse, error)
} }
type AccountService struct { type AccountService struct {
@ -60,58 +62,42 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackSt
} }
func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) {
var errResponse api.ErrResponse
var okResponse api.OKResponse
var err error var err error
// Construct the URL with the path parameter // Construct the URL with the path parameter
url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey)
lash marked this conversation as resolved
Review

I think it is better and safer to use the net/url package for handling urls?

I will add it as a separate issue.

I think it is better and safer to use the `net/url` package for handling urls? I will add it as a separate issue.
Review
https://git.grassecon.net/urdt/ussd/issues/125
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
errResponse.Description = err.Error() return nil, err
return nil, &errResponse
} }
// Set headers
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-GE-KEY", "xd") req.Header.Set("X-GE-KEY", "xd")
// Send the request
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
errResponse.Description = err.Error() return nil, err
return nil, &errResponse
} }
defer resp.Body.Close() defer resp.Body.Close()
Review

Check the HTTP response code first. If it is >= 400 (Bad request you can then unmarshal to an api.ErrResp.

Check the HTTP response code first. If it is >= 400 (Bad request you can then unmarshal to an api.ErrResp.
// Read the response body
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
errResponse.Description = err.Error() errResponse.Description = err.Error()
return nil, &errResponse return nil, err
} }
err = json.Unmarshal([]byte(body), &okResponse)
// Step 2: Unmarshal into the generic struct
var apiResponse ApiResponse
err = json.Unmarshal([]byte(body), &apiResponse)
if err != nil { if err != nil {
errResponse.Description = err.Error()
return nil, &errResponse
}
if apiResponse.Ok {
err = json.Unmarshal([]byte(body), &okResponse)
if err != nil {
errResponse.Description = err.Error()
return nil, &errResponse
}
return &okResponse, nil
} else {
err := json.Unmarshal([]byte(body), &errResponse) err := json.Unmarshal([]byte(body), &errResponse)
if err != nil { if err != nil {
errResponse.Description = err.Error() return nil, err
return nil, &errResponse
} }
return nil, &errResponse return nil, errors.New(errResponse.Description)
} }
if len(okResponse.Result) == EMPTY_RESPONSE {
Outdated
Review

I would just use the literal 0 here

I would just use the literal `0` here

If there is no matching tracking id, result.otx will be null.

If there is no matching tracking id, result.otx will be null.
Outdated
Review

@kamikazechaser please elaborate is there a missing case?

@kamikazechaser please elaborate is there a missing case?
return nil, errors.New("Empty api result")
}
return &okResponse, nil
} }
// CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
@ -141,17 +127,13 @@ func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceRespons
// If there is an error during the request or processing, this will be nil. // If there is an error during the request or processing, this will be nil.
// - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
// If no error occurs, this will be nil. // If no error occurs, this will be nil.
func (as *AccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { func (as *AccountService) CreateAccount() (*api.OKResponse, error) {
var errResponse api.ErrResponse
var okResponse api.OKResponse
var err error var err error
// Create a new request // Create a new request
req, err := http.NewRequest("POST", config.CreateAccountURL, nil) req, err := http.NewRequest("POST", config.CreateAccountURL, nil)
if err != nil { if err != nil {
errResponse.Description = err.Error() return nil, err
return nil, &errResponse
} }
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-GE-KEY", "xd") req.Header.Set("X-GE-KEY", "xd")
@ -159,38 +141,29 @@ func (as *AccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) {
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
errResponse.Description = err.Error() errResponse.Description = err.Error()
return nil, &errResponse return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
errResponse.Description = err.Error() return nil, err
return nil, &errResponse
} }
var apiResponse ApiResponse err = json.Unmarshal([]byte(body), &okResponse)
err = json.Unmarshal([]byte(body), &apiResponse)
if err != nil { if err != nil {
return nil, &errResponse
}
if apiResponse.Ok {
err = json.Unmarshal([]byte(body), &okResponse)
if err != nil {
errResponse.Description = err.Error()
return nil, &errResponse
}
return &okResponse, nil
} else {
err := json.Unmarshal([]byte(body), &errResponse) err := json.Unmarshal([]byte(body), &errResponse)
if err != nil { if err != nil {
errResponse.Description = err.Error() return nil, err
return nil, &errResponse
} }
return nil, &errResponse return nil, errors.New(errResponse.Description)
} }
if len(okResponse.Result) == EMPTY_RESPONSE {
return nil, errors.New("Empty api result")
}
return &okResponse, nil
} }
func (tas *TestAccountService) CreateAccount() (*api.OKResponse, *api.ErrResponse) { func (tas *TestAccountService) CreateAccount() (*api.OKResponse, error) {
return &api.OKResponse{ return &api.OKResponse{
Ok: true, Ok: true,
Description: "Account creation request received successfully", Description: "Account creation request received successfully",
@ -200,7 +173,6 @@ func (tas *TestAccountService) CreateAccount() (*api.OKResponse, *api.ErrRespons
} }
func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) {
balanceResponse := &models.BalanceResponse{ balanceResponse := &models.BalanceResponse{
Ok: true, Ok: true,
Result: struct { Result: struct {
@ -211,11 +183,10 @@ func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceRe
Nonce: json.Number("0"), Nonce: json.Number("0"),
}, },
} }
return balanceResponse, nil return balanceResponse, nil
} }
func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, *api.ErrResponse) { func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) {
return &api.OKResponse{ return &api.OKResponse{
Ok: true, Ok: true,
Description: "Account creation succeeded", Description: "Account creation succeeded",