client.go 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package client // import "miniflux.app/v2/client"
  4. import (
  5. "context"
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "net/url"
  11. "strconv"
  12. "strings"
  13. )
  14. // Client holds API procedure calls.
  15. type Client struct {
  16. request *request
  17. }
  18. // New returns a new Miniflux client.
  19. //
  20. // Deprecated: use NewClient instead.
  21. //
  22. //go:fix inline
  23. func New(endpoint string, credentials ...string) *Client {
  24. return NewClient(endpoint, credentials...)
  25. }
  26. // NewClient returns a new Miniflux client.
  27. func NewClient(endpoint string, credentials ...string) *Client {
  28. switch len(credentials) {
  29. case 2:
  30. return NewClientWithOptions(endpoint, WithCredentials(credentials[0], credentials[1]))
  31. case 1:
  32. return NewClientWithOptions(endpoint, WithAPIKey(credentials[0]))
  33. default:
  34. return NewClientWithOptions(endpoint)
  35. }
  36. }
  37. // NewClientWithOptions returns a new Miniflux client with options.
  38. func NewClientWithOptions(endpoint string, options ...Option) *Client {
  39. // Trim trailing slashes and /v1 from the endpoint.
  40. endpoint = strings.TrimSuffix(endpoint, "/")
  41. endpoint = strings.TrimSuffix(endpoint, "/v1")
  42. request := &request{endpoint: endpoint, client: http.DefaultClient}
  43. for _, option := range options {
  44. option(request)
  45. }
  46. return &Client{request: request}
  47. }
  48. func withDefaultTimeout() (context.Context, func()) {
  49. ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
  50. return ctx, cancel
  51. }
  52. // Healthcheck checks if the application is up and running.
  53. func (c *Client) Healthcheck() error {
  54. ctx, cancel := withDefaultTimeout()
  55. defer cancel()
  56. return c.HealthcheckContext(ctx)
  57. }
  58. // HealthcheckContext checks if the application is up and running.
  59. func (c *Client) HealthcheckContext(ctx context.Context) error {
  60. body, err := c.request.Get(ctx, "/healthcheck")
  61. if err != nil {
  62. return fmt.Errorf("miniflux: unable to perform healthcheck: %w", err)
  63. }
  64. defer body.Close()
  65. responseBodyContent, err := io.ReadAll(body)
  66. if err != nil {
  67. return fmt.Errorf("miniflux: unable to read healthcheck response: %w", err)
  68. }
  69. if string(responseBodyContent) != "OK" {
  70. return fmt.Errorf("miniflux: invalid healthcheck response: %q", responseBodyContent)
  71. }
  72. return nil
  73. }
  74. // Version returns the version of the Miniflux instance.
  75. func (c *Client) Version() (*VersionResponse, error) {
  76. ctx, cancel := withDefaultTimeout()
  77. defer cancel()
  78. return c.VersionContext(ctx)
  79. }
  80. // VersionContext returns the version of the Miniflux instance.
  81. func (c *Client) VersionContext(ctx context.Context) (*VersionResponse, error) {
  82. body, err := c.request.Get(ctx, "/v1/version")
  83. if err != nil {
  84. return nil, err
  85. }
  86. defer body.Close()
  87. var versionResponse *VersionResponse
  88. if err := json.NewDecoder(body).Decode(&versionResponse); err != nil {
  89. return nil, fmt.Errorf("miniflux: json error (%v)", err)
  90. }
  91. return versionResponse, nil
  92. }
  93. // Me returns the logged user information.
  94. func (c *Client) Me() (*User, error) {
  95. ctx, cancel := withDefaultTimeout()
  96. defer cancel()
  97. return c.MeContext(ctx)
  98. }
  99. // MeContext returns the logged user information.
  100. func (c *Client) MeContext(ctx context.Context) (*User, error) {
  101. body, err := c.request.Get(ctx, "/v1/me")
  102. if err != nil {
  103. return nil, err
  104. }
  105. defer body.Close()
  106. var user *User
  107. if err := json.NewDecoder(body).Decode(&user); err != nil {
  108. return nil, fmt.Errorf("miniflux: json error (%v)", err)
  109. }
  110. return user, nil
  111. }
  112. // Users returns all users.
  113. func (c *Client) Users() (Users, error) {
  114. ctx, cancel := withDefaultTimeout()
  115. defer cancel()
  116. return c.UsersContext(ctx)
  117. }
  118. // UsersContext returns all users.
  119. func (c *Client) UsersContext(ctx context.Context) (Users, error) {
  120. body, err := c.request.Get(ctx, "/v1/users")
  121. if err != nil {
  122. return nil, err
  123. }
  124. defer body.Close()
  125. var users Users
  126. if err := json.NewDecoder(body).Decode(&users); err != nil {
  127. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  128. }
  129. return users, nil
  130. }
  131. // UserByID returns a single user.
  132. func (c *Client) UserByID(userID int64) (*User, error) {
  133. ctx, cancel := withDefaultTimeout()
  134. defer cancel()
  135. return c.UserByIDContext(ctx, userID)
  136. }
  137. // UserByIDContext returns a single user.
  138. func (c *Client) UserByIDContext(ctx context.Context, userID int64) (*User, error) {
  139. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/users/%d", userID))
  140. if err != nil {
  141. return nil, err
  142. }
  143. defer body.Close()
  144. var user User
  145. if err := json.NewDecoder(body).Decode(&user); err != nil {
  146. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  147. }
  148. return &user, nil
  149. }
  150. // UserByUsername returns a single user.
  151. func (c *Client) UserByUsername(username string) (*User, error) {
  152. ctx, cancel := withDefaultTimeout()
  153. defer cancel()
  154. return c.UserByUsernameContext(ctx, username)
  155. }
  156. // UserByUsernameContext returns a single user.
  157. func (c *Client) UserByUsernameContext(ctx context.Context, username string) (*User, error) {
  158. body, err := c.request.Get(ctx, "/v1/users/"+username)
  159. if err != nil {
  160. return nil, err
  161. }
  162. defer body.Close()
  163. var user User
  164. if err := json.NewDecoder(body).Decode(&user); err != nil {
  165. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  166. }
  167. return &user, nil
  168. }
  169. // CreateUser creates a new user in the system.
  170. func (c *Client) CreateUser(username, password string, isAdmin bool) (*User, error) {
  171. ctx, cancel := withDefaultTimeout()
  172. defer cancel()
  173. return c.CreateUserContext(ctx, username, password, isAdmin)
  174. }
  175. // CreateUserContext creates a new user in the system.
  176. func (c *Client) CreateUserContext(ctx context.Context, username, password string, isAdmin bool) (*User, error) {
  177. body, err := c.request.Post(ctx, "/v1/users", &UserCreationRequest{
  178. Username: username,
  179. Password: password,
  180. IsAdmin: isAdmin,
  181. })
  182. if err != nil {
  183. return nil, err
  184. }
  185. defer body.Close()
  186. var user *User
  187. if err := json.NewDecoder(body).Decode(&user); err != nil {
  188. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  189. }
  190. return user, nil
  191. }
  192. // UpdateUser updates a user in the system.
  193. func (c *Client) UpdateUser(userID int64, userChanges *UserModificationRequest) (*User, error) {
  194. ctx, cancel := withDefaultTimeout()
  195. defer cancel()
  196. return c.UpdateUserContext(ctx, userID, userChanges)
  197. }
  198. // UpdateUserContext updates a user in the system.
  199. func (c *Client) UpdateUserContext(ctx context.Context, userID int64, userChanges *UserModificationRequest) (*User, error) {
  200. body, err := c.request.Put(ctx, fmt.Sprintf("/v1/users/%d", userID), userChanges)
  201. if err != nil {
  202. return nil, err
  203. }
  204. defer body.Close()
  205. var u *User
  206. if err := json.NewDecoder(body).Decode(&u); err != nil {
  207. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  208. }
  209. return u, nil
  210. }
  211. // DeleteUser removes a user from the system.
  212. func (c *Client) DeleteUser(userID int64) error {
  213. ctx, cancel := withDefaultTimeout()
  214. defer cancel()
  215. return c.DeleteUserContext(ctx, userID)
  216. }
  217. // DeleteUserContext removes a user from the system.
  218. func (c *Client) DeleteUserContext(ctx context.Context, userID int64) error {
  219. return c.request.Delete(ctx, fmt.Sprintf("/v1/users/%d", userID))
  220. }
  221. // APIKeys returns all API keys for the authenticated user.
  222. func (c *Client) APIKeys() (APIKeys, error) {
  223. ctx, cancel := withDefaultTimeout()
  224. defer cancel()
  225. return c.APIKeysContext(ctx)
  226. }
  227. // APIKeysContext returns all API keys for the authenticated user.
  228. func (c *Client) APIKeysContext(ctx context.Context) (APIKeys, error) {
  229. body, err := c.request.Get(ctx, "/v1/api-keys")
  230. if err != nil {
  231. return nil, err
  232. }
  233. defer body.Close()
  234. var apiKeys APIKeys
  235. if err := json.NewDecoder(body).Decode(&apiKeys); err != nil {
  236. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  237. }
  238. return apiKeys, nil
  239. }
  240. // CreateAPIKey creates a new API key for the authenticated user.
  241. func (c *Client) CreateAPIKey(description string) (*APIKey, error) {
  242. ctx, cancel := withDefaultTimeout()
  243. defer cancel()
  244. return c.CreateAPIKeyContext(ctx, description)
  245. }
  246. // CreateAPIKeyContext creates a new API key for the authenticated user.
  247. func (c *Client) CreateAPIKeyContext(ctx context.Context, description string) (*APIKey, error) {
  248. body, err := c.request.Post(ctx, "/v1/api-keys", &APIKeyCreationRequest{
  249. Description: description,
  250. })
  251. if err != nil {
  252. return nil, err
  253. }
  254. defer body.Close()
  255. var apiKey *APIKey
  256. if err := json.NewDecoder(body).Decode(&apiKey); err != nil {
  257. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  258. }
  259. return apiKey, nil
  260. }
  261. // DeleteAPIKey removes an API key for the authenticated user.
  262. func (c *Client) DeleteAPIKey(apiKeyID int64) error {
  263. ctx, cancel := withDefaultTimeout()
  264. defer cancel()
  265. return c.DeleteAPIKeyContext(ctx, apiKeyID)
  266. }
  267. // DeleteAPIKeyContext removes an API key for the authenticated user.
  268. func (c *Client) DeleteAPIKeyContext(ctx context.Context, apiKeyID int64) error {
  269. return c.request.Delete(ctx, fmt.Sprintf("/v1/api-keys/%d", apiKeyID))
  270. }
  271. // MarkAllAsRead marks all unread entries as read for a given user.
  272. func (c *Client) MarkAllAsRead(userID int64) error {
  273. ctx, cancel := withDefaultTimeout()
  274. defer cancel()
  275. return c.MarkAllAsReadContext(ctx, userID)
  276. }
  277. // MarkAllAsReadContext marks all unread entries as read for a given user.
  278. func (c *Client) MarkAllAsReadContext(ctx context.Context, userID int64) error {
  279. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/users/%d/mark-all-as-read", userID), nil)
  280. return err
  281. }
  282. // IntegrationsStatus fetches the integrations status for the signed-in user.
  283. func (c *Client) IntegrationsStatus() (bool, error) {
  284. ctx, cancel := withDefaultTimeout()
  285. defer cancel()
  286. return c.IntegrationsStatusContext(ctx)
  287. }
  288. // IntegrationsStatusContext fetches the integrations status for the signed-in user.
  289. func (c *Client) IntegrationsStatusContext(ctx context.Context) (bool, error) {
  290. body, err := c.request.Get(ctx, "/v1/integrations/status")
  291. if err != nil {
  292. return false, err
  293. }
  294. defer body.Close()
  295. var response struct {
  296. HasIntegrations bool `json:"has_integrations"`
  297. }
  298. if err := json.NewDecoder(body).Decode(&response); err != nil {
  299. return false, fmt.Errorf("miniflux: response error (%v)", err)
  300. }
  301. return response.HasIntegrations, nil
  302. }
  303. // Discover tries to find subscriptions on a website.
  304. func (c *Client) Discover(url string) (Subscriptions, error) {
  305. ctx, cancel := withDefaultTimeout()
  306. defer cancel()
  307. return c.DiscoverContext(ctx, url)
  308. }
  309. // DiscoverContext tries to find subscriptions from a website.
  310. func (c *Client) DiscoverContext(ctx context.Context, url string) (Subscriptions, error) {
  311. body, err := c.request.Post(ctx, "/v1/discover", map[string]string{"url": url})
  312. if err != nil {
  313. return nil, err
  314. }
  315. defer body.Close()
  316. var subscriptions Subscriptions
  317. if err := json.NewDecoder(body).Decode(&subscriptions); err != nil {
  318. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  319. }
  320. return subscriptions, nil
  321. }
  322. // Categories retrieves the list of categories.
  323. func (c *Client) Categories() (Categories, error) {
  324. ctx, cancel := withDefaultTimeout()
  325. defer cancel()
  326. return c.CategoriesContext(ctx)
  327. }
  328. // CategoriesContext retrieves the list of categories.
  329. func (c *Client) CategoriesContext(ctx context.Context) (Categories, error) {
  330. body, err := c.request.Get(ctx, "/v1/categories")
  331. if err != nil {
  332. return nil, err
  333. }
  334. defer body.Close()
  335. var categories Categories
  336. if err := json.NewDecoder(body).Decode(&categories); err != nil {
  337. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  338. }
  339. return categories, nil
  340. }
  341. // CategoriesWithCounters fetches the categories with their respective feed and unread counts.
  342. func (c *Client) CategoriesWithCounters() (Categories, error) {
  343. ctx, cancel := withDefaultTimeout()
  344. defer cancel()
  345. return c.CategoriesWithCountersContext(ctx)
  346. }
  347. // CategoriesWithCountersContext fetches the categories with their respective feed and unread counts.
  348. func (c *Client) CategoriesWithCountersContext(ctx context.Context) (Categories, error) {
  349. body, err := c.request.Get(ctx, "/v1/categories?counts=true")
  350. if err != nil {
  351. return nil, err
  352. }
  353. defer body.Close()
  354. var categories Categories
  355. if err := json.NewDecoder(body).Decode(&categories); err != nil {
  356. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  357. }
  358. return categories, nil
  359. }
  360. // CreateCategory creates a new category.
  361. func (c *Client) CreateCategory(title string) (*Category, error) {
  362. ctx, cancel := withDefaultTimeout()
  363. defer cancel()
  364. return c.CreateCategoryContext(ctx, title)
  365. }
  366. // CreateCategoryContext creates a new category.
  367. func (c *Client) CreateCategoryContext(ctx context.Context, title string) (*Category, error) {
  368. body, err := c.request.Post(ctx, "/v1/categories", &CategoryCreationRequest{
  369. Title: title,
  370. })
  371. if err != nil {
  372. return nil, err
  373. }
  374. defer body.Close()
  375. var category *Category
  376. if err := json.NewDecoder(body).Decode(&category); err != nil {
  377. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  378. }
  379. return category, nil
  380. }
  381. // CreateCategoryWithOptions creates a new category with options.
  382. func (c *Client) CreateCategoryWithOptions(createRequest *CategoryCreationRequest) (*Category, error) {
  383. ctx, cancel := withDefaultTimeout()
  384. defer cancel()
  385. return c.CreateCategoryWithOptionsContext(ctx, createRequest)
  386. }
  387. // CreateCategoryWithOptionsContext creates a new category with options.
  388. func (c *Client) CreateCategoryWithOptionsContext(ctx context.Context, createRequest *CategoryCreationRequest) (*Category, error) {
  389. body, err := c.request.Post(ctx, "/v1/categories", createRequest)
  390. if err != nil {
  391. return nil, err
  392. }
  393. defer body.Close()
  394. var category *Category
  395. if err := json.NewDecoder(body).Decode(&category); err != nil {
  396. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  397. }
  398. return category, nil
  399. }
  400. // UpdateCategory updates a category.
  401. func (c *Client) UpdateCategory(categoryID int64, title string) (*Category, error) {
  402. ctx, cancel := withDefaultTimeout()
  403. defer cancel()
  404. return c.UpdateCategoryContext(ctx, categoryID, title)
  405. }
  406. // UpdateCategoryContext updates a category.
  407. func (c *Client) UpdateCategoryContext(ctx context.Context, categoryID int64, title string) (*Category, error) {
  408. body, err := c.request.Put(ctx, fmt.Sprintf("/v1/categories/%d", categoryID), &CategoryModificationRequest{
  409. Title: new(title),
  410. })
  411. if err != nil {
  412. return nil, err
  413. }
  414. defer body.Close()
  415. var category *Category
  416. if err := json.NewDecoder(body).Decode(&category); err != nil {
  417. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  418. }
  419. return category, nil
  420. }
  421. // UpdateCategoryWithOptions updates a category with options.
  422. func (c *Client) UpdateCategoryWithOptions(categoryID int64, categoryChanges *CategoryModificationRequest) (*Category, error) {
  423. ctx, cancel := withDefaultTimeout()
  424. defer cancel()
  425. return c.UpdateCategoryWithOptionsContext(ctx, categoryID, categoryChanges)
  426. }
  427. // UpdateCategoryWithOptionsContext updates a category with options.
  428. func (c *Client) UpdateCategoryWithOptionsContext(ctx context.Context, categoryID int64, categoryChanges *CategoryModificationRequest) (*Category, error) {
  429. body, err := c.request.Put(ctx, fmt.Sprintf("/v1/categories/%d", categoryID), categoryChanges)
  430. if err != nil {
  431. return nil, err
  432. }
  433. defer body.Close()
  434. var category *Category
  435. if err := json.NewDecoder(body).Decode(&category); err != nil {
  436. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  437. }
  438. return category, nil
  439. }
  440. // MarkCategoryAsRead marks all unread entries in a category as read.
  441. func (c *Client) MarkCategoryAsRead(categoryID int64) error {
  442. ctx, cancel := withDefaultTimeout()
  443. defer cancel()
  444. return c.MarkCategoryAsReadContext(ctx, categoryID)
  445. }
  446. // MarkCategoryAsReadContext marks all unread entries in a category as read.
  447. func (c *Client) MarkCategoryAsReadContext(ctx context.Context, categoryID int64) error {
  448. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/categories/%d/mark-all-as-read", categoryID), nil)
  449. return err
  450. }
  451. // CategoryFeeds returns all feeds for a category.
  452. func (c *Client) CategoryFeeds(categoryID int64) (Feeds, error) {
  453. ctx, cancel := withDefaultTimeout()
  454. defer cancel()
  455. return c.CategoryFeedsContext(ctx, categoryID)
  456. }
  457. // CategoryFeedsContext returns all feeds for a category.
  458. func (c *Client) CategoryFeedsContext(ctx context.Context, categoryID int64) (Feeds, error) {
  459. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/categories/%d/feeds", categoryID))
  460. if err != nil {
  461. return nil, err
  462. }
  463. defer body.Close()
  464. var feeds Feeds
  465. if err := json.NewDecoder(body).Decode(&feeds); err != nil {
  466. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  467. }
  468. return feeds, nil
  469. }
  470. // DeleteCategory removes a category.
  471. func (c *Client) DeleteCategory(categoryID int64) error {
  472. ctx, cancel := withDefaultTimeout()
  473. defer cancel()
  474. return c.DeleteCategoryContext(ctx, categoryID)
  475. }
  476. // DeleteCategoryContext removes a category.
  477. func (c *Client) DeleteCategoryContext(ctx context.Context, categoryID int64) error {
  478. return c.request.Delete(ctx, fmt.Sprintf("/v1/categories/%d", categoryID))
  479. }
  480. // RefreshCategory refreshes a category.
  481. func (c *Client) RefreshCategory(categoryID int64) error {
  482. ctx, cancel := withDefaultTimeout()
  483. defer cancel()
  484. return c.RefreshCategoryContext(ctx, categoryID)
  485. }
  486. // RefreshCategoryContext refreshes a category.
  487. func (c *Client) RefreshCategoryContext(ctx context.Context, categoryID int64) error {
  488. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/categories/%d/refresh", categoryID), nil)
  489. return err
  490. }
  491. // Feeds gets all feeds.
  492. func (c *Client) Feeds() (Feeds, error) {
  493. ctx, cancel := withDefaultTimeout()
  494. defer cancel()
  495. return c.FeedsContext(ctx)
  496. }
  497. // FeedsContext gets all feeds.
  498. func (c *Client) FeedsContext(ctx context.Context) (Feeds, error) {
  499. body, err := c.request.Get(ctx, "/v1/feeds")
  500. if err != nil {
  501. return nil, err
  502. }
  503. defer body.Close()
  504. var feeds Feeds
  505. if err := json.NewDecoder(body).Decode(&feeds); err != nil {
  506. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  507. }
  508. return feeds, nil
  509. }
  510. // Export exports subscriptions as an OPML document.
  511. func (c *Client) Export() ([]byte, error) {
  512. ctx, cancel := withDefaultTimeout()
  513. defer cancel()
  514. return c.ExportContext(ctx)
  515. }
  516. // ExportContext exports subscriptions as an OPML document.
  517. func (c *Client) ExportContext(ctx context.Context) ([]byte, error) {
  518. body, err := c.request.Get(ctx, "/v1/export")
  519. if err != nil {
  520. return nil, err
  521. }
  522. defer body.Close()
  523. opml, err := io.ReadAll(body)
  524. if err != nil {
  525. return nil, err
  526. }
  527. return opml, nil
  528. }
  529. // Import imports an OPML file.
  530. func (c *Client) Import(f io.ReadCloser) error {
  531. ctx, cancel := withDefaultTimeout()
  532. defer cancel()
  533. return c.ImportContext(ctx, f)
  534. }
  535. // ImportContext imports an OPML file.
  536. func (c *Client) ImportContext(ctx context.Context, f io.ReadCloser) error {
  537. _, err := c.request.PostFile(ctx, "/v1/import", f)
  538. return err
  539. }
  540. // Feed gets a feed.
  541. func (c *Client) Feed(feedID int64) (*Feed, error) {
  542. ctx, cancel := withDefaultTimeout()
  543. defer cancel()
  544. return c.FeedContext(ctx, feedID)
  545. }
  546. // FeedContext gets a feed.
  547. func (c *Client) FeedContext(ctx context.Context, feedID int64) (*Feed, error) {
  548. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/feeds/%d", feedID))
  549. if err != nil {
  550. return nil, err
  551. }
  552. defer body.Close()
  553. var feed *Feed
  554. if err := json.NewDecoder(body).Decode(&feed); err != nil {
  555. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  556. }
  557. return feed, nil
  558. }
  559. // CreateFeed creates a new feed.
  560. func (c *Client) CreateFeed(feedCreationRequest *FeedCreationRequest) (int64, error) {
  561. ctx, cancel := withDefaultTimeout()
  562. defer cancel()
  563. return c.CreateFeedContext(ctx, feedCreationRequest)
  564. }
  565. // CreateFeedContext creates a new feed.
  566. func (c *Client) CreateFeedContext(ctx context.Context, feedCreationRequest *FeedCreationRequest) (int64, error) {
  567. body, err := c.request.Post(ctx, "/v1/feeds", feedCreationRequest)
  568. if err != nil {
  569. return 0, err
  570. }
  571. defer body.Close()
  572. type result struct {
  573. FeedID int64 `json:"feed_id"`
  574. }
  575. var r result
  576. if err := json.NewDecoder(body).Decode(&r); err != nil {
  577. return 0, fmt.Errorf("miniflux: response error (%v)", err)
  578. }
  579. return r.FeedID, nil
  580. }
  581. // UpdateFeed updates a feed.
  582. func (c *Client) UpdateFeed(feedID int64, feedChanges *FeedModificationRequest) (*Feed, error) {
  583. ctx, cancel := withDefaultTimeout()
  584. defer cancel()
  585. return c.UpdateFeedContext(ctx, feedID, feedChanges)
  586. }
  587. // UpdateFeedContext updates a feed.
  588. func (c *Client) UpdateFeedContext(ctx context.Context, feedID int64, feedChanges *FeedModificationRequest) (*Feed, error) {
  589. body, err := c.request.Put(ctx, fmt.Sprintf("/v1/feeds/%d", feedID), feedChanges)
  590. if err != nil {
  591. return nil, err
  592. }
  593. defer body.Close()
  594. var f *Feed
  595. if err := json.NewDecoder(body).Decode(&f); err != nil {
  596. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  597. }
  598. return f, nil
  599. }
  600. // ImportFeedEntry imports a single entry into a feed.
  601. func (c *Client) ImportFeedEntry(feedID int64, payload any) (int64, error) {
  602. ctx, cancel := withDefaultTimeout()
  603. defer cancel()
  604. body, err := c.request.Post(
  605. ctx,
  606. fmt.Sprintf("/v1/feeds/%d/entries/import", feedID),
  607. payload,
  608. )
  609. if err != nil {
  610. return 0, err
  611. }
  612. defer body.Close()
  613. var response struct {
  614. ID int64 `json:"id"`
  615. }
  616. if err := json.NewDecoder(body).Decode(&response); err != nil {
  617. return 0, fmt.Errorf("miniflux: json error (%v)", err)
  618. }
  619. return response.ID, nil
  620. }
  621. // MarkFeedAsRead marks all unread entries of the feed as read.
  622. func (c *Client) MarkFeedAsRead(feedID int64) error {
  623. ctx, cancel := withDefaultTimeout()
  624. defer cancel()
  625. return c.MarkFeedAsReadContext(ctx, feedID)
  626. }
  627. // MarkFeedAsReadContext marks all unread entries of the feed as read.
  628. func (c *Client) MarkFeedAsReadContext(ctx context.Context, feedID int64) error {
  629. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/feeds/%d/mark-all-as-read", feedID), nil)
  630. return err
  631. }
  632. // RefreshAllFeeds refreshes all feeds.
  633. func (c *Client) RefreshAllFeeds() error {
  634. ctx, cancel := withDefaultTimeout()
  635. defer cancel()
  636. return c.RefreshAllFeedsContext(ctx)
  637. }
  638. // RefreshAllFeedsContext refreshes all feeds.
  639. func (c *Client) RefreshAllFeedsContext(ctx context.Context) error {
  640. _, err := c.request.Put(ctx, "/v1/feeds/refresh", nil)
  641. return err
  642. }
  643. // RefreshFeed refreshes a feed.
  644. func (c *Client) RefreshFeed(feedID int64) error {
  645. ctx, cancel := withDefaultTimeout()
  646. defer cancel()
  647. return c.RefreshFeedContext(ctx, feedID)
  648. }
  649. // RefreshFeedContext refreshes a feed.
  650. func (c *Client) RefreshFeedContext(ctx context.Context, feedID int64) error {
  651. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/feeds/%d/refresh", feedID), nil)
  652. return err
  653. }
  654. // ForceRefreshFeed refreshes a feed, bypassing the HTTP cache and reprocessing existing entries.
  655. func (c *Client) ForceRefreshFeed(feedID int64) error {
  656. ctx, cancel := withDefaultTimeout()
  657. defer cancel()
  658. return c.ForceRefreshFeedContext(ctx, feedID)
  659. }
  660. // ForceRefreshFeedContext refreshes a feed, bypassing the HTTP cache and reprocessing existing entries.
  661. func (c *Client) ForceRefreshFeedContext(ctx context.Context, feedID int64) error {
  662. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/feeds/%d/refresh?force=true", feedID), nil)
  663. return err
  664. }
  665. // DeleteFeed removes a feed.
  666. func (c *Client) DeleteFeed(feedID int64) error {
  667. ctx, cancel := withDefaultTimeout()
  668. defer cancel()
  669. return c.DeleteFeedContext(ctx, feedID)
  670. }
  671. // DeleteFeedContext removes a feed.
  672. func (c *Client) DeleteFeedContext(ctx context.Context, feedID int64) error {
  673. return c.request.Delete(ctx, fmt.Sprintf("/v1/feeds/%d", feedID))
  674. }
  675. // FeedIcon gets a feed icon.
  676. func (c *Client) FeedIcon(feedID int64) (*FeedIcon, error) {
  677. ctx, cancel := withDefaultTimeout()
  678. defer cancel()
  679. return c.FeedIconContext(ctx, feedID)
  680. }
  681. // FeedIconContext gets a feed icon.
  682. func (c *Client) FeedIconContext(ctx context.Context, feedID int64) (*FeedIcon, error) {
  683. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/feeds/%d/icon", feedID))
  684. if err != nil {
  685. return nil, err
  686. }
  687. defer body.Close()
  688. var feedIcon *FeedIcon
  689. if err := json.NewDecoder(body).Decode(&feedIcon); err != nil {
  690. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  691. }
  692. return feedIcon, nil
  693. }
  694. // FeedEntry gets a single feed entry.
  695. func (c *Client) FeedEntry(feedID, entryID int64) (*Entry, error) {
  696. ctx, cancel := withDefaultTimeout()
  697. defer cancel()
  698. return c.FeedEntryContext(ctx, feedID, entryID)
  699. }
  700. // FeedEntryContext gets a single feed entry.
  701. func (c *Client) FeedEntryContext(ctx context.Context, feedID, entryID int64) (*Entry, error) {
  702. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/feeds/%d/entries/%d", feedID, entryID))
  703. if err != nil {
  704. return nil, err
  705. }
  706. defer body.Close()
  707. var entry *Entry
  708. if err := json.NewDecoder(body).Decode(&entry); err != nil {
  709. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  710. }
  711. return entry, nil
  712. }
  713. // CategoryEntry gets a single category entry.
  714. func (c *Client) CategoryEntry(categoryID, entryID int64) (*Entry, error) {
  715. ctx, cancel := withDefaultTimeout()
  716. defer cancel()
  717. return c.CategoryEntryContext(ctx, categoryID, entryID)
  718. }
  719. // CategoryEntryContext gets a single category entry.
  720. func (c *Client) CategoryEntryContext(ctx context.Context, categoryID, entryID int64) (*Entry, error) {
  721. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/categories/%d/entries/%d", categoryID, entryID))
  722. if err != nil {
  723. return nil, err
  724. }
  725. defer body.Close()
  726. var entry *Entry
  727. if err := json.NewDecoder(body).Decode(&entry); err != nil {
  728. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  729. }
  730. return entry, nil
  731. }
  732. // Entry gets a single entry.
  733. func (c *Client) Entry(entryID int64) (*Entry, error) {
  734. ctx, cancel := withDefaultTimeout()
  735. defer cancel()
  736. return c.EntryContext(ctx, entryID)
  737. }
  738. // EntryContext gets a single entry.
  739. func (c *Client) EntryContext(ctx context.Context, entryID int64) (*Entry, error) {
  740. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/entries/%d", entryID))
  741. if err != nil {
  742. return nil, err
  743. }
  744. defer body.Close()
  745. var entry *Entry
  746. if err := json.NewDecoder(body).Decode(&entry); err != nil {
  747. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  748. }
  749. return entry, nil
  750. }
  751. // EntryIDs returns entry IDs for the current user, optionally filtered by starred status and/or read status.
  752. func (c *Client) EntryIDs(filter *EntryIDsFilter) (*EntryIDsResultSet, error) {
  753. ctx, cancel := withDefaultTimeout()
  754. defer cancel()
  755. return c.EntryIDsContext(ctx, filter)
  756. }
  757. // EntryIDsContext returns entry IDs for the current user, optionally filtered by starred status and/or read status.
  758. func (c *Client) EntryIDsContext(ctx context.Context, filter *EntryIDsFilter) (*EntryIDsResultSet, error) {
  759. body, err := c.request.Get(ctx, buildEntryIDsFilterQueryString("/v1/entries/ids", filter))
  760. if err != nil {
  761. return nil, err
  762. }
  763. defer body.Close()
  764. var result EntryIDsResultSet
  765. if err := json.NewDecoder(body).Decode(&result); err != nil {
  766. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  767. }
  768. return &result, nil
  769. }
  770. func buildEntryIDsFilterQueryString(path string, filter *EntryIDsFilter) string {
  771. if filter == nil {
  772. return path
  773. }
  774. params := url.Values{}
  775. if filter.Limit > 0 {
  776. params.Set("limit", strconv.Itoa(filter.Limit))
  777. }
  778. if filter.Offset > 0 {
  779. params.Set("offset", strconv.Itoa(filter.Offset))
  780. }
  781. if filter.Starred != nil {
  782. params.Set("starred", strconv.FormatBool(*filter.Starred))
  783. }
  784. if filter.Status != "" {
  785. params.Set("status", filter.Status)
  786. }
  787. if len(params) == 0 {
  788. return path
  789. }
  790. return path + "?" + params.Encode()
  791. }
  792. // Entries fetches entries using the given filter.
  793. func (c *Client) Entries(filter *Filter) (*EntryResultSet, error) {
  794. ctx, cancel := withDefaultTimeout()
  795. defer cancel()
  796. return c.EntriesContext(ctx, filter)
  797. }
  798. // EntriesContext fetches entries.
  799. func (c *Client) EntriesContext(ctx context.Context, filter *Filter) (*EntryResultSet, error) {
  800. path := buildFilterQueryString("/v1/entries", filter)
  801. body, err := c.request.Get(ctx, path)
  802. if err != nil {
  803. return nil, err
  804. }
  805. defer body.Close()
  806. var result EntryResultSet
  807. if err := json.NewDecoder(body).Decode(&result); err != nil {
  808. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  809. }
  810. return &result, nil
  811. }
  812. // FeedEntries fetches entries for a feed using the given filter.
  813. func (c *Client) FeedEntries(feedID int64, filter *Filter) (*EntryResultSet, error) {
  814. ctx, cancel := withDefaultTimeout()
  815. defer cancel()
  816. return c.FeedEntriesContext(ctx, feedID, filter)
  817. }
  818. // FeedEntriesContext fetches feed entries.
  819. func (c *Client) FeedEntriesContext(ctx context.Context, feedID int64, filter *Filter) (*EntryResultSet, error) {
  820. path := buildFilterQueryString(fmt.Sprintf("/v1/feeds/%d/entries", feedID), filter)
  821. body, err := c.request.Get(ctx, path)
  822. if err != nil {
  823. return nil, err
  824. }
  825. defer body.Close()
  826. var result EntryResultSet
  827. if err := json.NewDecoder(body).Decode(&result); err != nil {
  828. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  829. }
  830. return &result, nil
  831. }
  832. // CategoryEntries fetches entries for a category using the given filter.
  833. func (c *Client) CategoryEntries(categoryID int64, filter *Filter) (*EntryResultSet, error) {
  834. ctx, cancel := withDefaultTimeout()
  835. defer cancel()
  836. return c.CategoryEntriesContext(ctx, categoryID, filter)
  837. }
  838. // CategoryEntriesContext fetches category entries.
  839. func (c *Client) CategoryEntriesContext(ctx context.Context, categoryID int64, filter *Filter) (*EntryResultSet, error) {
  840. path := buildFilterQueryString(fmt.Sprintf("/v1/categories/%d/entries", categoryID), filter)
  841. body, err := c.request.Get(ctx, path)
  842. if err != nil {
  843. return nil, err
  844. }
  845. defer body.Close()
  846. var result EntryResultSet
  847. if err := json.NewDecoder(body).Decode(&result); err != nil {
  848. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  849. }
  850. return &result, nil
  851. }
  852. // UpdateEntries updates the status of a list of entries.
  853. func (c *Client) UpdateEntries(entryIDs []int64, status string) error {
  854. ctx, cancel := withDefaultTimeout()
  855. defer cancel()
  856. return c.UpdateEntriesContext(ctx, entryIDs, status)
  857. }
  858. // UpdateEntriesContext updates the status of a list of entries.
  859. func (c *Client) UpdateEntriesContext(ctx context.Context, entryIDs []int64, status string) error {
  860. type payload struct {
  861. EntryIDs []int64 `json:"entry_ids"`
  862. Status string `json:"status"`
  863. }
  864. _, err := c.request.Put(ctx, "/v1/entries", &payload{EntryIDs: entryIDs, Status: status})
  865. return err
  866. }
  867. // UpdateEntriesStarred updates the starred state of a list of entries.
  868. func (c *Client) UpdateEntriesStarred(entryIDs []int64, starred bool) error {
  869. ctx, cancel := withDefaultTimeout()
  870. defer cancel()
  871. return c.UpdateEntriesStarredContext(ctx, entryIDs, starred)
  872. }
  873. // UpdateEntriesStarredContext updates the starred state of a list of entries.
  874. func (c *Client) UpdateEntriesStarredContext(ctx context.Context, entryIDs []int64, starred bool) error {
  875. type payload struct {
  876. EntryIDs []int64 `json:"entry_ids"`
  877. Starred *bool `json:"starred"`
  878. }
  879. _, err := c.request.Put(ctx, "/v1/entries", &payload{EntryIDs: entryIDs, Starred: &starred})
  880. return err
  881. }
  882. // UpdateEntry updates an entry.
  883. func (c *Client) UpdateEntry(entryID int64, entryChanges *EntryModificationRequest) (*Entry, error) {
  884. ctx, cancel := withDefaultTimeout()
  885. defer cancel()
  886. return c.UpdateEntryContext(ctx, entryID, entryChanges)
  887. }
  888. // UpdateEntryContext updates an entry.
  889. func (c *Client) UpdateEntryContext(ctx context.Context, entryID int64, entryChanges *EntryModificationRequest) (*Entry, error) {
  890. body, err := c.request.Put(ctx, fmt.Sprintf("/v1/entries/%d", entryID), entryChanges)
  891. if err != nil {
  892. return nil, err
  893. }
  894. defer body.Close()
  895. var entry *Entry
  896. if err := json.NewDecoder(body).Decode(&entry); err != nil {
  897. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  898. }
  899. return entry, nil
  900. }
  901. // ToggleStarred toggles the starred flag of an entry.
  902. func (c *Client) ToggleStarred(entryID int64) error {
  903. ctx, cancel := withDefaultTimeout()
  904. defer cancel()
  905. return c.ToggleStarredContext(ctx, entryID)
  906. }
  907. // ToggleStarredContext toggles entry starred value.
  908. func (c *Client) ToggleStarredContext(ctx context.Context, entryID int64) error {
  909. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/entries/%d/star", entryID), nil)
  910. return err
  911. }
  912. // SaveEntry sends an entry to a third-party service.
  913. func (c *Client) SaveEntry(entryID int64) error {
  914. ctx, cancel := withDefaultTimeout()
  915. defer cancel()
  916. return c.SaveEntryContext(ctx, entryID)
  917. }
  918. // SaveEntryContext sends an entry to a third-party service.
  919. func (c *Client) SaveEntryContext(ctx context.Context, entryID int64) error {
  920. _, err := c.request.Post(ctx, fmt.Sprintf("/v1/entries/%d/save", entryID), nil)
  921. return err
  922. }
  923. // FetchEntryOriginalContent fetches the original content of an entry using the scraper.
  924. func (c *Client) FetchEntryOriginalContent(entryID int64) (string, error) {
  925. ctx, cancel := withDefaultTimeout()
  926. defer cancel()
  927. return c.FetchEntryOriginalContentContext(ctx, entryID)
  928. }
  929. // FetchEntryOriginalContentContext fetches the original content of an entry using the scraper.
  930. func (c *Client) FetchEntryOriginalContentContext(ctx context.Context, entryID int64) (string, error) {
  931. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/entries/%d/fetch-content", entryID))
  932. if err != nil {
  933. return "", err
  934. }
  935. defer body.Close()
  936. var response struct {
  937. Content string `json:"content"`
  938. }
  939. if err := json.NewDecoder(body).Decode(&response); err != nil {
  940. return "", fmt.Errorf("miniflux: response error (%v)", err)
  941. }
  942. return response.Content, nil
  943. }
  944. // FetchCounters fetches feed counters.
  945. func (c *Client) FetchCounters() (*FeedCounters, error) {
  946. ctx, cancel := withDefaultTimeout()
  947. defer cancel()
  948. return c.FetchCountersContext(ctx)
  949. }
  950. // FetchCountersContext fetches feed counters.
  951. func (c *Client) FetchCountersContext(ctx context.Context) (*FeedCounters, error) {
  952. body, err := c.request.Get(ctx, "/v1/feeds/counters")
  953. if err != nil {
  954. return nil, err
  955. }
  956. defer body.Close()
  957. var result FeedCounters
  958. if err := json.NewDecoder(body).Decode(&result); err != nil {
  959. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  960. }
  961. return &result, nil
  962. }
  963. // FlushHistory deletes all entries with the status "read".
  964. func (c *Client) FlushHistory() error {
  965. ctx, cancel := withDefaultTimeout()
  966. defer cancel()
  967. return c.FlushHistoryContext(ctx)
  968. }
  969. // FlushHistoryContext deletes all entries with the status "read".
  970. func (c *Client) FlushHistoryContext(ctx context.Context) error {
  971. _, err := c.request.Put(ctx, "/v1/flush-history", nil)
  972. return err
  973. }
  974. // Icon fetches a feed icon.
  975. func (c *Client) Icon(iconID int64) (*FeedIcon, error) {
  976. ctx, cancel := withDefaultTimeout()
  977. defer cancel()
  978. return c.IconContext(ctx, iconID)
  979. }
  980. // IconContext fetches a feed icon.
  981. func (c *Client) IconContext(ctx context.Context, iconID int64) (*FeedIcon, error) {
  982. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/icons/%d", iconID))
  983. if err != nil {
  984. return nil, err
  985. }
  986. defer body.Close()
  987. var feedIcon *FeedIcon
  988. if err := json.NewDecoder(body).Decode(&feedIcon); err != nil {
  989. return nil, fmt.Errorf("miniflux: response error (%v)", err)
  990. }
  991. return feedIcon, nil
  992. }
  993. // Enclosure fetches a specific enclosure.
  994. func (c *Client) Enclosure(enclosureID int64) (*Enclosure, error) {
  995. ctx, cancel := withDefaultTimeout()
  996. defer cancel()
  997. return c.EnclosureContext(ctx, enclosureID)
  998. }
  999. // EnclosureContext fetches a specific enclosure.
  1000. func (c *Client) EnclosureContext(ctx context.Context, enclosureID int64) (*Enclosure, error) {
  1001. body, err := c.request.Get(ctx, fmt.Sprintf("/v1/enclosures/%d", enclosureID))
  1002. if err != nil {
  1003. return nil, err
  1004. }
  1005. defer body.Close()
  1006. var enclosure *Enclosure
  1007. if err := json.NewDecoder(body).Decode(&enclosure); err != nil {
  1008. return nil, fmt.Errorf("miniflux: response error(%v)", err)
  1009. }
  1010. return enclosure, nil
  1011. }
  1012. // UpdateEnclosure updates an enclosure.
  1013. func (c *Client) UpdateEnclosure(enclosureID int64, enclosureUpdate *EnclosureUpdateRequest) error {
  1014. ctx, cancel := withDefaultTimeout()
  1015. defer cancel()
  1016. return c.UpdateEnclosureContext(ctx, enclosureID, enclosureUpdate)
  1017. }
  1018. // UpdateEnclosureContext updates an enclosure.
  1019. func (c *Client) UpdateEnclosureContext(ctx context.Context, enclosureID int64, enclosureUpdate *EnclosureUpdateRequest) error {
  1020. _, err := c.request.Put(ctx, fmt.Sprintf("/v1/enclosures/%d", enclosureID), enclosureUpdate)
  1021. return err
  1022. }
  1023. func buildFilterQueryString(path string, filter *Filter) string {
  1024. if filter != nil {
  1025. values := url.Values{}
  1026. if filter.Status != "" {
  1027. values.Set("status", filter.Status)
  1028. }
  1029. if filter.Direction != "" {
  1030. values.Set("direction", filter.Direction)
  1031. }
  1032. if filter.Order != "" {
  1033. values.Set("order", filter.Order)
  1034. }
  1035. if filter.Limit >= 0 {
  1036. values.Set("limit", strconv.Itoa(filter.Limit))
  1037. }
  1038. if filter.Offset >= 0 {
  1039. values.Set("offset", strconv.Itoa(filter.Offset))
  1040. }
  1041. if filter.After > 0 {
  1042. values.Set("after", strconv.FormatInt(filter.After, 10))
  1043. }
  1044. if filter.Before > 0 {
  1045. values.Set("before", strconv.FormatInt(filter.Before, 10))
  1046. }
  1047. if filter.PublishedAfter > 0 {
  1048. values.Set("published_after", strconv.FormatInt(filter.PublishedAfter, 10))
  1049. }
  1050. if filter.PublishedBefore > 0 {
  1051. values.Set("published_before", strconv.FormatInt(filter.PublishedBefore, 10))
  1052. }
  1053. if filter.ChangedAfter > 0 {
  1054. values.Set("changed_after", strconv.FormatInt(filter.ChangedAfter, 10))
  1055. }
  1056. if filter.ChangedBefore > 0 {
  1057. values.Set("changed_before", strconv.FormatInt(filter.ChangedBefore, 10))
  1058. }
  1059. if filter.AfterEntryID > 0 {
  1060. values.Set("after_entry_id", strconv.FormatInt(filter.AfterEntryID, 10))
  1061. }
  1062. if filter.BeforeEntryID > 0 {
  1063. values.Set("before_entry_id", strconv.FormatInt(filter.BeforeEntryID, 10))
  1064. }
  1065. if filter.Starred != "" {
  1066. values.Set("starred", filter.Starred)
  1067. }
  1068. if filter.Search != "" {
  1069. values.Set("search", filter.Search)
  1070. }
  1071. if filter.CategoryID > 0 {
  1072. values.Set("category_id", strconv.FormatInt(filter.CategoryID, 10))
  1073. }
  1074. if filter.FeedID > 0 {
  1075. values.Set("feed_id", strconv.FormatInt(filter.FeedID, 10))
  1076. }
  1077. if filter.GloballyVisible {
  1078. values.Set("globally_visible", "true")
  1079. }
  1080. for _, status := range filter.Statuses {
  1081. values.Add("status", status)
  1082. }
  1083. for _, tag := range filter.Tags {
  1084. values.Add("tags", tag)
  1085. }
  1086. path = fmt.Sprintf("%s?%s", path, values.Encode())
  1087. }
  1088. return path
  1089. }