package client import ( "bytes" "context" "encoding/json" "fmt" "io" "log" "net/http" "strings" ) 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)) 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)) } var respData struct { Token string `json:"token"` } err = json.Unmarshal(body, &respData) if err != nil { return "", fmt.Errorf("error parsing JSON (status %d): %v", resp.StatusCode, err) } 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 }