Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add slack connection apis #432

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,17 +499,25 @@ func (c *Client) prepRequest(req *http.Request, authRequired bool, headers map[s
}

func dupeRequest(r *http.Request) (*http.Request, error) {
data, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, fmt.Errorf("failed to copy request body: %w", err)
}
var data []byte
// Body can be nil in GET requests, for example.
if r.Body != nil {
bodyData, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, fmt.Errorf("failed to copy request body: %w", err)
}

_ = r.Body.Close()
_ = r.Body.Close()

data = bodyData
}

dreq := r.Clone(r.Context())

r.Body = ioutil.NopCloser(bytes.NewReader(data))
dreq.Body = ioutil.NopCloser(bytes.NewReader(data))
if data != nil {
r.Body = ioutil.NopCloser(bytes.NewReader(data))
dreq.Body = ioutil.NopCloser(bytes.NewReader(data))
}
Comment on lines 501 to +520
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please raise this chunk as an isolated PR?


return dreq, nil
}
Expand Down
128 changes: 128 additions & 0 deletions slack_connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package pagerduty

import (
"context"
"fmt"
"net/http"

"github.com/google/go-querystring/query"
)

// SlackConnectionConfig is the configuration of a Slack connection as per the documentation
// https://developer.pagerduty.com/api-reference/c2NoOjExMjA5MzMy-slack-connection.
type SlackConnectionConfig struct {
Events []string `json:"events"`
Urgency *string `json:"urgency"`
Priorities []string `json:"priorities"`
}

// SlackConnection is an entity that represents a Slack connections as per the
// documentation https://developer.pagerduty.com/api-reference/c2NoOjExMjA5MzMy-slack-connection.
type SlackConnection struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type seems to be missing the ID string field.

SourceID string `json:"source_id"`
SourceName string `json:"source_name"`
SourceType string `json:"source_type"`

ChannelID string `json:"channel_id"`
ChannelName string `json:"channel_name"`
NotificationType string `json:"notification_type"`

Config SlackConnectionConfig `json:"config"`
}

// SlackConnectionObject is an API object returned by getter functions.
type SlackConnectionObject struct {
SlackConnection
APIObject
}
Comment on lines +34 to +37
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We intend to no longer use embedding in this project. Please list the fields on the type explicitly. See #318 for more details.

That said, I think we should probably just elide this type entirely and put all of the fields in the SlackConnection type.


// ListSlackConnectionsResponse is an API object returned by the list function.
type ListSlackConnectionsResponse struct {
Connections []SlackConnectionObject `json:"slack_connections"`
APIListObject
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment around embedding as well.

}

// ListSlackConnectionsOptions is the data structure used when calling the ListSlackConnections API endpoint.
type ListSlackConnectionsOptions struct {
// Limit is the pagination parameter that limits the number of results per
// page. PagerDuty defaults this value to 50 if omitted, and sets an upper
// bound of 100.
Limit uint `url:"limit,omitempty"`

// Offset is the pagination parameter that specifies the offset at which to
// start pagination results. When trying to request the next page of
// results, the new Offset value should be currentOffset + Limit.
Offset uint `url:"offset,omitempty"`
}

// CreateSlackConnectionWithContext creates a Slack connection.
func (c *Client) CreateSlackConnectionWithContext(ctx context.Context, slackTeamID string, s SlackConnection) (SlackConnectionObject, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove WithContext from the name, since there is not a non-context version.

d := map[string]SlackConnection{
"slack_connection": s,
}

resp, err := c.post(ctx, "/integration-slack/workspaces/"+slackTeamID+"/connections", d, nil)
return getSlackConnectionFromResponse(c, resp, err)
}

// GetSlackConnection gets a Slack connection.
func (c *Client) GetSlackConnectionWithContext(ctx context.Context, slackTeamID, connectionID string) (SlackConnectionObject, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove WithContext from the name, since there is not a non-context version.

resp, err := c.get(ctx, "/integration-slack/workspaces/"+slackTeamID+"/connections/"+connectionID)
return getSlackConnectionFromResponse(c, resp, err)
}

// DeleteSlackConnectionWithContext deletes a Slack connection.
func (c *Client) DeleteSlackConnectionWithContext(ctx context.Context, slackTeamID, connectionID string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove WithContext from the name, since there is not a non-context version.

_, err := c.delete(ctx, "/integration-slack/workspaces/"+slackTeamID+"/connections/"+connectionID)
return err
}

// UpdateSlackConnectionWithContext updates an existing Slack connection.
func (c *Client) UpdateSlackConnectionWithContext(ctx context.Context, slackTeamID, connectionID string, s SlackConnection) (SlackConnectionObject, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove WithContext from the name, since there is not a non-context version.

d := map[string]SlackConnection{
"slack_connection": s,
}

resp, err := c.put(ctx, "/integration-slack/workspaces/"+slackTeamID+"/connections/"+connectionID, d, nil)
return getSlackConnectionFromResponse(c, resp, err)
}

// ListSlackConnectionsWithContext lists Slack connections.
func (c *Client) ListSlackConnectionsWithContext(ctx context.Context, slackTeamID string, o ListSlackConnectionsOptions) (*ListSlackConnectionsResponse, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove WithContext from the name, since there is not a non-context version. Also, I don't think this should return a pointer type. We're going to be moving away from those when it makes sense (#270).

v, err := query.Values(o)
if err != nil {
return nil, err
}

resp, err := c.get(ctx, "/integration-slack/workspaces/"+slackTeamID+"/connections?"+v.Encode())
if err != nil {
return nil, err
}

var result ListSlackConnectionsResponse
if err = c.decodeJSON(resp, &result); err != nil {
return nil, err
}

return &result, nil
}

func getSlackConnectionFromResponse(c *Client, resp *http.Response, err error) (SlackConnectionObject, error) {
if err != nil {
return SlackConnectionObject{}, err
}

var target map[string]SlackConnectionObject
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return SlackConnectionObject{}, fmt.Errorf("Could not decode JSON response: %v", dErr)
}

const rootNode = "slack_connection"

t, nodeOK := target[rootNode]
if !nodeOK {
return SlackConnectionObject{}, fmt.Errorf("JSON response does not have %s field", rootNode)
}

return t, nil
}
Loading