From 4c7e25ae000170d16c2f3c26dcff772db6dd36d5 Mon Sep 17 00:00:00 2001 From: itchyny Date: Wed, 8 Nov 2023 20:40:00 +0900 Subject: [PATCH] Reduce code for building requests and decoding response bodies --- alert_group_settings.go | 102 ++++---------------- alerts.go | 111 ++++++---------------- aws_integrations.go | 142 +++++----------------------- channels.go | 68 +++----------- checks.go | 7 +- dashboards.go | 104 ++++----------------- downtimes.go | 78 +++------------- graph_annotation.go | 97 ++++--------------- graph_defs.go | 7 +- host_metadata.go | 63 ++++--------- hosts.go | 201 +++++++++++----------------------------- hosts_test.go | 4 +- invitations.go | 25 +---- mackerel.go | 68 ++++++++++---- mackerel_test.go | 22 +++-- metrics.go | 77 ++++++--------- monitors.go | 68 ++++---------- notification_groups.go | 88 ++++-------------- org.go | 23 +---- role_metadata.go | 63 ++++--------- roles.go | 67 ++------------ service_metadata.go | 63 ++++--------- services.go | 83 +++-------------- users.go | 47 ++-------- 24 files changed, 401 insertions(+), 1277 deletions(-) diff --git a/alert_group_settings.go b/alert_group_settings.go index b89f497..cceeb8e 100644 --- a/alert_group_settings.go +++ b/alert_group_settings.go @@ -1,10 +1,6 @@ package mackerel -import ( - "encoding/json" - "fmt" - "net/http" -) +import "fmt" // AlertGroupSetting represents a Mackerel alert group setting. // ref. https://mackerel.io/api-docs/entry/alert-group-settings @@ -18,102 +14,36 @@ type AlertGroupSetting struct { NotificationInterval uint64 `json:"notificationInterval,omitempty"` } -// FindAlertGroupSettings finds alert group settings +// FindAlertGroupSettings finds alert group settings. func (c *Client) FindAlertGroupSettings() ([]*AlertGroupSetting, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/alert-group-settings").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { AlertGroupSettings []*AlertGroupSetting `json:"alertGroupSettings"` - } - if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + }](c, "/api/v0/alert-group-settings") + if err != nil { return nil, err } - return data.AlertGroupSettings, nil } -// CreateAlertGroupSetting creates a alert group setting +// CreateAlertGroupSetting creates an alert group setting. func (c *Client) CreateAlertGroupSetting(param *AlertGroupSetting) (*AlertGroupSetting, error) { - resp, err := c.PostJSON("/api/v0/alert-group-settings", param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var alertGroupSetting AlertGroupSetting - if err := json.NewDecoder(resp.Body).Decode(&alertGroupSetting); err != nil { - return nil, err - } - - return &alertGroupSetting, nil + return requestPost[AlertGroupSetting](c, "/api/v0/alert-group-settings", param) } -// GetAlertGroupSetting gets alert group setting specified by ID +// GetAlertGroupSetting gets an alert group setting. func (c *Client) GetAlertGroupSetting(id string) (*AlertGroupSetting, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/alert-group-settings/%s", id)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var alertGroupSetting AlertGroupSetting - if err := json.NewDecoder(resp.Body).Decode(&alertGroupSetting); err != nil { - return nil, err - } - - return &alertGroupSetting, nil + path := fmt.Sprintf("/api/v0/alert-group-settings/%s", id) + return requestGet[AlertGroupSetting](c, path) } -// UpdateAlertGroupSetting updates a alert group setting +// UpdateAlertGroupSetting updates an alert group setting. func (c *Client) UpdateAlertGroupSetting(id string, param *AlertGroupSetting) (*AlertGroupSetting, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/alert-group-settings/%s", id), param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var alertGroupSetting AlertGroupSetting - if err := json.NewDecoder(resp.Body).Decode(&alertGroupSetting); err != nil { - return nil, err - } - - return &alertGroupSetting, nil + path := fmt.Sprintf("/api/v0/alert-group-settings/%s", id) + return requestPut[AlertGroupSetting](c, path, param) } -// DeleteAlertGroupSetting deletes a alert group setting specified by ID. +// DeleteAlertGroupSetting deletes an alert group setting. func (c *Client) DeleteAlertGroupSetting(id string) (*AlertGroupSetting, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/alert-group-settings/%s", id)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var alertGroupSetting AlertGroupSetting - if err := json.NewDecoder(resp.Body).Decode(&alertGroupSetting); err != nil { - return nil, err - } - - return &alertGroupSetting, nil + path := fmt.Sprintf("/api/v0/alert-group-settings/%s", id) + return requestDelete[AlertGroupSetting](c, path) } diff --git a/alerts.go b/alerts.go index e27a70c..58f5af1 100644 --- a/alerts.go +++ b/alerts.go @@ -1,9 +1,7 @@ package mackerel import ( - "encoding/json" "fmt" - "net/http" "net/url" ) @@ -61,108 +59,51 @@ type UpdateAlertResponse struct { Memo string `json:"memo,omitempty"` } -func (c *Client) findAlertsWithParam(v url.Values) (*AlertsResp, error) { - var d AlertsResp - u := c.urlFor("/api/v0/alerts") - u.RawQuery = v.Encode() - req, err := http.NewRequest("GET", u.String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - err = json.NewDecoder(resp.Body).Decode(&d) - if err != nil { - return nil, err - } - return &d, err +func (c *Client) findAlertsWithParams(params url.Values) (*AlertsResp, error) { + return requestGetWithParams[AlertsResp](c, "/api/v0/alerts", params) } -// FindAlerts find open alerts +// FindAlerts finds open alerts. func (c *Client) FindAlerts() (*AlertsResp, error) { - return c.findAlertsWithParam(url.Values{}) + return c.findAlertsWithParams(nil) } -// FindAlertsByNextID find next open alerts by next id +// FindAlertsByNextID finds next open alerts by next id. func (c *Client) FindAlertsByNextID(nextID string) (*AlertsResp, error) { - v := url.Values{} - v.Set("nextId", nextID) - return c.findAlertsWithParam(v) + params := url.Values{} + params.Set("nextId", nextID) + return c.findAlertsWithParams(params) } -// FindWithClosedAlerts find open and close alerts +// FindWithClosedAlerts finds open and close alerts. func (c *Client) FindWithClosedAlerts() (*AlertsResp, error) { - v := url.Values{} - v.Set("withClosed", "true") - return c.findAlertsWithParam(v) + params := url.Values{} + params.Set("withClosed", "true") + return c.findAlertsWithParams(params) } -// FindWithClosedAlertsByNextID find open and close alerts by next id +// FindWithClosedAlertsByNextID finds open and close alerts by next id. func (c *Client) FindWithClosedAlertsByNextID(nextID string) (*AlertsResp, error) { - v := url.Values{} - v.Set("nextId", nextID) - v.Set("withClosed", "true") - return c.findAlertsWithParam(v) + params := url.Values{} + params.Set("nextId", nextID) + params.Set("withClosed", "true") + return c.findAlertsWithParams(params) } -// GetAlert gets Alert +// GetAlert gets an alert. func (c *Client) GetAlert(alertID string) (*Alert, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/alerts/%s", alertID)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var alert *Alert - err = json.NewDecoder(resp.Body).Decode(&alert) - if err != nil { - return nil, err - } - return alert, err + path := fmt.Sprintf("/api/v0/alerts/%s", alertID) + return requestGet[Alert](c, path) } -// CloseAlert close alert +// CloseAlert closes an alert. func (c *Client) CloseAlert(alertID string, reason string) (*Alert, error) { - var reqBody struct { - Reason string `json:"reason"` - } - reqBody.Reason = reason - resp, err := c.PostJSON(fmt.Sprintf("/api/v0/alerts/%s/close", alertID), &reqBody) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data *Alert - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - - return data, nil + path := fmt.Sprintf("/api/v0/alerts/%s/close", alertID) + return requestPost[Alert](c, path, map[string]string{"reason": reason}) } -// UpdateAlert updates an Alert +// UpdateAlert updates an alert. func (c *Client) UpdateAlert(alertID string, param UpdateAlertParam) (*UpdateAlertResponse, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/alerts/%s", alertID), param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data UpdateAlertResponse - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - - return &data, nil + path := fmt.Sprintf("/api/v0/alerts/%s", alertID) + return requestPut[UpdateAlertResponse](c, path, param) } diff --git a/aws_integrations.go b/aws_integrations.go index eab391c..ce4f50f 100644 --- a/aws_integrations.go +++ b/aws_integrations.go @@ -1,12 +1,8 @@ package mackerel -import ( - "encoding/json" - "fmt" - "net/http" -) +import "fmt" -// AWSIntegration aws integration information +// AWSIntegration AWS integration information type AWSIntegration struct { ID string `json:"id"` Name string `json:"name"` @@ -45,143 +41,55 @@ type CreateAWSIntegrationParam struct { // UpdateAWSIntegrationParam parameters for UpdateAwsIntegration type UpdateAWSIntegrationParam CreateAWSIntegrationParam -// ListAWSIntegrationExcludableMetrics List of excludeable metric names for aws integration +// ListAWSIntegrationExcludableMetrics List of excludeable metric names for AWS integration type ListAWSIntegrationExcludableMetrics map[string][]string -// FindAWSIntegrations finds AWS Integration Settings +// FindAWSIntegrations finds AWS integration settings. func (c *Client) FindAWSIntegrations() ([]*AWSIntegration, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/aws-integrations").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { AWSIntegrations []*AWSIntegration `json:"aws_integrations"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/aws-integrations") if err != nil { return nil, err } - return data.AWSIntegrations, err + return data.AWSIntegrations, nil } -// FindAWSIntegration finds AWS Integration Setting -func (c *Client) FindAWSIntegration(awsIntegrationID string) (*AWSIntegration, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/aws-integrations/%s", awsIntegrationID)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var awsIntegration *AWSIntegration - err = json.NewDecoder(resp.Body).Decode(&awsIntegration) - if err != nil { - return nil, err - } - return awsIntegration, err -} - -// CreateAWSIntegration creates AWS Integration Setting +// CreateAWSIntegration creates an AWS integration setting. func (c *Client) CreateAWSIntegration(param *CreateAWSIntegrationParam) (*AWSIntegration, error) { - resp, err := c.PostJSON("/api/v0/aws-integrations", param) - defer closeResponse(resp) - if err != nil { - return nil, err - } + return requestPost[AWSIntegration](c, "/api/v0/aws-integrations", param) +} - var awsIntegration *AWSIntegration - err = json.NewDecoder(resp.Body).Decode(&awsIntegration) - if err != nil { - return nil, err - } - return awsIntegration, err +// FindAWSIntegration finds an AWS integration setting. +func (c *Client) FindAWSIntegration(awsIntegrationID string) (*AWSIntegration, error) { + path := fmt.Sprintf("/api/v0/aws-integrations/%s", awsIntegrationID) + return requestGet[AWSIntegration](c, path) } -// UpdateAWSIntegration updates AWS Integration Setting +// UpdateAWSIntegration updates an AWS integration setting. func (c *Client) UpdateAWSIntegration(awsIntegrationID string, param *UpdateAWSIntegrationParam) (*AWSIntegration, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/aws-integrations/%s", awsIntegrationID), param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var awsIntegration *AWSIntegration - err = json.NewDecoder(resp.Body).Decode(&awsIntegration) - if err != nil { - return nil, err - } - return awsIntegration, err + path := fmt.Sprintf("/api/v0/aws-integrations/%s", awsIntegrationID) + return requestPut[AWSIntegration](c, path, param) } -// DeleteAWSIntegration deletes AWS Integration Setting +// DeleteAWSIntegration deletes an AWS integration setting. func (c *Client) DeleteAWSIntegration(awsIntegrationID string) (*AWSIntegration, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/aws-integrations/%s", awsIntegrationID)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var awsIntegration *AWSIntegration - err = json.NewDecoder(resp.Body).Decode(&awsIntegration) - if err != nil { - return nil, err - } - return awsIntegration, err + path := fmt.Sprintf("/api/v0/aws-integrations/%s", awsIntegrationID) + return requestDelete[AWSIntegration](c, path) } -// CreateAWSIntegrationExternalID creates AWS Integration External ID +// CreateAWSIntegrationExternalID creates an AWS integration External ID. func (c *Client) CreateAWSIntegrationExternalID() (string, error) { - resp, err := c.PostJSON("/api/v0/aws-integrations-external-id", nil) - defer closeResponse(resp) - if err != nil { - return "", err - } - - var data struct { + data, err := requestPost[struct { ExternalID string `json:"externalId"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/aws-integrations-external-id", nil) if err != nil { return "", err } return data.ExternalID, nil } -// ListAWSIntegrationExcludableMetrics lists excludable metrics for AWS Integration +// ListAWSIntegrationExcludableMetrics lists excludable metrics for AWS integration. func (c *Client) ListAWSIntegrationExcludableMetrics() (*ListAWSIntegrationExcludableMetrics, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/aws-integrations-excludable-metrics").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var listAWSIntegrationExcludableMetrics *ListAWSIntegrationExcludableMetrics - err = json.NewDecoder(resp.Body).Decode(&listAWSIntegrationExcludableMetrics) - if err != nil { - return nil, err - } - return listAWSIntegrationExcludableMetrics, err + return requestGet[ListAWSIntegrationExcludableMetrics](c, "/api/v0/aws-integrations-excludable-metrics") } diff --git a/channels.go b/channels.go index 6b90419..cabec29 100644 --- a/channels.go +++ b/channels.go @@ -1,10 +1,6 @@ package mackerel -import ( - "encoding/json" - "fmt" - "net/http" -) +import "fmt" // Channel represents a Mackerel notification channel. // ref. https://mackerel.io/api-docs/entry/channels @@ -39,66 +35,24 @@ type Mentions struct { Critical string `json:"critical,omitempty"` } -// FindChannels requests the channels API and returns a list of Channel +// FindChannels finds channels. func (c *Client) FindChannels() ([]*Channel, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/channels").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Channels []*Channel `json:"channels"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/channels") if err != nil { return nil, err } - return data.Channels, err + return data.Channels, nil } -// CreateChannel requests the channels API with the given params to create a channel and returns the created channel. +// CreateChannel creates a channel. func (c *Client) CreateChannel(param *Channel) (*Channel, error) { - resp, err := c.PostJSON("/api/v0/channels", param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - channel := &Channel{} - err = json.NewDecoder(resp.Body).Decode(channel) - if err != nil { - return nil, err - } - return channel, nil + return requestPost[Channel](c, "/api/v0/channels", param) } -// DeleteChannel requests the channels API with the given id to delete the specified channel, and returns the deleted channel. -func (c *Client) DeleteChannel(id string) (*Channel, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/channels/%s", id)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - channel := &Channel{} - err = json.NewDecoder(resp.Body).Decode(channel) - if err != nil { - return nil, err - } - return channel, nil +// DeleteChannel deletes a channel. +func (c *Client) DeleteChannel(channelID string) (*Channel, error) { + path := fmt.Sprintf("/api/v0/channels/%s", channelID) + return requestDelete[Channel](c, path) } diff --git a/checks.go b/checks.go index f8ccb89..9048e71 100644 --- a/checks.go +++ b/checks.go @@ -61,9 +61,8 @@ type CheckReports struct { Reports []*CheckReport `json:"reports"` } -// PostCheckReports reports check monitoring results -func (c *Client) PostCheckReports(crs *CheckReports) error { - resp, err := c.PostJSON("/api/v0/monitoring/checks/report", crs) - defer closeResponse(resp) +// PostCheckReports reports check monitoring results. +func (c *Client) PostCheckReports(checkReports *CheckReports) error { + _, err := requestPost[any](c, "/api/v0/monitoring/checks/report", checkReports) return err } diff --git a/dashboards.go b/dashboards.go index cfcf183..9b11725 100644 --- a/dashboards.go +++ b/dashboards.go @@ -3,7 +3,6 @@ package mackerel import ( "encoding/json" "fmt" - "net/http" ) /* @@ -135,6 +134,7 @@ func (m Metric) MarshalJSON() ([]byte, error) { return json.Marshal(Alias(m)) } +// FormatRule information type FormatRule struct { Name string `json:"name"` Threshold float64 `json:"threshold"` @@ -198,6 +198,7 @@ func (r Range) MarshalJSON() ([]byte, error) { } } +// ReferenceLine information type ReferenceLine struct { Label string `json:"label"` Value float64 `json:"value"` @@ -211,103 +212,36 @@ type Layout struct { Height int64 `json:"height"` } -// FindDashboards find dashboards +// FindDashboards finds dashboards. func (c *Client) FindDashboards() ([]*Dashboard, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/dashboards").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Dashboards []*Dashboard `json:"dashboards"` - } - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - - return data.Dashboards, err -} - -// FindDashboard find dashboard -func (c *Client) FindDashboard(dashboardID string) (*Dashboard, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) + }](c, "/api/v0/dashboards") if err != nil { return nil, err } - - var data Dashboard - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, err + return data.Dashboards, nil } -// CreateDashboard creating dashboard +// CreateDashboard creates a dashboard. func (c *Client) CreateDashboard(param *Dashboard) (*Dashboard, error) { - resp, err := c.PostJSON("/api/v0/dashboards", param) - defer closeResponse(resp) - if err != nil { - return nil, err - } + return requestPost[Dashboard](c, "/api/v0/dashboards", param) +} - var data Dashboard - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, nil +// FindDashboard finds a dashboard. +func (c *Client) FindDashboard(dashboardID string) (*Dashboard, error) { + path := fmt.Sprintf("/api/v0/dashboards/%s", dashboardID) + return requestGet[Dashboard](c, path) } -// UpdateDashboard update dashboard +// UpdateDashboard updates a dashboard. func (c *Client) UpdateDashboard(dashboardID string, param *Dashboard) (*Dashboard, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID), param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data Dashboard - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, nil + path := fmt.Sprintf("/api/v0/dashboards/%s", dashboardID) + return requestPut[Dashboard](c, path, param) } -// DeleteDashboard delete dashboard +// DeleteDashboard deletes a dashboard. func (c *Client) DeleteDashboard(dashboardID string) (*Dashboard, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/dashboards/%s", dashboardID)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data Dashboard - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, nil + path := fmt.Sprintf("/api/v0/dashboards/%s", dashboardID) + return requestDelete[Dashboard](c, path) } diff --git a/downtimes.go b/downtimes.go index 4ca8564..ce26951 100644 --- a/downtimes.go +++ b/downtimes.go @@ -3,7 +3,6 @@ package mackerel import ( "encoding/json" "fmt" - "net/http" "time" ) @@ -128,83 +127,30 @@ func (w DowntimeWeekday) String() string { return weekdayToString[w] } -// FindDowntimes finds downtimes +// FindDowntimes finds downtimes. func (c *Client) FindDowntimes() ([]*Downtime, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/downtimes").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Downtimes []*Downtime `json:"downtimes"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/downtimes") if err != nil { return nil, err } - - return data.Downtimes, err + return data.Downtimes, nil } -// CreateDowntime creates a downtime +// CreateDowntime creates a downtime. func (c *Client) CreateDowntime(param *Downtime) (*Downtime, error) { - resp, err := c.PostJSON("/api/v0/downtimes", param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data Downtime - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, nil + return requestPost[Downtime](c, "/api/v0/downtimes", param) } -// UpdateDowntime updates a downtime +// UpdateDowntime updates a downtime. func (c *Client) UpdateDowntime(downtimeID string, param *Downtime) (*Downtime, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/downtimes/%s", downtimeID), param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data Downtime - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, nil + path := fmt.Sprintf("/api/v0/downtimes/%s", downtimeID) + return requestPut[Downtime](c, path, param) } -// DeleteDowntime deletes downtime +// DeleteDowntime deletes a downtime. func (c *Client) DeleteDowntime(downtimeID string) (*Downtime, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/downtimes/%s", downtimeID)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data Downtime - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, nil + path := fmt.Sprintf("/api/v0/downtimes/%s", downtimeID) + return requestDelete[Downtime](c, path) } diff --git a/graph_annotation.go b/graph_annotation.go index 995defe..97a4dae 100644 --- a/graph_annotation.go +++ b/graph_annotation.go @@ -1,14 +1,12 @@ package mackerel import ( - "encoding/json" "fmt" - "net/http" "net/url" "strconv" ) -// GraphAnnotation represents parameters to post graph annotation. +// GraphAnnotation represents parameters to post a graph annotation. type GraphAnnotation struct { ID string `json:"id,omitempty"` Title string `json:"title,omitempty"` @@ -19,90 +17,35 @@ type GraphAnnotation struct { Roles []string `json:"roles,omitempty"` } -// CreateGraphAnnotation creates graph annotation. -func (c *Client) CreateGraphAnnotation(annotation *GraphAnnotation) (*GraphAnnotation, error) { - resp, err := c.PostJSON("/api/v0/graph-annotations", annotation) - defer closeResponse(resp) - - if err != nil { - return nil, err - } +// FindGraphAnnotations fetches graph annotations. +func (c *Client) FindGraphAnnotations(service string, from int64, to int64) ([]*GraphAnnotation, error) { + params := url.Values{} + params.Add("service", service) + params.Add("from", strconv.FormatInt(from, 10)) + params.Add("to", strconv.FormatInt(to, 10)) - var anno GraphAnnotation - err = json.NewDecoder(resp.Body).Decode(&anno) + data, err := requestGetWithParams[struct { + GraphAnnotations []*GraphAnnotation `json:"graphAnnotations"` + }](c, "/api/v0/graph-annotations", params) if err != nil { return nil, err } - return &anno, nil + return data.GraphAnnotations, nil } -// FindGraphAnnotations fetches graph annotation. -func (c *Client) FindGraphAnnotations(service string, from int64, to int64) ([]GraphAnnotation, error) { - v := url.Values{} - v.Add("service", service) - v.Add("from", strconv.FormatInt(from, 10)) - v.Add("to", strconv.FormatInt(to, 10)) - - req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor("/api/v0/graph-annotations").String(), v.Encode()), nil) - if err != nil { - return nil, err - } - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { - GraphAnnotations []GraphAnnotation `json:"graphAnnotations"` - } - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return data.GraphAnnotations, err +// CreateGraphAnnotation creates a graph annotation. +func (c *Client) CreateGraphAnnotation(annotation *GraphAnnotation) (*GraphAnnotation, error) { + return requestPost[GraphAnnotation](c, "/api/v0/graph-annotations", annotation) } -// UpdateGraphAnnotation updates graph annotation. +// UpdateGraphAnnotation updates a graph annotation. func (c *Client) UpdateGraphAnnotation(annotationID string, annotation *GraphAnnotation) (*GraphAnnotation, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/graph-annotations/%s", annotationID), annotation) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var updatedAnnotation = GraphAnnotation{} - err = json.NewDecoder(resp.Body).Decode(&updatedAnnotation) - if err != nil { - return nil, err - } - - return &updatedAnnotation, nil + path := fmt.Sprintf("/api/v0/graph-annotations/%s", annotationID) + return requestPut[GraphAnnotation](c, path, annotation) } -// DeleteGraphAnnotation deletes graph annotation. +// DeleteGraphAnnotation deletes a graph annotation. func (c *Client) DeleteGraphAnnotation(annotationID string) (*GraphAnnotation, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/graph-annotations/%s", annotationID)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var annotation GraphAnnotation - err = json.NewDecoder(resp.Body).Decode(&annotation) - if err != nil { - return nil, err - } - return &annotation, nil + path := fmt.Sprintf("/api/v0/graph-annotations/%s", annotationID) + return requestDelete[GraphAnnotation](c, path) } diff --git a/graph_defs.go b/graph_defs.go index fc8ad34..f27ff97 100644 --- a/graph_defs.go +++ b/graph_defs.go @@ -15,9 +15,8 @@ type GraphDefsMetric struct { IsStacked bool `json:"isStacked"` } -// CreateGraphDefs create graph defs -func (c *Client) CreateGraphDefs(payloads []*GraphDefsParam) error { - resp, err := c.PostJSON("/api/v0/graph-defs/create", payloads) - defer closeResponse(resp) +// CreateGraphDefs creates graph definitions. +func (c *Client) CreateGraphDefs(graphDefs []*GraphDefsParam) error { + _, err := requestPost[any](c, "/api/v0/graph-defs/create", graphDefs) return err } diff --git a/host_metadata.go b/host_metadata.go index dd2c7be..cbeabd1 100644 --- a/host_metadata.go +++ b/host_metadata.go @@ -1,7 +1,6 @@ package mackerel import ( - "encoding/json" "fmt" "net/http" "time" @@ -18,77 +17,47 @@ type HostMetaDataResp struct { // HostMetaData represents host metadata body. type HostMetaData interface{} -// GetHostMetaData find host metadata. +// GetHostMetaData gets a host metadata. func (c *Client) GetHostMetaData(hostID, namespace string) (*HostMetaDataResp, error) { - url := c.urlFor(fmt.Sprintf("/api/v0/hosts/%s/metadata/%s", hostID, namespace)) - req, err := http.NewRequest("GET", url.String(), nil) + path := fmt.Sprintf("/api/v0/hosts/%s/metadata/%s", hostID, namespace) + metadata, header, err := requestGetAndReturnHeader[HostMetaData](c, path) if err != nil { return nil, err } - resp, err := c.Request(req) - defer closeResponse(resp) + lastModified, err := http.ParseTime(header.Get("Last-Modified")) if err != nil { return nil, err } - var data HostMetaDataResp - if err := json.NewDecoder(resp.Body).Decode(&data.HostMetaData); err != nil { - return nil, err - } - data.LastModified, err = http.ParseTime(resp.Header.Get("Last-Modified")) - if err != nil { - return nil, err - } - return &data, nil + return &HostMetaDataResp{HostMetaData: *metadata, LastModified: lastModified}, nil } // GetHostMetaDataNameSpaces fetches namespaces of host metadata. func (c *Client) GetHostMetaDataNameSpaces(hostID string) ([]string, error) { - url := c.urlFor(fmt.Sprintf("/api/v0/hosts/%s/metadata", hostID)) - req, err := http.NewRequest("GET", url.String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - var data struct { + data, err := requestGet[struct { MetaDatas []struct { NameSpace string `json:"namespace"` } `json:"metadata"` - } - if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + }](c, fmt.Sprintf("/api/v0/hosts/%s/metadata", hostID)) + if err != nil { return nil, err } - namespaces := make([]string, 0, len(data.MetaDatas)) - for _, metadata := range data.MetaDatas { - namespaces = append(namespaces, metadata.NameSpace) + namespaces := make([]string, len(data.MetaDatas)) + for i, metadata := range data.MetaDatas { + namespaces[i] = metadata.NameSpace } return namespaces, nil } -// PutHostMetaData put host metadata. +// PutHostMetaData puts a host metadata. func (c *Client) PutHostMetaData(hostID, namespace string, metadata HostMetaData) error { path := fmt.Sprintf("/api/v0/hosts/%s/metadata/%s", hostID, namespace) - resp, err := c.PutJSON(path, metadata) - defer closeResponse(resp) + _, err := requestPut[any](c, path, metadata) return err } -// DeleteHostMetaData delete host metadata. +// DeleteHostMetaData deletes a host metadata. func (c *Client) DeleteHostMetaData(hostID, namespace string) error { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/hosts/%s/metadata/%s", hostID, namespace)).String(), - nil, - ) - if err != nil { - return err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) + path := fmt.Sprintf("/api/v0/hosts/%s/metadata/%s", hostID, namespace) + _, err := requestDelete[any](c, path) return err } diff --git a/hosts.go b/hosts.go index 6cbc333..4cc05cd 100644 --- a/hosts.go +++ b/hosts.go @@ -1,9 +1,7 @@ package mackerel import ( - "encoding/json" "fmt" - "net/http" "net/url" "strings" "time" @@ -166,220 +164,129 @@ func (h *Host) IPAddresses() map[string]string { return ipAddresses } -// FindHost finds the host -func (c *Client) FindHost(id string) (*Host, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/hosts/%s", id)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { +// FindHost finds the host. +func (c *Client) FindHost(hostID string) (*Host, error) { + data, err := requestGet[struct { Host *Host `json:"host"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, fmt.Sprintf("/api/v0/hosts/%s", hostID)) if err != nil { return nil, err } - return data.Host, err + return data.Host, nil } -// FindHosts finds hosts +// FindHosts finds hosts. func (c *Client) FindHosts(param *FindHostsParam) ([]*Host, error) { - v := url.Values{} + params := url.Values{} if param.Service != "" { - v.Set("service", param.Service) + params.Set("service", param.Service) } - if len(param.Roles) >= 1 { - for _, role := range param.Roles { - v.Add("role", role) - } + for _, role := range param.Roles { + params.Add("role", role) } if param.Name != "" { - v.Set("name", param.Name) + params.Set("name", param.Name) } - if len(param.Statuses) >= 1 { - for _, status := range param.Statuses { - v.Add("status", status) - } + for _, status := range param.Statuses { + params.Add("status", status) } if param.CustomIdentifier != "" { - v.Set("customIdentifier", param.CustomIdentifier) - } - - req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor("/api/v0/hosts").String(), v.Encode()), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err + params.Set("customIdentifier", param.CustomIdentifier) } - var data struct { + data, err := requestGetWithParams[struct { Hosts []*Host `json:"hosts"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/hosts", params) if err != nil { return nil, err } - - return data.Hosts, err + return data.Hosts, nil } -// FindHostByCustomIdentifier finds host via CustomIdentifier +// FindHostByCustomIdentifier finds a host by the custom identifier. func (c *Client) FindHostByCustomIdentifier(customIdentifier string, param *FindHostByCustomIdentifierParam) (*Host, error) { - v := url.Values{} + path := "/api/v0/hosts-by-custom-identifier/" + url.PathEscape(customIdentifier) + params := url.Values{} if param.CaseInsensitive { - v.Set("caseInsensitive", "true") - } - - req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s?%s", c.urlFor("/api/v0/hosts-by-custom-identifier").String(), url.PathEscape(customIdentifier), v.Encode()), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err + params.Set("caseInsensitive", "true") } - - var data struct { + data, err := requestGetWithParams[struct { Host *Host `json:"host"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, path, params) if err != nil { return nil, err } - - return data.Host, err + return data.Host, nil } -// CreateHost creates host +// CreateHost creates a host. func (c *Client) CreateHost(param *CreateHostParam) (string, error) { - resp, err := c.PostJSON("/api/v0/hosts", param) - defer closeResponse(resp) - if err != nil { - return "", err - } - - var data struct { + data, err := requestPost[struct { ID string `json:"id"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/hosts", param) if err != nil { return "", err } return data.ID, nil } -// UpdateHost updates host +// UpdateHost updates a host. func (c *Client) UpdateHost(hostID string, param *UpdateHostParam) (string, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/hosts/%s", hostID), param) - defer closeResponse(resp) - if err != nil { - return "", err - } - - var data struct { + path := fmt.Sprintf("/api/v0/hosts/%s", hostID) + data, err := requestPut[struct { ID string `json:"id"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, path, param) if err != nil { return "", err } - return data.ID, nil } -// UpdateHostStatus updates host status +// UpdateHostStatus updates a host status. func (c *Client) UpdateHostStatus(hostID string, status string) error { - resp, err := c.PostJSON(fmt.Sprintf("/api/v0/hosts/%s/status", hostID), map[string]string{ - "status": status, - }) - defer closeResponse(resp) - if err != nil { - return err - } - return nil + path := fmt.Sprintf("/api/v0/hosts/%s/status", hostID) + _, err := requestPost[any](c, path, map[string]string{"status": status}) + return err } -// UpdateHostRoleFullnames updates host roles +// UpdateHostRoleFullnames updates host roles. func (c *Client) UpdateHostRoleFullnames(hostID string, roleFullnames []string) error { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/hosts/%s/role-fullnames", hostID), map[string][]string{ - "roleFullnames": roleFullnames, - }) - defer closeResponse(resp) - if err != nil { - return err - } - return nil + path := fmt.Sprintf("/api/v0/hosts/%s/role-fullnames", hostID) + _, err := requestPut[any](c, path, map[string][]string{"roleFullnames": roleFullnames}) + return err } -// RetireHost retires the host -func (c *Client) RetireHost(id string) error { - resp, err := c.PostJSON(fmt.Sprintf("/api/v0/hosts/%s/retire", id), "{}") - defer closeResponse(resp) +// RetireHost retires the host. +func (c *Client) RetireHost(hostID string) error { + path := fmt.Sprintf("/api/v0/hosts/%s/retire", hostID) + _, err := requestPost[any](c, path, nil) return err } -// BulkRetireHosts retires the hosts +// BulkRetireHosts retires the hosts. func (c *Client) BulkRetireHosts(ids []string) error { - resp, err := c.PostJSON("/api/v0/hosts/bulk-retire", map[string][]string{ - "ids": ids, - }) - defer closeResponse(resp) + _, err := requestPost[any](c, "/api/v0/hosts/bulk-retire", map[string][]string{"ids": ids}) return err } -// ListHostMetricNames lists metric names of a host -func (c *Client) ListHostMetricNames(id string) ([]string, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/hosts/%s/metric-names", id)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { +// ListHostMetricNames lists metric names of a host. +func (c *Client) ListHostMetricNames(hostID string) ([]string, error) { + data, err := requestGet[struct { Names []string `json:"names"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, fmt.Sprintf("/api/v0/hosts/%s/metric-names", hostID)) if err != nil { return nil, err } - return data.Names, err + return data.Names, nil } -// ListMonitoredStatues lists monitored statues of a host -func (c *Client) ListMonitoredStatues(id string) ([]MonitoredStatus, error) { - - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/hosts/%s/monitored-statuses", id)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { +// ListMonitoredStatues lists monitored statues of a host. +func (c *Client) ListMonitoredStatues(hostID string) ([]MonitoredStatus, error) { + data, err := requestGet[struct { MonitoredStatuses []MonitoredStatus `json:"monitoredStatuses"` - } - - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, fmt.Sprintf("/api/v0/hosts/%s/monitored-statuses", hostID)) if err != nil { return nil, err } - return data.MonitoredStatuses, nil } diff --git a/hosts_test.go b/hosts_test.go index 15eeafb..8e4d05e 100644 --- a/hosts_test.go +++ b/hosts_test.go @@ -152,8 +152,8 @@ func TestFindHosts(t *testing.T) { func TestFindHostByCustomIdentifier(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { - if req.URL.RawPath != "/api/v0/hosts-by-custom-identifier/mydb001%2F001" { - t.Error("request URL.RawPath should be /api/v0/hosts-by-custom-identifier/mydb001%$2F001 but: ", req.URL.RawPath) + if req.URL.Path != "/api/v0/hosts-by-custom-identifier/mydb001%2F001" { + t.Error("request URL.Path should be /api/v0/hosts-by-custom-identifier/mydb001%$2F001 but: ", req.URL.Path) } query := req.URL.Query() diff --git a/invitations.go b/invitations.go index 77eb9a2..0d5d2d3 100644 --- a/invitations.go +++ b/invitations.go @@ -1,10 +1,5 @@ package mackerel -import ( - "encoding/json" - "net/http" -) - // Invitation information type Invitation struct { Email string `json:"email,omitempty"` @@ -12,25 +7,13 @@ type Invitation struct { ExpiresAt int64 `json:"expiresAt,omitempty"` } -// FindInvitations find invitations. +// FindInvitations finds invitations. func (c *Client) FindInvitations() ([]*Invitation, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/invitations").String(), nil) - - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Invitations []*Invitation `json:"invitations"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/invitations") if err != nil { return nil, err } - return data.Invitations, err + return data.Invitations, nil } diff --git a/mackerel.go b/mackerel.go index e80ee9f..077ec3d 100644 --- a/mackerel.go +++ b/mackerel.go @@ -65,14 +65,13 @@ func NewClientWithOptions(apikey string, rawurl string, verbose bool) (*Client, }, nil } -func (c *Client) urlFor(path string) *url.URL { +func (c *Client) urlFor(path string, params url.Values) *url.URL { newURL, err := url.Parse(c.BaseURL.String()) if err != nil { - panic("invalid url passed") + panic("invalid base url") } - newURL.Path = path - + newURL.RawQuery = params.Encode() return newURL } @@ -131,34 +130,67 @@ func (c *Client) Request(req *http.Request) (resp *http.Response, err error) { return resp, nil } -// PostJSON shortcut method for posting json -func (c *Client) PostJSON(path string, payload interface{}) (*http.Response, error) { - return c.requestJSON("POST", path, payload) +func requestGet[T any](client *Client, path string) (*T, error) { + return requestNoBody[T](client, "GET", path, nil) +} + +func requestGetWithParams[T any](client *Client, path string, params url.Values) (*T, error) { + return requestNoBody[T](client, "GET", path, params) +} + +func requestGetAndReturnHeader[T any](client *Client, path string) (*T, http.Header, error) { + return requestInternal[T](client, "GET", path, nil, nil) +} + +func requestPost[T any](client *Client, path string, payload any) (*T, error) { + return requestJSON[T](client, "POST", path, payload) +} + +func requestPut[T any](client *Client, path string, payload any) (*T, error) { + return requestJSON[T](client, "PUT", path, payload) } -// PutJSON shortcut method for putting json -func (c *Client) PutJSON(path string, payload interface{}) (*http.Response, error) { - return c.requestJSON("PUT", path, payload) +func requestDelete[T any](client *Client, path string) (*T, error) { + return requestNoBody[T](client, "DELETE", path, nil) } -func (c *Client) requestJSON(method string, path string, payload interface{}) (*http.Response, error) { +func requestJSON[T any](client *Client, method, path string, payload any) (*T, error) { var body bytes.Buffer err := json.NewEncoder(&body).Encode(payload) if err != nil { return nil, err } + data, _, err := requestInternal[T](client, method, path, nil, &body) + return data, err +} + +func requestNoBody[T any](client *Client, method, path string, params url.Values) (*T, error) { + data, _, err := requestInternal[T](client, method, path, params, nil) + return data, err +} - req, err := http.NewRequest(method, c.urlFor(path).String(), &body) +func requestInternal[T any](client *Client, method, path string, params url.Values, body io.Reader) (*T, http.Header, error) { + req, err := http.NewRequest(method, client.urlFor(path, params).String(), body) if err != nil { - return nil, err + return nil, nil, err + } + if body != nil { + req.Header.Add("Content-Type", "application/json") } - req.Header.Add("Content-Type", "application/json") - return c.Request(req) -} -func closeResponse(resp *http.Response) { - if resp != nil { + resp, err := client.Request(req) + if err != nil { + return nil, nil, err + } + defer func() { io.Copy(io.Discard, resp.Body) // nolint resp.Body.Close() + }() + + var data T + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return nil, nil, err } + return &data, resp.Header, nil } diff --git a/mackerel_test.go b/mackerel_test.go index f02f1fe..ddae2d4 100644 --- a/mackerel_test.go +++ b/mackerel_test.go @@ -7,6 +7,7 @@ import ( "log" "net/http" "net/http/httptest" + "net/url" "os" "strings" "testing" @@ -26,7 +27,7 @@ func TestRequest(t *testing.T) { client, _ := NewClientWithOptions("dummy-key", ts.URL, false) - req, _ := http.NewRequest("GET", client.urlFor("/").String(), nil) + req, _ := http.NewRequest("GET", client.urlFor("/", nil).String(), nil) _, err := client.Request(req) if err != nil { t.Errorf("request is error %v", err) @@ -35,9 +36,18 @@ func TestRequest(t *testing.T) { func TestUrlFor(t *testing.T) { client, _ := NewClientWithOptions("dummy-key", "https://example.com/with/ignored/path", false) - xURL := "https://example.com/some/super/endpoint" - if url := client.urlFor("/some/super/endpoint").String(); url != xURL { - t.Errorf("urlFor should be '%s' but %s", xURL, url) + expected := "https://example.com/some/super/endpoint" + if url := client.urlFor("/some/super/endpoint", nil).String(); url != expected { + t.Errorf("urlFor should be %q but %q", expected, url) + } + + expected += "?test1=value1&test1=value2&test2=value2" + params := url.Values{} + params.Add("test1", "value1") + params.Add("test1", "value2") + params.Add("test2", "value2") + if url := client.urlFor("/some/super/endpoint", params).String(); url != expected { + t.Errorf("urlFor should be %q but %q", expected, url) } } @@ -50,7 +60,7 @@ func TestBuildReq(t *testing.T) { "X-Revision": []string{xRev}, } cl.UserAgent = "mackerel-agent" - req, _ := http.NewRequest("GET", cl.urlFor("/").String(), nil) + req, _ := http.NewRequest("GET", cl.urlFor("/", nil).String(), nil) req = cl.buildReq(req) if req.Header.Get("X-Api-Key") != "dummy-key" { @@ -76,7 +86,7 @@ func TestLogger(t *testing.T) { client, _ := NewClientWithOptions("dummy-key", ts.URL, true) var buf bytes.Buffer client.Logger = log.New(&buf, "", 0) - req, _ := http.NewRequest("GET", client.urlFor("/").String(), nil) + req, _ := http.NewRequest("GET", client.urlFor("/", nil).String(), nil) _, err := client.Request(req) if err != nil { t.Errorf("request is error %v", err) diff --git a/metrics.go b/metrics.go index 922753e..7fd5c2f 100644 --- a/metrics.go +++ b/metrics.go @@ -1,10 +1,8 @@ package mackerel import ( - "encoding/json" "errors" "fmt" - "net/http" "net/url" "strconv" ) @@ -26,14 +24,13 @@ type HostMetricValue struct { type LatestMetricValues map[string]map[string]*MetricValue // PostHostMetricValues post host metrics -func (c *Client) PostHostMetricValues(metricValues [](*HostMetricValue)) error { - resp, err := c.PostJSON("/api/v0/tsdb", metricValues) - defer closeResponse(resp) +func (c *Client) PostHostMetricValues(metricValues []*HostMetricValue) error { + _, err := requestPost[any](c, "/api/v0/tsdb", metricValues) return err } // PostHostMetricValuesByHostID post host metrics -func (c *Client) PostHostMetricValuesByHostID(hostID string, metricValues [](*MetricValue)) error { +func (c *Client) PostHostMetricValuesByHostID(hostID string, metricValues []*MetricValue) error { var hostMetricValues []*HostMetricValue for _, metricValue := range metricValues { hostMetricValues = append(hostMetricValues, &HostMetricValue{ @@ -44,84 +41,62 @@ func (c *Client) PostHostMetricValuesByHostID(hostID string, metricValues [](*Me return c.PostHostMetricValues(hostMetricValues) } -// PostServiceMetricValues post service metrics -func (c *Client) PostServiceMetricValues(serviceName string, metricValues [](*MetricValue)) error { - resp, err := c.PostJSON(fmt.Sprintf("/api/v0/services/%s/tsdb", serviceName), metricValues) - defer closeResponse(resp) +// PostServiceMetricValues posts service metrics. +func (c *Client) PostServiceMetricValues(serviceName string, metricValues []*MetricValue) error { + path := fmt.Sprintf("/api/v0/services/%s/tsdb", serviceName) + _, err := requestPost[any](c, path, metricValues) return err } -// FetchLatestMetricValues fetch latest metrics +// FetchLatestMetricValues fetches latest metrics. func (c *Client) FetchLatestMetricValues(hostIDs []string, metricNames []string) (LatestMetricValues, error) { - v := url.Values{} + params := url.Values{} for _, hostID := range hostIDs { - v.Add("hostId", hostID) + params.Add("hostId", hostID) } for _, metricName := range metricNames { - v.Add("name", metricName) + params.Add("name", metricName) } - req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor("/api/v0/tsdb/latest").String(), v.Encode()), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGetWithParams[struct { LatestMetricValues LatestMetricValues `json:"tsdbLatest"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/tsdb/latest", params) if err != nil { return nil, err } - - return data.LatestMetricValues, err + return data.LatestMetricValues, nil } -// FetchHostMetricValues retrieves the metric values for a Host +// FetchHostMetricValues fetches the metric values for a host. func (c *Client) FetchHostMetricValues(hostID string, metricName string, from int64, to int64) ([]MetricValue, error) { return c.fetchMetricValues(hostID, "", metricName, from, to) } -// FetchServiceMetricValues retrieves the metric values for a Service +// FetchServiceMetricValues fetches the metric values for a service. func (c *Client) FetchServiceMetricValues(serviceName string, metricName string, from int64, to int64) ([]MetricValue, error) { return c.fetchMetricValues("", serviceName, metricName, from, to) } func (c *Client) fetchMetricValues(hostID string, serviceName string, metricName string, from int64, to int64) ([]MetricValue, error) { - v := url.Values{} - v.Add("name", metricName) - v.Add("from", strconv.FormatInt(from, 10)) - v.Add("to", strconv.FormatInt(to, 10)) + params := url.Values{} + params.Add("name", metricName) + params.Add("from", strconv.FormatInt(from, 10)) + params.Add("to", strconv.FormatInt(to, 10)) - url := "" + path := "" if hostID != "" { - url = "/api/v0/hosts/" + hostID + "/metrics" + path = fmt.Sprintf("/api/v0/hosts/%s/metrics", hostID) } else if serviceName != "" { - url = "/api/v0/services/" + serviceName + "/metrics" + path = fmt.Sprintf("/api/v0/services/%s/metrics", serviceName) } else { return nil, errors.New("specify either host or service") } - req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", c.urlFor(url).String(), v.Encode()), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - var data struct { + data, err := requestGetWithParams[struct { MetricValues []MetricValue `json:"metrics"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, path, params) if err != nil { return nil, err } - return data.MetricValues, err + return data.MetricValues, nil } diff --git a/monitors.go b/monitors.go index 2d5a61f..c24d71e 100644 --- a/monitors.go +++ b/monitors.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "net/http" ) /* @@ -303,29 +302,17 @@ func (m *MonitorAnomalyDetection) MonitorName() string { return m.Name } // MonitorID returns monitor id. func (m *MonitorAnomalyDetection) MonitorID() string { return m.ID } -// FindMonitors find monitors +// FindMonitors finds monitors. func (c *Client) FindMonitors() ([]Monitor, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/monitors").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Monitors []json.RawMessage `json:"monitors"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/monitors") if err != nil { return nil, err } ms := make([]Monitor, 0, len(data.Monitors)) for _, rawmes := range data.Monitors { m, err := decodeMonitor(rawmes) - var e *unknownMonitorTypeError if err != nil { if errors.As(err, &e) { @@ -338,21 +325,11 @@ func (c *Client) FindMonitors() ([]Monitor, error) { return ms, err } -// GetMonitor get monitor. +// GetMonitor gets a monitor. func (c *Client) GetMonitor(monitorID string) (Monitor, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/monitors/%s", monitorID)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - var data struct { + data, err := requestGet[struct { Monitor json.RawMessage `json:"monitor"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, fmt.Sprintf("/api/v0/monitors/%s", monitorID)) if err != nil { return nil, err } @@ -363,44 +340,33 @@ func (c *Client) GetMonitor(monitorID string) (Monitor, error) { return m, err } -// CreateMonitor creating monitor +// CreateMonitor creates a monitor. func (c *Client) CreateMonitor(param Monitor) (Monitor, error) { - resp, err := c.PostJSON("/api/v0/monitors", param) - defer closeResponse(resp) + data, err := requestPost[json.RawMessage](c, "/api/v0/monitors", param) if err != nil { return nil, err } - return decodeMonitorReader(resp.Body) + return decodeMonitor(*data) } -// UpdateMonitor update monitor +// UpdateMonitor updates a monitor. func (c *Client) UpdateMonitor(monitorID string, param Monitor) (Monitor, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/monitors/%s", monitorID), param) - defer closeResponse(resp) + path := fmt.Sprintf("/api/v0/monitors/%s", monitorID) + data, err := requestPut[json.RawMessage](c, path, param) if err != nil { return nil, err } - return decodeMonitorReader(resp.Body) + return decodeMonitor(*data) } -// DeleteMonitor update monitor +// DeleteMonitor updates a monitor. func (c *Client) DeleteMonitor(monitorID string) (Monitor, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/monitors/%s", monitorID)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) + path := fmt.Sprintf("/api/v0/monitors/%s", monitorID) + data, err := requestDelete[json.RawMessage](c, path) if err != nil { return nil, err } - return decodeMonitorReader(resp.Body) + return decodeMonitor(*data) } type unknownMonitorTypeError struct { diff --git a/notification_groups.go b/notification_groups.go index 2356f31..730bd8c 100644 --- a/notification_groups.go +++ b/notification_groups.go @@ -1,10 +1,6 @@ package mackerel -import ( - "encoding/json" - "fmt" - "net/http" -) +import "fmt" // NotificationLevel represents a level of notification. type NotificationLevel string @@ -38,82 +34,30 @@ type NotificationGroupService struct { Name string `json:"name"` } -// CreateNotificationGroup creates a notification group. -func (c *Client) CreateNotificationGroup(param *NotificationGroup) (*NotificationGroup, error) { - resp, err := c.PostJSON("/api/v0/notification-groups", param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var notificationGroup NotificationGroup - if err := json.NewDecoder(resp.Body).Decode(¬ificationGroup); err != nil { - return nil, err - } - - return ¬ificationGroup, nil -} - -// FindNotificationGroups finds notification groups +// FindNotificationGroups finds notification groups. func (c *Client) FindNotificationGroups() ([]*NotificationGroup, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/notification-groups").String(), nil) - if err != nil { - return nil, err - } - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { NotificationGroups []*NotificationGroup `json:"notificationGroups"` - } - if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + }](c, "/api/v0/notification-groups") + if err != nil { return nil, err } - return data.NotificationGroups, nil } -// UpdateNotificationGroup updates a notification group -func (c *Client) UpdateNotificationGroup(id string, param *NotificationGroup) (*NotificationGroup, error) { - resp, err := c.PutJSON(fmt.Sprintf("/api/v0/notification-groups/%s", id), param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var notificationGroup NotificationGroup - if err := json.NewDecoder(resp.Body).Decode(¬ificationGroup); err != nil { - return nil, err - } +// CreateNotificationGroup creates a notification group. +func (c *Client) CreateNotificationGroup(param *NotificationGroup) (*NotificationGroup, error) { + return requestPost[NotificationGroup](c, "/api/v0/notification-groups", param) +} - return ¬ificationGroup, nil +// UpdateNotificationGroup updates a notification group. +func (c *Client) UpdateNotificationGroup(id string, param *NotificationGroup) (*NotificationGroup, error) { + path := fmt.Sprintf("/api/v0/notification-groups/%s", id) + return requestPut[NotificationGroup](c, path, param) } -// DeleteNotificationGroup deletes a notification group +// DeleteNotificationGroup deletes a notification group. func (c *Client) DeleteNotificationGroup(id string) (*NotificationGroup, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/notification-groups/%s", id)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var notificationGroup NotificationGroup - if err := json.NewDecoder(resp.Body).Decode(¬ificationGroup); err != nil { - return nil, err - } - return ¬ificationGroup, nil + path := fmt.Sprintf("/api/v0/notification-groups/%s", id) + return requestDelete[NotificationGroup](c, path) } diff --git a/org.go b/org.go index 448ed70..0f58fe6 100644 --- a/org.go +++ b/org.go @@ -1,30 +1,11 @@ package mackerel -import ( - "encoding/json" - "net/http" -) - // Org information type Org struct { Name string `json:"name"` } -// GetOrg get the org +// GetOrg gets the org. func (c *Client) GetOrg() (*Org, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/org").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - var data Org - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return nil, err - } - return &data, nil + return requestGet[Org](c, "/api/v0/org") } diff --git a/role_metadata.go b/role_metadata.go index 47b4b0b..83fb692 100644 --- a/role_metadata.go +++ b/role_metadata.go @@ -1,7 +1,6 @@ package mackerel import ( - "encoding/json" "fmt" "net/http" "time" @@ -18,77 +17,47 @@ type RoleMetaDataResp struct { // RoleMetaData represents role metadata body. type RoleMetaData interface{} -// GetRoleMetaData find role metadata. +// GetRoleMetaData gets a role metadata. func (c *Client) GetRoleMetaData(serviceName, roleName, namespace string) (*RoleMetaDataResp, error) { - url := c.urlFor(fmt.Sprintf("/api/v0/services/%s/roles/%s/metadata/%s", serviceName, roleName, namespace)) - req, err := http.NewRequest("GET", url.String(), nil) + path := fmt.Sprintf("/api/v0/services/%s/roles/%s/metadata/%s", serviceName, roleName, namespace) + metadata, header, err := requestGetAndReturnHeader[HostMetaData](c, path) if err != nil { return nil, err } - resp, err := c.Request(req) - defer closeResponse(resp) + lastModified, err := http.ParseTime(header.Get("Last-Modified")) if err != nil { return nil, err } - var data RoleMetaDataResp - if err := json.NewDecoder(resp.Body).Decode(&data.RoleMetaData); err != nil { - return nil, err - } - data.LastModified, err = http.ParseTime(resp.Header.Get("Last-Modified")) - if err != nil { - return nil, err - } - return &data, nil + return &RoleMetaDataResp{RoleMetaData: *metadata, LastModified: lastModified}, nil } // GetRoleMetaDataNameSpaces fetches namespaces of role metadata. func (c *Client) GetRoleMetaDataNameSpaces(serviceName, roleName string) ([]string, error) { - url := c.urlFor(fmt.Sprintf("/api/v0/services/%s/roles/%s/metadata", serviceName, roleName)) - req, err := http.NewRequest("GET", url.String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - var data struct { + data, err := requestGet[struct { MetaDatas []struct { NameSpace string `json:"namespace"` } `json:"metadata"` - } - if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + }](c, fmt.Sprintf("/api/v0/services/%s/roles/%s/metadata", serviceName, roleName)) + if err != nil { return nil, err } - namespaces := make([]string, 0, len(data.MetaDatas)) - for _, metadata := range data.MetaDatas { - namespaces = append(namespaces, metadata.NameSpace) + namespaces := make([]string, len(data.MetaDatas)) + for i, metadata := range data.MetaDatas { + namespaces[i] = metadata.NameSpace } return namespaces, nil } -// PutRoleMetaData put role metadata. +// PutRoleMetaData puts a role metadata. func (c *Client) PutRoleMetaData(serviceName, roleName, namespace string, metadata RoleMetaData) error { path := fmt.Sprintf("/api/v0/services/%s/roles/%s/metadata/%s", serviceName, roleName, namespace) - resp, err := c.PutJSON(path, metadata) - defer closeResponse(resp) + _, err := requestPut[any](c, path, metadata) return err } -// DeleteRoleMetaData delete role metadata. +// DeleteRoleMetaData deletes a role metadata. func (c *Client) DeleteRoleMetaData(serviceName, roleName, namespace string) error { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/services/%s/roles/%s/metadata/%s", serviceName, roleName, namespace)).String(), - nil, - ) - if err != nil { - return err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) + path := fmt.Sprintf("/api/v0/services/%s/roles/%s/metadata/%s", serviceName, roleName, namespace) + _, err := requestDelete[any](c, path) return err } diff --git a/roles.go b/roles.go index f6cc14f..3e29dcd 100644 --- a/roles.go +++ b/roles.go @@ -1,10 +1,6 @@ package mackerel -import ( - "encoding/json" - "fmt" - "net/http" -) +import "fmt" // Role represents Mackerel "role". type Role struct { @@ -17,66 +13,23 @@ type CreateRoleParam Role // FindRoles finds roles. func (c *Client) FindRoles(serviceName string) ([]*Role, error) { - uri := fmt.Sprintf("/api/v0/services/%s/roles", serviceName) - req, err := http.NewRequest("GET", c.urlFor(uri).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Roles []*Role `json:"roles"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, fmt.Sprintf("/api/v0/services/%s/roles", serviceName)) if err != nil { return nil, err } - return data.Roles, err + return data.Roles, nil } -// CreateRole creates role. +// CreateRole creates a role. func (c *Client) CreateRole(serviceName string, param *CreateRoleParam) (*Role, error) { - uri := fmt.Sprintf("/api/v0/services/%s/roles", serviceName) - resp, err := c.PostJSON(uri, param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - role := &Role{} - err = json.NewDecoder(resp.Body).Decode(role) - if err != nil { - return nil, err - } - return role, nil + path := fmt.Sprintf("/api/v0/services/%s/roles", serviceName) + return requestPost[Role](c, path, param) } -// DeleteRole deletes role. +// DeleteRole deletes a role. func (c *Client) DeleteRole(serviceName, roleName string) (*Role, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/services/%s/roles/%s", serviceName, roleName)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - role := &Role{} - err = json.NewDecoder(resp.Body).Decode(role) - if err != nil { - return nil, err - } - return role, nil + path := fmt.Sprintf("/api/v0/services/%s/roles/%s", serviceName, roleName) + return requestDelete[Role](c, path) } diff --git a/service_metadata.go b/service_metadata.go index 3109b01..eb4807d 100644 --- a/service_metadata.go +++ b/service_metadata.go @@ -1,7 +1,6 @@ package mackerel import ( - "encoding/json" "fmt" "net/http" "time" @@ -18,77 +17,47 @@ type ServiceMetaDataResp struct { // ServiceMetaData represents service metadata body. type ServiceMetaData interface{} -// GetServiceMetaData find service metadata. +// GetServiceMetaData gets service metadata. func (c *Client) GetServiceMetaData(serviceName, namespace string) (*ServiceMetaDataResp, error) { - url := c.urlFor(fmt.Sprintf("/api/v0/services/%s/metadata/%s", serviceName, namespace)) - req, err := http.NewRequest("GET", url.String(), nil) + path := fmt.Sprintf("/api/v0/services/%s/metadata/%s", serviceName, namespace) + metadata, header, err := requestGetAndReturnHeader[HostMetaData](c, path) if err != nil { return nil, err } - resp, err := c.Request(req) - defer closeResponse(resp) + lastModified, err := http.ParseTime(header.Get("Last-Modified")) if err != nil { return nil, err } - var data ServiceMetaDataResp - if err := json.NewDecoder(resp.Body).Decode(&data.ServiceMetaData); err != nil { - return nil, err - } - data.LastModified, err = http.ParseTime(resp.Header.Get("Last-Modified")) - if err != nil { - return nil, err - } - return &data, nil + return &ServiceMetaDataResp{ServiceMetaData: *metadata, LastModified: lastModified}, nil } // GetServiceMetaDataNameSpaces fetches namespaces of service metadata. func (c *Client) GetServiceMetaDataNameSpaces(serviceName string) ([]string, error) { - url := c.urlFor(fmt.Sprintf("/api/v0/services/%s/metadata", serviceName)) - req, err := http.NewRequest("GET", url.String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - var data struct { + data, err := requestGet[struct { MetaDatas []struct { NameSpace string `json:"namespace"` } `json:"metadata"` - } - if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + }](c, fmt.Sprintf("/api/v0/services/%s/metadata", serviceName)) + if err != nil { return nil, err } - namespaces := make([]string, 0, len(data.MetaDatas)) - for _, metadata := range data.MetaDatas { - namespaces = append(namespaces, metadata.NameSpace) + namespaces := make([]string, len(data.MetaDatas)) + for i, metadata := range data.MetaDatas { + namespaces[i] = metadata.NameSpace } return namespaces, nil } -// PutServiceMetaData put service metadata. +// PutServiceMetaData puts a service metadata. func (c *Client) PutServiceMetaData(serviceName, namespace string, metadata ServiceMetaData) error { path := fmt.Sprintf("/api/v0/services/%s/metadata/%s", serviceName, namespace) - resp, err := c.PutJSON(path, metadata) - defer closeResponse(resp) + _, err := requestPut[any](c, path, metadata) return err } -// DeleteServiceMetaData delete service metadata. +// DeleteServiceMetaData deletes a service metadata. func (c *Client) DeleteServiceMetaData(serviceName, namespace string) error { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/services/%s/metadata/%s", serviceName, namespace)).String(), - nil, - ) - if err != nil { - return err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) + path := fmt.Sprintf("/api/v0/services/%s/metadata/%s", serviceName, namespace) + _, err := requestDelete[any](c, path) return err } diff --git a/services.go b/services.go index fcf86d5..2981534 100644 --- a/services.go +++ b/services.go @@ -1,10 +1,6 @@ package mackerel -import ( - "encoding/json" - "fmt" - "net/http" -) +import "fmt" // Service represents Mackerel "service". type Service struct { @@ -21,86 +17,33 @@ type CreateServiceParam struct { // FindServices finds services. func (c *Client) FindServices() ([]*Service, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/services").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Services []*Service `json:"services"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/services") if err != nil { return nil, err } - return data.Services, err + return data.Services, nil } -// CreateService creates service +// CreateService creates a service. func (c *Client) CreateService(param *CreateServiceParam) (*Service, error) { - resp, err := c.PostJSON("/api/v0/services", param) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - service := &Service{} - err = json.NewDecoder(resp.Body).Decode(service) - if err != nil { - return nil, err - } - return service, nil + return requestPost[Service](c, "/api/v0/services", param) } -// DeleteService deletes service +// DeleteService deletes a service. func (c *Client) DeleteService(serviceName string) (*Service, error) { - req, err := http.NewRequest( - "DELETE", - c.urlFor(fmt.Sprintf("/api/v0/services/%s", serviceName)).String(), - nil, - ) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - service := &Service{} - err = json.NewDecoder(resp.Body).Decode(service) - if err != nil { - return nil, err - } - return service, nil + path := fmt.Sprintf("/api/v0/services/%s", serviceName) + return requestDelete[Service](c, path) } -// ListServiceMetricNames lists metric names of a service +// ListServiceMetricNames lists metric names of a service. func (c *Client) ListServiceMetricNames(serviceName string) ([]string, error) { - req, err := http.NewRequest("GET", c.urlFor(fmt.Sprintf("/api/v0/services/%s/metric-names", serviceName)).String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Names []string `json:"names"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, fmt.Sprintf("/api/v0/services/%s/metric-names", serviceName)) if err != nil { return nil, err } - return data.Names, err + return data.Names, nil } diff --git a/users.go b/users.go index c3aad22..d663da8 100644 --- a/users.go +++ b/users.go @@ -1,10 +1,6 @@ package mackerel -import ( - "encoding/json" - "fmt" - "net/http" -) +import "fmt" // User information type User struct { @@ -19,46 +15,19 @@ type User struct { JoinedAt int64 `json:"joinedAt,omitempty"` } -// FindUsers find users. +// FindUsers finds users. func (c *Client) FindUsers() ([]*User, error) { - req, err := http.NewRequest("GET", c.urlFor("/api/v0/users").String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - var data struct { + data, err := requestGet[struct { Users []*User `json:"users"` - } - err = json.NewDecoder(resp.Body).Decode(&data) + }](c, "/api/v0/users") if err != nil { return nil, err } - return data.Users, err + return data.Users, nil } -// DeleteUser delete users. +// DeleteUser deletes a user. func (c *Client) DeleteUser(userID string) (*User, error) { - req, err := http.NewRequest("DELETE", c.urlFor(fmt.Sprintf("/api/v0/users/%s", userID)).String(), nil) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.Request(req) - defer closeResponse(resp) - if err != nil { - return nil, err - } - - user := &User{} - err = json.NewDecoder(resp.Body).Decode(user) - if err != nil { - return nil, err - } - return user, nil + path := fmt.Sprintf("/api/v0/users/%s", userID) + return requestDelete[User](c, path) }