Skip to content

Commit

Permalink
Merge pull request #59 from pubnub/CE-3355-Unread-Message-Count-API
Browse files Browse the repository at this point in the history
Message Counts API
  • Loading branch information
crimsonred authored Mar 7, 2019
2 parents 915d0df + da6445a commit 9a5918f
Show file tree
Hide file tree
Showing 13 changed files with 558 additions and 8 deletions.
13 changes: 12 additions & 1 deletion .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
---
changelog:
-
changes:
-
text: "Implement history Message Counts"
type: improvement
-
text: "All request were secure (https), even when the Secure flag was false"
type: bug
date: Mar 5, 19
version: v4.1.7
-
changes:
-
Expand Down Expand Up @@ -276,6 +286,7 @@ features:
- STORAGE-COUNT
- STORAGE-DELETE-MESSAGES
- STORAGE-FETCH-MESSAGES
- STORAGE-MESSAGE-COUNT
subscribe:
- SUBSCRIBE-CHANNELS
- SUBSCRIBE-CHANNEL-GROUPS
Expand Down Expand Up @@ -313,4 +324,4 @@ supported-platforms:
- "Mac OS X 10.8 or later, amd64"
- "Windows 7 or later, amd64, 386"
version: "PubNub Go SDK"
version: v4.1.6
version: v4.1.7
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

# PubNub 4.1.6 client for Go
# PubNub 4.1.7 client for Go
* Go (1.9+)

# Please direct all Support Questions and Concerns to [email protected]
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.1.6
4.1.7
7 changes: 6 additions & 1 deletion endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,14 @@ func buildURL(o endpointOpts) (*url.URL, error) {

path = fmt.Sprintf("//%s%s", o.config().Origin, path)

secure := ""
if o.config().Secure {
secure = "s"
}

retURL := &url.URL{
Opaque: path,
Scheme: "https",
Scheme: fmt.Sprintf("http%s", secure),
Host: o.config().Origin,
RawQuery: stringifiedQuery,
}
Expand Down
2 changes: 2 additions & 0 deletions enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ const (
PNAccessManagerRevoke
// PNDeleteMessagesOperation is the enum used for the Delete Messages from History operation.
PNDeleteMessagesOperation
// PNMessageCountsOperation is the enum used for History with messages operation.
PNMessageCountsOperation
)

const (
Expand Down
36 changes: 36 additions & 0 deletions examples/cli/cli_demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func connect() {
config.PublishKey = "demo"
config.SubscribeKey = "demo"
config.SecretKey = "demo"
//config.Secure = false

config.AuthKey = "akey"

Expand Down Expand Up @@ -160,6 +161,7 @@ func showHelp() {
showSubscribeWithStateHelp()
showPresenceTimeoutHelp()
showPresenceHelp()
showMessageCountsHelp()
fmt.Println("")
fmt.Println("================")
fmt.Println(" || COMMANDS ||")
Expand All @@ -169,6 +171,12 @@ func showHelp() {
fmt.Println(" QUIT \n\tctrl+c ")
}

func showMessageCountsHelp() {
fmt.Println(" MessageCounts EXAMPLE: ")
fmt.Println(" messageCounts Channel(s) timetoken timetoken1,timetoken2")
fmt.Println(" messageCounts my-channel,my-channel1 15210190573608384 15210190573608384,15211140747622125")
}

func showGetStateHelp() {
fmt.Println(" GET STATE EXAMPLE: ")
fmt.Println(" getstate Channel ")
Expand Down Expand Up @@ -337,6 +345,8 @@ func readCommand(cmd string) {
setPresenceTimeout(command[1:])
case "presence":
runPresenceRequest(command[1:])
case "messageCounts":
messageCounts(command[1:])
case "q":
pn.UnsubscribeAll()
case "d":
Expand All @@ -346,6 +356,32 @@ func readCommand(cmd string) {
}
}

func messageCounts(args []string) {
if len(args) < 2 {
showMessageCountsHelp()
}

var channels []string
channels = strings.Split(args[0], ",")

var timetoken string
timetoken = args[1]

var channelsTimetoken []string
if len(args) > 2 {
channelsTimetoken = strings.Split(args[2], ",")
}

res, status, err := pn.MessageCounts().Channels(channels).Timetoken(timetoken).ChannelsTimetoken(channelsTimetoken).Execute()
fmt.Println(status)
fmt.Println(err)
for ch, v := range res.Channels {
fmt.Printf("%s %d", ch, v)
fmt.Println("")
}

}

func runPresenceRequest(args []string) {
if len(args) < 2 {
showPresenceHelp()
Expand Down
219 changes: 219 additions & 0 deletions message_counts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package pubnub

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"

"github.com/pubnub/go/pnerr"
"github.com/pubnub/go/utils"
"reflect"
"strings"

"net/http"
"net/url"
)

var emptyMessageCountsResp *MessageCountsResponse

const messageCountsPath = "/v3/history/sub-key/%s/message-counts/%s"

type messageCountsBuilder struct {
opts *messageCountsOpts
}

func newMessageCountsBuilder(pubnub *PubNub) *messageCountsBuilder {
builder := messageCountsBuilder{
opts: &messageCountsOpts{
pubnub: pubnub,
},
}

return &builder
}

func newMessageCountsBuilderWithContext(pubnub *PubNub,
context Context) *messageCountsBuilder {
builder := messageCountsBuilder{
opts: &messageCountsOpts{
pubnub: pubnub,
ctx: context,
},
}

return &builder
}

// Channels sets the Channels for the MessageCounts request.
func (b *messageCountsBuilder) Channels(channels []string) *messageCountsBuilder {
b.opts.Channels = channels
return b
}

// Timetoken sets the number of items to return in the MessageCounts request.
func (b *messageCountsBuilder) Timetoken(timetoken string) *messageCountsBuilder {
b.opts.Timetoken = timetoken
return b
}

// ChannelsTimetoken sets the order of messages in the MessageCounts request.
func (b *messageCountsBuilder) ChannelsTimetoken(channelsTimetoken []string) *messageCountsBuilder {
b.opts.ChannelsTimetoken = channelsTimetoken
return b
}

// QueryParam accepts a map, the keys and values of the map are passed as the query string parameters of the URL called by the API.
func (b *messageCountsBuilder) QueryParam(queryParam map[string]string) *messageCountsBuilder {
b.opts.QueryParam = queryParam

return b
}

// Transport sets the Transport for the MessageCounts request.
func (b *messageCountsBuilder) Transport(tr http.RoundTripper) *messageCountsBuilder {
b.opts.Transport = tr
return b
}

// Execute runs the MessageCounts request.
func (b *messageCountsBuilder) Execute() (*MessageCountsResponse, StatusResponse, error) {
rawJSON, status, err := executeRequest(b.opts)
if err != nil {
return emptyMessageCountsResp, status, err
}

return newMessageCountsResponse(rawJSON, b.opts, status)
}

type messageCountsOpts struct {
pubnub *PubNub

Channels []string
Timetoken string
ChannelsTimetoken []string

QueryParam map[string]string

// nil hacks
Transport http.RoundTripper

ctx Context
}

func (o *messageCountsOpts) config() Config {
return *o.pubnub.Config
}

func (o *messageCountsOpts) client() *http.Client {
return o.pubnub.GetClient()
}

func (o *messageCountsOpts) context() Context {
return o.ctx
}

func (o *messageCountsOpts) validate() error {
if o.config().SubscribeKey == "" {
return newValidationError(o, StrMissingSubKey)
}

if len(o.Channels) <= 0 {
return newValidationError(o, StrMissingChannel)
}

return nil
}

func (o *messageCountsOpts) buildPath() (string, error) {
channels := utils.JoinChannels(o.Channels)

return fmt.Sprintf(messageCountsPath,
o.pubnub.Config.SubscribeKey,
channels), nil
}

func (o *messageCountsOpts) buildQuery() (*url.Values, error) {
q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager)

q.Set("timetoken", o.Timetoken)
q.Set("channelsTimetoken", strings.Join(o.ChannelsTimetoken, ","))
SetQueryParam(q, o.QueryParam)

return q, nil
}

func (o *messageCountsOpts) jobQueue() chan *JobQItem {
return o.pubnub.jobQueue
}

func (o *messageCountsOpts) buildBody() ([]byte, error) {
return []byte{}, nil
}

func (o *messageCountsOpts) httpMethod() string {
return "GET"
}

func (o *messageCountsOpts) isAuthRequired() bool {
return true
}

func (o *messageCountsOpts) requestTimeout() int {
return o.pubnub.Config.NonSubscribeRequestTimeout
}

func (o *messageCountsOpts) connectTimeout() int {
return o.pubnub.Config.ConnectTimeout
}

func (o *messageCountsOpts) operationType() OperationType {
return PNMessageCountsOperation
}

func (o *messageCountsOpts) telemetryManager() *TelemetryManager {
return o.pubnub.telemetryManager
}

// MessageCountsResponse is the response to MessageCounts request. It contains a map of type MessageCountsResponseItem
type MessageCountsResponse struct {
Channels map[string]int
}

//http://ps.pndsn.com/v3/history/sub-key/demo/message-counts/my-channel,my-channel1?timestamp=1549982652&pnsdk=PubNub-Go/4.1.6&uuid=pn-82f145ea-adc3-4917-a11d-76a957347a82&timetoken=15499825804610610&channelsTimetoken=15499825804610610,15499925804610615&auth=akey&signature=pVDVge_suepcOlSMllpsXg_jpOjtEpW7B3HHFaViI4s=
//{"status": 200, "error": false, "error_message": "", "channels": {"my-channel1":1,"my-channel":2}}
func newMessageCountsResponse(jsonBytes []byte, o *messageCountsOpts,
status StatusResponse) (*MessageCountsResponse, StatusResponse, error) {

resp := &MessageCountsResponse{}

var value interface{}

err := json.Unmarshal(jsonBytes, &value)
if err != nil {
e := pnerr.NewResponseParsingError("Error unmarshalling response",
ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err)

return emptyMessageCountsResp, status, e
}

if result, ok := value.(map[string]interface{}); ok {
o.pubnub.Config.Log.Println(result["channels"])
if channels, ok1 := result["channels"].(map[string]interface{}); ok1 {
if channels != nil {
resp.Channels = make(map[string]int)
for ch, v := range channels {
resp.Channels[ch] = int(v.(float64))
}
} else {
o.pubnub.Config.Log.Printf("type assertion to map failed %v\n", result)
}
} else {
o.pubnub.Config.Log.Println("Assertion failed", reflect.TypeOf(result["channels"]))
}
} else {
o.pubnub.Config.Log.Printf("type assertion to map failed %v\n", value)
}

return resp, status, nil
}
Loading

0 comments on commit 9a5918f

Please sign in to comment.