feat: Centralize Kobo API models #1
No files matched your search
+8
-48
@@ -23,6 +23,7 @@ import (
|
||||
"golang.org/x/net/html"
|
||||
"readeckobo/internal/config"
|
||||
"readeckobo/internal/logger"
|
||||
"readeckobo/internal/models"
|
||||
"readeckobo/internal/readeck"
|
||||
)
|
||||
|
||||
@@ -74,25 +75,9 @@ func WithReadeckHTTPClient(client *http.Client) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// GetRequest represents the incoming request for /api/kobo/get
|
||||
type GetRequest struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
ContentType string `json:"contentType"`
|
||||
Count string `json:"count"`
|
||||
DetailType string `json:"detailType"`
|
||||
Offset string `json:"offset"`
|
||||
State string `json:"state"`
|
||||
Total string `json:"total"`
|
||||
Since any `json:"since"`
|
||||
}
|
||||
|
||||
// KoboGetResponse represents the outgoing response for /api/kobo/get
|
||||
type KoboGetResponse struct {
|
||||
Status int `json:"status"`
|
||||
List map[string]any `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
|
||||
|
||||
// HandleKoboGet handles the /api/kobo/get endpoint.
|
||||
func (a *App) HandleKoboGet(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -114,7 +99,7 @@ func (a *App) HandleKoboGet(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var req GetRequest
|
||||
var req models.KoboGetRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
a.Logger.Errorf("Error decoding /api/kobo/get request: %v, body: %s, URL: %s, Params: %v", err, string(bodyBytes), r.URL.Path, r.URL.Query())
|
||||
@@ -288,7 +273,7 @@ func (a *App) HandleKoboGet(w http.ResponseWriter, r *http.Request) {
|
||||
resultList[bm["item_id"].(string)] = bm
|
||||
}
|
||||
|
||||
resp := KoboGetResponse{
|
||||
resp := models.KoboGetResponse{
|
||||
Status: 1,
|
||||
List: resultList,
|
||||
Total: totalNonArchivedBookmarks, // Total number of non-archived bookmarks
|
||||
@@ -300,15 +285,7 @@ func (a *App) HandleKoboGet(w http.ResponseWriter, r *http.Request) {
|
||||
a.Logger.Errorf("Error encoding response for /api/kobo/get: %v, URL: %s, Params: %v", err, r.URL.Path, r.URL.Query())
|
||||
}
|
||||
}
|
||||
// DownloadRequest represents the incoming request for /api/kobo/download
|
||||
type DownloadRequest struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
Images int `json:"images"`
|
||||
Refresh int `json:"refresh"`
|
||||
Output string `json:"output"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
|
||||
// HandleKoboDownload handles the /api/kobo/download endpoint.
|
||||
func (a *App) HandleKoboDownload(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -330,7 +307,7 @@ func (a *App) HandleKoboDownload(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var req DownloadRequest
|
||||
var req models.KoboDownloadRequest
|
||||
// Use the restored body for decoding.
|
||||
if err := json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&req); err != nil {
|
||||
// If JSON decoding fails, try form parsing (Kobo devices might send form data for download)
|
||||
@@ -514,24 +491,7 @@ func getSitesToTry(host string) []string {
|
||||
return uniqueSites
|
||||
}
|
||||
|
||||
// ExistingItemAction represents an action on an existing item.
|
||||
type ExistingItemAction struct {
|
||||
Action string `json:"action"`
|
||||
ItemID string `json:"item_id"`
|
||||
}
|
||||
|
||||
// NewItemAction represents an action to create a new item.
|
||||
type NewItemAction struct {
|
||||
Action string `json:"action"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// SendRequest represents the incoming request for /api/kobo/send
|
||||
type SendRequest struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
Actions []any `json:"actions"`
|
||||
}
|
||||
|
||||
// HandleKoboSend handles the /api/kobo/send endpoint.
|
||||
func (a *App) HandleKoboSend(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -553,7 +513,7 @@ func (a *App) HandleKoboSend(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var req SendRequest
|
||||
var req models.KoboSendRequest
|
||||
// Use the restored body for decoding.
|
||||
if err := json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
|
||||
+17
-16
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"readeckobo/internal/config"
|
||||
"readeckobo/internal/logger"
|
||||
"readeckobo/internal/models"
|
||||
"readeckobo/internal/readeck"
|
||||
)
|
||||
|
||||
@@ -162,7 +163,7 @@ func TestCompareURLs(t *testing.T) {
|
||||
|
||||
type koboGetTestCase struct {
|
||||
name string
|
||||
reqBody *GetRequest
|
||||
reqBody *models.KoboGetRequest
|
||||
mockBookmarksSync []readeck.BookmarkSync
|
||||
mockBookmarkDetails map[string]*readeck.Bookmark
|
||||
mockBookmarksSyncErr error
|
||||
@@ -177,7 +178,7 @@ func TestHandleKoboGet(t *testing.T) {
|
||||
testCases := []koboGetTestCase{
|
||||
{
|
||||
name: "successful get",
|
||||
reqBody: &GetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
reqBody: &models.KoboGetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
mockBookmarksSync: []readeck.BookmarkSync{
|
||||
{ID: "1", Type: "update"},
|
||||
},
|
||||
@@ -260,7 +261,7 @@ func TestHandleKoboGet(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "delete sync type",
|
||||
reqBody: &GetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
reqBody: &models.KoboGetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
mockBookmarksSync: []readeck.BookmarkSync{
|
||||
{ID: "1", Type: "delete"},
|
||||
},
|
||||
@@ -314,7 +315,7 @@ func TestHandleKoboGet(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "get bookmark details error",
|
||||
reqBody: &GetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
reqBody: &models.KoboGetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
mockBookmarksSync: []readeck.BookmarkSync{
|
||||
{ID: "1", Type: "update"},
|
||||
},
|
||||
@@ -352,7 +353,7 @@ func TestHandleKoboGet(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "get bookmark details nil",
|
||||
reqBody: &GetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
reqBody: &models.KoboGetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
mockBookmarksSync: []readeck.BookmarkSync{
|
||||
{ID: "1", Type: "update"},
|
||||
},
|
||||
@@ -432,7 +433,7 @@ func TestHandleKoboGet(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "get bookmarks sync error",
|
||||
reqBody: &GetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
reqBody: &models.KoboGetRequest{Count: "1", AccessToken: mockDeviceToken},
|
||||
mockBookmarksSyncErr: fmt.Errorf("sync error"),
|
||||
expectedStatus: http.StatusInternalServerError,
|
||||
mockHTTPClientFunc: func(t *testing.T, tc *koboGetTestCase) *http.Client {
|
||||
@@ -475,7 +476,7 @@ func TestHandleKoboGet(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid access token",
|
||||
reqBody: &GetRequest{Count: "1", AccessToken: "invalid-device-token"},
|
||||
reqBody: &models.KoboGetRequest{Count: "1", AccessToken: "invalid-device-token"},
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
mockHTTPClientFunc: func(t *testing.T, tc *koboGetTestCase) *http.Client {
|
||||
return &http.Client{
|
||||
@@ -527,7 +528,7 @@ func TestHandleKoboGet(t *testing.T) {
|
||||
}
|
||||
|
||||
if tc.expectedStatus == http.StatusOK {
|
||||
var resp KoboGetResponse
|
||||
var resp models.KoboGetResponse
|
||||
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
@@ -557,7 +558,7 @@ func TestHandleKoboDownload(t *testing.T) {
|
||||
testCases := []koboDownloadTestCase{
|
||||
{
|
||||
name: "successful download (JSON)",
|
||||
reqBody: DownloadRequest{
|
||||
reqBody: models.KoboDownloadRequest{
|
||||
AccessToken: mockDeviceToken,
|
||||
URL: "http://example.com/article1",
|
||||
},
|
||||
@@ -655,7 +656,7 @@ func TestHandleKoboDownload(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "missing url",
|
||||
reqBody: DownloadRequest{
|
||||
reqBody: models.KoboDownloadRequest{
|
||||
AccessToken: mockDeviceToken,
|
||||
URL: "",
|
||||
},
|
||||
@@ -677,7 +678,7 @@ func TestHandleKoboDownload(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid access token",
|
||||
reqBody: DownloadRequest{
|
||||
reqBody: models.KoboDownloadRequest{
|
||||
AccessToken: "invalid-device-token",
|
||||
URL: "http://example.com/article1",
|
||||
},
|
||||
@@ -1065,7 +1066,7 @@ func TestHandleKoboSend(t *testing.T) {
|
||||
WithReadeckHTTPClient(tc.mockHTTPClientFunc(t, &tc, &updatedBookmarkID, &updatedBookmarkData, &createdBookmarkURL)),
|
||||
)
|
||||
|
||||
reqBody := SendRequest{AccessToken: tc.accessToken, Actions: tc.actions}
|
||||
reqBody := models.KoboSendRequest{AccessToken: tc.accessToken, Actions: tc.actions}
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal request body: %v", err)
|
||||
@@ -1125,7 +1126,7 @@ func TestHandleKoboSend(t *testing.T) {
|
||||
// koboGetWithArchivedTestCase defines the structure for test cases in TestHandleKoboGetWithArchived.
|
||||
type koboGetWithArchivedTestCase struct {
|
||||
name string
|
||||
reqBody *GetRequest
|
||||
reqBody *models.KoboGetRequest
|
||||
mockBookmarksSync []readeck.BookmarkSync
|
||||
mockBookmarkDetails map[string]*readeck.Bookmark
|
||||
expectedStatus int
|
||||
@@ -1138,7 +1139,7 @@ func TestHandleKoboGetWithArchived(t *testing.T) {
|
||||
testCases := []koboGetWithArchivedTestCase{
|
||||
{
|
||||
name: "successful get with archived",
|
||||
reqBody: &GetRequest{Count: "10", AccessToken: mockDeviceToken},
|
||||
reqBody: &models.KoboGetRequest{Count: "10", AccessToken: mockDeviceToken},
|
||||
mockBookmarksSync: []readeck.BookmarkSync{
|
||||
{ID: "1", Type: "update"},
|
||||
{ID: "2", Type: "update"},
|
||||
@@ -1255,7 +1256,7 @@ func TestHandleKoboGetWithArchived(t *testing.T) {
|
||||
WithReadeckHTTPClient(tc.mockHTTPClientFunc(t, &tc)),
|
||||
)
|
||||
|
||||
reqBody := GetRequest{Count: "10", AccessToken: mockDeviceToken} // Request more than the number of bookmarks
|
||||
reqBody := models.KoboGetRequest{Count: "10", AccessToken: mockDeviceToken} // Request more than the number of bookmarks
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal request body: %v", err)
|
||||
@@ -1269,7 +1270,7 @@ func TestHandleKoboGetWithArchived(t *testing.T) {
|
||||
t.Errorf("expected status %d, got %d", http.StatusOK, rr.Code)
|
||||
}
|
||||
|
||||
var resp KoboGetResponse
|
||||
var resp models.KoboGetResponse
|
||||
if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package models
|
||||
|
||||
// KoboGetRequest is the incoming request for /api/kobo/get
|
||||
type KoboGetRequest struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
ContentType string `json:"contentType"`
|
||||
Count string `json:"count"`
|
||||
DetailType string `json:"detailType"`
|
||||
Offset string `json:"offset"`
|
||||
State string `json:"state"`
|
||||
Total string `json:"total"`
|
||||
Since any `json:"since"`
|
||||
}
|
||||
|
||||
// KoboGetResponse represents the outgoing response for /api/kobo/get
|
||||
type KoboGetResponse struct {
|
||||
Status int `json:"status"`
|
||||
List map[string]any `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
// KoboDownloadRequest represents the incoming request for /api/kobo/download
|
||||
type KoboDownloadRequest struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
Images int `json:"images"`
|
||||
Refresh int `json:"refresh"`
|
||||
Output string `json:"output"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// KoboSendRequest represents the incoming request for /api/kobo/send
|
||||
type KoboSendRequest struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ConsumerKey string `json:"consumer_key"`
|
||||
Actions []any `json:"actions"`
|
||||
}
|
||||
Reference in new issue
Block a user