diff --git a/pkg/api.go b/pkg/api.go index 084eb7a..1ea01e6 100644 --- a/pkg/api.go +++ b/pkg/api.go @@ -84,6 +84,86 @@ func (ep *Etherpad) GetLastEdited(padID string) (time.Time, error) { return time.Unix(body.Data.LastEdited/1000, 0), nil } +// ListSavedRevisions returns the list of saved revisions of a Pad. +// See: https://etherpad.org/doc/v1.8.4/#index_listsavedrevisions_padid +func (ep *Etherpad) ListSavedRevisions(padID string) ([]int, error) { + params := map[string]interface{}{"padID": padID} + res, err := ep.sendRequest("listSavedRevisions", params) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var body struct { + Code int `json:"code"` + Message string `json:"message"` + Data struct { + SavedRevisions []int `json:"savedRevisions"` + } `json:"data"` + } + if err = json.NewDecoder(res.Body).Decode(&body); err != nil { + return nil, err + } + if body.Code != 0 { + return nil, fmt.Errorf("error: %s (code: %d)", body.Message, body.Code) + } + + return body.Data.SavedRevisions, nil +} + +// GetText returns the text of a Pad. +// See: https://etherpad.org/doc/v1.8.4/index.html#index_gettext_padid_rev +func (ep *Etherpad) GetText(padID string) (string, error) { + params := map[string]interface{}{"padID": padID} + res, err := ep.sendRequest("getText", params) + if err != nil { + return "", err + } + defer res.Body.Close() + + var body struct { + Code int `json:"code"` + Message string `json:"message"` + Data struct { + Text string `json:"text"` + } `json:"data"` + } + if err = json.NewDecoder(res.Body).Decode(&body); err != nil { + return "", err + } + + if body.Code != 0 { + return "", fmt.Errorf("error: %s (code: %d)", body.Message, body.Code) + } + + return body.Data.Text, nil +} + +// RestoreRevision restores a Pad to a specific revision. +// See: https://etherpad.org/doc/v1.8.4/#index_restorerevision_padid_rev +func (ep *Etherpad) RestoreRevision(padID, rev string) error { + params := map[string]interface{}{"padID": padID, "rev": rev} + res, err := ep.sendRequest("restoreRevision", params) + if err != nil { + return err + } + defer res.Body.Close() + + var body struct { + Code int `json:"code"` + Message string `json:"message"` + } + if err = json.NewDecoder(res.Body).Decode(&body); err != nil { + return err + } + + if body.Code != 0 { + return fmt.Errorf("error: %s (code: %d)", body.Message, body.Code) + } + + return nil +} + // DeletePad removes a Pad. // See: https://etherpad.org/doc/v1.8.4/#index_deletepad_padid func (ep *Etherpad) DeletePad(padID string) error { diff --git a/pkg/api_test.go b/pkg/api_test.go index 86486dd..ee49143 100644 --- a/pkg/api_test.go +++ b/pkg/api_test.go @@ -76,6 +76,100 @@ func TestEtherpad_GetLastEdited_NotFound(t *testing.T) { assert.Equal(t, int64(0), edited.Unix()) } +func TestEtherpad_ListSavedRevisions_Successful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"code": 0, "message":"ok", "data": {"savedRevisions": [2,42,1337]}}`)) + })) + defer ts.Close() + + etherpad := NewEtherpadClient(ts.URL, etherpadApiKey) + etherpad.Client = ts.Client() + + revs, err := etherpad.ListSavedRevisions("pad") + assert.Nil(t, err) + assert.Equal(t, 3, len(revs)) +} + +func TestEtherpad_ListSavedRevisions_NotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"code": 1, "message":"padID does not exist", "data": null}`)) + })) + defer ts.Close() + + etherpad := NewEtherpadClient(ts.URL, etherpadApiKey) + etherpad.Client = ts.Client() + + revs, err := etherpad.ListSavedRevisions("pad") + assert.NotNil(t, err) + assert.Equal(t, 0, len(revs)) +} + +func TestEtherpad_GetText_Successful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"code": 0, "message":"ok", "data": {"text": "Hello World"}}`)) + })) + defer ts.Close() + + etherpad := NewEtherpadClient(ts.URL, etherpadApiKey) + etherpad.Client = ts.Client() + + text, err := etherpad.GetText("pad") + assert.Nil(t, err) + assert.Equal(t, "Hello World", text) +} + +func TestEtherpad_GetText_NotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"code": 1, "message":"padID does not exist", "data": null}`)) + })) + defer ts.Close() + + etherpad := NewEtherpadClient(ts.URL, etherpadApiKey) + etherpad.Client = ts.Client() + + text, err := etherpad.GetText("pad") + assert.NotNil(t, err) + assert.Equal(t, "", text) +} + +func TestEtherpad_RestoreRevision_Successful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"code": 0, "message":"ok", "data": null}`)) + })) + defer ts.Close() + + etherpad := NewEtherpadClient(ts.URL, etherpadApiKey) + etherpad.Client = ts.Client() + + err := etherpad.RestoreRevision("pad", "1") + assert.Nil(t, err) +} + +func TestEtherpad_RestoreRevision_NotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"code": 1, "message":"padID does not exist", "data": null}`)) + })) + defer ts.Close() + + etherpad := NewEtherpadClient(ts.URL, etherpadApiKey) + etherpad.Client = ts.Client() + + err := etherpad.RestoreRevision("pad", "1") + assert.NotNil(t, err) +} + func TestEtherpad_DeletePad_Successful(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK)