Skip to content

Commit

Permalink
feat: add String Comments API methods (#28)
Browse files Browse the repository at this point in the history
* feat: add String Comments API methods

* test: add unit tests for String Comments API methods
  • Loading branch information
vorobeyme authored Apr 29, 2024
1 parent 0ffeebe commit e9b41ce
Show file tree
Hide file tree
Showing 5 changed files with 678 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crowdin/crowdin.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Client struct {
SourceFiles *SourceFilesService
SourceStrings *SourceStringsService
StringTranslations *StringTranslationsService
StringComments *StringCommentsService
Translations *TranslationsService
TranslationStatus *TranslationStatusService
}
Expand Down Expand Up @@ -82,6 +83,7 @@ func NewClient(token string, opts ...ClientOption) (*Client, error) {
c.TranslationStatus = &TranslationStatusService{client: c}
c.SourceStrings = &SourceStringsService{client: c}
c.StringTranslations = &StringTranslationsService{client: c}
c.StringComments = &StringCommentsService{client: c}

return c, nil
}
Expand Down
1 change: 1 addition & 0 deletions crowdin/crowdin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func testClientServices(t *testing.T, c *Client) {
"SourceFiles",
"SourceStrings",
"StringTranslations",
"StringComments",
"Translations",
"TranslationStatus",
}
Expand Down
151 changes: 151 additions & 0 deletions crowdin/model/string_comments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package model

import (
"errors"
"fmt"
"net/url"
"strings"
)

// StringComment represents a Crowdin string comment.
type StringComment struct {
ID int `json:"id"`
Text string `json:"text"`
UserID int `json:"userId"`
StringID int `json:"stringId"`
User *User `json:"user"`
String *String `json:"string"`
ProjectID int `json:"projectId"`
LanguageID string `json:"languageId"`
Type string `json:"type"`
IssueType string `json:"issueType"`
IssueStatus string `json:"issueStatus"`
ResolverID int `json:"resolverId"`
Resolver *User `json:"resolver"`
ResolvedAt string `json:"resolvedAt"`
CreatedAt string `json:"createdAt"`

IsShared *bool `json:"isShared,omitempty"`
SenderOrganization *Organization `json:"senderOrganization,omitempty"`
ResolverOrganization *Organization `json:"resolverOrganization,omitempty"`
}

type String struct {
ID int `json:"id"`
Text string `json:"text"`
Type string `json:"type"`
Context string `json:"context"`
FileID int `json:"fileId"`
}

type Organization struct {
ID int `json:"id"`
Domain string `json:"domain"`
}

// StringCommentsResponse defines the structure of of the response when
// getting a single string comment.
type StringCommentsResponse struct {
Data *StringComment `json:"data"`
}

// StringCommentsListResponse defines the structure of the response when
// getting a list of string comments.
type StringCommentsListResponse struct {
Data []*StringCommentsResponse `json:"data"`
}

// StringCommentsListOptions specifies the optional parameters to the
// StringCommentsService.List method.
type StringCommentsListOptions struct {
// Sort results by specified field.
// Enum: id, text, type, createdAt, resolvedAt, issueStatus, issueType.
// Example: orderBy=createdAt desc,text
OrderBy string `url:"orderBy,omitempty"`
// String Identifier.
StringID int `url:"stringId,omitempty"`
// Defines string comment type.
// Enum: comment, issue.
// Note: `type=comment` can't be used with `issueType` or `issueStatus`
// in same request.
Type string `url:"type,omitempty"`
// Defines issue type. It can be one issue type or multiple issue types.
// Enum: general_question, translation_mistake, context_request, source_mistake.
// Example: issueType=general_question,translation_mistake
IssueType []string `url:"issueType,omitempty"`
// Defines issue resolution status.
// Enum: resolved, unresolved.
IssueStatus string `url:"issueStatus,omitempty"`

ListOptions
}

// Values returns the url.Values representation of the StringCommentsListOptions.
// It implements the crowdin.ListOptionsProvider interface.
func (o *StringCommentsListOptions) Values() (url.Values, bool) {
if o == nil {
return nil, false
}

v, _ := o.ListOptions.Values()

if o.OrderBy != "" {
v.Set("orderBy", o.OrderBy)
}
if o.StringID != 0 {
v.Set("stringId", fmt.Sprintf("%d", o.StringID))
}
if o.Type != "" {
v.Set("type", o.Type)
}
if o.IssueType != nil {
v.Set("issueType", strings.Join(o.IssueType, ","))
}
if o.IssueStatus != "" {
v.Set("issueStatus", o.IssueStatus)
}

return v, len(v) > 0
}

// StringCommentsAddRequest defines the structure of the request to add a
// new string comment.
type StringCommentsAddRequest struct {
// Text of the comment.
Text string `json:"text"`
// String Identifier.
StringID int `json:"stringId"`
// Target Language Identifier.
TargetLanguageID string `json:"targetLanguageId"`
// Defines comment or issue.
// Enum: comment, issue.
Type string `json:"type"`
// Defines issue type.
// Enum: general_question, translation_mistake, context_request, source_mistake.
// Default: general_question.
IssueType string `json:"issueType,omitempty"`
// Defines shared comment or issue.
IsShared *bool `json:"isShared,omitempty"`
}

// Validate checks if the StringCommentsAddRequest is valid.
// It implements the crowdin.RequestValidator interface.
func (r *StringCommentsAddRequest) Validate() error {
if r == nil {
return ErrNilRequest
}
if r.Text == "" {
return errors.New("text is required")
}
if r.StringID == 0 {
return errors.New("stringId is required")
}
if r.TargetLanguageID == "" {
return errors.New("targetLanguageId is required")
}
if r.Type == "" {
return errors.New("type is required")
}

return nil
}
84 changes: 84 additions & 0 deletions crowdin/string_comments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package crowdin

import (
"context"
"fmt"

"github.com/crowdin/crowdin-api-client-go/crowdin/model"
)

// Use API to list, add, edit or remove string comments.
//
// Crowdin API docs: https://developer.crowdin.com/api/v2/#tag/String-Comments
type StringCommentsService struct {
client *Client
}

// List returns a list of string comments.
//
// https://developer.crowdin.com/api/v2/#operation/api.projects.comments.getMany
func (s *StringCommentsService) List(ctx context.Context, projectID int, opts *model.StringCommentsListOptions) (
[]*model.StringComment, *Response, error,
) {
res := new(model.StringCommentsListResponse)
resp, err := s.client.Get(ctx, fmt.Sprintf("/api/v2/projects/%d/comments", projectID), opts, res)
if err != nil {
return nil, resp, err
}

list := make([]*model.StringComment, 0, len(res.Data))
for _, comment := range res.Data {
list = append(list, comment.Data)
}

return list, resp, nil
}

// Get returns a string comment by its ID.
//
// https://developer.crowdin.com/api/v2/#operation/api.projects.comments.post
func (s *StringCommentsService) Get(ctx context.Context, projectID, commentID int) (
*model.StringComment, *Response, error,
) {
res := new(model.StringCommentsResponse)
resp, err := s.client.Get(ctx, fmt.Sprintf("/api/v2/projects/%d/comments/%d", projectID, commentID), nil, res)

return res.Data, resp, err
}

// Add creates a new string comment.
//
// https://developer.crowdin.com/api/v2/#operation/api.projects.comments.post
func (s *StringCommentsService) Add(ctx context.Context, projectID int, req *model.StringCommentsAddRequest) (
*model.StringComment, *Response, error,
) {
res := new(model.StringCommentsResponse)
resp, err := s.client.Post(ctx, fmt.Sprintf("/api/v2/projects/%d/comments", projectID), req, res)

return res.Data, resp, err
}

// Edit updates a string comment.
//
// Request body:
// - op: The operation to perform. Enum: replace, test.
// - path: A JSON Pointer as defined by RFC 6901. Enum: "/text", "/issueStatus".
// - value: The value to be used within the operations.
// The value must be string.
//
// https://developer.crowdin.com/api/v2/#operation/api.projects.comments.patch
func (s *StringCommentsService) Edit(ctx context.Context, projectID, commentID int, req []*model.UpdateRequest) (
*model.StringComment, *Response, error,
) {
res := new(model.StringCommentsResponse)
resp, err := s.client.Patch(ctx, fmt.Sprintf("/api/v2/projects/%d/comments/%d", projectID, commentID), req, res)

return res.Data, resp, err
}

// Delete removes a string comment.
//
// https://developer.crowdin.com/api/v2/#operation/api.projects.comments.delete
func (s *StringCommentsService) Delete(ctx context.Context, projectID, commentID int) (*Response, error) {
return s.client.Delete(ctx, fmt.Sprintf("/api/v2/projects/%d/comments/%d", projectID, commentID))
}
Loading

0 comments on commit e9b41ce

Please sign in to comment.