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 comments object #355

Merged
merged 4 commits into from Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
140 changes: 140 additions & 0 deletions comment.go
@@ -0,0 +1,140 @@
package tfe

import (
"context"
"fmt"
"net/url"
)

// Compile-time proof of interface implementation.
var _ Comments = (*comments)(nil)

// Comments describes all the comment related methods that the
// Terraform Enterprise API supports.
//
// TFE API docs:
// https://www.terraform.io/docs/cloud/api/comments.html
type Comments interface {
// List all comments of the given run.
List(ctx context.Context, runID string, options *CommentListOptions) (*CommentList, error)

// Read a comment by its ID.
Read(ctx context.Context, CommentID string) (*Comment, error)

// Create a new comment with the given options.
Create(ctx context.Context, runID string, options CommentCreateOptions) (*Comment, error)
}

// Comments implements Comments.
type comments struct {
client *Client
}

// CommentList represents a list of comments.
type CommentList struct {
*Pagination
Items []*Comment
}

// Comment represents a Terraform Enterprise comment..
type Comment struct {
ID string `jsonapi:"primary,comments"`
Body string `jsonapi:"attr,body"`
}

// CommentListOptions represents the options for listing comments.
type CommentListOptions struct {
ListOptions
}

type CommentCreateOptions struct {
// Type is a public field utilized by JSON:API to
// set the resource type via the field tag.
// It is not a user-defined value and does not need to be set.
// https://jsonapi.org/format/#crud-creating
Type string `jsonapi:"primary,comments"`

// Required: Body of the comment.
Body *string `jsonapi:"attr,body"`
alex-ikse marked this conversation as resolved.
Show resolved Hide resolved

// Optional: Run where the comment is attached
Run *Run `jsonapi:"relation,run"`
alex-ikse marked this conversation as resolved.
Show resolved Hide resolved
}

// List all comments of the given run.
func (s *comments) List(ctx context.Context, runID string, options *CommentListOptions) (*CommentList, error) {
if !validStringID(&runID) {
return nil, ErrInvalidRunID
}
if err := options.valid(); err != nil {
return nil, err
}

u := fmt.Sprintf("runs/%s/comments", url.QueryEscape(runID))
req, err := s.client.newRequest("GET", u, options)
if err != nil {
return nil, err
}

cl := &CommentList{}
err = s.client.do(ctx, req, cl)
if err != nil {
return nil, err
}

return cl, nil
}

// Create a new comment with the given options.
func (s *comments) Create(ctx context.Context, runID string, options CommentCreateOptions) (*Comment, error) {
if !validStringID(&runID) {
return nil, ErrInvalidRunID
}

if !validString(options.Body) {
alex-ikse marked this conversation as resolved.
Show resolved Hide resolved
return nil, ErrCommentBody
}

u := fmt.Sprintf("runs/%s/comments", url.QueryEscape(runID))
req, err := s.client.newRequest("POST", u, &options)
if err != nil {
return nil, err
}

comm := &Comment{}
err = s.client.do(ctx, req, comm)
if err != nil {
return nil, err
}

return comm, err
}

// Read a comment by its ID.
func (s *comments) Read(ctx context.Context, CommentID string) (*Comment, error) {
if !validStringID(&CommentID) {
return nil, ErrInvalidCommentID
}

u := fmt.Sprintf("comments/%s", url.QueryEscape(CommentID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}

comm := &Comment{}
err = s.client.do(ctx, req, comm)
if err != nil {
return nil, err
}

return comm, nil
}

func (o *CommentListOptions) valid() error {
if o == nil {
return nil // nothing to validate
}

return nil
}
4 changes: 4 additions & 0 deletions errors.go
Expand Up @@ -145,6 +145,8 @@ var (
ErrInvalidVariableID = errors.New("invalid value for variable ID")

ErrInvalidNotificationTrigger = errors.New("invalid value for notification trigger")

ErrInvalidCommentID = errors.New("invalid value for comment ID")
)

// Missing values for required field/option
Expand Down Expand Up @@ -248,4 +250,6 @@ var (
ErrRequiredOnlyOneField = errors.New("only one of usernames or organization membership ids can be provided")

ErrRequiredUsernameOrMembershipIds = errors.New("usernames or organization membership ids are required")

ErrCommentBody = errors.New("comment body is required")
)
1 change: 1 addition & 0 deletions run.go
Expand Up @@ -118,6 +118,7 @@ type Run struct {
PolicyChecks []*PolicyCheck `jsonapi:"relation,policy-checks"`
TaskStages []*TaskStage `jsonapi:"relation,task-stages,omitempty"`
Workspace *Workspace `jsonapi:"relation,workspace"`
Comments []*Comment `jsonapi:"relation,comments"`
}

// RunActions represents the run actions.
Expand Down
2 changes: 2 additions & 0 deletions tfe.go
Expand Up @@ -110,6 +110,7 @@ type Client struct {
AgentPools AgentPools
AgentTokens AgentTokens
Applies Applies
Comments Comments
ConfigurationVersions ConfigurationVersions
CostEstimates CostEstimates
NotificationConfigurations NotificationConfigurations
Expand Down Expand Up @@ -251,6 +252,7 @@ func NewClient(cfg *Config) (*Client, error) {
client.AgentPools = &agentPools{client: client}
client.AgentTokens = &agentTokens{client: client}
client.Applies = &applies{client: client}
client.Comments = &comments{client: client}
client.ConfigurationVersions = &configurationVersions{client: client}
client.CostEstimates = &costEstimates{client: client}
client.NotificationConfigurations = &notificationConfigurations{client: client}
Expand Down