Breaking - Implement proper error handling. Everything passes errors up

This commit is contained in:
Steven Polley 2018-07-05 21:20:52 -06:00
parent f2e6e33a53
commit 9cef8aeaa2
7 changed files with 230 additions and 105 deletions

View File

@ -148,31 +148,49 @@ type Company struct {
//GetCompanyByName expects an exact match, perhaps an improvement could be made to support wildcard characters. //GetCompanyByName expects an exact match, perhaps an improvement could be made to support wildcard characters.
//Will return a pointer to a slice of Company's. //Will return a pointer to a slice of Company's.
func (cw *ConnectwiseSite) GetCompanyByName(companyName string) *[]Company { func (cw *ConnectwiseSite) GetCompanyByName(companyName string) (*[]Company, error) {
restAction := "/company/companies"
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
cwurl := cw.BuildURL("/company/companies")
parameters := url.Values{} parameters := url.Values{}
parameters.Add("conditions", "name=\""+companyName+"\"") parameters.Add("conditions", "name=\""+companyName+"\"")
cwurl.RawQuery = parameters.Encode() cwurl.RawQuery = parameters.Encode()
body := cw.GetRequest(cwurl) body, err := cw.GetRequest(cwurl)
if err != nil {
return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
companies := []Company{} companies := []Company{}
check(json.Unmarshal(body, &companies)) err = json.Unmarshal(body, &companies)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
}
return &companies return &companies, nil
} }
//GetCompanyByID expects the Connectwise Company ID and returns a pointer to a Company //GetCompanyByID expects the Connectwise Company ID and returns a pointer to a Company
//Does not return a slice like GetCompanyByName as the ID will only ever have one match //Does not return a slice like GetCompanyByName as the ID will only ever have one match
func (cw *ConnectwiseSite) GetCompanyByID(companyID int) *Company { func (cw *ConnectwiseSite) GetCompanyByID(companyID int) (*Company, error) {
restAction := fmt.Sprintf("/company/companies/%d", companyID)
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
body, err := cw.GetRequest(cwurl)
if err != nil {
return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
company := Company{} company := Company{}
err = json.Unmarshal(body, &company)
cwurl := cw.BuildURL(fmt.Sprintf("/company/companies/%d", companyID)) if err != nil {
return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
body := cw.GetRequest(cwurl) }
fmt.Print(string(body))
check(json.Unmarshal(body, &company)) return &company, nil
return &company
} }

View File

@ -3,7 +3,6 @@ package connectwise
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"log"
) )
//ConnectwiseSite is a stuct containing the URL of the site and the API authorization token in the format that CW expects it. //ConnectwiseSite is a stuct containing the URL of the site and the API authorization token in the format that CW expects it.
@ -12,12 +11,6 @@ type ConnectwiseSite struct {
Auth string Auth string
} }
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
//NewSite returns a pointer to a ConnectwiseSite struct with the site and auth string available for use in API requests //NewSite returns a pointer to a ConnectwiseSite struct with the site and auth string available for use in API requests
func NewSite(site string, publicKey string, privateKey string, company string) *ConnectwiseSite { func NewSite(site string, publicKey string, privateKey string, company string) *ConnectwiseSite {
//The auth string must be formatted in this way when used in requests to the API //The auth string must be formatted in this way when used in requests to the API

View File

@ -115,42 +115,70 @@ type Agreement struct {
} `json:"billToSite,omitempty"` } `json:"billToSite,omitempty"`
} }
//GetAgreements returns a list of agreements, not paginated and currently not that usefule //GetAgreements returns a list of agreements, not paginated and currently not that useful
//TBD: Pagination and filtering options still need to be considered //TBD: Pagination and filtering options still need to be considered
func (cw *ConnectwiseSite) GetAgreements() *[]Agreement { func (cw *ConnectwiseSite) GetAgreements() (*[]Agreement, error) {
restAction := "/finance/agreements"
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
//Build the request URL body, err := cw.GetRequest(cwurl)
cwurl := cw.BuildURL("/finance/agreements") if err != nil {
return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
body := cw.GetRequest(cwurl) }
agreements := []Agreement{} agreements := []Agreement{}
check(json.Unmarshal(body, &agreements)) err = json.Unmarshal(body, &agreements)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
}
return &agreements return &agreements, nil
} }
func (cw *ConnectwiseSite) GetAgreementsByCompanyName(companyName string) *[]Agreement { //GetAgreementsByCompanyName returns a list of agreements that belong to an exact matching company name
//TBD: Pagination and filtering options still need to be considered
func (cw *ConnectwiseSite) GetAgreementsByCompanyName(companyName string) (*[]Agreement, error) {
restAction := "/finance/agreements"
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
cwurl := cw.BuildURL("/finance/agreements")
parameters := url.Values{} parameters := url.Values{}
parameters.Add("conditions", "company/name=\""+companyName+"\"") parameters.Add("conditions", "company/name=\""+companyName+"\"")
cwurl.RawQuery = parameters.Encode() cwurl.RawQuery = parameters.Encode()
body := cw.GetRequest(cwurl) body, err := cw.GetRequest(cwurl)
if err != nil {
return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
agreements := []Agreement{} agreements := []Agreement{}
check(json.Unmarshal(body, &agreements)) err = json.Unmarshal(body, &agreements)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
}
return &agreements return &agreements, nil
} }
//GetBillingCycles is not complete //GetBillingCycles is not complete
//TBD: Finish this. //TBD: Finish this.
/*
func (cw *ConnectwiseSite) GetBillingCycles() { func (cw *ConnectwiseSite) GetBillingCycles() {
restAction := "/finance/billingCycles"
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
cwurl := cw.BuildURL("/finance/billingCycles") body, err := cw.GetRequest(cwurl)
if err != nil {
body := cw.GetRequest(cwurl) return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
fmt.Print(string(body)) fmt.Print(string(body))
// check(json.Unmarshal(body, &ticket)) // check(json.Unmarshal(body, &ticket))
} }
*/

View File

@ -4,74 +4,99 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"net/url" "net/url"
) )
//BuildURL will take a REST action such as "/companies/company/5" and then append the CW site to it and return a pointer to a url.URL //BuildURL will take a REST action such as "/companies/company/5" and then append the CW site to it and return a pointer to a url.URL
func (cw *ConnectwiseSite) BuildURL(restAction string) *url.URL { func (cw *ConnectwiseSite) BuildURL(restAction string) (*url.URL, error) {
var cwurl *url.URL var cwurl *url.URL
cwurl, err := url.Parse(cw.Site) cwurl, err := url.Parse(cw.Site)
check(err) if err != nil {
return nil, fmt.Errorf("could not %s as url: %g", cw.Site, err)
}
cwurl.Path += restAction cwurl.Path += restAction
return cwurl return cwurl, nil
} }
//Checks for HTTP errors, and if all looks good, returns the body of the HTTP response as a byte slice //Checks for HTTP errors, and if all looks good, returns the body of the HTTP response as a byte slice
//TBD: Needs to accept 201 and 204 (returned for Create and Delete operations) //TBD: Needs to accept 201 and 204 (returned for Create and Delete operations)
func getHTTPResponseBody(resp *http.Response) []byte { func getHTTPResponseBody(resp *http.Response) ([]byte, error) {
if (resp.StatusCode != http.StatusOK) && (resp.StatusCode != http.StatusCreated) && (resp.StatusCode != http.StatusNoContent) { if (resp.StatusCode != http.StatusOK) && (resp.StatusCode != http.StatusCreated) && (resp.StatusCode != http.StatusNoContent) {
out := fmt.Sprintf("CW API returned HTTP Status Code %s\n%s", resp.Status, resp.Body) return nil, fmt.Errorf("cw api returned unexpected http status %i - %s", resp.StatusCode, resp.Status)
log.Fatal(out) }
return make([]byte, 0) defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("could not read response body of request")
} }
body, err := ioutil.ReadAll(resp.Body) return body, nil
check(err)
return body
} }
//GetRequest takes a ConnectwiseSite and request URL, and returns the body of the response //GetRequest takes a ConnectwiseSite and request URL, and returns the body of the response
func (cw *ConnectwiseSite) GetRequest(cwurl *url.URL) []byte { func (cw *ConnectwiseSite) GetRequest(cwurl *url.URL) ([]byte, error) {
client := &http.Client{} client := &http.Client{}
req, err := http.NewRequest("GET", cwurl.String(), nil) req, err := http.NewRequest("GET", cwurl.String(), nil)
check(err) if err != nil {
return nil, fmt.Errorf("could not construct http request: %g", err)
}
req.Header.Set("Authorization", cw.Auth) req.Header.Set("Authorization", cw.Auth)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
response, err := client.Do(req) response, err := client.Do(req)
check(err) if err != nil {
defer response.Body.Close() return nil, fmt.Errorf("could not perform http get request: %g", err)
}
return getHTTPResponseBody(response) body, err := getHTTPResponseBody(response)
if err != nil {
return nil, fmt.Errorf("could not get http response body: %g", err)
}
return body, nil
} }
//PostRequest takes a ConnectwiseSite and request URL, and returns the body of the response //PostRequest takes a ConnectwiseSite and request URL, and returns the body of the response
func (cw *ConnectwiseSite) PostRequest(cwurl *url.URL, body io.Reader) []byte { func (cw *ConnectwiseSite) PostRequest(cwurl *url.URL, reqBody io.Reader) ([]byte, error) {
client := &http.Client{} client := &http.Client{}
req, err := http.NewRequest("POST", cwurl.String(), body) req, err := http.NewRequest("POST", cwurl.String(), reqBody)
check(err) if err != nil {
return nil, fmt.Errorf("could not construct http request: %g", err)
}
req.Header.Set("Authorization", cw.Auth) req.Header.Set("Authorization", cw.Auth)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
response, err := client.Do(req) response, err := client.Do(req)
check(err) if err != nil {
defer response.Body.Close() return nil, fmt.Errorf("could not perform http post request: %g", err)
}
return getHTTPResponseBody(response) body, err := getHTTPResponseBody(response)
if err != nil {
return nil, fmt.Errorf("could not get http response body: %g", err)
}
return body, nil
} }
//DeleteRequest takes a ConnectwiseSite and request URL, and returns the body of the response //DeleteRequest takes a ConnectwiseSite and request URL, and returns the body of the response
func (cw *ConnectwiseSite) DeleteRequest(cwurl *url.URL) []byte { func (cw *ConnectwiseSite) DeleteRequest(cwurl *url.URL) ([]byte, error) {
client := &http.Client{} client := &http.Client{}
req, err := http.NewRequest("DELETE", cwurl.String(), nil) req, err := http.NewRequest("DELETE", cwurl.String(), nil)
check(err) if err != nil {
return nil, fmt.Errorf("could not construct http request: %g", err)
}
req.Header.Set("Authorization", cw.Auth) req.Header.Set("Authorization", cw.Auth)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
response, err := client.Do(req) response, err := client.Do(req)
check(err) if err != nil {
defer response.Body.Close() return nil, fmt.Errorf("could not perform http delete request: %g", err)
}
return getHTTPResponseBody(response)
body, err := getHTTPResponseBody(response)
if err != nil {
return nil, fmt.Errorf("could not get http response body: %g", err)
}
return body, nil
} }

View File

@ -192,36 +192,63 @@ type ConfigurationReference struct {
} }
//GetTicketByID expects a ticket ID and returns a pointer to a Ticket struct //GetTicketByID expects a ticket ID and returns a pointer to a Ticket struct
func (cw *ConnectwiseSite) GetTicketByID(ticketID int) *Ticket { func (cw *ConnectwiseSite) GetTicketByID(ticketID int) (*Ticket, error) {
restAction := fmt.Sprintf("/service/tickets/%d", ticketID)
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
cwurl := cw.BuildURL(fmt.Sprintf("/service/tickets/%d", ticketID)) body, err := cw.GetRequest(cwurl)
if err != nil {
body := cw.GetRequest(cwurl) return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
ticket := Ticket{} ticket := Ticket{}
check(json.Unmarshal(body, &ticket)) err = json.Unmarshal(body, &ticket)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
}
return &ticket return &ticket, nil
} }
//GetTicketTimeEntriesByID expects a ticket ID and returns a pointer a to a slice of TimeEntryReference's, all the time entries attached to that ticket //GetTicketTimeEntriesByID expects a ticket ID and returns a pointer a to a slice of TimeEntryReference's, all the time entries attached to that ticket
func (cw *ConnectwiseSite) GetTicketTimeEntriesByID(ticketID int) *[]TimeEntryReference { func (cw *ConnectwiseSite) GetTicketTimeEntriesByID(ticketID int) (*[]TimeEntryReference, error) {
restAction := fmt.Sprintf("/service/tickets/%d/timeentries", ticketID)
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
cwurl := cw.BuildURL(fmt.Sprintf("/service/tickets/%d/timeentries", ticketID)) body, err := cw.GetRequest(cwurl)
if err != nil {
body := cw.GetRequest(cwurl) return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
timeEntryReference := []TimeEntryReference{} timeEntryReference := []TimeEntryReference{}
check(json.Unmarshal(body, &timeEntryReference)) // *[]TimeEntryReference err = json.Unmarshal(body, &timeEntryReference)
if err != nil {
return &timeEntryReference return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
} }
func (cw *ConnectwiseSite) GetTicketConfigurationsByID(ticketID int) *[]ConfigurationReference { return &timeEntryReference, nil
}
cwurl := cw.BuildURL(fmt.Sprintf("/service/tickets/%d/configurations", ticketID)) func (cw *ConnectwiseSite) GetTicketConfigurationsByID(ticketID int) (*[]ConfigurationReference, error) {
restAction := fmt.Sprintf("/service/tickets/%d/configurations", ticketID)
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
body := cw.GetRequest(cwurl) body, err := cw.GetRequest(cwurl)
if err != nil {
return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
configurationReference := []ConfigurationReference{} configurationReference := []ConfigurationReference{}
check(json.Unmarshal(body, &configurationReference)) err = json.Unmarshal(body, &configurationReference)
if err != nil {
return &configurationReference return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
}
return &configurationReference, nil
} }

View File

@ -17,35 +17,61 @@ type Callback struct {
InactiveFlag bool InactiveFlag bool
} }
func (cw *ConnectwiseSite) GetCallbacks() *[]Callback { func (cw *ConnectwiseSite) GetCallbacks() (*[]Callback, error) {
restAction := "/system/callbacks"
URL := cw.BuildURL("/system/callbacks") cwurl, err := cw.BuildURL(restAction)
body := cw.GetRequest(URL) if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
body, err := cw.GetRequest(cwurl)
if err != nil {
return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
}
callbacks := []Callback{} callbacks := []Callback{}
check(json.Unmarshal(body, &callbacks)) err = json.Unmarshal(body, &callbacks)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
}
return &callbacks return &callbacks, nil
} }
//TBD: This should return something? //TBD: This should return something useful, response body??
func (cw *ConnectwiseSite) NewCallback(callback Callback) { func (cw *ConnectwiseSite) NewCallback(callback Callback) error {
restAction := "/system/callbacks"
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return fmt.Errorf("could not build url %s: %g", restAction, err)
}
URL := cw.BuildURL("/system/callbacks")
jsonCallback, err := json.Marshal(callback) jsonCallback, err := json.Marshal(callback)
check(err) if err != nil {
return fmt.Errorf("could not marshal json data: %g", err)
}
jsonBuffer := bytes.NewReader(jsonCallback) jsonBuffer := bytes.NewReader(jsonCallback)
cw.PostRequest(URL, jsonBuffer) _, err = cw.PostRequest(cwurl, jsonBuffer)
if err != nil {
return fmt.Errorf("could not post request %s: %g", cwurl, err)
} }
func (cw *ConnectwiseSite) DeleteCallback(callback int) { return nil
}
URL := cw.BuildURL(fmt.Sprintf("/system/callbacks/%d", callback))
body := cw.DeleteRequest(URL) //TBD: This should return something useful, response body??
fmt.Print(string(body)) func (cw *ConnectwiseSite) DeleteCallback(callback int) error {
restAction := fmt.Sprintf("/system/callbacks/%d", callback)
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return fmt.Errorf("could not build url %s: %g", restAction, err)
}
_, err = cw.DeleteRequest(cwurl)
if err != nil {
return fmt.Errorf("could not delete request %s: %g", cwurl, err)
}
return nil
} }

View File

@ -83,15 +83,23 @@ type TimeEntry struct {
} }
} }
func (cw *ConnectwiseSite) GetTimeEntryByID(timeEntryID int) *TimeEntry { func (cw *ConnectwiseSite) GetTimeEntryByID(timeEntryID int) (*TimeEntry, error) {
restAction := fmt.Sprintf("/time/entries/%d", timeEntryID)
cwurl, err := cw.BuildURL(restAction)
if err != nil {
return nil, fmt.Errorf("could not build url %s: %g", restAction, err)
}
Url := cw.BuildURL(fmt.Sprintf("/time/entries/%d", timeEntryID)) body, err := cw.GetRequest(cwurl)
if err != nil {
body := cw.GetRequest(Url) return nil, fmt.Errorf("could not get request %s: %g", cwurl, err)
fmt.Print(string(body)) }
timeEntry := TimeEntry{} timeEntry := TimeEntry{}
check(json.Unmarshal(body, &timeEntry)) err = json.Unmarshal(body, &timeEntry)
if err != nil {
return &timeEntry return nil, fmt.Errorf("failed to unmarshal body into struct: %g", err)
}
return &timeEntry, nil
} }