Refactor to make the maintanance simpler
- Fixed issue with webhooks
This commit is contained in:
+446
@@ -0,0 +1,446 @@
|
||||
// Copyright 2014 Claudemiro Alves Feitosa Neto. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"ipe/events"
|
||||
"ipe/storage"
|
||||
"ipe/utils"
|
||||
)
|
||||
|
||||
// // Maximum event size permitted 10 kB
|
||||
// See: http://blogs.gnome.org/cneumair/2008/09/30/1-kb-1024-bytes-no-1-kb-1000-bytes/
|
||||
const maxDataEventSize = 10 * 1000
|
||||
|
||||
// Prepare QueryString
|
||||
func prepareQueryString(params url.Values) string {
|
||||
var keys []string
|
||||
|
||||
for key := range params {
|
||||
keys = append(keys, strings.ToLower(key))
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
var pieces []string
|
||||
|
||||
for _, key := range keys {
|
||||
pieces = append(pieces, key+"="+params.Get(key))
|
||||
}
|
||||
|
||||
return strings.Join(pieces, "&")
|
||||
}
|
||||
|
||||
// Authenticate pusher
|
||||
// see: https://gist.github.com/mloughran/376898
|
||||
//
|
||||
// The signature is a HMAC SHA256 hex digest.
|
||||
// This is generated by signing a string made up of the following components concatenated with newline characters \n.
|
||||
//
|
||||
// * The uppercase request method (e.g. POST)
|
||||
// * The request path (e.g. /some/resource)
|
||||
// * The query parameters sorted by key, with keys converted to lowercase, then joined as in the query string.
|
||||
// Note that the string must not be url escaped (e.g. given the keys auth_key: foo, Name: Something else, you get auth_key=foo&name=Something else)
|
||||
func Authentication(storage storage.Storage) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
pathVars = mux.Vars(r)
|
||||
appID = pathVars["app_id"]
|
||||
)
|
||||
|
||||
app, err := storage.GetAppByAppID(appID)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
http.Error(w, "Not authorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
query := r.URL.Query()
|
||||
|
||||
signature := query.Get("auth_signature")
|
||||
query.Del("auth_signature")
|
||||
|
||||
queryString := prepareQueryString(query)
|
||||
|
||||
toSign := strings.ToUpper(r.Method) + "\n" + r.URL.Path + "\n" + queryString
|
||||
|
||||
if utils.HashMAC([]byte(toSign), []byte(app.Secret)) == signature {
|
||||
next.ServeHTTP(w, r)
|
||||
} else {
|
||||
log.Error("Not authorized")
|
||||
http.Error(w, "Not authorized", http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the application is disabled
|
||||
func CheckAppDisabled(storage storage.Storage) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
pathVars = mux.Vars(r)
|
||||
appID = pathVars["app_id"]
|
||||
)
|
||||
|
||||
currentApp, err := storage.GetAppByAppID(appID)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Could not found an app with app_id: %s", appID), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if currentApp.ApplicationDisabled {
|
||||
http.Error(w, "Application disabled", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
}
|
||||
|
||||
type PostEvents struct{ storage storage.Storage }
|
||||
|
||||
func NewPostEvents(storage storage.Storage) *PostEvents {
|
||||
return &PostEvents{storage: storage}
|
||||
}
|
||||
|
||||
// ServeHTTPC An event consists of a name and data (typically JSON) which may be sent to all subscribers to a particular channel or channels.
|
||||
// This is conventionally known as triggering an event.
|
||||
//
|
||||
// The body should contain a Hash of parameters encoded as JSON where data parameter itself is JSON encoded.
|
||||
//
|
||||
// Not Implemented:
|
||||
// Note that these parameters may be provided in the query string, although this is discouraged.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// {"name":"foo","channels":["project-3"],"data":"{\"some\":\"data\"}"}
|
||||
//
|
||||
// Response is an empty JSON hash.
|
||||
//
|
||||
// POST /apps/{app_id}/events
|
||||
func (h *PostEvents) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
pathVars = mux.Vars(r)
|
||||
appID = pathVars["app_id"]
|
||||
)
|
||||
|
||||
app, err := h.storage.GetAppByAppID(appID)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Could not found an app with app_id: %s", appID), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
var input struct {
|
||||
Name string `json:"name"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
Channels []string `json:"channels,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
SocketID string `json:"socket_id,omitempty"`
|
||||
}
|
||||
|
||||
err = json.NewDecoder(r.Body).Decode(&input)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "Bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// The event data should not be larger than 10KB.
|
||||
if len(input.Data) > maxDataEventSize {
|
||||
http.Error(w, "Request too large.", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info(input.Channels)
|
||||
if len(input.Channel) > 0 && len(input.Channels) == 0 {
|
||||
input.Channels = append(input.Channels, input.Channel)
|
||||
}
|
||||
|
||||
for _, c := range input.Channels {
|
||||
channel := app.FindOrCreateChannelByChannelID(c)
|
||||
|
||||
if err := app.Publish(channel, events.Raw{Event: input.Name, Channel: c, Data: input.Data}, input.SocketID); err != nil {
|
||||
log.Errorf("error publishing event %+v", err)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if _, err := w.Write([]byte("{}")); err != nil {
|
||||
log.Errorf("unexpected error while writing into response %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type GetChannels struct{ storage storage.Storage }
|
||||
|
||||
func NewGetChannels(storage storage.Storage) *GetChannels {
|
||||
return &GetChannels{storage: storage}
|
||||
}
|
||||
|
||||
// Allows fetching a hash of occupied channels (optionally filtered by prefix),
|
||||
// and optionally one or more attributes for each channel.
|
||||
//
|
||||
// Notes:
|
||||
// 'user_count' is the only attribute documented on the Pusher API
|
||||
//
|
||||
// Example:
|
||||
// {
|
||||
// "channels": {
|
||||
// "presence-foobar": {
|
||||
// user_count: 42
|
||||
// },
|
||||
// "presence-another": {
|
||||
// user_count: 123
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// GET /apps/{app_id}/channels
|
||||
func (h *GetChannels) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
pathVars = mux.Vars(r)
|
||||
queryVars = r.URL.Query()
|
||||
appID = pathVars["app_id"]
|
||||
filter = queryVars.Get("filter_by_prefix")
|
||||
info = queryVars.Get("info")
|
||||
attributes = strings.Split(info, ",")
|
||||
)
|
||||
|
||||
requestedUserCount := false
|
||||
|
||||
for _, a := range attributes {
|
||||
if a == "user_count" {
|
||||
requestedUserCount = true
|
||||
}
|
||||
}
|
||||
|
||||
// If an attribute such as user_count is requested, and the request is not limited
|
||||
// to presence channels, the API will return an error (400 code)
|
||||
if requestedUserCount && filter != "presence-" {
|
||||
http.Error(w, "Attribute user_count is restricted to presence channels", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
app, err := h.storage.GetAppByAppID(appID)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Could not found an app with app_id: %s", appID), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
channels := make(map[string]interface{})
|
||||
|
||||
switch filter {
|
||||
case "presence-":
|
||||
for _, c := range app.PresenceChannels() {
|
||||
if requestedUserCount {
|
||||
channels[c.ID] = struct {
|
||||
UserCount int `json:"user_count"`
|
||||
}{
|
||||
c.TotalUsers(),
|
||||
}
|
||||
} else {
|
||||
channels[c.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
case "public-":
|
||||
for _, c := range app.PublicChannels() {
|
||||
channels[c.ID] = struct{}{}
|
||||
}
|
||||
case "private-":
|
||||
for _, c := range app.PrivateChannels() {
|
||||
channels[c.ID] = struct{}{}
|
||||
}
|
||||
default:
|
||||
for _, c := range app.Channels() {
|
||||
channels[c.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
js := make(map[string]interface{}, 1)
|
||||
js["channels"] = channels
|
||||
|
||||
if err := json.NewEncoder(w).Encode(js); err != nil {
|
||||
log.Error(err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
type GetChannel struct{ storage storage.Storage }
|
||||
|
||||
func NewGetChannel(storage storage.Storage) *GetChannel {
|
||||
return &GetChannel{storage: storage}
|
||||
}
|
||||
|
||||
// Fetch info for one channel
|
||||
//
|
||||
// Example:
|
||||
// {
|
||||
// occupied: true,
|
||||
// user_count: 42,
|
||||
// subscription_count: 42
|
||||
// }
|
||||
//
|
||||
// GET /apps/{app_id}/channels/{channel_name}
|
||||
func (h *GetChannel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
pathVars = mux.Vars(r)
|
||||
queryVars = r.URL.Query()
|
||||
appID = pathVars["app_id"]
|
||||
channelName = pathVars["channel_name"]
|
||||
info = queryVars.Get("info")
|
||||
attributes = strings.Split(info, ",")
|
||||
)
|
||||
|
||||
app, err := h.storage.GetAppByAppID(appID)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Could not found an app with app_id: %s", appID), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Channel name could not be empty
|
||||
if strings.TrimSpace(channelName) == "" {
|
||||
http.Error(w, "Empty channel name", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Attributes requested
|
||||
requestedUserCount := false
|
||||
requestedSubscriptionCount := false
|
||||
|
||||
for _, a := range attributes {
|
||||
switch a {
|
||||
case "subscription_count":
|
||||
requestedSubscriptionCount = true
|
||||
case "user_count":
|
||||
requestedUserCount = true
|
||||
}
|
||||
}
|
||||
|
||||
channel, err := app.FindChannelByChannelID(channelName)
|
||||
|
||||
// Channel exists?
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Could not find a channel with id %s", channelName), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// If an attribute such as user_count is requested, and the request is not limited
|
||||
// to presence channels, the API will return an error (400 code)
|
||||
if requestedUserCount && !channel.IsPresence() {
|
||||
http.Error(w, "Attribute user_count is restricted to presence channels", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Output
|
||||
dtoChannel := struct {
|
||||
Occupied bool `json:"occupied"`
|
||||
UserCount int `json:"user_count,omitempty"`
|
||||
SubscriptionCount int `json:"subscription_count,omitempty"`
|
||||
}{Occupied: channel.IsOccupied()}
|
||||
|
||||
switch {
|
||||
case requestedSubscriptionCount && requestedUserCount:
|
||||
dtoChannel.UserCount = channel.TotalUsers()
|
||||
dtoChannel.SubscriptionCount = channel.TotalSubscriptions()
|
||||
|
||||
case requestedUserCount:
|
||||
dtoChannel.UserCount = channel.TotalUsers()
|
||||
|
||||
case requestedSubscriptionCount:
|
||||
dtoChannel.SubscriptionCount = channel.TotalSubscriptions()
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(dtoChannel); err != nil {
|
||||
log.Error(err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
type GetChannelUsers struct{ storage storage.Storage }
|
||||
|
||||
func NewGetChannelUsers(storage storage.Storage) *GetChannelUsers {
|
||||
return &GetChannelUsers{storage: storage}
|
||||
}
|
||||
|
||||
// Allowed only for presence-channels
|
||||
//
|
||||
// Example:
|
||||
// {
|
||||
// "users": [
|
||||
// { "id": "1" },
|
||||
// { "id": "2" }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// GET /apps/{app_id}/channels/{channel_name}/users
|
||||
func (h *GetChannelUsers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
pathVars = mux.Vars(r)
|
||||
appID = pathVars["app_id"]
|
||||
channelName = pathVars["channel_name"]
|
||||
)
|
||||
|
||||
isPresence := utils.IsPresenceChannel(channelName)
|
||||
|
||||
if !isPresence {
|
||||
http.Error(w, "This api endpoint is restricted to presence channels.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
app, err := h.storage.GetAppByAppID(appID)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Could not found an app with app_id: %s", appID), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Get the channel
|
||||
channel, err := app.FindChannelByChannelID(channelName)
|
||||
|
||||
// Channel exists?
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Could not find a channel with id %s", channelName), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
result := make(map[string][]interface{})
|
||||
|
||||
var users []interface{}
|
||||
|
||||
for _, s := range channel.Subscriptions() {
|
||||
users = append(users, struct {
|
||||
ID string `json:"id"`
|
||||
}{s.ID})
|
||||
}
|
||||
|
||||
result["users"] = users
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(result); err != nil {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"ipe/app"
|
||||
channel2 "ipe/channel"
|
||||
"ipe/connection"
|
||||
"ipe/mocks"
|
||||
"ipe/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
testApp *app.Application
|
||||
database storage.Storage
|
||||
id = 0
|
||||
)
|
||||
|
||||
func newTestApp() *app.Application {
|
||||
a := app.NewApplication("Test", strconv.Itoa(id), "123", "123", false, false, true, false, "")
|
||||
id++
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func init() {
|
||||
testApp = newTestApp()
|
||||
|
||||
channel := channel2.New("presence-c1")
|
||||
testApp.AddChannel(channel)
|
||||
testApp.AddChannel(channel2.New("c2"))
|
||||
testApp.AddChannel(channel2.New("private-c3"))
|
||||
|
||||
conn := connection.New("123.456", mocks.MockSocket{})
|
||||
_ = testApp.Subscribe(channel, conn, "{}")
|
||||
|
||||
conn = connection.New("321.654", mocks.MockSocket{})
|
||||
_ = testApp.Subscribe(channel, conn, "{}")
|
||||
|
||||
_storage := storage.NewInMemory()
|
||||
_ = _storage.AddApp(testApp)
|
||||
|
||||
database = _storage
|
||||
}
|
||||
|
||||
// All channels
|
||||
func Test_getChannels_all(t *testing.T) {
|
||||
appID := testApp.AppID
|
||||
|
||||
r, _ := http.NewRequest("GET", fmt.Sprintf("/apps/%s/channels", appID), nil)
|
||||
r = mux.SetURLVars(r, map[string]string{
|
||||
"app_id": appID,
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := &GetChannels{database}
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("w.Code == %d, wants %d", w.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &data)
|
||||
|
||||
channels := data["channels"].(map[string]interface{})
|
||||
|
||||
if len(channels) != 3 {
|
||||
t.Errorf("len(%q) == %d, want %d", channels, len(channels), 3)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getChannels_filter_by_presence_prefix(t *testing.T) {
|
||||
appID := testApp.AppID
|
||||
|
||||
r, _ := http.NewRequest("GET", fmt.Sprintf("/apps/%s/channels?filter_by_prefix=presence-", appID), nil)
|
||||
r = mux.SetURLVars(r, map[string]string{
|
||||
"app_id": appID,
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := &GetChannels{database}
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("w.Code == %d, wants %d", w.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &data)
|
||||
|
||||
channels := data["channels"].(map[string]interface{})
|
||||
|
||||
if len(channels) != 1 {
|
||||
t.Errorf("len(%q) == %d, want %d", channels, len(channels), 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Only presence channels and user_count
|
||||
func Test_getChannels_filter_by_presence_prefix_and_user_count(t *testing.T) {
|
||||
appID := testApp.AppID
|
||||
|
||||
r, _ := http.NewRequest("GET", fmt.Sprintf("/apps/%s/channels?filter_by_prefix=presence-&info=user_count", appID), nil)
|
||||
r = mux.SetURLVars(r, map[string]string{
|
||||
"app_id": appID,
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := &GetChannels{database}
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("w.Code == %d, wants %d", w.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &data)
|
||||
|
||||
channels := data["channels"].(map[string]interface{})
|
||||
|
||||
if len(channels) != 1 {
|
||||
t.Errorf("len(%q) == %d, want %d", channels, len(channels), 1)
|
||||
}
|
||||
|
||||
c, exists := channels["presence-c1"]
|
||||
|
||||
if !exists {
|
||||
t.Errorf("!exists == %t, want %t", !exists, false)
|
||||
}
|
||||
|
||||
_channel := c.(map[string]interface{})
|
||||
|
||||
if _channel["user_count"] != float64(1) {
|
||||
t.Errorf("_channel['user_count'] == %f, want %d", _channel["user_count"], 1)
|
||||
}
|
||||
}
|
||||
|
||||
// User count only alowed in Presence channels
|
||||
func Test_getChannels_filter_by_private_prefix_and_info_user_count(t *testing.T) {
|
||||
appID := testApp.AppID
|
||||
|
||||
r, _ := http.NewRequest("GET", fmt.Sprintf("/apps/%s/channels?filter_by_prefix=private-&info=user_count", appID), nil)
|
||||
r = mux.SetURLVars(r, map[string]string{
|
||||
"app_id": appID,
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := &GetChannels{database}
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("w.Code == %d, wants %d", w.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getChannels_filter_by_public_prefix(t *testing.T) {
|
||||
appID := testApp.AppID
|
||||
|
||||
r, _ := http.NewRequest("GET", fmt.Sprintf("/apps/%s/channels?filter_by_prefix=public-", appID), nil)
|
||||
r = mux.SetURLVars(r, map[string]string{
|
||||
"app_id": appID,
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := &GetChannels{database}
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("w.Code == %d, wants %d", w.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &data)
|
||||
|
||||
channels := data["channels"].(map[string]interface{})
|
||||
|
||||
if len(channels) != 1 {
|
||||
t.Errorf("len(%q) == %d, want %d", channels, len(channels), 1)
|
||||
}
|
||||
|
||||
_, exists := channels["c2"]
|
||||
|
||||
if !exists {
|
||||
t.Errorf("!exists == %t, want %t", !exists, false)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getChannels_filter_by_private_prefix(t *testing.T) {
|
||||
appID := testApp.AppID
|
||||
|
||||
r, _ := http.NewRequest("GET", fmt.Sprintf("/apps/%s/channels?filter_by_prefix=private-", appID), nil)
|
||||
r = mux.SetURLVars(r, map[string]string{
|
||||
"app_id": appID,
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := &GetChannels{database}
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("w.Code == %d, wants %d", w.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &data)
|
||||
|
||||
channels := data["channels"].(map[string]interface{})
|
||||
|
||||
if len(channels) != 1 {
|
||||
t.Errorf("len(%q) == %d, want %d", channels, len(channels), 1)
|
||||
}
|
||||
|
||||
_, exists := channels["private-c3"]
|
||||
|
||||
if !exists {
|
||||
t.Errorf("!exists == %t, want %t", !exists, false)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user