Separate remote http implementation from interface
This commit is contained in:
		
							parent
							
								
									134989a92d
								
							
						
					
					
						commit
						23c65655c2
					
				
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -4,12 +4,14 @@ go 1.23.4 | ||||
| 
 | ||||
| require ( | ||||
| 	git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250111151614-46bf21b7b8bd | ||||
| 	github.com/grassrootseconomics/eth-custodial v1.3.0-beta | ||||
| 	github.com/grassrootseconomics/ussd-data-service v1.2.0-beta | ||||
| 	github.com/stretchr/testify v1.9.0 | ||||
| ) | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect | ||||
| 	github.com/joho/godotenv v1.5.1 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect | ||||
| 	github.com/stretchr/objx v0.5.2 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
|  | ||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -2,8 +2,12 @@ git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250111151614 | ||||
| git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250111151614-46bf21b7b8bd/go.mod h1:E6W7ZOa7ZvVr0Bc5ot0LNSwpSPYq4hXlAIvEPy3AJ7U= | ||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU= | ||||
| github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo= | ||||
| github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk= | ||||
| github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0= | ||||
| github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= | ||||
| github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= | ||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= | ||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= | ||||
|  | ||||
| @ -1,18 +1,9 @@ | ||||
| package remote | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"git.grassecon.net/grassrootseconomics/visedriver/config" | ||||
| 	"git.grassecon.net/grassrootseconomics/visedriver/models" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 	"git.grassecon.net/grassrootseconomics/sarafu-api/models" | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| ) | ||||
| 
 | ||||
| @ -26,269 +17,3 @@ type AccountServiceInterface interface { | ||||
| 	TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) | ||||
| 	CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) | ||||
| } | ||||
| 
 | ||||
| type AccountService struct { | ||||
| } | ||||
| 
 | ||||
| // Parameters:
 | ||||
| //   - trackingId: A unique identifier for the account.This should be obtained from a previous call to
 | ||||
| //     CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the
 | ||||
| //     AccountResponse struct can be used here to check the account status during a transaction.
 | ||||
| //
 | ||||
| // Returns:
 | ||||
| //   - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string.
 | ||||
| //   - 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
 | ||||
| func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { | ||||
| 	var r models.TrackStatusResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.TrackURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &r, nil | ||||
| } | ||||
| 
 | ||||
| // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account whose balance needs to be checked.
 | ||||
| func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { | ||||
| 	var balanceResult models.BalanceResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.BalanceURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doRequest(ctx, req, &balanceResult) | ||||
| 	return &balanceResult, err | ||||
| } | ||||
| 
 | ||||
| // CreateAccount creates a new account in the custodial system.
 | ||||
| // Returns:
 | ||||
| //   - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account.
 | ||||
| //     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.
 | ||||
| //     If no error occurs, this will be nil.
 | ||||
| func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { | ||||
| 	var r models.AccountResult | ||||
| 	// Create a new request
 | ||||
| 	req, err := http.NewRequest("POST", config.CreateAccountURL, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, err = doRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &r, nil | ||||
| } | ||||
| 
 | ||||
| // FetchVouchers retrieves the token holdings for a given public key from the data indexer API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { | ||||
| 	var r struct { | ||||
| 		Holdings []dataserviceapi.TokenHoldings `json:"holdings"` | ||||
| 	} | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherHoldingsURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Holdings, nil | ||||
| } | ||||
| 
 | ||||
| // FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { | ||||
| 	var r struct { | ||||
| 		Transfers []dataserviceapi.Last10TxResponse `json:"transfers"` | ||||
| 	} | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherTransfersURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return r.Transfers, nil | ||||
| } | ||||
| 
 | ||||
| // VoucherData retrieves voucher metadata from the data indexer API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - address: The voucher address.
 | ||||
| func (as *AccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { | ||||
| 	var r struct { | ||||
| 		TokenDetails models.VoucherDataResult `json:"tokenDetails"` | ||||
| 	} | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherDataURL, address) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doRequest(ctx, req, &r) | ||||
| 	return &r.TokenDetails, err | ||||
| } | ||||
| 
 | ||||
| // TokenTransfer creates a new token transfer in the custodial system.
 | ||||
| // Returns:
 | ||||
| //   - *models.TokenTransferResponse: A pointer to an TokenTransferResponse struct containing the trackingId.
 | ||||
| //     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.
 | ||||
| //     If no error occurs, this will be nil.
 | ||||
| func (as *AccountService) TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) { | ||||
| 	var r models.TokenTransferResponse | ||||
| 
 | ||||
| 	// Create request payload
 | ||||
| 	payload := map[string]string{ | ||||
| 		"amount":       amount, | ||||
| 		"from":         from, | ||||
| 		"to":           to, | ||||
| 		"tokenAddress": tokenAddress, | ||||
| 	} | ||||
| 
 | ||||
| 	payloadBytes, err := json.Marshal(payload) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Create a new request
 | ||||
| 	req, err := http.NewRequest("POST", config.TokenTransferURL, bytes.NewBuffer(payloadBytes)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	_, err = doRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &r, nil | ||||
| } | ||||
| 
 | ||||
| // CheckAliasAddress retrieves the address of an alias from the API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - alias: The alias of the user.
 | ||||
| func (as *AccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { | ||||
| 	var r dataserviceapi.AliasAddress | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.CheckAliasURL, alias) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doRequest(ctx, req, &r) | ||||
| 	return &r, err | ||||
| } | ||||
| 
 | ||||
| func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { | ||||
| 	var okResponse api.OKResponse | ||||
| 	var errResponse api.ErrResponse | ||||
| 
 | ||||
| 	req.Header.Set("Authorization", "Bearer "+config.BearerToken) | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 
 | ||||
| 	logRequestDetails(req) | ||||
| 
 | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		log.Printf("Failed to make %s request to endpoint: %s with reason: %s", req.Method, req.URL, err.Error()) | ||||
| 		errResponse.Description = err.Error() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	log.Printf("Received response for %s: Status Code: %d | Content-Type: %s", req.URL, resp.StatusCode, resp.Header.Get("Content-Type")) | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode >= http.StatusBadRequest { | ||||
| 		err := json.Unmarshal([]byte(body), &errResponse) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors.New(errResponse.Description) | ||||
| 	} | ||||
| 	err = json.Unmarshal([]byte(body), &okResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(okResponse.Result) == 0 { | ||||
| 		return nil, errors.New("Empty api result") | ||||
| 	} | ||||
| 
 | ||||
| 	v, err := json.Marshal(okResponse.Result) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	err = json.Unmarshal(v, &rcpt) | ||||
| 	return &okResponse, err | ||||
| } | ||||
| 
 | ||||
| func logRequestDetails(req *http.Request) { | ||||
| 	var bodyBytes []byte | ||||
| 	contentType := req.Header.Get("Content-Type") | ||||
| 	if req.Body != nil { | ||||
| 		bodyBytes, err := io.ReadAll(req.Body) | ||||
| 		if err != nil { | ||||
| 			log.Printf("Error reading request body: %s", err) | ||||
| 			return | ||||
| 		} | ||||
| 		req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) | ||||
| 	} else { | ||||
| 		bodyBytes = []byte("-") | ||||
| 	} | ||||
| 
 | ||||
| 	log.Printf("URL: %s | Content-Type: %s | Method: %s| Request Body: %s", req.URL, contentType, req.Method, string(bodyBytes)) | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user