edge-connect-client/client/client.go

316 lines
7.3 KiB
Go
Raw Normal View History

2025-09-16 13:02:33 +02:00
package client
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
2025-09-16 13:02:33 +02:00
"log"
"net/http"
"strings"
2025-09-16 13:02:33 +02:00
)
var ErrResourceNotFound = fmt.Errorf("resource not found")
type EdgeConnect struct {
BaseURL string
HttpClient *http.Client
Credentials Credentials
}
type Credentials struct {
Username string
Password string
}
func (e *EdgeConnect) RetrieveToken(ctx context.Context) (string, error) {
json_data, err := json.Marshal(map[string]string{
"username": e.Credentials.Username,
"password": e.Credentials.Password,
})
if err != nil {
return "", err
}
baseURL := strings.TrimRight(e.BaseURL, "/")
request, err := http.NewRequestWithContext(ctx, "POST", baseURL+"/api/v1/login", bytes.NewBuffer(json_data))
2025-09-16 13:02:33 +02:00
if err != nil {
return "", err
}
request.Header.Set("Content-Type", "application/json")
resp, err := e.HttpClient.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
// Read the entire response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %v", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("login failed with status %d: %s", resp.StatusCode, string(body))
}
2025-09-16 13:02:33 +02:00
var respData struct {
Token string `json:"token"`
}
err = json.Unmarshal(body, &respData)
2025-09-16 13:02:33 +02:00
if err != nil {
return "", fmt.Errorf("error parsing JSON (status %d): %v", resp.StatusCode, err)
2025-09-16 13:02:33 +02:00
}
return respData.Token, nil
}
func (e *EdgeConnect) CreateApp(ctx context.Context, input NewAppInput) error {
json_data, err := json.Marshal(input)
if err != nil {
return err
}
response, err := call[App](ctx, e, "/api/v1/auth/ctrl/CreateApp", json_data)
if err != nil {
return err
}
return response.Error()
}
func (e *EdgeConnect) ShowApp(ctx context.Context, appkey AppKey, region string) (App, error) {
input := struct {
App App `json:"App"`
Region string `json:"Region"`
}{
App: App{Key: appkey},
Region: region,
}
json_data, err := json.Marshal(input)
if err != nil {
return App{}, err
}
responses, err := call[App](ctx, e, "/api/v1/auth/ctrl/ShowApp", json_data)
if err != nil {
return App{}, err
}
if responses.StatusCode == http.StatusNotFound {
return App{}, fmt.Errorf("Error retrieving App: %w", ErrResourceNotFound)
}
if !responses.IsSuccessful() {
return App{}, responses.Error()
}
apps := responses.GetData()
if len(apps) > 0 {
return apps[0], nil
}
return App{}, fmt.Errorf("could not find app with region/key: %s/%v: %w", region, appkey, ErrResourceNotFound)
}
func (e *EdgeConnect) ShowApps(ctx context.Context, appkey AppKey, region string) ([]App, error) {
input := struct {
App App `json:"App"`
Region string `json:"Region"`
}{
App: App{Key: appkey},
Region: region,
}
json_data, err := json.Marshal(input)
if err != nil {
return nil, err
}
responses, err := call[App](ctx, e, "/api/v1/auth/ctrl/ShowApp", json_data)
if err != nil {
return nil, err
}
if !responses.IsSuccessful() && responses.StatusCode != http.StatusNotFound {
return nil, responses.Error()
}
return responses.GetData(), nil
}
func (e *EdgeConnect) DeleteApp(ctx context.Context, appkey AppKey, region string) error {
input := struct {
App App `json:"App"`
Region string `json:"Region"`
}{
App: App{Key: appkey},
Region: region,
}
json_data, err := json.Marshal(input)
if err != nil {
return err
}
response, err := call[App](ctx, e, "/api/v1/auth/ctrl/DeleteApp", json_data)
if err != nil {
return err
}
if !response.IsSuccessful() && response.StatusCode != 404 {
return response.Error()
}
return nil
}
func (e *EdgeConnect) CreateAppInstance(ctx context.Context, input NewAppInstanceInput) error {
json_data, err := json.Marshal(input)
if err != nil {
log.Printf("failed to marshal NewAppInstanceInput %v\n", err)
return err
}
responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/CreateAppInst", json_data)
if err != nil {
return err
}
return responses.Error()
}
func (e *EdgeConnect) ShowAppInstance(ctx context.Context, appinstkey AppInstanceKey, region string) (AppInstance, error) {
input := struct {
App AppInstance `json:"appinst"`
Region string `json:"Region"`
}{
App: AppInstance{Key: appinstkey},
Region: region,
}
json_data, err := json.Marshal(input)
if err != nil {
return AppInstance{}, err
}
responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/ShowAppInst", json_data)
if err != nil {
return AppInstance{}, err
}
if responses.StatusCode == http.StatusNotFound {
return AppInstance{}, fmt.Errorf("Error retrieving AppInstance: %w", ErrResourceNotFound)
}
if !responses.IsSuccessful() {
return AppInstance{}, responses.Error()
}
data := responses.GetData()
if len(data) > 0 {
return data[0], nil
}
return AppInstance{}, fmt.Errorf("could not find app instance: %v: %w", responses, ErrResourceNotFound)
}
func (e *EdgeConnect) ShowAppInstances(ctx context.Context, appinstkey AppInstanceKey, region string) ([]AppInstance, error) {
input := struct {
App AppInstance `json:"appinst"`
Region string `json:"Region"`
}{
App: AppInstance{Key: appinstkey},
Region: region,
}
json_data, err := json.Marshal(input)
if err != nil {
return nil, err
}
responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/ShowAppInst", json_data)
if err != nil {
return nil, err
}
if !responses.IsSuccessful() && responses.StatusCode != http.StatusNotFound {
return nil, responses.Error()
}
return responses.GetData(), nil
}
func (e *EdgeConnect) DeleteAppInstance(ctx context.Context, appinstancekey AppInstanceKey, region string) error {
input := struct {
AppInstance AppInstance `json:"appinst"`
Region string `json:"Region"`
}{
AppInstance: AppInstance{Key: appinstancekey},
Region: region,
}
json_data, err := json.Marshal(input)
if err != nil {
return err
}
responses, err := call[AppInstance](ctx, e, "/api/v1/auth/ctrl/DeleteAppInst", json_data)
if err != nil {
return err
}
return responses.Error()
}
func call[T Message](ctx context.Context, client *EdgeConnect, path string, body []byte) (Responses[T], error) {
token, err := client.RetrieveToken(ctx)
if err != nil {
return Responses[T]{}, err
}
request, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", client.BaseURL, path), bytes.NewBuffer(body))
if err != nil {
return Responses[T]{}, err
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
resp, err := client.HttpClient.Do(request)
if err != nil {
return Responses[T]{}, err
}
defer resp.Body.Close()
responses := Responses[T]{}
responses.StatusCode = resp.StatusCode
if responses.StatusCode == http.StatusNotFound {
return responses, nil
}
decoder := json.NewDecoder(resp.Body)
for {
var d Response[T]
if err := decoder.Decode(&d); err != nil {
if err.Error() == "EOF" {
break
}
log.Printf("Error in call %s: %v", path, err)
return Responses[T]{}, fmt.Errorf("Error in call %s: %w", path, err)
}
responses.Responses = append(responses.Responses, d)
}
log.Printf("call(): %s resulting in http status %v and %v responses\n", path, resp.StatusCode, len(responses.GetMessages()))
for i, v := range responses.GetMessages() {
log.Printf("call(): response[%v]: %s\n", i, v)
}
return responses, nil
}