diff --git a/.gitignore b/.gitignore index 9be5652..51a1aad 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -example/main.go \ No newline at end of file +example/main.go +.vscode/tasks.json diff --git a/itglue/itglue.go b/itglue/itglue.go index 061a916..fd05cea 100644 --- a/itglue/itglue.go +++ b/itglue/itglue.go @@ -4,15 +4,18 @@ import ( "fmt" "io" "io/ioutil" - "log" "net/http" "net/url" ) -func check(err error) { - if err != nil { - log.Fatal(err) - } +type Metadata struct { + CurrentPage int `json:"current-page"` + NextPage interface{} `json:"next-page"` + PrevPage interface{} `json:"prev-page"` + TotalPages int `json:"total-pages"` + TotalCount int `json:"total-count"` + Filters struct { + } `json:"filters"` } //ITGAPI contains the ITG API URL for North America, as well as the API key. @@ -28,43 +31,48 @@ func NewITGAPI(apiKey string) *ITGAPI { return &ITGAPI{Site: "https://api.itglue.com", APIKey: apiKey} } -func getHTTPResponseBody(resp *http.Response) []byte { +func getHTTPResponseBody(resp *http.Response) ([]byte, error) { if resp.StatusCode != http.StatusOK { - - out := fmt.Sprintf("ITG returned HTTP status code %s\n%s", resp.Status, resp.Body) - log.Fatal(out) - return make([]byte, 0) //TBD: Don't hack + return nil, fmt.Errorf("ITG returned HTTP status code %s\n%s", resp.Status, resp.Body) } body, err := ioutil.ReadAll(resp.Body) - check(err) + if err != nil { + return nil, fmt.Errorf("could not read http response body: %s", err) + } - return body + return body, nil } //BuildURL expects a restaction to be passed to it //Returns the full request URL containing the ITG API domain prepended to the rest action -func (itg *ITGAPI) BuildURL(restAction string) *url.URL { +func (itg *ITGAPI) BuildURL(restAction string) (*url.URL, error) { var itgurl *url.URL itgurl, err := url.Parse(itg.Site) - check(err) + if err != nil { + return nil, fmt.Errorf("could not parse url %s: %s", itg.Site, err) + } itgurl.Path += restAction - return itgurl + return itgurl, nil } //GetRequest allows a custom GET request to the API to be made. //Also used internally by this package by the binding functions //Expects URL to be passed //Returns the response body as a byte slice -func (itg *ITGAPI) GetRequest(itgurl *url.URL) []byte { +func (itg *ITGAPI) GetRequest(itgurl *url.URL) ([]byte, error) { client := &http.Client{} req, err := http.NewRequest("GET", itgurl.String(), nil) - check(err) + if err != nil { + return nil, fmt.Errorf("could not create http request: %s", err) + } req.Header.Set("Content-Type", "application/vnd.api+json") req.Header.Set("x-api-key", itg.APIKey) req.Header.Set("cache-control", "no-cache") response, err := client.Do(req) - check(err) + if err != nil { + return nil, fmt.Errorf("http request failed: %s", err) + } defer response.Body.Close() return getHTTPResponseBody(response) @@ -74,15 +82,19 @@ func (itg *ITGAPI) GetRequest(itgurl *url.URL) []byte { //Also used internally by this package by the binding functions //Expects a URL and a body to be passed //Returns the response body as a byte slice -func (itg *ITGAPI) PostRequest(itgurl *url.URL, body io.Reader) []byte { +func (itg *ITGAPI) PostRequest(itgurl *url.URL, body io.Reader) ([]byte, error) { client := &http.Client{} req, err := http.NewRequest("POST", itgurl.String(), body) - check(err) + if err != nil { + return nil, fmt.Errorf("could not create http request: %s", err) + } req.Header.Set("Content-Type", "application/vnd.api+json") req.Header.Set("x-api-key", itg.APIKey) req.Header.Set("cache-control", "no-cache") response, err := client.Do(req) - check(err) + if err != nil { + return nil, fmt.Errorf("http request failed: %s", err) + } defer response.Body.Close() return getHTTPResponseBody(response) @@ -93,15 +105,19 @@ func (itg *ITGAPI) PostRequest(itgurl *url.URL, body io.Reader) []byte { //Also used internally by this package by the binding functions //Expects a URL and a body to be passed //Returns the response body as a byte slice -func (itg *ITGAPI) PatchRequest(itgurl *url.URL, body io.Reader) []byte { +func (itg *ITGAPI) PatchRequest(itgurl *url.URL, body io.Reader) ([]byte, error) { client := &http.Client{} req, err := http.NewRequest("PATCH", itgurl.String(), body) - check(err) + if err != nil { + return nil, fmt.Errorf("could not create http request: %s", err) + } req.Header.Set("Content-Type", "application/vnd.api+json") req.Header.Set("x-api-key", itg.APIKey) req.Header.Set("cache-control", "no-cache") response, err := client.Do(req) - check(err) + if err != nil { + return nil, fmt.Errorf("http request failed: %s", err) + } defer response.Body.Close() return getHTTPResponseBody(response) diff --git a/itglue/organizations.go b/itglue/organizations.go index df32a0f..641c13e 100644 --- a/itglue/organizations.go +++ b/itglue/organizations.go @@ -4,8 +4,32 @@ import ( "encoding/json" "fmt" "net/url" + "time" ) +type OrganizationTypeData struct { + ID string `json:"id"` + Type string `json:"type"` + Attributes struct { + Name string `json:"name"` + CreatedAt time.Time `json:"created-at"` + UpdatedAt time.Time `json:"updated-at"` + Synced bool `json:"synced"` + } `json:"attributes"` + + Meta struct { + CurrentPage int `json:"current-page"` + NextPage interface{} `json:"next-page"` + PrevPage interface{} `json:"prev-page"` + TotalPages int `json:"total-pages"` + TotalCount int `json:"total-count"` + Filters struct { + } `json:"filters"` + } `json:"meta"` + Links struct { + } `json:"links"` +} + //OrganizationInternalData contains the schema of an Organization in IT Glue without the "data" wrapper. //This allows us to reuse the schema when data is either a JSON object or an array, depending on what results are returned type OrganizationInternalData struct { @@ -36,13 +60,40 @@ type OrganizationList struct { Data []struct{ OrganizationInternalData } `json:"data"` } +///organization_types +func (itg *ITGAPI) GetOrganizationTypes() error { + itgurl, err := itg.BuildURL("/organization_types") + if err != nil { + return fmt.Errorf("could not build URL: %s", err) + } + body, err := itg.GetRequest(itgurl) + if err != nil { + return fmt.Errorf("request failed: %s", err) + } + fmt.Println(string(body)) + + /*organization := &Organization{} + err := json.Unmarshal(body, organization) + if err != nil { + return nil, fmt.Errorf("could not get organization: %s", err) + } + */ + return nil +} + //GetOrganizationByID expects an ITG organization ID //Returns a pointer to an Organization struct func (itg *ITGAPI) GetOrganizationByID(organizationID int) (*Organization, error) { - itgurl := itg.BuildURL(fmt.Sprintf("/organizations/%d", organizationID)) - body := itg.GetRequest(itgurl) + itgurl, err := itg.BuildURL(fmt.Sprintf("/organizations/%d", organizationID)) + if err != nil { + return nil, fmt.Errorf("could not build URL: %s", err) + } + body, err := itg.GetRequest(itgurl) + if err != nil { + return nil, fmt.Errorf("request failed: %s", err) + } organization := &Organization{} - err := json.Unmarshal(body, organization) + err = json.Unmarshal(body, organization) if err != nil { return nil, fmt.Errorf("could not get organization: %s", err) } @@ -51,13 +102,19 @@ func (itg *ITGAPI) GetOrganizationByID(organizationID int) (*Organization, error //GetOrganizationByName expects an exact matching organization name and returns an OrganizationList func (itg *ITGAPI) GetOrganizationByName(organizationName string) (*OrganizationList, error) { - itgurl := itg.BuildURL("/organizations") + itgurl, err := itg.BuildURL("/organizations") + if err != nil { + return nil, fmt.Errorf("could not build URL: %s", err) + } params := url.Values{} params.Add("filter[name]", organizationName) itgurl.RawQuery = params.Encode() - body := itg.GetRequest(itgurl) + body, err := itg.GetRequest(itgurl) + if err != nil { + return nil, fmt.Errorf("request failed: %s", err) + } organization := &OrganizationList{} - err := json.Unmarshal(body, organization) + err = json.Unmarshal(body, organization) if err != nil { return nil, fmt.Errorf("could not get organization: %s", err) }