package api import ( "bytes" "encoding/json" "fmt" "io" "net/http" "regexp" "strconv" "strings" "time" ) type Session struct { Client *http.Client Cookie string LastAccessed time.Time } func NewSession() *Session { return &Session{ Client: &http.Client{}, LastAccessed: time.Now(), } } func (s *Session) ValidateSession() error { url := "https://api.servicetrade.com/api/auth/validate" req, err := http.NewRequest("GET", url, nil) if err != nil { return err } req.Header.Set("Cookie", s.Cookie) resp, err := s.Client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("session validation failed") } s.LastAccessed = time.Now() return nil } func (s *Session) Login(email, password string) error { url := "https://api.servicetrade.com/api/auth" payload := map[string]string{ "username": email, "password": password, } payloadBytes, _ := json.Marshal(payload) req, err := http.NewRequest("POST", url, bytes.NewBuffer(payloadBytes)) if err != nil { return fmt.Errorf("error creating request: %v", err) } req.Header.Set("Content-Type", "application/json") resp, err := s.Client.Do(req) if err != nil { return fmt.Errorf("error sending request: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("failed to authenticate: %s, response: %s", resp.Status, string(body)) } for _, cookie := range resp.Cookies() { if strings.Contains(cookie.Name, "PHPSESSID") { s.Cookie = cookie.String() fmt.Println(s.Cookie) return nil } } return fmt.Errorf("failed to retrieve session cookie; authentication may have failed") } func (s *Session) Logout() error { if s.Cookie == "" { return fmt.Errorf("no active session to end") } url := "https://api.servicetrade.com/api/auth" req, err := http.NewRequest("DELETE", url, nil) if err != nil { return fmt.Errorf("failed to create DELETE request to end session: %v", err) } req.Header.Set("Cookie", s.Cookie) resp, err := s.Client.Do(req) if err != nil { return fmt.Errorf("failed to send DELETE request to end session: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 && resp.StatusCode != 204 { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("failed to end session: %s, response: %s", resp.Status, string(body)) } s.Cookie = "" return nil } func (s *Session) GetAttachmentsForJob(jobID string) (map[string]interface{}, error) { url := fmt.Sprintf("https://api.servicetrade.com/api/job/%s/paperwork", jobID) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("error creating request: %v", err) } req.Header.Set("Cookie", s.Cookie) resp, err := s.Client.Do(req) if err != nil { return nil, fmt.Errorf("error sending request: %v", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { return nil, fmt.Errorf("failed to get attachments: %s, response: %s", resp.Status, string(body)) } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("error unmarshalling response: %v, body: %s", err, string(body)) } return result, nil } func (s *Session) DeleteAttachment(endpoint string) error { req, err := http.NewRequest("DELETE", endpoint, nil) if err != nil { return fmt.Errorf("failed to create DELETE request: %v", err) } req.Header.Set("Cookie", s.Cookie) resp, err := s.Client.Do(req) if err != nil { return fmt.Errorf("failed to send DELETE request: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 && resp.StatusCode != 204 { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("failed to delete attachment: %s, response: %s", resp.Status, string(body)) } return nil } func (s *Session) GetDeficiencyInfoForJob(jobID string) ([][]string, error) { url := fmt.Sprintf("https://api.servicetrade.com/api/deficiency/%s", jobID) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("error creating request: %v", err) } req.Header.Set("Cookie", s.Cookie) resp, err := s.Client.Do(req) if err != nil { return nil, fmt.Errorf("error sending request: %v", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { return nil, fmt.Errorf("failed to get deficiency info: %s, response: %s", resp.Status, string(body)) } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("error unmarshalling response: %v, body: %s", err, string(body)) } var deficiencyLogs [][]string if data, ok := result["data"].([]interface{}); ok { for _, item := range data { if deficiency, ok := item.(map[string]interface{}); ok { id := fmt.Sprintf("%v", deficiency["id"]) description := fmt.Sprintf("%v", deficiency["description"]) status := fmt.Sprintf("%v", deficiency["status"]) deficiencyLogs = append(deficiencyLogs, []string{jobID, id, description, status}) } } } return deficiencyLogs, nil } func (s *Session) GetDeficiencyById(deficiencyId string) (map[string]interface{}, error) { url := fmt.Sprintf("https://api.servicetrade.com/api/deficiency/%s", deficiencyId) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("error creating request: %v", err) } req.Header.Set("Cookie", s.Cookie) resp, err := s.Client.Do(req) if err != nil { return nil, fmt.Errorf("error sending request: %v", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { return nil, fmt.Errorf("failed to get deficiency info: %s, response: %s", resp.Status, string(body)) } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("error unmarshalling response: %v, body: %s", err, string(body)) } return result, nil } func (s *Session) GetInvoice(identifier string) (map[string]interface{}, error) { var url string // Regular expression to check if the identifier starts with a letter isInvoiceNumber, _ := regexp.MatchString(`^[A-Za-z]`, identifier) if isInvoiceNumber { url = fmt.Sprintf("https://api.servicetrade.com/api/invoice?invoiceNumber=%s", identifier) } else { // Check if the identifier is a valid number if _, err := strconv.Atoi(identifier); err != nil { return nil, fmt.Errorf("invalid invoice identifier: %s", identifier) } url = fmt.Sprintf("https://api.servicetrade.com/api/invoice/%s", identifier) } req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("error creating request: %v", err) } req.Header.Set("Cookie", s.Cookie) resp, err := s.Client.Do(req) if err != nil { return nil, fmt.Errorf("error sending request: %v", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { return nil, fmt.Errorf("failed to get invoice info: %s, response: %s", resp.Status, string(body)) } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("error unmarshalling response: %v, body: %s", err, string(body)) } // Check if the response contains a 'data' field if data, ok := result["data"].(map[string]interface{}); ok { // If 'invoices' field exists, it's a search by invoice number if invoices, ok := data["invoices"].([]interface{}); ok && len(invoices) > 0 { if invoice, ok := invoices[0].(map[string]interface{}); ok { return invoice, nil } } else { // If 'invoices' doesn't exist, it's a direct invoice lookup by ID return data, nil } } return nil, fmt.Errorf("no invoice found in the response") }