diff --git a/api.go b/api.go index d69b64d..6a51ad2 100644 --- a/api.go +++ b/api.go @@ -20,6 +20,7 @@ type Api struct { Projects func() *ProjectService QueuedProcesses func() *QueuedProcessService Screenshots func() *ScreenshotService + Segments func() *SegmentationService Snapshots func() *SnapshotService Tasks func() *TaskService Teams func() *TeamService @@ -65,6 +66,7 @@ func New(apiToken string, options ...ClientOption) (*Api, error) { c.Tasks = func() *TaskService { return &TaskService{BaseService: bs, listOpts: taskOpts} } c.Screenshots = func() *ScreenshotService { return &ScreenshotService{BaseService: bs, listOpts: scOpts} } + c.Segments = func() *SegmentationService { return &SegmentationService{BaseService: bs} } c.Snapshots = func() *SnapshotService { return &SnapshotService{bs} } c.Languages = func() *LanguageService { return &LanguageService{bs} } c.Translations = func() *TranslationService { return &TranslationService{BaseService: bs, opts: trOpts} } diff --git a/api_test.go b/api_test.go index a8493b0..55a1ae9 100644 --- a/api_test.go +++ b/api_test.go @@ -11,9 +11,10 @@ import ( ) const ( - baseURLPath = "/api2" - testApiToken = "apiToken" - testProjectID = "3002780358964f9bab5a92.87762498" + baseURLPath = "/api2" + testApiToken = "apiToken" + testProjectID = "3002780358964f9bab5a92.87762498" + assertionTemplate = "%s returned \n%+v\nexpected\n%+v" ) func setup() (api *Api, mux *http.ServeMux, serverURL string, teardown func()) { diff --git a/svc_key.go b/svc_key.go index 7fa3d10..b05ed54 100644 --- a/svc_key.go +++ b/svc_key.go @@ -101,7 +101,7 @@ type CreateKeysRequest struct { KeyRequestOptions } -// Separate struct for bulk updating +// BulkUpdateKey Separate struct for bulk updating type BulkUpdateKey struct { KeyID int64 `json:"key_id"` NewKey diff --git a/svc_segment.go b/svc_segment.go new file mode 100644 index 0000000..3077ee7 --- /dev/null +++ b/svc_segment.go @@ -0,0 +1,157 @@ +package lokalise + +import ( + "fmt" + "github.com/go-resty/resty/v2" + "github.com/google/go-querystring/query" +) + +const ( + pathSegments = "segments" +) + +// SegmentationService supports List, Retrieve and Update commands +type SegmentationService struct { + BaseService + + listOpts SegmentsListOptions + retrieveOpts SegmentsRetrieveOptions +} + +type Segment struct { + SegmentNumber int64 `json:"segment_number"` + LanguageIso string `json:"language_iso"` + ModifiedAt string `json:"modified_at"` + ModifiedAtTimestamp int64 `json:"modified_at_timestamp"` + ModifiedBy int64 `json:"modified_by"` + ModifiedByEmail string `json:"modified_by_email"` + Value string `json:"value"` + IsFuzzy bool `json:"is_fuzzy"` + IsReviewed bool `json:"is_reviewed"` + ReviewedBy int64 `json:"reviewed_by"` + Words int64 `json:"words"` + CustomTranslationStatuses []TranslationStatus `json:"custom_translation_statuses"` +} + +type SegmentsResponse struct { + WithProjectID + Segments []Segment `json:"segments"` + Errors []ErrorKeys `json:"error,omitempty"` +} + +type SegmentResponse struct { + WithProjectID + KeyID int64 `json:"key_id"` + LanguageISO string `json:"language_iso"` + Segment Segment `json:"segment"` +} + +type SegmentUpdateRequest struct { + Value string `json:"value"` // could be string or json for plural keys. + IsFuzzy *bool `json:"is_fuzzy,omitempty"` + IsReviewed *bool `json:"is_reviewed,omitempty"` + CustomTranslationStatusIds []int64 `json:"custom_translation_status_ids,omitempty"` +} + +func (s *SegmentationService) List(projectID string, keyID int64, languageIso string) (r SegmentsResponse, err error) { + resp, err := s.getList( + s.Ctx(), + fmt.Sprintf("%s/%s/%s/%d/%s/%s", pathProjects, projectID, pathKeys, keyID, pathSegments, languageIso), + &r, + s.ListOpts(), + ) + + if err != nil { + return + } + return r, apiError(resp) +} + +func (s *SegmentationService) Retrieve( + projectID string, + keyID int64, + languageIso string, + segmentNumber int64, +) (r SegmentResponse, err error) { + resp, err := s.getList( + s.Ctx(), + segmentPath(projectID, keyID, languageIso, segmentNumber), + &r, + s.RetrieveOpts(), + ) + + if err != nil { + return + } + return r, apiError(resp) +} + +func (s *SegmentationService) Update( + projectID string, + keyID int64, + languageIso string, + segmentNumber int64, + updateRequest SegmentUpdateRequest, +) (r SegmentResponse, err error) { + resp, err := s.put(s.Ctx(), segmentPath(projectID, keyID, languageIso, segmentNumber), &r, updateRequest) + + if err != nil { + return + } + return r, apiError(resp) +} + +func segmentPath(projectID string, keyID int64, languageIso string, segmentNumber int64) string { + return fmt.Sprintf( + "%s/%s/%s/%d/%s/%s/%d", + pathProjects, + projectID, + pathKeys, + keyID, + pathSegments, + languageIso, + segmentNumber, + ) +} + +// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Additional methods +// _____________________________________________________________________________________________________________________ + +type SegmentsListOptions struct { + // Possible values are 1 and 0. + DisableReferences uint8 `url:"disable_references,omitempty"` + FilterIsReviewed uint8 `url:"filter_is_reviewed,omitempty"` + FilterUnverified uint8 `url:"filter_unverified,omitempty"` + FilterUntranslated uint8 `url:"filter_untranslated,omitempty"` + + FilterQAIssues string `url:"filter_qa_issues,omitempty"` +} + +func (s *SegmentationService) ListOpts() SegmentsListOptions { return s.listOpts } +func (s *SegmentationService) SetListOptions(o SegmentsListOptions) { s.listOpts = o } +func (s *SegmentationService) WithListOptions(o SegmentsListOptions) *SegmentationService { + s.listOpts = o + return s +} + +func (options SegmentsListOptions) Apply(req *resty.Request) { + v, _ := query.Values(options) + req.SetQueryString(v.Encode()) +} + +type SegmentsRetrieveOptions struct { + DisableReferences uint8 `url:"disable_references,omitempty"` +} + +func (options SegmentsRetrieveOptions) Apply(req *resty.Request) { + v, _ := query.Values(options) + req.SetQueryString(v.Encode()) +} + +func (s *SegmentationService) RetrieveOpts() SegmentsRetrieveOptions { return s.retrieveOpts } +func (s *SegmentationService) SetRetrieveOptions(o SegmentsRetrieveOptions) { s.retrieveOpts = o } +func (s *SegmentationService) WithRetrieveOptions(o SegmentsRetrieveOptions) *SegmentationService { + s.retrieveOpts = o + return s +} diff --git a/svc_segment_test.go b/svc_segment_test.go new file mode 100644 index 0000000..e4e39cb --- /dev/null +++ b/svc_segment_test.go @@ -0,0 +1,152 @@ +package lokalise + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestSegmentationService_List(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + var keyID int64 = 640 + languageIso := "en_US" + + mux.HandleFunc( + fmt.Sprintf("/projects/%s/keys/%d/segments/%s", testProjectID, keyID, languageIso), + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + testMethod(t, r, "GET") + testHeader(t, r, apiTokenHeader, testApiToken) + + _, _ = fmt.Fprint(w, `{ + "project_id": "`+testProjectID+`", + "segments": [ + { + "segment_number": 1, + "language_iso": "en_US" + } + ] + }`) + }) + + r, err := client.Segments().List(testProjectID, keyID, languageIso) + if err != nil { + t.Errorf("Segments.List returned error: %v", err) + } + + want := []Segment{ + { + SegmentNumber: 1, + LanguageIso: languageIso, + }, + } + + if !reflect.DeepEqual(r.Segments, want) { + t.Errorf(assertionTemplate, "Segments.List", r.Segments, want) + } +} + +func TestSegmentationService_Retrieve(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + var keyID int64 = 640 + languageIso := "en_US" + var segmentNumber int64 = 1 + + mux.HandleFunc( + fmt.Sprintf("/projects/%s/keys/%d/segments/%s/%d", testProjectID, keyID, languageIso, segmentNumber), + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + testMethod(t, r, "GET") + testHeader(t, r, apiTokenHeader, testApiToken) + + _, _ = fmt.Fprint(w, `{ + "project_id": "`+testProjectID+`", + "key_id": 640, + "language_iso": "en_US", + "segment": { + "segment_number": 1, + "language_iso": "en_US" + } + }`) + }) + + r, err := client.Segments().Retrieve(testProjectID, keyID, languageIso, segmentNumber) + if err != nil { + t.Errorf("Segments.Retrieve returned error: %v", err) + } + + want := Segment{ + SegmentNumber: segmentNumber, + LanguageIso: languageIso, + } + + if !reflect.DeepEqual(r.Segment, want) { + t.Errorf(assertionTemplate, "Segments.Retrieve", r.Segment, want) + } +} + +func TestSegmentationService_Update(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + var keyID int64 = 640 + languageIso := "en_US" + var segmentNumber int64 = 1 + + mux.HandleFunc( + fmt.Sprintf("/projects/%s/keys/%d/segments/%s/%d", testProjectID, keyID, languageIso, segmentNumber), + func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + testMethod(t, r, "PUT") + testHeader(t, r, apiTokenHeader, testApiToken) + data := `{ + "value": "Quick brown fox jumps over the lazy dog.", + "is_fuzzy": false, + "is_reviewed": true, + "custom_translation_status_ids": [ + 2, 3 + ] + }` + + req := new(bytes.Buffer) + _ = json.Compact(req, []byte(data)) + + testBody(t, r, req.String()) + + _, _ = fmt.Fprint(w, `{ + "project_id": "`+testProjectID+`", + "key_id": 640, + "language_iso": "en_US", + "segment": { + "segment_number": 1, + "language_iso": "en_US" + } + }`) + }) + + r, err := client.Segments().Update(testProjectID, 640, "en_US", 1, SegmentUpdateRequest{ + Value: "Quick brown fox jumps over the lazy dog.", + IsFuzzy: Bool(false), + IsReviewed: Bool(true), + CustomTranslationStatusIds: []int64{2, 3}, + }) + if err != nil { + t.Errorf("Segments.Update returned error: %v", err) + } + + want := Segment{ + SegmentNumber: segmentNumber, + LanguageIso: languageIso, + } + + if !reflect.DeepEqual(r.Segment, want) { + t.Errorf(assertionTemplate, "Segments.Update", r.Segment, want) + } +}