Skip to content

Commit

Permalink
feat: add subscribe param
Browse files Browse the repository at this point in the history
  • Loading branch information
wunter8 committed Nov 5, 2024
1 parent 630f295 commit 9241b05
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
**Features:**

* Add username/password auth to email publishing ([#1164](https://github.com/binwiederhier/ntfy/pull/1164), thanks to [@bishtawi](https://github.com/bishtawi))
* Add `latest` subscription param for grabbing just the most recent message (thanks to [@wunter8](https://github.com/wunter8))

**Bug fixes + maintenance:**

Expand Down
8 changes: 8 additions & 0 deletions docs/subscribe/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,14 @@ curl -s "ntfy.sh/mytopic/json?since=1645970742"
curl -s "ntfy.sh/mytopic/json?since=nFS3knfcQ1xe"
```

### Fetch latest message
If you only want the most recent message sent to a topic and do not have a message ID or timestamp to use with
`since=`, you can use `since=latest` to grab the most recent message from the cache for a particular topic.

```
curl -s "ntfy.sh/mytopic/json?poll=1&since=latest"
```

### Fetch scheduled messages
Messages that are [scheduled to be delivered](../publish.md#scheduled-delivery) at a later date are not typically
returned when subscribing via the API, which makes sense, because after all, the messages have technically not been
Expand Down
17 changes: 17 additions & 0 deletions server/message_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ const (
WHERE topic = ? AND (id > ? OR published = 0)
ORDER BY time, id
`
selectMessagesLatestQuery = `
SELECT mid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding
FROM messages
WHERE topic = ? AND published = 1
ORDER BY time DESC, id DESC
LIMIT 1
`
selectMessagesDueQuery = `
SELECT mid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding
FROM messages
Expand Down Expand Up @@ -416,6 +423,8 @@ func (c *messageCache) addMessages(ms []*message) error {
func (c *messageCache) Messages(topic string, since sinceMarker, scheduled bool) ([]*message, error) {
if since.IsNone() {
return make([]*message, 0), nil
} else if since.IsLatest() {
return c.messagesLatest(topic)
} else if since.IsID() {
return c.messagesSinceID(topic, since, scheduled)
}
Expand Down Expand Up @@ -462,6 +471,14 @@ func (c *messageCache) messagesSinceID(topic string, since sinceMarker, schedule
return readMessages(rows)
}

func (c *messageCache) messagesLatest(topic string) ([]*message, error) {
rows, err := c.db.Query(selectMessagesLatestQuery, topic)
if err != nil {
return nil, err
}
return readMessages(rows)
}

func (c *messageCache) MessagesDue() ([]*message, error) {
rows, err := c.db.Query(selectMessagesDueQuery, time.Now().Unix())
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions server/message_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func testCacheMessages(t *testing.T, c *messageCache) {
require.Equal(t, 1, len(messages))
require.Equal(t, "my other message", messages[0].Message)

// mytopic: latest
messages, _ = c.Messages("mytopic", sinceLatestMessage, false)
require.Equal(t, 1, len(messages))
require.Equal(t, "my other message", messages[0].Message)

// example: count
counts, err = c.MessageCounts()
require.Nil(t, err)
Expand Down
6 changes: 4 additions & 2 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1556,8 +1556,8 @@ func (s *Server) sendOldMessages(topics []*topic, since sinceMarker, scheduled b

// parseSince returns a timestamp identifying the time span from which cached messages should be received.
//
// Values in the "since=..." parameter can be either a unix timestamp or a duration (e.g. 12h), or
// "all" for all messages.
// Values in the "since=..." parameter can be either a unix timestamp or a duration (e.g. 12h),
// "all" for all messages, or "latest" for the most recent message for a topic
func parseSince(r *http.Request, poll bool) (sinceMarker, error) {
since := readParam(r, "x-since", "since", "si")

Expand All @@ -1569,6 +1569,8 @@ func parseSince(r *http.Request, poll bool) (sinceMarker, error) {
return sinceNoMessages, nil
} else if since == "all" {
return sinceAllMessages, nil
} else if since == "latest" {
return sinceLatestMessage, nil
} else if since == "none" {
return sinceNoMessages, nil
}
Expand Down
5 changes: 5 additions & 0 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,11 @@ func TestServer_PublishAndPollSince(t *testing.T) {
require.Equal(t, 1, len(messages))
require.Equal(t, "test 2", messages[0].Message)

response = request(t, s, "GET", "/mytopic/json?poll=1&since=latest", "", nil)
messages = toMessages(t, response.Body.String())
require.Equal(t, 1, len(messages))
require.Equal(t, "test 2", messages[0].Message)

response = request(t, s, "GET", "/mytopic/json?poll=1&since=INVALID", "", nil)
require.Equal(t, 40008, toHTTPError(t, response.Body.String()).Code)
}
Expand Down
11 changes: 8 additions & 3 deletions server/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,12 @@ func (t sinceMarker) IsNone() bool {
return t == sinceNoMessages
}

func (t sinceMarker) IsLatest() bool {
return t == sinceLatestMessage
}

func (t sinceMarker) IsID() bool {
return t.id != ""
return t.id != "" && t.id != "latest"
}

func (t sinceMarker) Time() time.Time {
Expand All @@ -182,8 +186,9 @@ func (t sinceMarker) ID() string {
}

var (
sinceAllMessages = sinceMarker{time.Unix(0, 0), ""}
sinceNoMessages = sinceMarker{time.Unix(1, 0), ""}
sinceAllMessages = sinceMarker{time.Unix(0, 0), ""}
sinceNoMessages = sinceMarker{time.Unix(1, 0), ""}
sinceLatestMessage = sinceMarker{time.Unix(0, 0), "latest"}
)

type queryFilter struct {
Expand Down

0 comments on commit 9241b05

Please sign in to comment.