From 9b4d03088268b06a8a179563435da82606c698db Mon Sep 17 00:00:00 2001 From: Rajat Kalsy Date: Tue, 27 Aug 2019 03:48:27 +0530 Subject: [PATCH] Objects API (#74) * object api * grant v3 changes WIP * Objects WIP * Object API WIP * Object API - spaces - WIP * Object API - memberships - WIP * Objects Memberships WIP * object API - memberships WIP * object API - memberships WIP * objects WIP - enums * Object WIP - Listeners * Objects WIP, update space memberships and update members * Example updates for Object API * Objects API WIP * Cleanup * CreateUser unit tests * CreateUser unit tests * Create space unit tests * Objects Unit tests WIP * Objects Unit Tests, signature updates * package fix * some updates * Object integration tests, renaming * objects renaming * Objects Events fixes * Objects integration tests * Integration test listener * Integration test listener updates * Integration test objects listener improvements * Integration test objects listener improvements * Telemetry for objects API * Version bump * codacy suggestions * Codacy fixes 2 * Codacy fixes 3 * Update yml --- .pubnub.yml | 30 +- README.md | 2 +- VERSION | 2 +- endpoints.go | 9 +- enums.go | 142 +++++ examples/cli/cli_demo.go | 675 +++++++++++++++++++++- grant_request.go | 60 +- grant_request_test.go | 8 +- history_request_test.go | 2 +- listener_manager.go | 132 ++++- objects_common.go | 86 +++ objects_create_space.go | 226 ++++++++ objects_create_space_test.go | 108 ++++ objects_create_user.go | 245 ++++++++ objects_create_user_test.go | 113 ++++ objects_delete_space.go | 169 ++++++ objects_delete_space_test.go | 85 +++ objects_delete_user.go | 170 ++++++ objects_delete_user_test.go | 85 +++ objects_get_members.go | 235 ++++++++ objects_get_members_test.go | 125 ++++ objects_get_memberships.go | 235 ++++++++ objects_get_memberships_test.go | 123 ++++ objects_get_space.go | 182 ++++++ objects_get_space_test.go | 103 ++++ objects_get_spaces.go | 229 ++++++++ objects_get_spaces_test.go | 115 ++++ objects_get_user.go | 182 ++++++ objects_get_user_test.go | 106 ++++ objects_get_users.go | 229 ++++++++ objects_get_users_test.go | 118 ++++ objects_manage_members.go | 276 +++++++++ objects_manage_members_test.go | 166 ++++++ objects_manage_memberships.go | 274 +++++++++ objects_manage_memberships_test.go | 164 ++++++ objects_update_space.go | 226 ++++++++ objects_update_space_test.go | 108 ++++ objects_update_user.go | 244 ++++++++ objects_update_user_test.go | 113 ++++ pubnub.go | 112 ++++ request.go | 27 +- subscription_manager.go | 220 ++++++- telemetry_manager.go | 29 + tests/e2e/grant_test.go | 24 +- tests/e2e/helper.go | 10 +- tests/e2e/objects_test.go | 896 +++++++++++++++++++++++++++++ tests/e2e/signal_request_test.go | 4 +- tests/e2e/subscribe_test.go | 3 - utils/crypto_test.go | 39 ++ utils/string_utils.go | 10 +- 50 files changed, 7187 insertions(+), 89 deletions(-) create mode 100644 objects_common.go create mode 100644 objects_create_space.go create mode 100644 objects_create_space_test.go create mode 100644 objects_create_user.go create mode 100644 objects_create_user_test.go create mode 100644 objects_delete_space.go create mode 100644 objects_delete_space_test.go create mode 100644 objects_delete_user.go create mode 100644 objects_delete_user_test.go create mode 100644 objects_get_members.go create mode 100644 objects_get_members_test.go create mode 100644 objects_get_memberships.go create mode 100644 objects_get_memberships_test.go create mode 100644 objects_get_space.go create mode 100644 objects_get_space_test.go create mode 100644 objects_get_spaces.go create mode 100644 objects_get_spaces_test.go create mode 100644 objects_get_user.go create mode 100644 objects_get_user_test.go create mode 100644 objects_get_users.go create mode 100644 objects_get_users_test.go create mode 100644 objects_manage_members.go create mode 100644 objects_manage_members_test.go create mode 100644 objects_manage_memberships.go create mode 100644 objects_manage_memberships_test.go create mode 100644 objects_update_space.go create mode 100644 objects_update_space_test.go create mode 100644 objects_update_user.go create mode 100644 objects_update_user_test.go create mode 100644 tests/e2e/objects_test.go diff --git a/.pubnub.yml b/.pubnub.yml index e628c8c1..9ad09adf 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,5 +1,15 @@ --- changelog: + - + changes: + - + text: "Objects API" + type: improvement + - + text: "Grant Optimizations" + type: improvement + date: Aug 28, 19 + version: v4.2.7 - changes: - @@ -351,6 +361,24 @@ features: - SUBSCRIBE-WITH-USERSTATE - SUBSCRIBE-PUBSUB-V2 - SUBSCRIBE-SIGNAL-LISTENER + - SUBSCRIBE-MEMBERSHIP-LISTENER + - SUBSCRIBE-SPACE-LISTENER + - SUBSCRIBE-USER-LISTENER + objects: + - OBJECTS-GET-USERS + - OBJECTS-GET-USER + - OBJECTS-CREATE-USER + - OBJECTS-UPDATE-USER + - OBJECTS-DELETE-USER + - OBJECTS-GET-SPACES + - OBJECTS-CREATE-SPACE + - OBJECTS-GET-SPACE + - OBJECTS-UPDATE-SPACE + - OBJECTS-DELETE-SPACE + - OBJECTS-GET-MEMBERSHIPS + - OBJECTS-MANAGE-MEMBERSHIPS + - OBJECTS-GET-MEMBERS + - OBJECTS-MANAGE-MEMBERS time: - TIME-TIME unsubscribe: @@ -380,4 +408,4 @@ supported-platforms: - "Mac OS X 10.8 or later, amd64" - "Windows 7 or later, amd64, 386" version: "PubNub Go SDK" -version: v4.2.6 \ No newline at end of file +version: v4.2.7 \ No newline at end of file diff --git a/README.md b/README.md index 5e5aaae1..ee85cab8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PubNub 4.2.6 client for Go +# PubNub 4.2.7 client for Go * Go (1.9+) # Please direct all Support Questions and Concerns to Support@PubNub.com diff --git a/VERSION b/VERSION index d6f85abf..4739c61f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.6 +4.2.7 diff --git a/endpoints.go b/endpoints.go index ad0f1c7e..dfb03ada 100644 --- a/endpoints.go +++ b/endpoints.go @@ -75,15 +75,10 @@ func buildURL(o endpointOpts) (*url.URL, error) { signedInput := o.config().SubscribeKey + "\n" + o.config().PublishKey + "\n" - if o.operationType() == PNAccessManagerGrant || - o.operationType() == PNAccessManagerRevoke { - signedInput += "grant\n" - } else { - signedInput += fmt.Sprintf("%s\n", path) - } + signedInput += fmt.Sprintf("%s\n", path) signedInput += utils.PreparePamParams(query) - //o.config().Log.Println("signedInput:", signedInput) + o.config().Log.Println("signedInput:", signedInput) signature = utils.GetHmacSha256(o.config().SecretKey, signedInput) } diff --git a/enums.go b/enums.go index ab5b4621..e6920505 100644 --- a/enums.go +++ b/enums.go @@ -14,6 +14,74 @@ type ReconnectionPolicy int // PNPushType is used as an enum to catgorize the available Push Types type PNPushType int +// PNUserSpaceInclude is used as an enum to catgorize the available User and Space include types +type PNUserSpaceInclude int + +// PNMembershipsInclude is used as an enum to catgorize the available Memberships include types +type PNMembershipsInclude int + +// PNMembersInclude is used as an enum to catgorize the available Members include types +type PNMembersInclude int + +// PNObjectsEvent is used as an enum to catgorize the available Object Events +type PNObjectsEvent string + +// PNObjectsEventType is used as an enum to catgorize the available Object Event types +type PNObjectsEventType string + +const ( + // PNObjectsUserEvent is the enum when the event of type `user` occurs + PNObjectsUserEvent PNObjectsEventType = "user" + // PNObjectsSpaceEvent is the enum when the event of type `space` occurs + PNObjectsSpaceEvent = "space" + // PNObjectsMembershipEvent is the enum when the event of type `membership` occurs + PNObjectsMembershipEvent = "membership" +) + +const ( + // PNObjectsEventCreate is the enum when the event `create` occurs + PNObjectsEventCreate PNObjectsEvent = "create" + // PNObjectsEventUpdate is the enum when the event `update` occurs + PNObjectsEventUpdate = "update" + // PNObjectsEventDelete is the enum when the event `delete` occurs + PNObjectsEventDelete = "delete" +) + +const ( + // PNUserSpaceCustom is the enum equivalent to the value `custom` available User and Space include types + PNUserSpaceCustom PNUserSpaceInclude = 1 + iota +) + +func (s PNUserSpaceInclude) String() string { + return [...]string{"custom"}[s-1] +} + +const ( + // PNMembershipsCustom is the enum equivalent to the value `custom` available Memberships include types + PNMembershipsCustom PNMembershipsInclude = 1 + iota + // PNMembershipsSpace is the enum equivalent to the value `space` available Memberships include types + PNMembershipsSpace + // PNMembershipsSpaceCustom is the enum equivalent to the value `space.custom` available Memberships include types + PNMembershipsSpaceCustom +) + +func (s PNMembershipsInclude) String() string { + return [...]string{"custom", "space", "space.custom"}[s-1] +} + +const ( + // PNMembersCustom is the enum equivalent to the value `custom` available Members include types + PNMembersCustom PNMembersInclude = 1 + iota + // PNMembersUser is the enum equivalent to the value `user` available Members include types + PNMembersUser + // PNMembersUserCustom is the enum equivalent to the value `user.custom` available Members include types + PNMembersUserCustom +) + +func (s PNMembersInclude) String() string { + return [...]string{"custom", "user", "user.custom"}[s-1] +} + // PNMessageType is used as an enum to catgorize the Subscribe response. type PNMessageType int @@ -122,6 +190,34 @@ const ( PNMessageCountsOperation // PNSignalOperation is the enum used for Signal opertaion. PNSignalOperation + // PNCreateUserOperation is the enum used to create users in the Object API. + PNCreateUserOperation + // PNGetUsersOperation is the enum used to get users in the Object API. + PNGetUsersOperation + // PNGetUserOperation is the enum used to get user in the Object API. + PNGetUserOperation + // PNUpdateUserOperation is the enum used to update users in the Object API. + PNUpdateUserOperation + // PNDeleteUserOperation is the enum used to delete users in the Object API. + PNDeleteUserOperation + // PNGetSpaceOperation is the enum used to get space in the Object API. + PNGetSpaceOperation + // PNGetSpacesOperation is the enum used to get spaces in the Object API. + PNGetSpacesOperation + // PNCreateSpaceOperation is the enum used to create space in the Object API. + PNCreateSpaceOperation + // PNDeleteSpaceOperation is the enum used to delete space in the Object API. + PNDeleteSpaceOperation + // PNUpdateSpaceOperation is the enum used to update space in the Object API. + PNUpdateSpaceOperation + // PNGetMembershipsOperation is the enum used to get memberships in the Object API. + PNGetMembershipsOperation + // PNGetMembersOperation is the enum used to get members in the Object API. + PNGetMembersOperation + // PNManageMembershipsOperation is the enum used to manage memberships in the Object API. + PNManageMembershipsOperation + // PNManageMembersOperation is the enum used to manage members in the Object API. + PNManageMembersOperation ) const ( @@ -175,6 +271,21 @@ var operations = [...]string{ "Grant", "Revoke", "Delete messages", + "Signal", + "Create User", + "Get Users", + "Fetch User", + "Update User", + "Delete User", + "Get Space", + "Get Spaces", + "Create Space", + "Delete Space", + "Update Space", + "PNGetMembershipsOperation", + "PNGetMembersOperation", + "PNManageMembershipsOperation", + "PNManageMembersOperation", } func (c StatusCategory) String() string { @@ -292,6 +403,37 @@ func (t OperationType) String() string { case PNDeleteMessagesOperation: return "Delete messages" + case PNSignalOperation: + return "Signal" + + case PNCreateUserOperation: + return "Create User" + case PNGetUsersOperation: + return "Get Users" + case PNGetUserOperation: + return "Fetch Users" + case PNUpdateUserOperation: + return "Update User" + case PNDeleteUserOperation: + return "Delete User" + case PNGetSpaceOperation: + return "Get Space" + case PNGetSpacesOperation: + return "Get Spaces" + case PNCreateSpaceOperation: + return "Create Space" + case PNDeleteSpaceOperation: + return "Delete Space" + case PNUpdateSpaceOperation: + return "Update Space" + case PNGetMembershipsOperation: + return "Get Memberships" + case PNGetMembersOperation: + return "Get Members" + case PNManageMembershipsOperation: + return "Manage Memberships" + case PNManageMembersOperation: + return "Manage Members" default: return "No Category Matched" } diff --git a/examples/cli/cli_demo.go b/examples/cli/cli_demo.go index 2e28fd69..16e205db 100644 --- a/examples/cli/cli_demo.go +++ b/examples/cli/cli_demo.go @@ -55,11 +55,15 @@ func connect() { //config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) config.Log = infoLogger config.Log.SetPrefix("PubNub :-> ") - config.PublishKey = "demo" - config.SubscribeKey = "demo" - config.SecretKey = "demo" + config.PublishKey = "pub-c-3ed95c83-12e6-4cda-9d69-c47ba2abb57e" //"demo" //"demo" + config.SubscribeKey = "sub-c-26a73b0a-c3f2-11e9-8b24-569e8a5c3af3" //"demo" //"sub-c-10b61350-bec7-11e9-a375-f698c1d99dce" //"demo" // + //config.SecretKey = //"pam" //"demo" - config.AuthKey = "akey" + //config.PublishKey = "pub-c-cdea0ef1-c571-4b72-b43f-ff1dc8aa4c5d" + //config.SubscribeKey = "sub-c-4757f09c-c3f2-11e9-9d00-8a58a5558306" + //config.SecretKey = "sec-c-YTYxNzVjYzctNDY2MS00N2NmLTg2NjYtNGRlNWY1NjMxMDBm" + + //config.AuthKey = "akey" config.CipherKey = "enigma" pn = pubnub.NewPubNub(config) @@ -105,6 +109,52 @@ func connect() { fmt.Println(fmt.Sprintf("%s %s", outputPrefix, presence)) fmt.Println("") fmt.Println(fmt.Sprintf("%s", outputSuffix)) + case userEvent := <-listener.UserEvent: + fmt.Print(fmt.Sprintf("%s Subscribe Response:", outputPrefix)) + fmt.Println(" --- UserEvent: ") + fmt.Println(fmt.Sprintf("%s %s", outputPrefix, userEvent)) + fmt.Println(fmt.Sprintf("%s userEvent.Channel: %s", outputPrefix, userEvent.Channel)) + fmt.Println(fmt.Sprintf("%s userEvent.SubscribedChannel: %s", outputPrefix, userEvent.SubscribedChannel)) + fmt.Println(fmt.Sprintf("%s userEvent.Event: %s", outputPrefix, userEvent.Event)) + fmt.Println(fmt.Sprintf("%s userEvent.UserID: %s", outputPrefix, userEvent.UserID)) + fmt.Println(fmt.Sprintf("%s userEvent.Description: %s", outputPrefix, userEvent.Description)) + fmt.Println(fmt.Sprintf("%s userEvent.Timestamp: %s", outputPrefix, userEvent.Timestamp)) + fmt.Println(fmt.Sprintf("%s userEvent.Name: %s", outputPrefix, userEvent.Name)) + fmt.Println(fmt.Sprintf("%s userEvent.ExternalID: %s", outputPrefix, userEvent.ExternalID)) + fmt.Println(fmt.Sprintf("%s userEvent.ProfileURL: %s", outputPrefix, userEvent.ProfileURL)) + fmt.Println(fmt.Sprintf("%s userEvent.Email: %s", outputPrefix, userEvent.Email)) + fmt.Println(fmt.Sprintf("%s userEvent.Created: %s", outputPrefix, userEvent.Created)) + fmt.Println(fmt.Sprintf("%s userEvent.Updated: %s", outputPrefix, userEvent.Updated)) + fmt.Println(fmt.Sprintf("%s userEvent.ETag: %s", outputPrefix, userEvent.ETag)) + fmt.Println(fmt.Sprintf("%s userEvent.Custom: %v", outputPrefix, userEvent.Custom)) + + case spaceEvent := <-listener.SpaceEvent: + fmt.Print(fmt.Sprintf("%s Subscribe Response:", outputPrefix)) + fmt.Println(" --- SpaceEvent: ") + fmt.Println(fmt.Sprintf("%s %s", outputPrefix, spaceEvent)) + fmt.Println(fmt.Sprintf("%s spaceEvent.Channel: %s", outputPrefix, spaceEvent.Channel)) + fmt.Println(fmt.Sprintf("%s spaceEvent.SubscribedChannel: %s", outputPrefix, spaceEvent.SubscribedChannel)) + fmt.Println(fmt.Sprintf("%s spaceEvent.Event: %s", outputPrefix, spaceEvent.Event)) + fmt.Println(fmt.Sprintf("%s spaceEvent.SpaceID: %s", outputPrefix, spaceEvent.SpaceID)) + fmt.Println(fmt.Sprintf("%s spaceEvent.Description: %s", outputPrefix, spaceEvent.Description)) + fmt.Println(fmt.Sprintf("%s spaceEvent.Timestamp: %s", outputPrefix, spaceEvent.Timestamp)) + fmt.Println(fmt.Sprintf("%s spaceEvent.Created: %s", outputPrefix, spaceEvent.Created)) + fmt.Println(fmt.Sprintf("%s spaceEvent.Updated: %s", outputPrefix, spaceEvent.Updated)) + fmt.Println(fmt.Sprintf("%s spaceEvent.ETag: %s", outputPrefix, spaceEvent.ETag)) + fmt.Println(fmt.Sprintf("%s spaceEvent.Custom: %v", outputPrefix, spaceEvent.Custom)) + + case membershipEvent := <-listener.MembershipEvent: + fmt.Print(fmt.Sprintf("%s Subscribe Response:", outputPrefix)) + fmt.Println(" --- MembershipEvent: ") + fmt.Println(fmt.Sprintf("%s %s", outputPrefix, membershipEvent)) + fmt.Println(fmt.Sprintf("%s membershipEvent.Channel: %s", outputPrefix, membershipEvent.Channel)) + fmt.Println(fmt.Sprintf("%s membershipEvent.SubscribedChannel: %s", outputPrefix, membershipEvent.SubscribedChannel)) + fmt.Println(fmt.Sprintf("%s membershipEvent.Event: %s", outputPrefix, membershipEvent.Event)) + fmt.Println(fmt.Sprintf("%s membershipEvent.SpaceID: %s", outputPrefix, membershipEvent.SpaceID)) + fmt.Println(fmt.Sprintf("%s membershipEvent.UserID: %s", outputPrefix, membershipEvent.UserID)) + fmt.Println(fmt.Sprintf("%s membershipEvent.Description: %s", outputPrefix, membershipEvent.Description)) + fmt.Println(fmt.Sprintf("%s membershipEvent.Timestamp: %s", outputPrefix, membershipEvent.Timestamp)) + fmt.Println(fmt.Sprintf("%s membershipEvent.Custom: %v", outputPrefix, membershipEvent.Custom)) } } }() @@ -170,6 +220,21 @@ func showHelp() { showPresenceTimeoutHelp() showPresenceHelp() showMessageCountsHelp() + showSignalHelp() + showCreateUserHelp() + showGetUsersHelp() + showEditMembershipsHelp() + showUpdateMembersHelp() + showGetSpaceMembershipsHelp() + showGetMembersHelp() + showGetSpacesHelp() + showUpdateSpaceHelp() + showDeleteSpaceHelp() + showCreateSpaceHelp() + showGetSpaceHelp() + showDeleteUserHelp() + showUpdateUserHelp() + showGetUserHelp() fmt.Println("") fmt.Println("================") fmt.Println(" || COMMANDS ||") @@ -179,10 +244,94 @@ func showHelp() { fmt.Println(" QUIT \n\tctrl+c ") } +func showEditMembershipsHelp() { + fmt.Println(" EditMemberships EXAMPLE: ") + fmt.Println(" updatespacemem spaceid id a/u/r limit count") + fmt.Println(" updatespacemem id0 id1 a 100 true") + +} +func showUpdateMembersHelp() { + fmt.Println(" UpdateMembers EXAMPLE: ") + fmt.Println(" updatemem memebers id a/u/r limit count") + fmt.Println(" updatemem id0 id0 a 100 true") +} + +func showGetSpaceMembershipsHelp() { + fmt.Println(" GetSpaceMemberships EXAMPLE: ") + fmt.Println(" getspacemem spaceid limit count start") + fmt.Println(" getspacemem id0 100 true Mymx") + +} +func showGetMembersHelp() { + fmt.Println(" GetMembers EXAMPLE: ") + fmt.Println(" getmem userid limit count start") + fmt.Println(" getmem id0 100 true Mymx") + +} +func showGetSpacesHelp() { + fmt.Println(" GetSpaces EXAMPLE: ") + fmt.Println(" getspaces limit count start") + fmt.Println(" getspaces 100 true MjWn") + +} +func showUpdateSpaceHelp() { + fmt.Println(" UpdateSpace EXAMPLE: ") + fmt.Println(" updatespace id name desc") + fmt.Println(" updatespace id0 name desc") + +} +func showDeleteSpaceHelp() { + fmt.Println(" DeleteSpace EXAMPLE: ") + fmt.Println(" delspace id") + fmt.Println(" delspace id0") + +} +func showCreateSpaceHelp() { + fmt.Println(" CreateSpace EXAMPLE: ") + fmt.Println(" createspace id name desc") + fmt.Println(" createspace id0 name desc") + +} +func showGetSpaceHelp() { + fmt.Println(" GetSpace EXAMPLE: ") + fmt.Println(" getspace id") + fmt.Println(" getspace id0") + +} +func showDeleteUserHelp() { + fmt.Println(" DeleteUser EXAMPLE: ") + fmt.Println(" deleteuser id") + fmt.Println(" deleteuser id0") +} + +func showUpdateUserHelp() { + fmt.Println(" UpdateUser EXAMPLE: ") + fmt.Println(" updateuser id name extid url email") + fmt.Println(" updateuser id0 name extid purl email") +} + +func showGetUserHelp() { + fmt.Println(" GetUser EXAMPLE: ") + fmt.Println(" getuser id") + fmt.Println(" getuser id0") +} + func showMessageCountsHelp() { fmt.Println(" MessageCounts EXAMPLE: ") - fmt.Println(" messageCounts Channel(s) timetoken1,timetoken2") - fmt.Println(" messageCounts my-channel,my-channel1 15210190573608384,15211140747622125") + fmt.Println(" messagecounts Channel(s) timetoken1,timetoken2") + fmt.Println(" messagecounts my-channel,my-channel1 15210190573608384,15211140747622125") +} + +func showGetUsersHelp() { + fmt.Println(" GetUsers EXAMPLE: ") + fmt.Println(" getusers limit count start") + fmt.Println(" getusers 100 true MjWn") +} + +func showCreateUserHelp() { + fmt.Println(" CreateUser EXAMPLE: ") + fmt.Println(" createuser id name extid url email") + fmt.Println(" createuser id0 name extid purl email") } func showSignalHelp() { @@ -360,10 +509,38 @@ func readCommand(cmd string) { setPresenceTimeout(command[1:]) case "presence": runPresenceRequest(command[1:]) - case "messageCounts": + case "messagecounts": messageCounts(command[1:]) case "signal": signal(command[1:]) + case "createuser": + createUser(command[1:]) + case "getusers": + getUsers(command[1:]) + case "getuser": + getUser(command[1:]) + case "updateuser": + updateUser(command[1:]) + case "deleteuser": + deleteUser(command[1:]) + case "getspaces": + getSpaces(command[1:]) + case "createspace": + createSpace(command[1:]) + case "delspace": + deleteSpace(command[1:]) + case "updatespace": + updateSpace(command[1:]) + case "getspace": + getSpace(command[1:]) + case "getspacemem": + getSpaceMemberships(command[1:]) + case "getmem": + getMembers(command[1:]) + case "updatespacemem": + manageMemberships(command[1:]) + case "updatemem": + manageMembers(command[1:]) case "q": pn.UnsubscribeAll() case "d": @@ -373,6 +550,486 @@ func readCommand(cmd string) { } } +func manageMembers(args []string) { + if len(args) < 5 { + showEditMembershipsHelp() + return + } + spaceID := args[0] + id0 := args[1] + //id1 := args[2] + //id2 := args[3] + action := args[2] + var limit int + + n, err := strconv.ParseInt(args[3], 10, 64) + if err == nil { + limit = int(n) + } + count, _ := strconv.ParseBool(args[4]) + var start string + if len(args) > 5 { + start = args[5] + } + + incl := []pubnub.PNMembersInclude{ + pubnub.PNMembersCustom, + pubnub.PNMembersUser, + pubnub.PNMembersUserCustom, + } + + custom := make(map[string]interface{}) + custom["a1"] = "b1" + custom["c1"] = "d1" + + in := pubnub.PNMembersInput{ + ID: id0, + Custom: custom, + } + + inArr := []pubnub.PNMembersInput{ + in, + } + + custom2 := make(map[string]interface{}) + custom2["a2"] = "b2" + custom2["c2"] = "d2" + + up := pubnub.PNMembersInput{ + ID: id0, + Custom: custom2, + } + + upArr := []pubnub.PNMembersInput{ + up, + } + + re := pubnub.PNMembersRemove{ + ID: id0, + } + + reArr := []pubnub.PNMembersRemove{ + re, + } + + if action == "a" { + reArr = []pubnub.PNMembersRemove{} + upArr = []pubnub.PNMembersInput{} + } else if action == "u" { + reArr = []pubnub.PNMembersRemove{} + inArr = []pubnub.PNMembersInput{} + } else if action == "r" { + upArr = []pubnub.PNMembersInput{} + inArr = []pubnub.PNMembersInput{} + } + + if start != "" { + res, status, err := pn.ManageMembers().SpaceID(spaceID).Add(inArr).Update(upArr).Remove(reArr).Include(incl).Limit(limit).Count(count).Start(start).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + + } else { + res, status, err := pn.ManageMembers().SpaceID(spaceID).Add(inArr).Update(upArr).Remove(reArr).Include(incl).Limit(limit).Count(count).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } +} + +func manageMemberships(args []string) { + if len(args) < 5 { + showUpdateMembersHelp() + return + } + userID := args[0] + id0 := args[1] + //id1 := args[2] + //id2 := args[3] + action := args[2] + var limit int + + n, err := strconv.ParseInt(args[3], 10, 64) + if err == nil { + limit = int(n) + } + count, _ := strconv.ParseBool(args[4]) + var start string + if len(args) > 5 { + start = args[5] + } + + incl := []pubnub.PNMembershipsInclude{ + pubnub.PNMembershipsCustom, + pubnub.PNMembershipsSpace, + pubnub.PNMembershipsSpaceCustom, + } + + custom3 := make(map[string]interface{}) + custom3["a3"] = "b3" + custom3["c3"] = "d3" + + in := pubnub.PNMembershipsInput{ + ID: id0, + Custom: custom3, + } + + inArr := []pubnub.PNMembershipsInput{ + in, + } + + custom4 := make(map[string]interface{}) + custom4["a4"] = "b4" + custom4["c4"] = "d4" + + up := pubnub.PNMembershipsInput{ + ID: id0, + Custom: custom4, + } + + upArr := []pubnub.PNMembershipsInput{ + up, + } + + re := pubnub.PNMembershipsRemove{ + ID: id0, + } + + reArr := []pubnub.PNMembershipsRemove{ + re, + } + + if action == "a" { + reArr = []pubnub.PNMembershipsRemove{} + upArr = []pubnub.PNMembershipsInput{} + } else if action == "u" { + reArr = []pubnub.PNMembershipsRemove{} + inArr = []pubnub.PNMembershipsInput{} + } else if action == "r" { + upArr = []pubnub.PNMembershipsInput{} + inArr = []pubnub.PNMembershipsInput{} + } + + if start != "" { + res, status, err := pn.ManageMemberships().Add(inArr).Update(upArr).Remove(reArr).Include(incl).Limit(limit).Count(count).Start(start).Execute() + //res, status, err := pn.UpdateMembers().UserID(userID).Add(inArr).Include(incl).Limit(limit).Count(count).Start(start).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + + } else { + + res, status, err := pn.ManageMemberships().UserID(userID).Add(inArr).Update(upArr).Remove(reArr).Include(incl).Limit(limit).Count(count).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } +} + +func getSpaceMemberships(args []string) { + if len(args) < 3 { + showGetSpaceMembershipsHelp() + return + } + id := args[0] + + var limit int + + n, err := strconv.ParseInt(args[1], 10, 64) + if err == nil { + limit = int(n) + } + count, _ := strconv.ParseBool(args[2]) + var start string + if len(args) > 3 { + start = args[3] + } + + incl := []pubnub.PNMembershipsInclude{ + pubnub.PNMembershipsCustom, + pubnub.PNMembershipsSpace, + pubnub.PNMembershipsSpaceCustom, + } + if start != "" { + res, status, err := pn.GetMemberships().UserID(id).Include(incl).Limit(limit).Count(count).Start(start).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } else { + res, status, err := pn.GetMemberships().UserID(id).Include(incl).Limit(limit).Count(count).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } +} + +func getMembers(args []string) { + if len(args) < 3 { + showGetMembersHelp() + return + } + id := args[0] + + var limit int + + n, err := strconv.ParseInt(args[1], 10, 64) + if err == nil { + limit = int(n) + } + count, _ := strconv.ParseBool(args[2]) + var start string + if len(args) > 3 { + start = args[3] + } + + incl := []pubnub.PNMembersInclude{ + pubnub.PNMembersCustom, + pubnub.PNMembersUser, + pubnub.PNMembersUserCustom, + } + if start != "" { + res, status, err := pn.GetMembers().SpaceID(id).Include(incl).Limit(limit).Count(count).Start(start).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } else { + res, status, err := pn.GetMembers().SpaceID(id).Include(incl).Limit(limit).Count(count).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } +} + +func getSpaces(args []string) { + if len(args) < 2 { + showGetSpacesHelp() + return + } + var limit int + + n, err := strconv.ParseInt(args[0], 10, 64) + if err == nil { + limit = int(n) + } + count, _ := strconv.ParseBool(args[1]) + var start string + if len(args) > 2 { + start = args[2] + } + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + if start != "" { + res, status, err := pn.GetSpaces().Include(incl).Limit(limit).Count(count).Start(start).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } else { + res, status, err := pn.GetSpaces().Include(incl).Limit(limit).Count(count).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } +} + +func updateSpace(args []string) { + if len(args) < 2 { + showUpdateSpaceHelp() + return + } + id := args[0] + name := args[1] + desc := args[2] + + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, status, err := pn.UpdateSpace().ID(id).Name(name).Description(desc).Include(incl).Custom(custom).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + +func deleteSpace(args []string) { + if len(args) < 1 { + showDeleteSpaceHelp() + return + } + id := args[0] + + res, status, err := pn.DeleteSpace().ID(id).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + +func createSpace(args []string) { + if len(args) < 3 { + showCreateSpaceHelp() + return + } + id := args[0] + name := args[1] + desc := args[2] + + custom := make(map[string]interface{}) + custom["a"] = "b" + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + pubnub.PNUserSpaceCustom, + } + + //res, status, err := pn.CreateSpace().ID("id0").Name("name").Description("desc").Include([]string{"custom"}).Custom(custom).Execute() + res, status, err := pn.CreateSpace().ID(id).Name(name).Description(desc).Include(incl).Custom(custom).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + +func getSpace(args []string) { + if len(args) < 1 { + showGetSpaceHelp() + return + } + id := args[0] + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, status, err := pn.GetSpace().ID(id).Include(incl).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + +func deleteUser(args []string) { + if len(args) < 1 { + showDeleteUserHelp() + return + } + id := args[0] + + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + res, status, err := pn.DeleteUser().ID(id).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + +func updateUser(args []string) { + if len(args) < 5 { + showUpdateUserHelp() + return + } + id := args[0] + name := args[1] + extid := args[2] + purl := args[3] + email := args[4] + + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, status, err := pn.UpdateUser().Include(incl).ID(id).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + +func getUser(args []string) { + if len(args) < 1 { + showGetUserHelp() + return + } + id := args[0] + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, status, err := pn.GetUser().Include(incl).ID(id).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + +func getUsers(args []string) { + if len(args) < 2 { + showGetUsersHelp() + return + } + var limit int + + n, err := strconv.ParseInt(args[0], 10, 64) + if err == nil { + limit = int(n) + } + count, _ := strconv.ParseBool(args[1]) + var start string + if len(args) > 2 { + start = args[2] + } + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + if start != "" { + res, status, err := pn.GetUsers().Include(incl).Start(start).Limit(limit).Count(count).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + + } else { + res, status, err := pn.GetUsers().Include(incl).Limit(limit).Count(count).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) + } + +} + +func createUser(args []string) { + if len(args) < 5 { + showCreateUserHelp() + return + } + id := args[0] + name := args[1] + extid := args[2] + purl := args[3] + email := args[4] + + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, status, err := pn.CreateUser().Include(incl).ID(id).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() + fmt.Println("status", status) + fmt.Println("err", err) + fmt.Println("res", res) +} + func signal(args []string) { if len(args) < 2 { showSignalHelp() @@ -385,9 +1042,9 @@ func signal(args []string) { message := args[1] res, status, err := pn.Signal().Channel(channel).Message(message).Execute() - fmt.Println(status) + fmt.Println("status", status) fmt.Println(err) - fmt.Println(res) + fmt.Println("res", res) } diff --git a/grant_request.go b/grant_request.go index 2fa09a6c..20fb43ca 100644 --- a/grant_request.go +++ b/grant_request.go @@ -17,16 +17,19 @@ import ( // PNGrantType grant types type PNGrantType int -const grantPath = "/v1/auth/grant/sub-key/%s" +const grantPath = "/v2/auth/grant/sub-key/%s" +const grantV3Path = "/v3/auth/grant/sub-key/%s" const ( - // PNReadEnabled Read Enabled + // PNReadEnabled Read Enabled. Applies to Subscribe, History, Presence, Objects PNReadEnabled PNGrantType = 1 + iota - // PNWriteEnabled Write Enabled + // PNWriteEnabled Write Enabled. Applies to Publish, Objects PNWriteEnabled - // PNManageEnabled Manage Enabled + // PNManageEnabled Manage Enabled. Applies to Channel-Groups, Objects PNManageEnabled - // PNDeleteEnabled Delete Enabled + // PNDeleteEnabled Delete Enabled. Applies to History, Objects PNDeleteEnabled + // PNCreateEnabled Create Enabled. Applies to Objects + PNCreateEnabled ) var emptyGrantResponse *GrantResponse @@ -35,6 +38,15 @@ type grantBuilder struct { opts *grantOpts } +type patternPermissions struct { +} +type patterns struct { + ChannelsPattern string + ChannelGroupsPattern string + SpacesPattern string + UsersPattern string +} + func newGrantBuilder(pubnub *PubNub) *grantBuilder { builder := grantBuilder{ opts: &grantOpts{ @@ -80,6 +92,12 @@ func (b *grantBuilder) Delete(del bool) *grantBuilder { return b } +func (b *grantBuilder) Create(create bool) *grantBuilder { + b.opts.Create = create + + return b +} + // TTL in minutes for which granted permissions are valid. // // Min: 1 @@ -115,6 +133,34 @@ func (b *grantBuilder) ChannelGroups(groups []string) *grantBuilder { return b } +// Users sets the Users for the Grant request. +func (b *grantBuilder) Users(users []string) *grantBuilder { + b.opts.Users = users + + return b +} + +// Patterns sets the Patterns for the Grant request. +func (b *grantBuilder) Patterns(pattern string, resourceTypes patterns) *grantBuilder { + // b.opts.Patterns = patterns + + return b +} + +// Spaces sets the Spaces for the Grant request. +func (b *grantBuilder) Spaces(spaces []string) *grantBuilder { + b.opts.Spaces = spaces + + return b +} + +// Meta sets the Meta for the Grant request. +func (b *grantBuilder) Meta(meta map[string]interface{}) *grantBuilder { + b.opts.Meta = meta + + 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 *grantBuilder) QueryParam(queryParam map[string]string) *grantBuilder { b.opts.QueryParam = queryParam @@ -140,6 +186,9 @@ type grantOpts struct { Channels []string ChannelGroups []string QueryParam map[string]string + Meta map[string]interface{} + Spaces []string + Users []string // Stringified permissions // Setting 'true' or 'false' will apply permissions to level @@ -147,6 +196,7 @@ type grantOpts struct { Write bool Manage bool Delete bool + Create bool // Max: 525600 // Min: 1 // Default: 1440 diff --git a/grant_request_test.go b/grant_request_test.go index 153004e6..4adf2d41 100644 --- a/grant_request_test.go +++ b/grant_request_test.go @@ -31,7 +31,7 @@ func TestGrantRequestBasic(t *testing.T) { } h.AssertPathsEqual(t, - fmt.Sprintf("/v1/auth/grant/sub-key/%s", opts.pubnub.Config.SubscribeKey), + fmt.Sprintf("/v2/auth/grant/sub-key/%s", opts.pubnub.Config.SubscribeKey), u.EscapedPath(), []int{}) query, err := opts.buildQuery() @@ -121,7 +121,7 @@ func TestNewGrantBuilder(t *testing.T) { } h.AssertPathsEqual(t, - fmt.Sprintf("/v1/auth/grant/sub-key/%s", o.opts.pubnub.Config.SubscribeKey), + fmt.Sprintf("/v2/auth/grant/sub-key/%s", o.opts.pubnub.Config.SubscribeKey), u.EscapedPath(), []int{}) query, err := o.opts.buildQuery() @@ -164,7 +164,7 @@ func TestNewGrantBuilderDelFalse(t *testing.T) { } h.AssertPathsEqual(t, - fmt.Sprintf("/v1/auth/grant/sub-key/%s", o.opts.pubnub.Config.SubscribeKey), + fmt.Sprintf("/v2/auth/grant/sub-key/%s", o.opts.pubnub.Config.SubscribeKey), u.EscapedPath(), []int{}) query, err := o.opts.buildQuery() @@ -211,7 +211,7 @@ func TestNewGrantBuilderContext(t *testing.T) { } h.AssertPathsEqual(t, - fmt.Sprintf("/v1/auth/grant/sub-key/%s", o.opts.pubnub.Config.SubscribeKey), + fmt.Sprintf("/v2/auth/grant/sub-key/%s", o.opts.pubnub.Config.SubscribeKey), u.EscapedPath(), []int{}) query, err := o.opts.buildQuery() diff --git a/history_request_test.go b/history_request_test.go index 43d74265..23bc3751 100644 --- a/history_request_test.go +++ b/history_request_test.go @@ -313,7 +313,7 @@ func TestHistoryPNOtherYay(t *testing.T) { break default: - fmt.Println(reflect.TypeOf(v).Kind(), reflect.TypeOf(data).Kind(), v, data) + //fmt.Println(reflect.TypeOf(v).Kind(), reflect.TypeOf(data).Kind(), v, data) assert.Fail("failed") break diff --git a/listener_manager.go b/listener_manager.go index 38b0f981..0523a421 100644 --- a/listener_manager.go +++ b/listener_manager.go @@ -6,18 +6,24 @@ import ( // type Listener struct { - Status chan *PNStatus - Message chan *PNMessage - Presence chan *PNPresence - Signal chan *PNMessage + Status chan *PNStatus + Message chan *PNMessage + Presence chan *PNPresence + Signal chan *PNMessage + UserEvent chan *PNUserEvent + SpaceEvent chan *PNSpaceEvent + MembershipEvent chan *PNMembershipEvent } func NewListener() *Listener { return &Listener{ - Status: make(chan *PNStatus), - Message: make(chan *PNMessage), - Presence: make(chan *PNPresence), - Signal: make(chan *PNMessage), + Status: make(chan *PNStatus), + Message: make(chan *PNMessage), + Presence: make(chan *PNPresence), + Signal: make(chan *PNMessage), + UserEvent: make(chan *PNUserEvent), + SpaceEvent: make(chan *PNSpaceEvent), + MembershipEvent: make(chan *PNMembershipEvent), } } @@ -112,6 +118,61 @@ func (m *ListenerManager) announceSignal(message *PNMessage) { }() } +func (m *ListenerManager) announceUserEvent(message *PNUserEvent) { + go func() { + m.RLock() + AnnounceUserEventLabel: + for l := range m.listeners { + select { + case <-m.exitListener: + m.pubnub.Config.Log.Println("announceUserEvent exitListener") + break AnnounceUserEventLabel + + case l.UserEvent <- message: + m.pubnub.Config.Log.Println("l.UserEvent", message) + } + } + m.RUnlock() + }() +} + +func (m *ListenerManager) announceSpaceEvent(message *PNSpaceEvent) { + go func() { + m.RLock() + AnnounceSpaceEventLabel: + for l := range m.listeners { + m.pubnub.Config.Log.Println("l.SpaceEvent", l) + select { + case <-m.exitListener: + m.pubnub.Config.Log.Println("announceSpaceEvent exitListener") + break AnnounceSpaceEventLabel + + case l.SpaceEvent <- message: + m.pubnub.Config.Log.Println("l.SpaceEvent", message) + } + } + m.RUnlock() + }() +} + +func (m *ListenerManager) announceMembershipEvent(message *PNMembershipEvent) { + go func() { + m.RLock() + AnnounceMembershipEvent: + for l := range m.listeners { + select { + case <-m.exitListener: + m.pubnub.Config.Log.Println("announceMembershipEvent exitListener") + break AnnounceMembershipEvent + + case l.MembershipEvent <- message: + m.pubnub.Config.Log.Println("l.MembershipEvent", message) + } + } + m.RUnlock() + }() +} + func (m *ListenerManager) announcePresence(presence *PNPresence) { go func() { m.RLock() @@ -129,7 +190,7 @@ func (m *ListenerManager) announcePresence(presence *PNPresence) { }() } -// +// PNStatus is the status struct type PNStatus struct { Category StatusCategory Operation OperationType @@ -145,6 +206,7 @@ type PNStatus struct { AffectedChannelGroups []string } +// PNMessage is the Message Response for Subscribe type PNMessage struct { Message interface{} UserMetadata interface{} @@ -156,6 +218,7 @@ type PNMessage struct { Timetoken int64 } +// PNPresence is the Message Response for Presence type PNPresence struct { Event string UUID string @@ -173,3 +236,54 @@ type PNPresence struct { Timeout []string HereNowRefresh bool } + +// PNUserEvent is the Response for an User Event +type PNUserEvent struct { + Event PNObjectsEvent + UserID string + Description string + Timestamp string + Name string + ExternalID string + ProfileURL string + Email string + Created string + Updated string + ETag string + Custom map[string]interface{} + SubscribedChannel string + ActualChannel string + Channel string + Subscription string +} + +// PNSpaceEvent is the Response for a Space Event +type PNSpaceEvent struct { + Event PNObjectsEvent + SpaceID string + Description string + Timestamp string + Name string + Created string + Updated string + ETag string + Custom map[string]interface{} + SubscribedChannel string + ActualChannel string + Channel string + Subscription string +} + +// PNMembershipEvent is the Response for a Membership Event +type PNMembershipEvent struct { + Event PNObjectsEvent + UserID string + SpaceID string + Description string + Timestamp string + Custom map[string]interface{} + SubscribedChannel string + ActualChannel string + Channel string + Subscription string +} diff --git a/objects_common.go b/objects_common.go new file mode 100644 index 00000000..1cb761ef --- /dev/null +++ b/objects_common.go @@ -0,0 +1,86 @@ +package pubnub + +// PNUser is the Objects API user struct +type PNUser struct { + ID string `json:"id"` + Name string `json:"name"` + ExternalID string `json:"externalId"` + ProfileURL string `json:"profileUrl"` + Email string `json:"email"` + Created string `json:"created"` + Updated string `json:"updated"` + ETag string `json:"eTag"` + Custom map[string]interface{} `json:"custom"` +} + +// PNSpace is the Objects API space struct +type PNSpace struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Created string `json:"created"` + Updated string `json:"updated"` + ETag string `json:"eTag"` + Custom map[string]interface{} `json:"custom"` +} + +// PNMembers is the Objects API Members struct +type PNMembers struct { + ID string `json:"id"` + User PNUser `json:"user"` + Created string `json:"created"` + Updated string `json:"updated"` + ETag string `json:"eTag"` + Custom map[string]interface{} `json:"custom"` +} + +// PNMemberships is the Objects API Memberships struct +type PNMemberships struct { + ID string `json:"id"` + Space PNSpace `json:"space"` + Created string `json:"created"` + Updated string `json:"updated"` + ETag string `json:"eTag"` + Custom map[string]interface{} `json:"custom"` +} + +// PNMembersInput is the Objects API Members input struct used to add members +type PNMembersInput struct { + ID string `json:"id"` + Custom map[string]interface{} `json:"custom"` +} + +// PNMembersRemove is the Objects API Members struct used to remove members +type PNMembersRemove struct { + ID string `json:"id"` +} + +// PNMembershipsInput is the Objects API Memberships input struct used to add members +type PNMembershipsInput struct { + ID string `json:"id"` + Custom map[string]interface{} `json:"custom"` +} + +// PNMembershipsRemove is the Objects API Memberships struct used to remove members +type PNMembershipsRemove struct { + ID string `json:"id"` +} + +// PNObjectsResponse is the Objects API collective Response struct of all methods. +type PNObjectsResponse struct { + Event PNObjectsEvent `json:"event"` // enum value + EventType PNObjectsEventType `json:"type"` // enum value + Name string `json:"name"` + UserID string `json:"userId"` // the user id if user related + SpaceID string `json:"spaceId"` // the space id if space related + Description string `json:"description"` // the description of what happened + Timestamp string `json:"timestamp"` // the timetoken of the event + ExternalID string `json:"externalId"` + ProfileURL string `json:"profileUrl"` + Email string `json:"email"` + Created string `json:"created"` + Updated string `json:"updated"` + ETag string `json:"eTag"` + Custom map[string]interface{} `json:"custom"` + Data map[string]interface{} `json:"data"` +} diff --git a/objects_create_space.go b/objects_create_space.go new file mode 100644 index 00000000..ada8b953 --- /dev/null +++ b/objects_create_space.go @@ -0,0 +1,226 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyPNCreateSpaceResponse *PNCreateSpaceResponse + +const createSpacePath = "/v1/objects/%s/spaces" + +type createSpaceBuilder struct { + opts *createSpaceOpts +} + +func newCreateSpaceBuilder(pubnub *PubNub) *createSpaceBuilder { + builder := createSpaceBuilder{ + opts: &createSpaceOpts{ + pubnub: pubnub, + }, + } + + return &builder +} + +func newCreateSpaceBuilderWithContext(pubnub *PubNub, + context Context) *createSpaceBuilder { + builder := createSpaceBuilder{ + opts: &createSpaceOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +type createSpaceBody struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Custom map[string]interface{} `json:"custom"` +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *createSpaceBuilder) Include(include []PNUserSpaceInclude) *createSpaceBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *createSpaceBuilder) ID(id string) *createSpaceBuilder { + b.opts.ID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *createSpaceBuilder) Name(name string) *createSpaceBuilder { + b.opts.Name = name + + return b +} + +func (b *createSpaceBuilder) Description(description string) *createSpaceBuilder { + b.opts.Description = description + + return b +} + +func (b *createSpaceBuilder) Custom(custom map[string]interface{}) *createSpaceBuilder { + b.opts.Custom = custom + + 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 *createSpaceBuilder) QueryParam(queryParam map[string]string) *createSpaceBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the createSpace request. +func (b *createSpaceBuilder) Transport(tr http.RoundTripper) *createSpaceBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the createSpace request. +func (b *createSpaceBuilder) Execute() (*PNCreateSpaceResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNCreateSpaceResponse, status, err + } + + return newPNCreateSpaceResponse(rawJSON, b.opts, status) +} + +type createSpaceOpts struct { + pubnub *PubNub + + Include []string + ID string + Name string + Description string + Custom map[string]interface{} + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *createSpaceOpts) config() Config { + return *o.pubnub.Config +} + +func (o *createSpaceOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *createSpaceOpts) context() Context { + return o.ctx +} + +func (o *createSpaceOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *createSpaceOpts) buildPath() (string, error) { + return fmt.Sprintf(createSpacePath, + o.pubnub.Config.SubscribeKey), nil +} + +func (o *createSpaceOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *createSpaceOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *createSpaceOpts) buildBody() ([]byte, error) { + b := &createSpaceBody{ + ID: o.ID, + Name: o.Name, + Description: o.Description, + Custom: o.Custom, + } + + jsonEncBytes, errEnc := json.Marshal(b) + + if errEnc != nil { + o.pubnub.Config.Log.Printf("ERROR: Serialization error: %s\n", errEnc.Error()) + return []byte{}, errEnc + } + return jsonEncBytes, nil + +} + +func (o *createSpaceOpts) httpMethod() string { + return "POST" +} + +func (o *createSpaceOpts) isAuthRequired() bool { + return true +} + +func (o *createSpaceOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *createSpaceOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *createSpaceOpts) operationType() OperationType { + return PNCreateSpaceOperation +} + +func (o *createSpaceOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNCreateSpaceResponse is the Objects API Response for create space +type PNCreateSpaceResponse struct { + Status int `json:"status"` + Data PNSpace `json:"data"` +} + +func newPNCreateSpaceResponse(jsonBytes []byte, o *createSpaceOpts, + status StatusResponse) (*PNCreateSpaceResponse, StatusResponse, error) { + + resp := &PNCreateSpaceResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNCreateSpaceResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_create_space_test.go b/objects_create_space_test.go new file mode 100644 index 00000000..deb67d74 --- /dev/null +++ b/objects_create_space_test.go @@ -0,0 +1,108 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertCreateSpace(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newCreateSpaceBuilder(pn) + if testContext { + o = newCreateSpaceBuilderWithContext(pn, backgroundContext) + } + + o.Include(incl) + o.ID("id0") + o.Name("name") + o.Description("exturl") + o.Custom(custom) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/spaces", pn.Config.SubscribeKey), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + + expectedBody := "{\"id\":\"id0\",\"name\":\"name\",\"description\":\"exturl\",\"custom\":{\"a\":\"b\",\"c\":\"d\"}}" + + assert.Equal(expectedBody, string(body)) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + } + +} + +func TestCreateSpace(t *testing.T) { + AssertCreateSpace(t, true, false) +} + +func TestCreateSpaceContext(t *testing.T) { + AssertCreateSpace(t, true, true) +} + +func TestCreateSpaceResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &createSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNCreateSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":{"id":"id2","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-19T15:05:51.493894Z","updated":"2019-08-19T15:05:51.493894Z","eTag":"Aee9zsKNndXlHw"}} +func TestCreateSpaceResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &createSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":{"id":"id2","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-19T15:05:51.493894Z","updated":"2019-08-19T15:05:51.493894Z","eTag":"Aee9zsKNndXlHw"}}`) + + r, _, err := newPNCreateSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal("id2", r.Data.ID) + assert.Equal("name", r.Data.Name) + assert.Equal("desc", r.Data.Description) + assert.Equal("2019-08-19T15:05:51.493894Z", r.Data.Created) + assert.Equal("2019-08-19T15:05:51.493894Z", r.Data.Updated) + assert.Equal("Aee9zsKNndXlHw", r.Data.ETag) + assert.Equal("b", r.Data.Custom["a"]) + + assert.Nil(err) +} diff --git a/objects_create_user.go b/objects_create_user.go new file mode 100644 index 00000000..0db783d5 --- /dev/null +++ b/objects_create_user.go @@ -0,0 +1,245 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyPNCreateUserResponse *PNCreateUserResponse + +const createUserPath = "/v1/objects/%s/users" + +type createUserBuilder struct { + opts *createUserOpts +} + +func newCreateUserBuilder(pubnub *PubNub) *createUserBuilder { + builder := createUserBuilder{ + opts: &createUserOpts{ + pubnub: pubnub, + }, + } + + return &builder +} + +func newCreateUserBuilderWithContext(pubnub *PubNub, + context Context) *createUserBuilder { + builder := createUserBuilder{ + opts: &createUserOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +type createUserBody struct { + ID string `json:"id"` + Name string `json:"name"` + ExternalID string `json:"externalId"` + ProfileURL string `json:"profileUrl"` + Email string `json:"email"` + Custom map[string]interface{} `json:"custom"` +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *createUserBuilder) Include(include []PNUserSpaceInclude) *createUserBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *createUserBuilder) ID(id string) *createUserBuilder { + b.opts.ID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *createUserBuilder) Name(name string) *createUserBuilder { + b.opts.Name = name + + return b +} + +func (b *createUserBuilder) ExternalID(externalID string) *createUserBuilder { + b.opts.ExternalID = externalID + + return b +} + +func (b *createUserBuilder) ProfileURL(profileURL string) *createUserBuilder { + b.opts.ProfileURL = profileURL + + return b +} + +func (b *createUserBuilder) Email(email string) *createUserBuilder { + b.opts.Email = email + + return b +} + +func (b *createUserBuilder) Custom(custom map[string]interface{}) *createUserBuilder { + b.opts.Custom = custom + + 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 *createUserBuilder) QueryParam(queryParam map[string]string) *createUserBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the createUser request. +func (b *createUserBuilder) Transport(tr http.RoundTripper) *createUserBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the createUser request. +func (b *createUserBuilder) Execute() (*PNCreateUserResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNCreateUserResponse, status, err + } + + return newPNCreateUserResponse(rawJSON, b.opts, status) +} + +type createUserOpts struct { + pubnub *PubNub + + Include []string + ID string + Name string + ExternalID string + ProfileURL string + Email string + Custom map[string]interface{} + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *createUserOpts) config() Config { + return *o.pubnub.Config +} + +func (o *createUserOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *createUserOpts) context() Context { + return o.ctx +} + +func (o *createUserOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *createUserOpts) buildPath() (string, error) { + return fmt.Sprintf(createUserPath, + o.pubnub.Config.SubscribeKey), nil +} + +func (o *createUserOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *createUserOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *createUserOpts) buildBody() ([]byte, error) { + b := &createUserBody{ + ID: o.ID, + Name: o.Name, + ExternalID: o.ExternalID, + ProfileURL: o.ProfileURL, + Email: o.Email, + Custom: o.Custom, + } + + jsonEncBytes, errEnc := json.Marshal(b) + + if errEnc != nil { + o.pubnub.Config.Log.Printf("ERROR: Serialization error: %s\n", errEnc.Error()) + return []byte{}, errEnc + } + return jsonEncBytes, nil + +} + +func (o *createUserOpts) httpMethod() string { + return "POST" +} + +func (o *createUserOpts) isAuthRequired() bool { + return true +} + +func (o *createUserOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *createUserOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *createUserOpts) operationType() OperationType { + return PNCreateUserOperation +} + +func (o *createUserOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNCreateUserResponse is the Objects API Response for create user +type PNCreateUserResponse struct { + Status int `json:"status"` + Data PNUser `json:"data"` +} + +func newPNCreateUserResponse(jsonBytes []byte, o *createUserOpts, + status StatusResponse) (*PNCreateUserResponse, StatusResponse, error) { + + resp := &PNCreateUserResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNCreateUserResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_create_user_test.go b/objects_create_user_test.go new file mode 100644 index 00000000..a7b226fc --- /dev/null +++ b/objects_create_user_test.go @@ -0,0 +1,113 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertCreateUser(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newCreateUserBuilder(pn) + if testContext { + o = newCreateUserBuilderWithContext(pn, backgroundContext) + } + + o.Include(incl) + o.ID("id0") + o.Name("name") + o.ExternalID("exturl") + o.ProfileURL("prourl") + o.Email("email") + o.Custom(custom) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/users", pn.Config.SubscribeKey), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + + expectedBody := "{\"id\":\"id0\",\"name\":\"name\",\"externalId\":\"exturl\",\"profileUrl\":\"prourl\",\"email\":\"email\",\"custom\":{\"a\":\"b\",\"c\":\"d\"}}" + + assert.Equal(expectedBody, string(body)) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + } + +} + +func TestCreateUser(t *testing.T) { + AssertCreateUser(t, true, false) +} + +func TestCreateUserContext(t *testing.T) { + AssertCreateUser(t, true, true) +} + +func TestCreateUserResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &createUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNCreateUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":{"id":"id2","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-19T14:44:54.837392Z","updated":"2019-08-19T14:44:54.837392Z","eTag":"AbyT4v2p6K7fpQE"}} +func TestCreateUserResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &createUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":{"id":"id2","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-19T14:44:54.837392Z","updated":"2019-08-19T14:44:54.837392Z","eTag":"AbyT4v2p6K7fpQE"}}`) + + r, _, err := newPNCreateUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal("id2", r.Data.ID) + assert.Equal("name", r.Data.Name) + assert.Equal("extid", r.Data.ExternalID) + assert.Equal("purl", r.Data.ProfileURL) + assert.Equal("email", r.Data.Email) + assert.Equal("2019-08-19T14:44:54.837392Z", r.Data.Created) + assert.Equal("2019-08-19T14:44:54.837392Z", r.Data.Updated) + assert.Equal("AbyT4v2p6K7fpQE", r.Data.ETag) + assert.Equal("b", r.Data.Custom["a"]) + assert.Equal("d", r.Data.Custom["c"]) + + assert.Nil(err) +} diff --git a/objects_delete_space.go b/objects_delete_space.go new file mode 100644 index 00000000..32badc4e --- /dev/null +++ b/objects_delete_space.go @@ -0,0 +1,169 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pubnub/go/pnerr" +) + +var emptyPNDeleteSpaceResponse *PNDeleteSpaceResponse + +const deleteSpacePath = "/v1/objects/%s/spaces/%s" + +type deleteSpaceBuilder struct { + opts *deleteSpaceOpts +} + +func newDeleteSpaceBuilder(pubnub *PubNub) *deleteSpaceBuilder { + builder := deleteSpaceBuilder{ + opts: &deleteSpaceOpts{ + pubnub: pubnub, + }, + } + + return &builder +} + +func newDeleteSpaceBuilderWithContext(pubnub *PubNub, + context Context) *deleteSpaceBuilder { + builder := deleteSpaceBuilder{ + opts: &deleteSpaceOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +func (b *deleteSpaceBuilder) ID(id string) *deleteSpaceBuilder { + b.opts.ID = id + + 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 *deleteSpaceBuilder) QueryParam(queryParam map[string]string) *deleteSpaceBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the deleteSpace request. +func (b *deleteSpaceBuilder) Transport(tr http.RoundTripper) *deleteSpaceBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the deleteSpace request. +func (b *deleteSpaceBuilder) Execute() (*PNDeleteSpaceResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNDeleteSpaceResponse, status, err + } + + return newPNDeleteSpaceResponse(rawJSON, b.opts, status) +} + +type deleteSpaceOpts struct { + pubnub *PubNub + ID string + QueryParam map[string]string + Transport http.RoundTripper + + ctx Context +} + +func (o *deleteSpaceOpts) config() Config { + return *o.pubnub.Config +} + +func (o *deleteSpaceOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *deleteSpaceOpts) context() Context { + return o.ctx +} + +func (o *deleteSpaceOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *deleteSpaceOpts) buildPath() (string, error) { + return fmt.Sprintf(deleteSpacePath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *deleteSpaceOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *deleteSpaceOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *deleteSpaceOpts) buildBody() ([]byte, error) { + return []byte{}, nil + +} + +func (o *deleteSpaceOpts) httpMethod() string { + return "DELETE" +} + +func (o *deleteSpaceOpts) isAuthRequired() bool { + return true +} + +func (o *deleteSpaceOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *deleteSpaceOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *deleteSpaceOpts) operationType() OperationType { + return PNDeleteSpaceOperation +} + +func (o *deleteSpaceOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNDeleteSpaceResponse is the Objects API Response for delete space +type PNDeleteSpaceResponse struct { + Status int `json:"status"` + Data interface{} `json:"data"` +} + +func newPNDeleteSpaceResponse(jsonBytes []byte, o *deleteSpaceOpts, + status StatusResponse) (*PNDeleteSpaceResponse, StatusResponse, error) { + + resp := &PNDeleteSpaceResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNDeleteSpaceResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_delete_space_test.go b/objects_delete_space_test.go new file mode 100644 index 00000000..976aebfa --- /dev/null +++ b/objects_delete_space_test.go @@ -0,0 +1,85 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/stretchr/testify/assert" +) + +func AssertDeleteSpace(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + o := newDeleteSpaceBuilder(pn) + if testContext { + o = newDeleteSpaceBuilderWithContext(pn, backgroundContext) + } + + o.ID("id0") + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/spaces/%s", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + } + +} + +func TestDeleteSpace(t *testing.T) { + AssertDeleteSpace(t, true, false) +} + +func TestDeleteSpaceContext(t *testing.T) { + AssertDeleteSpace(t, true, true) +} + +func TestDeleteSpaceResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &deleteSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNDeleteSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":null} +func TestDeleteSpaceResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &deleteSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":null}`) + + r, _, err := newPNDeleteSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(nil, r.Data) + + assert.Nil(err) +} diff --git a/objects_delete_user.go b/objects_delete_user.go new file mode 100644 index 00000000..ed3b2c51 --- /dev/null +++ b/objects_delete_user.go @@ -0,0 +1,170 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pubnub/go/pnerr" +) + +var emptyPNDeleteUserResponse *PNDeleteUserResponse + +const deleteUserPath = "/v1/objects/%s/users/%s" + +type deleteUserBuilder struct { + opts *deleteUserOpts +} + +func newDeleteUserBuilder(pubnub *PubNub) *deleteUserBuilder { + builder := deleteUserBuilder{ + opts: &deleteUserOpts{ + pubnub: pubnub, + }, + } + + return &builder +} + +func newDeleteUserBuilderWithContext(pubnub *PubNub, + context Context) *deleteUserBuilder { + builder := deleteUserBuilder{ + opts: &deleteUserOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +func (b *deleteUserBuilder) ID(id string) *deleteUserBuilder { + b.opts.ID = id + + 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 *deleteUserBuilder) QueryParam(queryParam map[string]string) *deleteUserBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the deleteUser request. +func (b *deleteUserBuilder) Transport(tr http.RoundTripper) *deleteUserBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the deleteUser request. +func (b *deleteUserBuilder) Execute() (*PNDeleteUserResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNDeleteUserResponse, status, err + } + + return newPNDeleteUserResponse(rawJSON, b.opts, status) +} + +type deleteUserOpts struct { + pubnub *PubNub + ID string + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *deleteUserOpts) config() Config { + return *o.pubnub.Config +} + +func (o *deleteUserOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *deleteUserOpts) context() Context { + return o.ctx +} + +func (o *deleteUserOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *deleteUserOpts) buildPath() (string, error) { + return fmt.Sprintf(deleteUserPath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *deleteUserOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *deleteUserOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *deleteUserOpts) buildBody() ([]byte, error) { + return []byte{}, nil + +} + +func (o *deleteUserOpts) httpMethod() string { + return "DELETE" +} + +func (o *deleteUserOpts) isAuthRequired() bool { + return true +} + +func (o *deleteUserOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *deleteUserOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *deleteUserOpts) operationType() OperationType { + return PNDeleteUserOperation +} + +func (o *deleteUserOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNDeleteUserResponse is the Objects API Response for delete user +type PNDeleteUserResponse struct { + Status int `json:"status"` + Data interface{} `json:"data"` +} + +func newPNDeleteUserResponse(jsonBytes []byte, o *deleteUserOpts, + status StatusResponse) (*PNDeleteUserResponse, StatusResponse, error) { + + resp := &PNDeleteUserResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNDeleteUserResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_delete_user_test.go b/objects_delete_user_test.go new file mode 100644 index 00000000..28cf4c59 --- /dev/null +++ b/objects_delete_user_test.go @@ -0,0 +1,85 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/stretchr/testify/assert" +) + +func AssertDeleteUser(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + o := newDeleteUserBuilder(pn) + if testContext { + o = newDeleteUserBuilderWithContext(pn, backgroundContext) + } + + o.ID("id0") + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/users/%s", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + } + +} + +func TestDeleteUser(t *testing.T) { + AssertDeleteUser(t, true, false) +} + +func TestDeleteUserContext(t *testing.T) { + AssertDeleteUser(t, true, true) +} + +func TestDeleteUserResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &deleteUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNDeleteUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":null} +func TestDeleteUserResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &deleteUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":null}`) + + r, _, err := newPNDeleteUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(nil, r.Data) + + assert.Nil(err) +} diff --git a/objects_get_members.go b/objects_get_members.go new file mode 100644 index 00000000..9b815451 --- /dev/null +++ b/objects_get_members.go @@ -0,0 +1,235 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyGetMembersResponse *PNGetMembersResponse + +const getMembersPath = "/v1/objects/%s/spaces/%s/users" + +const membersLimit = 100 + +type getMembersBuilder struct { + opts *getMembersOpts +} + +func newGetMembersBuilder(pubnub *PubNub) *getMembersBuilder { + builder := getMembersBuilder{ + opts: &getMembersOpts{ + pubnub: pubnub, + }, + } + builder.opts.Limit = membersLimit + + return &builder +} + +func newGetMembersBuilderWithContext(pubnub *PubNub, + context Context) *getMembersBuilder { + builder := getMembersBuilder{ + opts: &getMembersOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +func (b *getMembersBuilder) SpaceID(id string) *getMembersBuilder { + b.opts.ID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getMembersBuilder) Include(include []PNMembersInclude) *getMembersBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getMembersBuilder) Limit(limit int) *getMembersBuilder { + b.opts.Limit = limit + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getMembersBuilder) Start(start string) *getMembersBuilder { + b.opts.Start = start + + return b +} + +func (b *getMembersBuilder) End(end string) *getMembersBuilder { + b.opts.End = end + + return b +} + +func (b *getMembersBuilder) Count(count bool) *getMembersBuilder { + b.opts.Count = count + + 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 *getMembersBuilder) QueryParam(queryParam map[string]string) *getMembersBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the getMembers request. +func (b *getMembersBuilder) Transport(tr http.RoundTripper) *getMembersBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the getMembers request. +func (b *getMembersBuilder) Execute() (*PNGetMembersResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyGetMembersResponse, status, err + } + + return newPNGetMembersResponse(rawJSON, b.opts, status) +} + +type getMembersOpts struct { + pubnub *PubNub + ID string + Limit int + Include []string + Start string + End string + Count bool + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *getMembersOpts) config() Config { + return *o.pubnub.Config +} + +func (o *getMembersOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *getMembersOpts) context() Context { + return o.ctx +} + +func (o *getMembersOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *getMembersOpts) buildPath() (string, error) { + return fmt.Sprintf(getMembersPath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *getMembersOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + q.Set("limit", strconv.Itoa(o.Limit)) + + if o.Start != "" { + q.Set("start", o.Start) + } + + if o.Count { + q.Set("count", "1") + } else { + q.Set("count", "0") + } + + if o.End != "" { + q.Set("end", o.End) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *getMembersOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *getMembersOpts) buildBody() ([]byte, error) { + return []byte{}, nil +} + +func (o *getMembersOpts) httpMethod() string { + return "GET" +} + +func (o *getMembersOpts) isAuthRequired() bool { + return true +} + +func (o *getMembersOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *getMembersOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *getMembersOpts) operationType() OperationType { + return PNGetMembersOperation +} + +func (o *getMembersOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNGetMembersResponse is the Objects API Response for Get Members +type PNGetMembersResponse struct { + Status int `json:"status"` + Data []PNMembers `json:"data"` + TotalCount int `json:"totalCount"` + Next string `json:"next"` + Prev string `json:"prev"` +} + +func newPNGetMembersResponse(jsonBytes []byte, o *getMembersOpts, + status StatusResponse) (*PNGetMembersResponse, StatusResponse, error) { + + resp := &PNGetMembersResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyGetMembersResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_get_members_test.go b/objects_get_members_test.go new file mode 100644 index 00000000..ef7ea4ae --- /dev/null +++ b/objects_get_members_test.go @@ -0,0 +1,125 @@ +package pubnub + +import ( + "fmt" + "strconv" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertGetMembers(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + incl := []PNMembersInclude{ + PNMembersCustom, + } + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newGetMembersBuilder(pn) + if testContext { + o = newGetMembersBuilderWithContext(pn, backgroundContext) + } + + spaceID := "id0" + limit := 90 + start := "Mxmy" + end := "Nxny" + + o.SpaceID(spaceID) + o.Include(incl) + o.Limit(limit) + o.Start(start) + o.End(end) + o.Count(false) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/spaces/%s/users", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + assert.Equal(strconv.Itoa(limit), u.Get("limit")) + assert.Equal(start, u.Get("start")) + assert.Equal(end, u.Get("end")) + assert.Equal("0", u.Get("count")) + } + +} + +func TestGetMembers(t *testing.T) { + AssertGetMembers(t, true, false) +} + +func TestGetMembersContext(t *testing.T) { + AssertGetMembers(t, true, true) +} + +func TestGetMembersResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getMembersOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNGetMembersResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":[{"id":"id0","custom":{"a3":"b3","c3":"d3"},"user":{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"},"created":"2019-08-20T13:26:24.07832Z","updated":"2019-08-20T13:26:24.07832Z","eTag":"AamrnoXdpdmzjwE"}],"totalCount":1,"next":"MQ","prev":"NQ"} +func TestGetMembersResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getMembersOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":[{"id":"id0","custom":{"a3":"b3","c3":"d3"},"user":{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"},"created":"2019-08-20T13:26:24.07832Z","updated":"2019-08-20T13:26:24.07832Z","eTag":"AamrnoXdpdmzjwE"}],"totalCount":1,"next":"MQ","prev":"NQ"}`) + + r, _, err := newPNGetMembersResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(1, r.TotalCount) + assert.Equal("MQ", r.Next) + assert.Equal("NQ", r.Prev) + assert.Equal("id0", r.Data[0].ID) + assert.Equal("name", r.Data[0].User.Name) + assert.Equal("extid", r.Data[0].User.ExternalID) + assert.Equal("purl", r.Data[0].User.ProfileURL) + assert.Equal("email", r.Data[0].User.Email) + assert.Equal("2019-08-20T13:26:19.140324Z", r.Data[0].User.Created) + assert.Equal("2019-08-20T13:26:19.140324Z", r.Data[0].User.Updated) + assert.Equal("AbyT4v2p6K7fpQE", r.Data[0].User.ETag) + assert.Equal("b", r.Data[0].User.Custom["a"]) + assert.Equal("d", r.Data[0].User.Custom["c"]) + assert.Equal("2019-08-20T13:26:24.07832Z", r.Data[0].Created) + assert.Equal("2019-08-20T13:26:24.07832Z", r.Data[0].Updated) + assert.Equal("AamrnoXdpdmzjwE", r.Data[0].ETag) + assert.Equal("b3", r.Data[0].Custom["a3"]) + assert.Equal("d3", r.Data[0].Custom["c3"]) + + assert.Nil(err) +} diff --git a/objects_get_memberships.go b/objects_get_memberships.go new file mode 100644 index 00000000..038a205f --- /dev/null +++ b/objects_get_memberships.go @@ -0,0 +1,235 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyGetMembershipsResponse *PNGetMembershipsResponse + +const getMembershipsPath = "/v1/objects/%s/users/%s/spaces" + +const spaceMembershipLimit = 100 + +type getMembershipsBuilder struct { + opts *getMembershipsOpts +} + +func newGetMembershipsBuilder(pubnub *PubNub) *getMembershipsBuilder { + builder := getMembershipsBuilder{ + opts: &getMembershipsOpts{ + pubnub: pubnub, + }, + } + builder.opts.Limit = spaceMembershipLimit + + return &builder +} + +func newGetMembershipsBuilderWithContext(pubnub *PubNub, + context Context) *getMembershipsBuilder { + builder := getMembershipsBuilder{ + opts: &getMembershipsOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +func (b *getMembershipsBuilder) UserID(id string) *getMembershipsBuilder { + b.opts.ID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getMembershipsBuilder) Include(include []PNMembershipsInclude) *getMembershipsBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getMembershipsBuilder) Limit(limit int) *getMembershipsBuilder { + b.opts.Limit = limit + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getMembershipsBuilder) Start(start string) *getMembershipsBuilder { + b.opts.Start = start + + return b +} + +func (b *getMembershipsBuilder) End(end string) *getMembershipsBuilder { + b.opts.End = end + + return b +} + +func (b *getMembershipsBuilder) Count(count bool) *getMembershipsBuilder { + b.opts.Count = count + + 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 *getMembershipsBuilder) QueryParam(queryParam map[string]string) *getMembershipsBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the getMemberships request. +func (b *getMembershipsBuilder) Transport(tr http.RoundTripper) *getMembershipsBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the getMemberships request. +func (b *getMembershipsBuilder) Execute() (*PNGetMembershipsResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyGetMembershipsResponse, status, err + } + + return newPNGetMembershipsResponse(rawJSON, b.opts, status) +} + +type getMembershipsOpts struct { + pubnub *PubNub + ID string + Limit int + Include []string + Start string + End string + Count bool + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *getMembershipsOpts) config() Config { + return *o.pubnub.Config +} + +func (o *getMembershipsOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *getMembershipsOpts) context() Context { + return o.ctx +} + +func (o *getMembershipsOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *getMembershipsOpts) buildPath() (string, error) { + return fmt.Sprintf(getMembershipsPath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *getMembershipsOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + q.Set("limit", strconv.Itoa(o.Limit)) + + if o.Start != "" { + q.Set("start", o.Start) + } + + if o.Count { + q.Set("count", "1") + } else { + q.Set("count", "0") + } + + if o.End != "" { + q.Set("end", o.End) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *getMembershipsOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *getMembershipsOpts) buildBody() ([]byte, error) { + return []byte{}, nil +} + +func (o *getMembershipsOpts) httpMethod() string { + return "GET" +} + +func (o *getMembershipsOpts) isAuthRequired() bool { + return true +} + +func (o *getMembershipsOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *getMembershipsOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *getMembershipsOpts) operationType() OperationType { + return PNGetMembershipsOperation +} + +func (o *getMembershipsOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNGetMembershipsResponse is the Objects API Response for Get Memberships +type PNGetMembershipsResponse struct { + Status int `json:"status"` + Data []PNMemberships `json:"data"` + TotalCount int `json:"totalCount"` + Next string `json:"next"` + Prev string `json:"prev"` +} + +func newPNGetMembershipsResponse(jsonBytes []byte, o *getMembershipsOpts, + status StatusResponse) (*PNGetMembershipsResponse, StatusResponse, error) { + + resp := &PNGetMembershipsResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyGetMembershipsResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_get_memberships_test.go b/objects_get_memberships_test.go new file mode 100644 index 00000000..629a9aa4 --- /dev/null +++ b/objects_get_memberships_test.go @@ -0,0 +1,123 @@ +package pubnub + +import ( + "fmt" + "strconv" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertGetMemberships(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + incl := []PNMembershipsInclude{ + PNMembershipsCustom, + } + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newGetMembershipsBuilder(pn) + if testContext { + o = newGetMembershipsBuilderWithContext(pn, backgroundContext) + } + + userID := "id0" + limit := 90 + start := "Mxmy" + end := "Nxny" + + o.UserID(userID) + o.Include(incl) + o.Limit(limit) + o.Start(start) + o.End(end) + o.Count(false) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/users/%s/spaces", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + assert.Equal(strconv.Itoa(limit), u.Get("limit")) + assert.Equal(start, u.Get("start")) + assert.Equal(end, u.Get("end")) + assert.Equal("0", u.Get("count")) + } + +} + +func TestGetMemberships(t *testing.T) { + AssertGetMemberships(t, true, false) +} + +func TestGetMembershipsContext(t *testing.T) { + AssertGetMemberships(t, true, true) +} + +func TestGetMembershipsResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getMembershipsOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNGetMembershipsResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":[{"id":"id0","custom":{"a3":"b3","c3":"d3"},"space":{"id":"id0","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T13:26:08.341297Z","eTag":"Aee9zsKNndXlHw"},"created":"2019-08-20T13:26:24.07832Z","updated":"2019-08-20T13:26:24.07832Z","eTag":"AamrnoXdpdmzjwE"}],"totalCount":1,"next":"MQ"} +func TestGetMembershipsResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getMembershipsOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":[{"id":"id0","custom":{"a3":"b3","c3":"d3"},"space":{"id":"id0","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T13:26:08.341297Z","eTag":"Aee9zsKNndXlHw"},"created":"2019-08-20T13:26:24.07832Z","updated":"2019-08-20T13:26:24.07832Z","eTag":"AamrnoXdpdmzjwE"}],"totalCount":1,"next":"MQ", "prev":"NQ"}`) + + r, _, err := newPNGetMembershipsResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(1, r.TotalCount) + assert.Equal("MQ", r.Next) + assert.Equal("NQ", r.Prev) + assert.Equal("id0", r.Data[0].ID) + assert.Equal("name", r.Data[0].Space.Name) + assert.Equal("desc", r.Data[0].Space.Description) + assert.Equal("2019-08-20T13:26:08.341297Z", r.Data[0].Space.Created) + assert.Equal("2019-08-20T13:26:08.341297Z", r.Data[0].Space.Updated) + assert.Equal("Aee9zsKNndXlHw", r.Data[0].Space.ETag) + assert.Equal("b", r.Data[0].Space.Custom["a"]) + assert.Equal(nil, r.Data[0].Space.Custom["c"]) + assert.Equal("2019-08-20T13:26:24.07832Z", r.Data[0].Created) + assert.Equal("2019-08-20T13:26:24.07832Z", r.Data[0].Updated) + assert.Equal("AamrnoXdpdmzjwE", r.Data[0].ETag) + assert.Equal("b3", r.Data[0].Custom["a3"]) + assert.Equal("d3", r.Data[0].Custom["c3"]) + + assert.Nil(err) +} diff --git a/objects_get_space.go b/objects_get_space.go new file mode 100644 index 00000000..184cae97 --- /dev/null +++ b/objects_get_space.go @@ -0,0 +1,182 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyPNGetSpaceResponse *PNGetSpaceResponse + +const getSpacePath = "/v1/objects/%s/spaces/%s" + +type getSpaceBuilder struct { + opts *getSpaceOpts +} + +func newGetSpaceBuilder(pubnub *PubNub) *getSpaceBuilder { + builder := getSpaceBuilder{ + opts: &getSpaceOpts{ + pubnub: pubnub, + }, + } + return &builder +} + +func newGetSpaceBuilderWithContext(pubnub *PubNub, + context Context) *getSpaceBuilder { + builder := getSpaceBuilder{ + opts: &getSpaceOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getSpaceBuilder) Include(include []PNUserSpaceInclude) *getSpaceBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getSpaceBuilder) ID(id string) *getSpaceBuilder { + b.opts.ID = id + + 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 *getSpaceBuilder) QueryParam(queryParam map[string]string) *getSpaceBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the getSpace request. +func (b *getSpaceBuilder) Transport(tr http.RoundTripper) *getSpaceBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the getSpace request. +func (b *getSpaceBuilder) Execute() (*PNGetSpaceResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNGetSpaceResponse, status, err + } + + return newPNGetSpaceResponse(rawJSON, b.opts, status) +} + +type getSpaceOpts struct { + pubnub *PubNub + ID string + Include []string + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *getSpaceOpts) config() Config { + return *o.pubnub.Config +} + +func (o *getSpaceOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *getSpaceOpts) context() Context { + return o.ctx +} + +func (o *getSpaceOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *getSpaceOpts) buildPath() (string, error) { + return fmt.Sprintf(getSpacePath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *getSpaceOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *getSpaceOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *getSpaceOpts) buildBody() ([]byte, error) { + return []byte{}, nil +} + +func (o *getSpaceOpts) httpMethod() string { + return "GET" +} + +func (o *getSpaceOpts) isAuthRequired() bool { + return true +} + +func (o *getSpaceOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *getSpaceOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *getSpaceOpts) operationType() OperationType { + return PNGetSpaceOperation +} + +func (o *getSpaceOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNGetSpaceResponse is the Objects API Response for Get Space +type PNGetSpaceResponse struct { + Status int `json:"status"` + Data PNSpace `json:"data"` +} + +func newPNGetSpaceResponse(jsonBytes []byte, o *getSpaceOpts, + status StatusResponse) (*PNGetSpaceResponse, StatusResponse, error) { + + resp := &PNGetSpaceResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNGetSpaceResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_get_space_test.go b/objects_get_space_test.go new file mode 100644 index 00000000..0a53dc4a --- /dev/null +++ b/objects_get_space_test.go @@ -0,0 +1,103 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertGetSpace(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newGetSpaceBuilder(pn) + if testContext { + o = newGetSpaceBuilderWithContext(pn, backgroundContext) + } + + o.Include(incl) + o.ID("id0") + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/spaces/%s", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + } + +} + +func TestGetSpace(t *testing.T) { + AssertGetSpace(t, true, false) +} + +func TestGetSpaceContext(t *testing.T) { + AssertGetSpace(t, true, true) +} + +func TestGetSpaceResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNGetSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":{"id":"id0","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T13:26:08.341297Z","eTag":"Aee9zsKNndXlHw"}} +func TestGetSpaceResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":{"id":"id0","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T13:26:08.341297Z","eTag":"Aee9zsKNndXlHw"}}`) + + r, _, err := newPNGetSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal("id0", r.Data.ID) + assert.Equal("name", r.Data.Name) + assert.Equal("desc", r.Data.Description) + assert.Equal("2019-08-20T13:26:08.341297Z", r.Data.Created) + assert.Equal("2019-08-20T13:26:08.341297Z", r.Data.Updated) + assert.Equal("Aee9zsKNndXlHw", r.Data.ETag) + assert.Equal("b", r.Data.Custom["a"]) + + assert.Nil(err) +} diff --git a/objects_get_spaces.go b/objects_get_spaces.go new file mode 100644 index 00000000..11dd1b50 --- /dev/null +++ b/objects_get_spaces.go @@ -0,0 +1,229 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyGetSpacesResponse *PNGetSpacesResponse + +const getSpacesPath = "/v1/objects/%s/spaces" + +const spaceLimit = 100 + +type getSpacesBuilder struct { + opts *getSpacesOpts +} + +func newGetSpacesBuilder(pubnub *PubNub) *getSpacesBuilder { + builder := getSpacesBuilder{ + opts: &getSpacesOpts{ + pubnub: pubnub, + }, + } + builder.opts.Limit = spaceLimit + + return &builder +} + +func newGetSpacesBuilderWithContext(pubnub *PubNub, + context Context) *getSpacesBuilder { + builder := getSpacesBuilder{ + opts: &getSpacesOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getSpacesBuilder) Include(include []PNUserSpaceInclude) *getSpacesBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getSpacesBuilder) Limit(limit int) *getSpacesBuilder { + b.opts.Limit = limit + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getSpacesBuilder) Start(start string) *getSpacesBuilder { + b.opts.Start = start + + return b +} + +func (b *getSpacesBuilder) End(end string) *getSpacesBuilder { + b.opts.End = end + + return b +} + +func (b *getSpacesBuilder) Count(count bool) *getSpacesBuilder { + b.opts.Count = count + + 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 *getSpacesBuilder) QueryParam(queryParam map[string]string) *getSpacesBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the getSpaces request. +func (b *getSpacesBuilder) Transport(tr http.RoundTripper) *getSpacesBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the getSpaces request. +func (b *getSpacesBuilder) Execute() (*PNGetSpacesResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyGetSpacesResponse, status, err + } + + return newPNGetSpacesResponse(rawJSON, b.opts, status) +} + +type getSpacesOpts struct { + pubnub *PubNub + + Limit int + Include []string + Start string + End string + Count bool + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *getSpacesOpts) config() Config { + return *o.pubnub.Config +} + +func (o *getSpacesOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *getSpacesOpts) context() Context { + return o.ctx +} + +func (o *getSpacesOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *getSpacesOpts) buildPath() (string, error) { + return fmt.Sprintf(getSpacesPath, + o.pubnub.Config.SubscribeKey), nil +} + +func (o *getSpacesOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + q.Set("limit", strconv.Itoa(o.Limit)) + + if o.Start != "" { + q.Set("start", o.Start) + } + + if o.Count { + q.Set("count", "1") + } else { + q.Set("count", "0") + } + + if o.End != "" { + q.Set("end", o.End) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *getSpacesOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *getSpacesOpts) buildBody() ([]byte, error) { + return []byte{}, nil +} + +func (o *getSpacesOpts) httpMethod() string { + return "GET" +} + +func (o *getSpacesOpts) isAuthRequired() bool { + return true +} + +func (o *getSpacesOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *getSpacesOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *getSpacesOpts) operationType() OperationType { + return PNGetSpacesOperation +} + +func (o *getSpacesOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNGetSpacesResponse is the Objects API Response for Get Spaces +type PNGetSpacesResponse struct { + Status int `json:"status"` + Data []PNSpace `json:"data"` + TotalCount int `json:"totalCount"` + Next string `json:"next"` + Prev string `json:"prev"` +} + +func newPNGetSpacesResponse(jsonBytes []byte, o *getSpacesOpts, + status StatusResponse) (*PNGetSpacesResponse, StatusResponse, error) { + + resp := &PNGetSpacesResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyGetSpacesResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_get_spaces_test.go b/objects_get_spaces_test.go new file mode 100644 index 00000000..acc0c0c5 --- /dev/null +++ b/objects_get_spaces_test.go @@ -0,0 +1,115 @@ +package pubnub + +import ( + "fmt" + "strconv" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertGetSpaces(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newGetSpacesBuilder(pn) + if testContext { + o = newGetSpacesBuilderWithContext(pn, backgroundContext) + } + + limit := 90 + start := "Mxmy" + end := "Nxny" + + o.Include(incl) + o.Limit(limit) + o.Start(start) + o.End(end) + o.Count(false) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/spaces", pn.Config.SubscribeKey), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + assert.Equal(strconv.Itoa(limit), u.Get("limit")) + assert.Equal(start, u.Get("start")) + assert.Equal(end, u.Get("end")) + assert.Equal("0", u.Get("count")) + } + +} + +func TestGetSpaces(t *testing.T) { + AssertGetSpaces(t, true, false) +} + +func TestGetSpacesContext(t *testing.T) { + AssertGetSpaces(t, true, true) +} + +func TestGetSpacesResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getSpacesOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNGetSpacesResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":[{"id":"id0","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T13:26:08.341297Z","eTag":"Aee9zsKNndXlHw"},{"id":"id01","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T14:44:52.799969Z","updated":"2019-08-20T14:44:52.799969Z","eTag":"Aee9zsKNndXlHw"}],"totalCount":2,"next":"Mg"} +func TestGetSpacesResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getSpacesOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":[{"id":"id0","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T13:26:08.341297Z","eTag":"Aee9zsKNndXlHw"},{"id":"id01","name":"name","description":"desc","custom":{"a":"b"},"created":"2019-08-20T14:44:52.799969Z","updated":"2019-08-20T14:44:52.799969Z","eTag":"Aee9zsKNndXlHw"}],"totalCount":2,"next":"Mg","prev":"Nd"}`) + + r, _, err := newPNGetSpacesResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(2, r.TotalCount) + assert.Equal("Mg", r.Next) + assert.Equal("Nd", r.Prev) + assert.Equal("id0", r.Data[0].ID) + assert.Equal("name", r.Data[0].Name) + assert.Equal("desc", r.Data[0].Description) + assert.Equal("2019-08-20T13:26:08.341297Z", r.Data[0].Created) + assert.Equal("2019-08-20T13:26:08.341297Z", r.Data[0].Updated) + assert.Equal("Aee9zsKNndXlHw", r.Data[0].ETag) + assert.Equal("b", r.Data[0].Custom["a"]) + + assert.Nil(err) +} diff --git a/objects_get_user.go b/objects_get_user.go new file mode 100644 index 00000000..034458d2 --- /dev/null +++ b/objects_get_user.go @@ -0,0 +1,182 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyPNGetUserResponse *PNGetUserResponse + +const getUserPath = "/v1/objects/%s/users/%s" + +type getUserBuilder struct { + opts *getUserOpts +} + +func newGetUserBuilder(pubnub *PubNub) *getUserBuilder { + builder := getUserBuilder{ + opts: &getUserOpts{ + pubnub: pubnub, + }, + } + return &builder +} + +func newGetUserBuilderWithContext(pubnub *PubNub, + context Context) *getUserBuilder { + builder := getUserBuilder{ + opts: &getUserOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getUserBuilder) Include(include []PNUserSpaceInclude) *getUserBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getUserBuilder) ID(id string) *getUserBuilder { + b.opts.ID = id + + 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 *getUserBuilder) QueryParam(queryParam map[string]string) *getUserBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the getUser request. +func (b *getUserBuilder) Transport(tr http.RoundTripper) *getUserBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the getUser request. +func (b *getUserBuilder) Execute() (*PNGetUserResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNGetUserResponse, status, err + } + + return newPNGetUserResponse(rawJSON, b.opts, status) +} + +type getUserOpts struct { + pubnub *PubNub + ID string + Include []string + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *getUserOpts) config() Config { + return *o.pubnub.Config +} + +func (o *getUserOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *getUserOpts) context() Context { + return o.ctx +} + +func (o *getUserOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *getUserOpts) buildPath() (string, error) { + return fmt.Sprintf(getUserPath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *getUserOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *getUserOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *getUserOpts) buildBody() ([]byte, error) { + return []byte{}, nil +} + +func (o *getUserOpts) httpMethod() string { + return "GET" +} + +func (o *getUserOpts) isAuthRequired() bool { + return true +} + +func (o *getUserOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *getUserOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *getUserOpts) operationType() OperationType { + return PNGetUserOperation +} + +func (o *getUserOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNGetUserResponse is the Objects API Response for Get User +type PNGetUserResponse struct { + Status int `json:"status"` + Data PNUser `json:"data"` +} + +func newPNGetUserResponse(jsonBytes []byte, o *getUserOpts, + status StatusResponse) (*PNGetUserResponse, StatusResponse, error) { + + resp := &PNGetUserResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNGetUserResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_get_user_test.go b/objects_get_user_test.go new file mode 100644 index 00000000..cbfb7c4e --- /dev/null +++ b/objects_get_user_test.go @@ -0,0 +1,106 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertGetUser(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newGetUserBuilder(pn) + if testContext { + o = newGetUserBuilderWithContext(pn, backgroundContext) + } + + o.Include(incl) + o.ID("id0") + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/users/%s", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + } + +} + +func TestGetUser(t *testing.T) { + AssertGetUser(t, true, false) +} + +func TestGetUserContext(t *testing.T) { + AssertGetUser(t, true, true) +} + +func TestGetUserResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNGetUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"}} +func TestGetUserResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"}}`) + + r, _, err := newPNGetUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal("id0", r.Data.ID) + assert.Equal("name", r.Data.Name) + assert.Equal("extid", r.Data.ExternalID) + assert.Equal("purl", r.Data.ProfileURL) + assert.Equal("email", r.Data.Email) + assert.Equal("2019-08-20T13:26:19.140324Z", r.Data.Created) + assert.Equal("2019-08-20T13:26:19.140324Z", r.Data.Updated) + assert.Equal("AbyT4v2p6K7fpQE", r.Data.ETag) + assert.Equal("b", r.Data.Custom["a"]) + assert.Equal("d", r.Data.Custom["c"]) + + assert.Nil(err) +} diff --git a/objects_get_users.go b/objects_get_users.go new file mode 100644 index 00000000..26f03113 --- /dev/null +++ b/objects_get_users.go @@ -0,0 +1,229 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyPNGetUsersResponse *PNGetUsersResponse + +const getUsersPath = "/v1/objects/%s/users" + +const usersLimit = 100 + +type getUsersBuilder struct { + opts *getUsersOpts +} + +func newGetUsersBuilder(pubnub *PubNub) *getUsersBuilder { + builder := getUsersBuilder{ + opts: &getUsersOpts{ + pubnub: pubnub, + }, + } + builder.opts.Limit = usersLimit + + return &builder +} + +func newGetUsersBuilderWithContext(pubnub *PubNub, + context Context) *getUsersBuilder { + builder := getUsersBuilder{ + opts: &getUsersOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getUsersBuilder) Include(include []PNUserSpaceInclude) *getUsersBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getUsersBuilder) Limit(limit int) *getUsersBuilder { + b.opts.Limit = limit + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *getUsersBuilder) Start(start string) *getUsersBuilder { + b.opts.Start = start + + return b +} + +func (b *getUsersBuilder) End(end string) *getUsersBuilder { + b.opts.End = end + + return b +} + +func (b *getUsersBuilder) Count(count bool) *getUsersBuilder { + b.opts.Count = count + + 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 *getUsersBuilder) QueryParam(queryParam map[string]string) *getUsersBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the getUsers request. +func (b *getUsersBuilder) Transport(tr http.RoundTripper) *getUsersBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the getUsers request. +func (b *getUsersBuilder) Execute() (*PNGetUsersResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNGetUsersResponse, status, err + } + + return newPNGetUsersResponse(rawJSON, b.opts, status) +} + +type getUsersOpts struct { + pubnub *PubNub + + Limit int + Include []string + Start string + End string + Count bool + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *getUsersOpts) config() Config { + return *o.pubnub.Config +} + +func (o *getUsersOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *getUsersOpts) context() Context { + return o.ctx +} + +func (o *getUsersOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *getUsersOpts) buildPath() (string, error) { + return fmt.Sprintf(getUsersPath, + o.pubnub.Config.SubscribeKey), nil +} + +func (o *getUsersOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + q.Set("limit", strconv.Itoa(o.Limit)) + + if o.Start != "" { + q.Set("start", o.Start) + } + + if o.Count { + q.Set("count", "1") + } else { + q.Set("count", "0") + } + + if o.End != "" { + q.Set("end", o.End) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *getUsersOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *getUsersOpts) buildBody() ([]byte, error) { + return []byte{}, nil +} + +func (o *getUsersOpts) httpMethod() string { + return "GET" +} + +func (o *getUsersOpts) isAuthRequired() bool { + return true +} + +func (o *getUsersOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *getUsersOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *getUsersOpts) operationType() OperationType { + return PNGetUsersOperation +} + +func (o *getUsersOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNGetUsersResponse is the Objects API Response for Get Users +type PNGetUsersResponse struct { + Status int `json:"status"` + Data []PNUser `json:"data"` + TotalCount int `json:"totalCount"` + Next string `json:"next"` + Prev string `json:"prev"` +} + +func newPNGetUsersResponse(jsonBytes []byte, o *getUsersOpts, + status StatusResponse) (*PNGetUsersResponse, StatusResponse, error) { + + resp := &PNGetUsersResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNGetUsersResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_get_users_test.go b/objects_get_users_test.go new file mode 100644 index 00000000..aaf14efb --- /dev/null +++ b/objects_get_users_test.go @@ -0,0 +1,118 @@ +package pubnub + +import ( + "fmt" + "strconv" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertGetUsers(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newGetUsersBuilder(pn) + if testContext { + o = newGetUsersBuilderWithContext(pn, backgroundContext) + } + + limit := 90 + start := "Mxmy" + end := "Nxny" + + o.Include(incl) + o.Limit(limit) + o.Start(start) + o.End(end) + o.Count(false) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/users", pn.Config.SubscribeKey), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + assert.Empty(body) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + assert.Equal(strconv.Itoa(limit), u.Get("limit")) + assert.Equal(start, u.Get("start")) + assert.Equal(end, u.Get("end")) + assert.Equal("0", u.Get("count")) + } + +} + +func TestGetUsers(t *testing.T) { + AssertGetUsers(t, true, false) +} + +func TestGetUsersContext(t *testing.T) { + AssertGetUsers(t, true, true) +} + +func TestGetUsersResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getUsersOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNGetUsersResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":[{"id":"id2","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-19T14:44:54.837392Z","updated":"2019-08-19T14:44:54.837392Z","eTag":"AbyT4v2p6K7fpQE"},{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"}],"totalCount":2,"next":"Mg"} +func TestGetUsersResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &getUsersOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":[{"id":"id2","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-19T14:44:54.837392Z","updated":"2019-08-19T14:44:54.837392Z","eTag":"AbyT4v2p6K7fpQE"},{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"}],"totalCount":2,"next":"Mg","prev":"Nd"}`) + + r, _, err := newPNGetUsersResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(2, r.TotalCount) + assert.Equal("Mg", r.Next) + assert.Equal("Nd", r.Prev) + assert.Equal("id2", r.Data[0].ID) + assert.Equal("name", r.Data[0].Name) + assert.Equal("extid", r.Data[0].ExternalID) + assert.Equal("purl", r.Data[0].ProfileURL) + assert.Equal("email", r.Data[0].Email) + assert.Equal("2019-08-19T14:44:54.837392Z", r.Data[0].Created) + assert.Equal("2019-08-19T14:44:54.837392Z", r.Data[0].Updated) + assert.Equal("AbyT4v2p6K7fpQE", r.Data[0].ETag) + assert.Equal("b", r.Data[0].Custom["a"]) + assert.Equal("d", r.Data[0].Custom["c"]) + + assert.Nil(err) +} diff --git a/objects_manage_members.go b/objects_manage_members.go new file mode 100644 index 00000000..30f97805 --- /dev/null +++ b/objects_manage_members.go @@ -0,0 +1,276 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyManageMembersResponse *PNManageMembersResponse + +const manageMembersPath = "/v1/objects/%s/spaces/%s/users" + +const manageMembersLimit = 100 + +type manageMembersBuilder struct { + opts *manageMembersOpts +} + +func newManageMembersBuilder(pubnub *PubNub) *manageMembersBuilder { + builder := manageMembersBuilder{ + opts: &manageMembersOpts{ + pubnub: pubnub, + }, + } + builder.opts.Limit = spaceLimit + + return &builder +} + +func newManageMembersBuilderWithContext(pubnub *PubNub, + context Context) *manageMembersBuilder { + builder := manageMembersBuilder{ + opts: &manageMembersOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembersBuilder) Include(include []PNMembersInclude) *manageMembersBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembersBuilder) SpaceID(id string) *manageMembersBuilder { + b.opts.SpaceID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembersBuilder) Limit(limit int) *manageMembersBuilder { + b.opts.Limit = limit + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembersBuilder) Start(start string) *manageMembersBuilder { + b.opts.Start = start + + return b +} + +func (b *manageMembersBuilder) End(end string) *manageMembersBuilder { + b.opts.End = end + + return b +} + +func (b *manageMembersBuilder) Count(count bool) *manageMembersBuilder { + b.opts.Count = count + + return b +} + +func (b *manageMembersBuilder) Add(membershipInput []PNMembersInput) *manageMembersBuilder { + b.opts.MembershipAdd = membershipInput + + return b +} + +func (b *manageMembersBuilder) Update(membershipInput []PNMembersInput) *manageMembersBuilder { + b.opts.MembershipUpdate = membershipInput + + return b +} + +func (b *manageMembersBuilder) Remove(membershipRemove []PNMembersRemove) *manageMembersBuilder { + b.opts.MembershipRemove = membershipRemove + + 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 *manageMembersBuilder) QueryParam(queryParam map[string]string) *manageMembersBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the manageMembers request. +func (b *manageMembersBuilder) Transport(tr http.RoundTripper) *manageMembersBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the manageMembers request. +func (b *manageMembersBuilder) Execute() (*PNManageMembersResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyManageMembersResponse, status, err + } + + return newPNManageMembersResponse(rawJSON, b.opts, status) +} + +type manageMembersOpts struct { + pubnub *PubNub + SpaceID string + Limit int + Include []string + Start string + End string + Count bool + QueryParam map[string]string + MembershipRemove []PNMembersRemove + MembershipAdd []PNMembersInput + MembershipUpdate []PNMembersInput + Transport http.RoundTripper + + ctx Context +} + +func (o *manageMembersOpts) config() Config { + return *o.pubnub.Config +} + +func (o *manageMembersOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *manageMembersOpts) context() Context { + return o.ctx +} + +func (o *manageMembersOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *manageMembersOpts) buildPath() (string, error) { + return fmt.Sprintf(manageMembersPath, + o.pubnub.Config.SubscribeKey, o.SpaceID), nil +} + +func (o *manageMembersOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + q.Set("limit", strconv.Itoa(o.Limit)) + + if o.Start != "" { + q.Set("start", o.Start) + } + + if o.Count { + q.Set("count", "1") + } else { + q.Set("count", "0") + } + + if o.End != "" { + q.Set("end", o.End) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *manageMembersOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +// PNMembersInputChangeSet is the Objects API input to add, remove or update members +type PNMembersInputChangeSet struct { + Add []PNMembersInput `json:"add"` + Update []PNMembersInput `json:"update"` + Remove []PNMembersRemove `json:"remove"` +} + +func (o *manageMembersOpts) buildBody() ([]byte, error) { + b := &PNMembersInputChangeSet{ + Add: o.MembershipAdd, + Update: o.MembershipUpdate, + Remove: o.MembershipRemove, + } + + jsonEncBytes, errEnc := json.Marshal(b) + + if errEnc != nil { + o.pubnub.Config.Log.Printf("ERROR: Serialization error: %s\n", errEnc.Error()) + return []byte{}, errEnc + } + return jsonEncBytes, nil + +} + +func (o *manageMembersOpts) httpMethod() string { + return "PATCH" +} + +func (o *manageMembersOpts) isAuthRequired() bool { + return true +} + +func (o *manageMembersOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *manageMembersOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *manageMembersOpts) operationType() OperationType { + return PNManageMembersOperation +} + +func (o *manageMembersOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNManageMembersResponse is the Objects API Response for ManageMembers +type PNManageMembersResponse struct { + Status int `json:"status"` + Data []PNMembers `json:"data"` + TotalCount int `json:"totalCount"` + Next string `json:"next"` + Prev string `json:"prev"` +} + +func newPNManageMembersResponse(jsonBytes []byte, o *manageMembersOpts, + status StatusResponse) (*PNManageMembersResponse, StatusResponse, error) { + + resp := &PNManageMembersResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyManageMembersResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_manage_members_test.go b/objects_manage_members_test.go new file mode 100644 index 00000000..f270cc2f --- /dev/null +++ b/objects_manage_members_test.go @@ -0,0 +1,166 @@ +package pubnub + +import ( + "fmt" + "strconv" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertManageMembers(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + incl := []PNMembersInclude{ + PNMembersCustom, + } + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newManageMembersBuilder(pn) + if testContext { + o = newManageMembersBuilderWithContext(pn, backgroundContext) + } + + spaceID := "id0" + limit := 90 + start := "Mxmy" + end := "Nxny" + + o.SpaceID(spaceID) + o.Include(incl) + o.Limit(limit) + o.Start(start) + o.End(end) + o.Count(false) + o.QueryParam(queryParam) + + id0 := "id0" + + custom := make(map[string]interface{}) + custom["a1"] = "b1" + custom["c1"] = "d1" + + in := PNMembersInput{ + ID: id0, + Custom: custom, + } + + inArr := []PNMembersInput{ + in, + } + + custom2 := make(map[string]interface{}) + custom2["a2"] = "b2" + custom2["c2"] = "d2" + + up := PNMembersInput{ + ID: id0, + Custom: custom2, + } + + upArr := []PNMembersInput{ + up, + } + + re := PNMembersRemove{ + ID: id0, + } + + reArr := []PNMembersRemove{ + re, + } + o.Add(inArr) + o.Update(upArr) + o.Remove(reArr) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/spaces/%s/users", pn.Config.SubscribeKey, spaceID), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + expectedBody := "{\"add\":[{\"id\":\"id0\",\"custom\":{\"a1\":\"b1\",\"c1\":\"d1\"}}],\"update\":[{\"id\":\"id0\",\"custom\":{\"a2\":\"b2\",\"c2\":\"d2\"}}],\"remove\":[{\"id\":\"id0\"}]}" + + assert.Equal(expectedBody, string(body)) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + assert.Equal(strconv.Itoa(limit), u.Get("limit")) + assert.Equal(start, u.Get("start")) + assert.Equal(end, u.Get("end")) + assert.Equal("0", u.Get("count")) + } + +} + +func TestManageMembers(t *testing.T) { + AssertManageMembers(t, true, false) +} + +func TestManageMembersContext(t *testing.T) { + AssertManageMembers(t, true, true) +} + +func TestManageMembersResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &manageMembersOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNManageMembersResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":[{"id":"userid4","custom":{"a1":"b1","c1":"d1"},"user":{"id":"userid4","name":"userid4name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-23T10:36:27.083453Z","updated":"2019-08-23T10:36:27.083453Z","eTag":"AbuLvdnC9JnYEA"},"created":"2019-08-23T10:41:35.503214Z","updated":"2019-08-23T10:41:35.503214Z","eTag":"AZK3l4nQsrWG9gE"}],"totalCount":1,"next":"MQ"} +func TestManageMembersResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &manageMembersOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":[{"id":"userid4","custom":{"a1":"b1","c1":"d1"},"user":{"id":"userid4","name":"userid4name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-23T10:36:27.083453Z","updated":"2019-08-23T10:36:27.083453Z","eTag":"AbuLvdnC9JnYEA"},"created":"2019-08-23T10:41:35.503214Z","updated":"2019-08-23T10:41:35.503214Z","eTag":"AZK3l4nQsrWG9gE"}],"totalCount":1,"next":"MQ"}`) + + r, _, err := newPNManageMembersResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(1, r.TotalCount) + assert.Equal("MQ", r.Next) + assert.Equal("userid4", r.Data[0].ID) + assert.Equal("2019-08-23T10:41:35.503214Z", r.Data[0].Created) + assert.Equal("2019-08-23T10:41:35.503214Z", r.Data[0].Updated) + assert.Equal("AZK3l4nQsrWG9gE", r.Data[0].ETag) + assert.Equal("b1", r.Data[0].Custom["a1"]) + assert.Equal("d1", r.Data[0].Custom["c1"]) + assert.Equal("userid4", r.Data[0].User.ID) + assert.Equal("userid4name", r.Data[0].User.Name) + assert.Equal("extid", r.Data[0].User.ExternalID) + assert.Equal("purl", r.Data[0].User.ProfileURL) + assert.Equal("email", r.Data[0].User.Email) + assert.Equal("2019-08-23T10:36:27.083453Z", r.Data[0].User.Created) + assert.Equal("2019-08-23T10:36:27.083453Z", r.Data[0].User.Updated) + assert.Equal("AbuLvdnC9JnYEA", r.Data[0].User.ETag) + assert.Equal("b", r.Data[0].User.Custom["a"]) + assert.Equal("d", r.Data[0].User.Custom["c"]) + + assert.Nil(err) +} diff --git a/objects_manage_memberships.go b/objects_manage_memberships.go new file mode 100644 index 00000000..e84751d6 --- /dev/null +++ b/objects_manage_memberships.go @@ -0,0 +1,274 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyManageMembershipsResponse *PNManageMembershipsResponse + +const manageMembershipsPath = "/v1/objects/%s/users/%s/spaces" + +const userSpaceMembershipsLimit = 100 + +type manageMembershipsBuilder struct { + opts *manageMembershipsOpts +} + +func newManageMembershipsBuilder(pubnub *PubNub) *manageMembershipsBuilder { + builder := manageMembershipsBuilder{ + opts: &manageMembershipsOpts{ + pubnub: pubnub, + }, + } + builder.opts.Limit = spaceLimit + + return &builder +} + +func newManageMembershipsBuilderWithContext(pubnub *PubNub, + context Context) *manageMembershipsBuilder { + builder := manageMembershipsBuilder{ + opts: &manageMembershipsOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembershipsBuilder) Include(include []PNMembershipsInclude) *manageMembershipsBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembershipsBuilder) UserID(id string) *manageMembershipsBuilder { + b.opts.UserID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembershipsBuilder) Limit(limit int) *manageMembershipsBuilder { + b.opts.Limit = limit + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *manageMembershipsBuilder) Start(start string) *manageMembershipsBuilder { + b.opts.Start = start + + return b +} + +func (b *manageMembershipsBuilder) End(end string) *manageMembershipsBuilder { + b.opts.End = end + + return b +} + +func (b *manageMembershipsBuilder) Count(count bool) *manageMembershipsBuilder { + b.opts.Count = count + + return b +} + +func (b *manageMembershipsBuilder) Add(userMembershipInput []PNMembershipsInput) *manageMembershipsBuilder { + b.opts.MembershipsAdd = userMembershipInput + + return b +} + +func (b *manageMembershipsBuilder) Update(userMembershipInput []PNMembershipsInput) *manageMembershipsBuilder { + b.opts.MembershipsUpdate = userMembershipInput + + return b +} + +func (b *manageMembershipsBuilder) Remove(userMembershipRemove []PNMembershipsRemove) *manageMembershipsBuilder { + b.opts.MembershipsRemove = userMembershipRemove + + 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 *manageMembershipsBuilder) QueryParam(queryParam map[string]string) *manageMembershipsBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the manageMemberships request. +func (b *manageMembershipsBuilder) Transport(tr http.RoundTripper) *manageMembershipsBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the manageMemberships request. +func (b *manageMembershipsBuilder) Execute() (*PNManageMembershipsResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyManageMembershipsResponse, status, err + } + + return newPNManageMembershipsResponse(rawJSON, b.opts, status) +} + +type manageMembershipsOpts struct { + pubnub *PubNub + UserID string + Limit int + Include []string + Start string + End string + Count bool + QueryParam map[string]string + MembershipsRemove []PNMembershipsRemove + MembershipsAdd []PNMembershipsInput + MembershipsUpdate []PNMembershipsInput + Transport http.RoundTripper + + ctx Context +} + +func (o *manageMembershipsOpts) config() Config { + return *o.pubnub.Config +} + +func (o *manageMembershipsOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *manageMembershipsOpts) context() Context { + return o.ctx +} + +func (o *manageMembershipsOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *manageMembershipsOpts) buildPath() (string, error) { + return fmt.Sprintf(manageMembershipsPath, + o.pubnub.Config.SubscribeKey, o.UserID), nil +} + +func (o *manageMembershipsOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + q.Set("limit", strconv.Itoa(o.Limit)) + + if o.Start != "" { + q.Set("start", o.Start) + } + + if o.Count { + q.Set("count", "1") + } else { + q.Set("count", "0") + } + + if o.End != "" { + q.Set("end", o.End) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *manageMembershipsOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +// PNMembershipsInputChangeSet is the Objects API input to add, remove or update membership +type PNMembershipsInputChangeSet struct { + Add []PNMembershipsInput `json:"add"` + Update []PNMembershipsInput `json:"update"` + Remove []PNMembershipsRemove `json:"remove"` +} + +func (o *manageMembershipsOpts) buildBody() ([]byte, error) { + b := &PNMembershipsInputChangeSet{ + Add: o.MembershipsAdd, + Update: o.MembershipsUpdate, + Remove: o.MembershipsRemove, + } + + jsonEncBytes, errEnc := json.Marshal(b) + + if errEnc != nil { + o.pubnub.Config.Log.Printf("ERROR: Serialization error: %s\n", errEnc.Error()) + return []byte{}, errEnc + } + return jsonEncBytes, nil +} + +func (o *manageMembershipsOpts) httpMethod() string { + return "PATCH" +} + +func (o *manageMembershipsOpts) isAuthRequired() bool { + return true +} + +func (o *manageMembershipsOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *manageMembershipsOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *manageMembershipsOpts) operationType() OperationType { + return PNManageMembershipsOperation +} + +func (o *manageMembershipsOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNManageMembershipsResponse is the Objects API Response for ManageMemberships +type PNManageMembershipsResponse struct { + Status int `json:"status"` + Data []PNMemberships `json:"data"` + TotalCount int `json:"totalCount"` + Next string `json:"next"` + Prev string `json:"prev"` +} + +func newPNManageMembershipsResponse(jsonBytes []byte, o *manageMembershipsOpts, + status StatusResponse) (*PNManageMembershipsResponse, StatusResponse, error) { + + resp := &PNManageMembershipsResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyManageMembershipsResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_manage_memberships_test.go b/objects_manage_memberships_test.go new file mode 100644 index 00000000..8ea5053d --- /dev/null +++ b/objects_manage_memberships_test.go @@ -0,0 +1,164 @@ +package pubnub + +import ( + "fmt" + "strconv" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertManageMemberships(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + + incl := []PNMembershipsInclude{ + PNMembershipsCustom, + } + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newManageMembershipsBuilder(pn) + if testContext { + o = newManageMembershipsBuilderWithContext(pn, backgroundContext) + } + + userID := "id0" + limit := 90 + start := "Mxmy" + end := "Nxny" + + o.UserID(userID) + o.Include(incl) + o.Limit(limit) + o.Start(start) + o.End(end) + o.Count(false) + o.QueryParam(queryParam) + + id0 := "id0" + + custom3 := make(map[string]interface{}) + custom3["a3"] = "b3" + custom3["c3"] = "d3" + + in := PNMembershipsInput{ + ID: id0, + Custom: custom3, + } + + inArr := []PNMembershipsInput{ + in, + } + + custom4 := make(map[string]interface{}) + custom4["a4"] = "b4" + custom4["c4"] = "d4" + + up := PNMembershipsInput{ + ID: id0, + Custom: custom4, + } + + upArr := []PNMembershipsInput{ + up, + } + + re := PNMembershipsRemove{ + ID: id0, + } + + reArr := []PNMembershipsRemove{ + re, + } + + o.Add(inArr) + o.Update(upArr) + o.Remove(reArr) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/users/%s/spaces", pn.Config.SubscribeKey, userID), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + expectedBody := "{\"add\":[{\"id\":\"id0\",\"custom\":{\"a3\":\"b3\",\"c3\":\"d3\"}}],\"update\":[{\"id\":\"id0\",\"custom\":{\"a4\":\"b4\",\"c4\":\"d4\"}}],\"remove\":[{\"id\":\"id0\"}]}" + + assert.Equal(expectedBody, string(body)) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + assert.Equal(strconv.Itoa(limit), u.Get("limit")) + assert.Equal(start, u.Get("start")) + assert.Equal(end, u.Get("end")) + assert.Equal("0", u.Get("count")) + } + +} + +func TestManageMemberships(t *testing.T) { + AssertManageMemberships(t, true, false) +} + +func TestManageMembershipsContext(t *testing.T) { + AssertManageMemberships(t, true, true) +} + +func TestManageMembershipsResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &manageMembershipsOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNManageMembershipsResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +// {"status":200,"data":[{"id":"spaceid3","custom":{"a3":"b3","c3":"d3"},"space":{"id":"spaceid3","name":"spaceid3name","description":"spaceid3desc","custom":{"a":"b"},"created":"2019-08-23T10:34:43.985248Z","updated":"2019-08-23T10:34:43.985248Z","eTag":"Aazjn7vC3oDDYw"},"created":"2019-08-23T10:41:17.156491Z","updated":"2019-08-23T10:41:17.156491Z","eTag":"AamrnoXdpdmzjwE"}],"totalCount":1,"next":"MQ"} +func TestManageMembershipsResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &manageMembershipsOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":[{"id":"spaceid3","custom":{"a3":"b3","c3":"d3"},"space":{"id":"spaceid3","name":"spaceid3name","description":"spaceid3desc","custom":{"a":"b"},"created":"2019-08-23T10:34:43.985248Z","updated":"2019-08-23T10:34:43.985248Z","eTag":"Aazjn7vC3oDDYw"},"created":"2019-08-23T10:41:17.156491Z","updated":"2019-08-23T10:41:17.156491Z","eTag":"AamrnoXdpdmzjwE"}],"totalCount":1,"next":"MQ"}`) + + r, _, err := newPNManageMembershipsResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal(1, r.TotalCount) + assert.Equal("MQ", r.Next) + assert.Equal("spaceid3", r.Data[0].ID) + assert.Equal("spaceid3", r.Data[0].Space.ID) + assert.Equal("spaceid3name", r.Data[0].Space.Name) + assert.Equal("spaceid3desc", r.Data[0].Space.Description) + assert.Equal("2019-08-23T10:34:43.985248Z", r.Data[0].Space.Created) + assert.Equal("2019-08-23T10:34:43.985248Z", r.Data[0].Space.Updated) + assert.Equal("Aazjn7vC3oDDYw", r.Data[0].Space.ETag) + assert.Equal("b", r.Data[0].Space.Custom["a"]) + assert.Equal("2019-08-23T10:41:17.156491Z", r.Data[0].Created) + assert.Equal("2019-08-23T10:41:17.156491Z", r.Data[0].Updated) + assert.Equal("AamrnoXdpdmzjwE", r.Data[0].ETag) + assert.Equal("b3", r.Data[0].Custom["a3"]) + assert.Equal("d3", r.Data[0].Custom["c3"]) + + assert.Nil(err) +} diff --git a/objects_update_space.go b/objects_update_space.go new file mode 100644 index 00000000..6470fdd0 --- /dev/null +++ b/objects_update_space.go @@ -0,0 +1,226 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" + "io/ioutil" + "net/http" + "net/url" +) + +var emptyPNUpdateSpaceResponse *PNUpdateSpaceResponse + +const updateSpacePath = "/v1/objects/%s/spaces/%s" + +type updateSpaceBuilder struct { + opts *updateSpaceOpts +} + +func newUpdateSpaceBuilder(pubnub *PubNub) *updateSpaceBuilder { + builder := updateSpaceBuilder{ + opts: &updateSpaceOpts{ + pubnub: pubnub, + }, + } + + return &builder +} + +func newUpdateSpaceBuilderWithContext(pubnub *PubNub, + context Context) *updateSpaceBuilder { + builder := updateSpaceBuilder{ + opts: &updateSpaceOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// UpdateSpaceBody is the input to update space +type UpdateSpaceBody struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Custom map[string]interface{} `json:"custom"` +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *updateSpaceBuilder) Include(include []PNUserSpaceInclude) *updateSpaceBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *updateSpaceBuilder) ID(id string) *updateSpaceBuilder { + b.opts.ID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *updateSpaceBuilder) Name(name string) *updateSpaceBuilder { + b.opts.Name = name + + return b +} + +func (b *updateSpaceBuilder) Description(description string) *updateSpaceBuilder { + b.opts.Description = description + + return b +} + +func (b *updateSpaceBuilder) Custom(custom map[string]interface{}) *updateSpaceBuilder { + b.opts.Custom = custom + + 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 *updateSpaceBuilder) QueryParam(queryParam map[string]string) *updateSpaceBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the updateSpace request. +func (b *updateSpaceBuilder) Transport(tr http.RoundTripper) *updateSpaceBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the updateSpace request. +func (b *updateSpaceBuilder) Execute() (*PNUpdateSpaceResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNUpdateSpaceResponse, status, err + } + + return newPNUpdateSpaceResponse(rawJSON, b.opts, status) +} + +type updateSpaceOpts struct { + pubnub *PubNub + Include []string + ID string + Name string + Description string + Custom map[string]interface{} + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *updateSpaceOpts) config() Config { + return *o.pubnub.Config +} + +func (o *updateSpaceOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *updateSpaceOpts) context() Context { + return o.ctx +} + +func (o *updateSpaceOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *updateSpaceOpts) buildPath() (string, error) { + return fmt.Sprintf(updateSpacePath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *updateSpaceOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *updateSpaceOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *updateSpaceOpts) buildBody() ([]byte, error) { + b := &UpdateSpaceBody{ + ID: o.ID, + Name: o.Name, + Description: o.Description, + Custom: o.Custom, + } + + jsonEncBytes, errEnc := json.Marshal(b) + + if errEnc != nil { + o.pubnub.Config.Log.Printf("ERROR: Serialization error: %s\n", errEnc.Error()) + return []byte{}, errEnc + } + return jsonEncBytes, nil + +} + +func (o *updateSpaceOpts) httpMethod() string { + return "PATCH" +} + +func (o *updateSpaceOpts) isAuthRequired() bool { + return true +} + +func (o *updateSpaceOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *updateSpaceOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *updateSpaceOpts) operationType() OperationType { + return PNUpdateSpaceOperation +} + +func (o *updateSpaceOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNUpdateSpaceResponse is the Objects API Response for Update Space +type PNUpdateSpaceResponse struct { + Status int `json:"status"` + Data PNSpace `json:"data"` +} + +func newPNUpdateSpaceResponse(jsonBytes []byte, o *updateSpaceOpts, + status StatusResponse) (*PNUpdateSpaceResponse, StatusResponse, error) { + + resp := &PNUpdateSpaceResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNUpdateSpaceResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_update_space_test.go b/objects_update_space_test.go new file mode 100644 index 00000000..5ad5cbf7 --- /dev/null +++ b/objects_update_space_test.go @@ -0,0 +1,108 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertUpdateSpace(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newUpdateSpaceBuilder(pn) + if testContext { + o = newUpdateSpaceBuilderWithContext(pn, backgroundContext) + } + + o.Include(incl) + o.ID("id0") + o.Name("name") + o.Description("exturl") + o.Custom(custom) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/spaces/%s", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + + expectedBody := "{\"id\":\"id0\",\"name\":\"name\",\"description\":\"exturl\",\"custom\":{\"a\":\"b\",\"c\":\"d\"}}" + + assert.Equal(expectedBody, string(body)) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + } + +} + +func TestUpdateSpace(t *testing.T) { + AssertUpdateSpace(t, true, false) +} + +func TestUpdateSpaceContext(t *testing.T) { + AssertUpdateSpace(t, true, true) +} + +func TestUpdateSpaceResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &updateSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNUpdateSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":{"id":"id0","name":"name","description":"desc","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T14:48:11.675743Z","eTag":"AYKH2s7ZlYKoJA"}} +func TestUpdateSpaceResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &updateSpaceOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":{"id":"id0","name":"name","description":"desc","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:08.341297Z","updated":"2019-08-20T14:48:11.675743Z","eTag":"AYKH2s7ZlYKoJA"}}`) + + r, _, err := newPNUpdateSpaceResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal("id0", r.Data.ID) + assert.Equal("name", r.Data.Name) + assert.Equal("desc", r.Data.Description) + assert.Equal("2019-08-20T13:26:08.341297Z", r.Data.Created) + assert.Equal("2019-08-20T14:48:11.675743Z", r.Data.Updated) + assert.Equal("AYKH2s7ZlYKoJA", r.Data.ETag) + assert.Equal("b", r.Data.Custom["a"]) + + assert.Nil(err) +} diff --git a/objects_update_user.go b/objects_update_user.go new file mode 100644 index 00000000..be5e4773 --- /dev/null +++ b/objects_update_user.go @@ -0,0 +1,244 @@ +package pubnub + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/pubnub/go/pnerr" + "github.com/pubnub/go/utils" +) + +var emptyPNUpdateUserResponse *PNUpdateUserResponse + +const updateUserPath = "/v1/objects/%s/users/%s" + +type updateUserBuilder struct { + opts *updateUserOpts +} + +func newUpdateUserBuilder(pubnub *PubNub) *updateUserBuilder { + builder := updateUserBuilder{ + opts: &updateUserOpts{ + pubnub: pubnub, + }, + } + + return &builder +} + +func newUpdateUserBuilderWithContext(pubnub *PubNub, + context Context) *updateUserBuilder { + builder := updateUserBuilder{ + opts: &updateUserOpts{ + pubnub: pubnub, + ctx: context, + }, + } + + return &builder +} + +// UpdateUserBody is the input to update user +type UpdateUserBody struct { + ID string `json:"id"` + Name string `json:"name"` + ExternalID string `json:"externalId"` + ProfileURL string `json:"profileUrl"` + Email string `json:"email"` + Custom map[string]interface{} `json:"custom"` +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *updateUserBuilder) Include(include []PNUserSpaceInclude) *updateUserBuilder { + b.opts.Include = utils.EnumArrayToStringArray(fmt.Sprint(include)) + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *updateUserBuilder) ID(id string) *updateUserBuilder { + b.opts.ID = id + + return b +} + +// Auth sets the Authorization key with permissions to perform the request. +func (b *updateUserBuilder) Name(name string) *updateUserBuilder { + b.opts.Name = name + + return b +} + +func (b *updateUserBuilder) ExternalID(externalID string) *updateUserBuilder { + b.opts.ExternalID = externalID + + return b +} + +func (b *updateUserBuilder) ProfileURL(profileURL string) *updateUserBuilder { + b.opts.ProfileURL = profileURL + + return b +} + +func (b *updateUserBuilder) Email(email string) *updateUserBuilder { + b.opts.Email = email + + return b +} + +func (b *updateUserBuilder) Custom(custom map[string]interface{}) *updateUserBuilder { + b.opts.Custom = custom + + 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 *updateUserBuilder) QueryParam(queryParam map[string]string) *updateUserBuilder { + b.opts.QueryParam = queryParam + + return b +} + +// Transport sets the Transport for the updateUser request. +func (b *updateUserBuilder) Transport(tr http.RoundTripper) *updateUserBuilder { + b.opts.Transport = tr + return b +} + +// Execute runs the updateUser request. +func (b *updateUserBuilder) Execute() (*PNUpdateUserResponse, StatusResponse, error) { + rawJSON, status, err := executeRequest(b.opts) + if err != nil { + return emptyPNUpdateUserResponse, status, err + } + + return newPNUpdateUserResponse(rawJSON, b.opts, status) +} + +type updateUserOpts struct { + pubnub *PubNub + Include []string + ID string + Name string + ExternalID string + ProfileURL string + Email string + Custom map[string]interface{} + QueryParam map[string]string + + Transport http.RoundTripper + + ctx Context +} + +func (o *updateUserOpts) config() Config { + return *o.pubnub.Config +} + +func (o *updateUserOpts) client() *http.Client { + return o.pubnub.GetClient() +} + +func (o *updateUserOpts) context() Context { + return o.ctx +} + +func (o *updateUserOpts) validate() error { + if o.config().SubscribeKey == "" { + return newValidationError(o, StrMissingSubKey) + } + + return nil +} + +func (o *updateUserOpts) buildPath() (string, error) { + return fmt.Sprintf(updateUserPath, + o.pubnub.Config.SubscribeKey, o.ID), nil +} + +func (o *updateUserOpts) buildQuery() (*url.Values, error) { + + q := defaultQuery(o.pubnub.Config.UUID, o.pubnub.telemetryManager) + + if o.Include != nil { + q.Set("include", string(utils.JoinChannels(o.Include))) + } + SetQueryParam(q, o.QueryParam) + + return q, nil +} + +func (o *updateUserOpts) jobQueue() chan *JobQItem { + return o.pubnub.jobQueue +} + +func (o *updateUserOpts) buildBody() ([]byte, error) { + b := &UpdateUserBody{ + ID: o.ID, + Name: o.Name, + ExternalID: o.ExternalID, + ProfileURL: o.ProfileURL, + Email: o.Email, + Custom: o.Custom, + } + + jsonEncBytes, errEnc := json.Marshal(b) + + if errEnc != nil { + o.pubnub.Config.Log.Printf("ERROR: Serialization error: %s\n", errEnc.Error()) + return []byte{}, errEnc + } + return jsonEncBytes, nil + +} + +func (o *updateUserOpts) httpMethod() string { + return "PATCH" +} + +func (o *updateUserOpts) isAuthRequired() bool { + return true +} + +func (o *updateUserOpts) requestTimeout() int { + return o.pubnub.Config.NonSubscribeRequestTimeout +} + +func (o *updateUserOpts) connectTimeout() int { + return o.pubnub.Config.ConnectTimeout +} + +func (o *updateUserOpts) operationType() OperationType { + return PNUpdateUserOperation +} + +func (o *updateUserOpts) telemetryManager() *TelemetryManager { + return o.pubnub.telemetryManager +} + +// PNUpdateUserResponse is the Objects API Response for Update user +type PNUpdateUserResponse struct { + Status int `json:"status"` + Data PNUser `json:"data"` +} + +func newPNUpdateUserResponse(jsonBytes []byte, o *updateUserOpts, + status StatusResponse) (*PNUpdateUserResponse, StatusResponse, error) { + + resp := &PNUpdateUserResponse{} + + err := json.Unmarshal(jsonBytes, &resp) + if err != nil { + e := pnerr.NewResponseParsingError("Error unmarshalling response", + ioutil.NopCloser(bytes.NewBufferString(string(jsonBytes))), err) + + return emptyPNUpdateUserResponse, status, e + } + + return resp, status, nil +} diff --git a/objects_update_user_test.go b/objects_update_user_test.go new file mode 100644 index 00000000..56538649 --- /dev/null +++ b/objects_update_user_test.go @@ -0,0 +1,113 @@ +package pubnub + +import ( + "fmt" + "testing" + + h "github.com/pubnub/go/tests/helpers" + "github.com/pubnub/go/utils" + "github.com/stretchr/testify/assert" +) + +func AssertUpdateUser(t *testing.T, checkQueryParam, testContext bool) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + incl := []PNUserSpaceInclude{ + PNUserSpaceCustom, + } + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + queryParam := map[string]string{ + "q1": "v1", + "q2": "v2", + } + + if !checkQueryParam { + queryParam = nil + } + + inclStr := utils.EnumArrayToStringArray(fmt.Sprint(incl)) + + o := newUpdateUserBuilder(pn) + if testContext { + o = newUpdateUserBuilderWithContext(pn, backgroundContext) + } + + o.Include(incl) + o.ID("id0") + o.Name("name") + o.ExternalID("exturl") + o.ProfileURL("prourl") + o.Email("email") + o.Custom(custom) + o.QueryParam(queryParam) + + path, err := o.opts.buildPath() + assert.Nil(err) + + h.AssertPathsEqual(t, + fmt.Sprintf("/v1/objects/%s/users/%s", pn.Config.SubscribeKey, "id0"), + path, []int{}) + + body, err := o.opts.buildBody() + assert.Nil(err) + + expectedBody := "{\"id\":\"id0\",\"name\":\"name\",\"externalId\":\"exturl\",\"profileUrl\":\"prourl\",\"email\":\"email\",\"custom\":{\"a\":\"b\",\"c\":\"d\"}}" + + assert.Equal(expectedBody, string(body)) + + if checkQueryParam { + u, _ := o.opts.buildQuery() + assert.Equal("v1", u.Get("q1")) + assert.Equal("v2", u.Get("q2")) + assert.Equal(string(utils.JoinChannels(inclStr)), u.Get("include")) + } + +} + +func TestUpdateUser(t *testing.T) { + AssertUpdateUser(t, true, false) +} + +func TestUpdateUserContext(t *testing.T) { + AssertUpdateUser(t, true, true) +} + +func TestUpdateUserResponseValueError(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &updateUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`s`) + + _, _, err := newPNUpdateUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal("pubnub/parsing: Error unmarshalling response: {s}", err.Error()) +} + +//{"status":200,"data":{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"}} +func TestUpdateUserResponseValuePass(t *testing.T) { + assert := assert.New(t) + pn := NewPubNub(NewDemoConfig()) + opts := &updateUserOpts{ + pubnub: pn, + } + jsonBytes := []byte(`{"status":200,"data":{"id":"id0","name":"name","externalId":"extid","profileUrl":"purl","email":"email","custom":{"a":"b","c":"d"},"created":"2019-08-20T13:26:19.140324Z","updated":"2019-08-20T13:26:19.140324Z","eTag":"AbyT4v2p6K7fpQE"}}`) + + r, _, err := newPNUpdateUserResponse(jsonBytes, opts, StatusResponse{}) + assert.Equal(200, r.Status) + assert.Equal("id0", r.Data.ID) + assert.Equal("name", r.Data.Name) + assert.Equal("extid", r.Data.ExternalID) + assert.Equal("purl", r.Data.ProfileURL) + assert.Equal("email", r.Data.Email) + assert.Equal("2019-08-20T13:26:19.140324Z", r.Data.Created) + assert.Equal("2019-08-20T13:26:19.140324Z", r.Data.Updated) + assert.Equal("AbyT4v2p6K7fpQE", r.Data.ETag) + assert.Equal("b", r.Data.Custom["a"]) + assert.Equal("d", r.Data.Custom["c"]) + + assert.Nil(err) +} diff --git a/pubnub.go b/pubnub.go index 3e915ca8..22e7b1c6 100644 --- a/pubnub.go +++ b/pubnub.go @@ -106,6 +106,118 @@ func (pn *PubNub) MessageCountsWithContext(ctx Context) *messageCountsBuilder { return newMessageCountsBuilderWithContext(pn, ctx) } +func (pn *PubNub) CreateUser() *createUserBuilder { + return newCreateUserBuilder(pn) +} + +func (pn *PubNub) CreateUserWithContext(ctx Context) *createUserBuilder { + return newCreateUserBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) GetUsers() *getUsersBuilder { + return newGetUsersBuilder(pn) +} + +func (pn *PubNub) GetUsersWithContext(ctx Context) *getUsersBuilder { + return newGetUsersBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) GetUser() *getUserBuilder { + return newGetUserBuilder(pn) +} + +func (pn *PubNub) GetUserWithContext(ctx Context) *getUserBuilder { + return newGetUserBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) UpdateUser() *updateUserBuilder { + return newUpdateUserBuilder(pn) +} + +func (pn *PubNub) UpdateUserWithContext(ctx Context) *updateUserBuilder { + return newUpdateUserBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) DeleteUser() *deleteUserBuilder { + return newDeleteUserBuilder(pn) +} + +func (pn *PubNub) DeleteUserWithContext(ctx Context) *deleteUserBuilder { + return newDeleteUserBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) CreateSpace() *createSpaceBuilder { + return newCreateSpaceBuilder(pn) +} + +func (pn *PubNub) CreateSpaceWithContext(ctx Context) *createSpaceBuilder { + return newCreateSpaceBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) GetSpaces() *getSpacesBuilder { + return newGetSpacesBuilder(pn) +} + +func (pn *PubNub) GetSpacesWithContext(ctx Context) *getSpacesBuilder { + return newGetSpacesBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) GetSpace() *getSpaceBuilder { + return newGetSpaceBuilder(pn) +} + +func (pn *PubNub) GetSpaceWithContext(ctx Context) *getSpaceBuilder { + return newGetSpaceBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) UpdateSpace() *updateSpaceBuilder { + return newUpdateSpaceBuilder(pn) +} + +func (pn *PubNub) UpdateSpaceWithContext(ctx Context) *updateSpaceBuilder { + return newUpdateSpaceBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) DeleteSpace() *deleteSpaceBuilder { + return newDeleteSpaceBuilder(pn) +} + +func (pn *PubNub) DeleteSpaceWithContext(ctx Context) *deleteSpaceBuilder { + return newDeleteSpaceBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) GetMemberships() *getMembershipsBuilder { + return newGetMembershipsBuilder(pn) +} + +func (pn *PubNub) GetMembershipsWithContext(ctx Context) *getMembershipsBuilder { + return newGetMembershipsBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) GetMembers() *getMembersBuilder { + return newGetMembersBuilder(pn) +} + +func (pn *PubNub) GetMembersWithContext(ctx Context) *getMembersBuilder { + return newGetMembersBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) ManageMembers() *manageMembersBuilder { + return newManageMembersBuilder(pn) +} + +func (pn *PubNub) ManageMembersWithContext(ctx Context) *manageMembersBuilder { + return newManageMembersBuilderWithContext(pn, ctx) +} + +func (pn *PubNub) ManageMemberships() *manageMembershipsBuilder { + return newManageMembershipsBuilder(pn) +} + +func (pn *PubNub) ManageMembershipsWithContext(ctx Context) *manageMembershipsBuilder { + return newManageMembershipsBuilderWithContext(pn, ctx) +} + func (pn *PubNub) Signal() *signalBuilder { return newSignalBuilder(pn) } diff --git a/request.go b/request.go index 7943b9f3..e493e547 100644 --- a/request.go +++ b/request.go @@ -47,6 +47,17 @@ func addToJobQ(req *http.Request, client *http.Client, opts endpointOpts, j chan opts.jobQueue() <- jqi } +func buildBody(opts endpointOpts, url *url.URL) (io.Reader, error) { + b, err := opts.buildBody() + if err != nil { + opts.config().Log.Println("PNUnknownCategory", err, url) + return nil, err + } + opts.config().Log.Println("BODY", string(b)) + + return bytes.NewReader(b), nil +} + func executeRequest(opts endpointOpts) ([]byte, StatusResponse, error) { err := opts.validate() @@ -71,18 +82,21 @@ func executeRequest(opts endpointOpts) ([]byte, StatusResponse, error) { var req *http.Request if opts.httpMethod() == "POST" { - b, err := opts.buildBody() + body, err := buildBody(opts, url) if err != nil { - opts.config().Log.Println("PNUnknownCategory", err, url) - return nil, - createStatus(PNUnknownCategory, "", ResponseInfo{}, err), - err + return nil, createStatus(PNUnknownCategory, "", ResponseInfo{}, err), err } - body := bytes.NewReader(b) req, err = newRequest("POST", url, body, opts.config().UseHTTP2) } else if opts.httpMethod() == "DELETE" { req, err = newRequest("DELETE", url, nil, opts.config().UseHTTP2) + } else if opts.httpMethod() == "PATCH" { + body, err := buildBody(opts, url) + if err != nil { + return nil, createStatus(PNUnknownCategory, "", ResponseInfo{}, err), err + } + + req, err = newRequest("PATCH", url, body, opts.config().UseHTTP2) } else { req, err = newRequest("GET", url, nil, opts.config().UseHTTP2) } @@ -243,7 +257,6 @@ func parseResponse(resp *http.Response, opts endpointOpts) ([]byte, StatusRespon } opts.config().Log.Println("200 OK: resp.StatusCode, resp.Status, resp.Body, resp.Request.URL, string(body)", resp.StatusCode, resp.Status, resp.Body, resp.Request.URL, string(body)) - return body, status, nil } diff --git a/subscription_manager.go b/subscription_manager.go index 9aa52f9c..23f53354 100644 --- a/subscription_manager.go +++ b/subscription_manager.go @@ -565,7 +565,7 @@ func processSubscribePayload(m *SubscriptionManager, payload subscribeMessage) { if presencePayload, ok = payload.Payload.(map[string]interface{}); !ok { m.listenerManager.announceStatus(&PNStatus{ Category: PNUnknownCategory, - ErrorData: errors.New("Response presence parsing error"), + ErrorData: errors.New("Presence response parsing error"), Error: true, Operation: PNSubscribeOperation, AffectedChannels: []string{channel}, @@ -637,9 +637,29 @@ func processSubscribePayload(m *SubscriptionManager, payload subscribeMessage) { } var messagePayload interface{} - if payload.MessageType == PNMessageTypeSignal { - messagePayload = payload.Payload - } else { + switch payload.MessageType { + case PNMessageTypeSignal: + pnMessageResult := createPNMessageResult(payload.Payload, actualCh, subscribedCh, channel, subscriptionMatch, payload.IssuingClientID, payload.UserMetadata, timetoken) + m.pubnub.Config.Log.Println("announceSignal,", pnMessageResult) + m.listenerManager.announceSignal(pnMessageResult) + case PNMessageTypeObjects: + pnUserEvent, pnSpaceEvent, pnMembershipEvent, eventType := createPNObjectsResult(payload.Payload, m, actualCh, subscribedCh, channel, subscriptionMatch) + m.pubnub.Config.Log.Println("announceObjects,", pnUserEvent, pnSpaceEvent, pnMembershipEvent, eventType) + //go func() { + switch eventType { + case PNObjectsUserEvent: + m.pubnub.Config.Log.Println("pnUserEvent:", pnUserEvent) + m.listenerManager.announceUserEvent(pnUserEvent) + case PNObjectsSpaceEvent: + m.pubnub.Config.Log.Println("pnSpaceEvent:", pnSpaceEvent) + m.listenerManager.announceSpaceEvent(pnSpaceEvent) + case PNObjectsMembershipEvent: + m.pubnub.Config.Log.Println("pnMembershipEvent:", pnMembershipEvent) + m.listenerManager.announceMembershipEvent(pnMembershipEvent) + } + //}() + + default: var err error messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config) if err != nil { @@ -652,30 +672,188 @@ func processSubscribePayload(m *SubscriptionManager, payload subscribeMessage) { } m.pubnub.Config.Log.Println("DecryptString: err", err, pnStatus) m.listenerManager.announceStatus(pnStatus) + } + pnMessageResult := createPNMessageResult(messagePayload, actualCh, subscribedCh, channel, subscriptionMatch, payload.IssuingClientID, payload.UserMetadata, timetoken) + m.pubnub.Config.Log.Println("announceMessage,", pnMessageResult) + m.listenerManager.announceMessage(pnMessageResult) } - pnMessageResult := &PNMessage{ - Message: messagePayload, - ActualChannel: actualCh, - SubscribedChannel: subscribedCh, - Channel: channel, - Subscription: subscriptionMatch, - Timetoken: timetoken, - Publisher: payload.IssuingClientID, - UserMetadata: payload.UserMetadata, + // if payload.MessageType == PNMessageTypeSignal { + // messagePayload = payload.Payload + // } else if payload.MessageType == PNMessageTypeObjects { + // messagePayload = payload.Payload + // } else { + // var err error + // messagePayload, err = parseCipherInterface(payload.Payload, m.pubnub.Config) + // if err != nil { + // pnStatus := &PNStatus{ + // Category: PNBadRequestCategory, + // ErrorData: err, + // Error: true, + // Operation: PNSubscribeOperation, + // AffectedChannels: []string{channel}, + // } + // m.pubnub.Config.Log.Println("DecryptString: err", err, pnStatus) + // m.listenerManager.announceStatus(pnStatus) + // } + // } + + // if payload.MessageType == PNMessageTypeSignal { + // m.pubnub.Config.Log.Println("announceSignal,", pnMessageResult) + // m.listenerManager.announceSignal(pnMessageResult) + + // } else { + // m.pubnub.Config.Log.Println("announceMessage,", pnMessageResult) + // m.listenerManager.announceMessage(pnMessageResult) + // } + m.pubnub.Config.Log.Println("after announceMessage") + } +} + +func createPNObjectsResult(objPayload interface{}, m *SubscriptionManager, actualCh, subscribedCh, channel, subscriptionMatch string) (*PNUserEvent, *PNSpaceEvent, *PNMembershipEvent, PNObjectsEventType) { + var objectsPayload map[string]interface{} + var ok bool + if objectsPayload, ok = objPayload.(map[string]interface{}); !ok { + m.listenerManager.announceStatus(&PNStatus{ + Category: PNUnknownCategory, + ErrorData: errors.New("Objects response parsing error"), + Error: true, + Operation: PNSubscribeOperation, + AffectedChannels: []string{channel}, + }) + } + eventType := PNObjectsEventType(objectsPayload["type"].(string)) + event := PNObjectsEvent(objectsPayload["event"].(string)) + var id, userID, spaceID, description, timestamp, created, updated, eTag, name, externalID, profileURL, email string + var custom, data map[string]interface{} + if objectsPayload["data"] != nil { + data = objectsPayload["data"].(map[string]interface{}) + if data["userId"] != nil { + userID = data["userId"].(string) + } + if data["id"] != nil { + id = data["id"].(string) + } + if data["spaceId"] != nil { + spaceID = data["spaceId"].(string) + } + if data["name"] != nil { + name = data["name"].(string) + } + if data["externalId"] != nil { + externalID = data["externalId"].(string) + } + if data["profileUrl"] != nil { + profileURL = data["profileUrl"].(string) + } + if data["email"] != nil { + email = data["email"].(string) + } + if data["description"] != nil { + description = data["description"].(string) + } + if data["timestamp"] != nil { + timestamp = data["timestamp"].(string) + } + if data["created"] != nil { + created = data["created"].(string) + } + if data["updated"] != nil { + updated = data["updated"].(string) + } + if data["eTag"] != nil { + eTag = data["eTag"].(string) + } + if data["custom"] != nil { + custom = data["custom"].(map[string]interface{}) } - if payload.MessageType == PNMessageTypeSignal { - m.pubnub.Config.Log.Println("announceSignal,", pnMessageResult) - m.listenerManager.announceSignal(pnMessageResult) + } - } else { - m.pubnub.Config.Log.Println("announceMessage,", pnMessageResult) - m.listenerManager.announceMessage(pnMessageResult) - } - m.pubnub.Config.Log.Println("after announceMessage") + pnObjectsResult := &PNObjectsResponse{ + Event: event, + EventType: eventType, + UserID: userID, + SpaceID: spaceID, + Description: description, + Timestamp: timestamp, + Created: created, + Updated: updated, + ETag: eTag, + Custom: custom, + Data: data, + Name: name, + ExternalID: externalID, + ProfileURL: profileURL, + Email: email, } + + pnSpaceEvent := &PNSpaceEvent{ + Event: pnObjectsResult.Event, + SpaceID: id, + Description: pnObjectsResult.Description, + Timestamp: pnObjectsResult.Timestamp, + Name: pnObjectsResult.Name, + Created: pnObjectsResult.Created, + Updated: pnObjectsResult.Updated, + ETag: pnObjectsResult.ETag, + Custom: pnObjectsResult.Custom, + ActualChannel: actualCh, + SubscribedChannel: subscribedCh, + Channel: channel, + Subscription: subscriptionMatch, + } + + pnUserEvent := &PNUserEvent{ + Event: pnObjectsResult.Event, + UserID: id, + Timestamp: pnObjectsResult.Timestamp, + Created: pnObjectsResult.Created, + Updated: pnObjectsResult.Updated, + ETag: pnObjectsResult.ETag, + Custom: pnObjectsResult.Custom, + Name: pnObjectsResult.Name, + ExternalID: pnObjectsResult.ExternalID, + ProfileURL: pnObjectsResult.ProfileURL, + Email: pnObjectsResult.Email, + ActualChannel: actualCh, + SubscribedChannel: subscribedCh, + Channel: channel, + Subscription: subscriptionMatch, + } + + pnMembershipEvent := &PNMembershipEvent{ + Event: pnObjectsResult.Event, + UserID: pnObjectsResult.UserID, + SpaceID: pnObjectsResult.SpaceID, + Description: pnObjectsResult.Description, + Timestamp: pnObjectsResult.Timestamp, + Custom: pnObjectsResult.Custom, + ActualChannel: actualCh, + SubscribedChannel: subscribedCh, + Channel: channel, + Subscription: subscriptionMatch, + } + + return pnUserEvent, pnSpaceEvent, pnMembershipEvent, eventType +} + +func createPNMessageResult(messagePayload interface{}, actualCh, subscribedCh, channel, subscriptionMatch, issuingClientID string, userMetadata interface{}, timetoken int64) *PNMessage { + + pnMessageResult := &PNMessage{ + Message: messagePayload, + ActualChannel: actualCh, + SubscribedChannel: subscribedCh, + Channel: channel, + Subscription: subscriptionMatch, + Timetoken: timetoken, + Publisher: issuingClientID, + UserMetadata: userMetadata, + } + + return pnMessageResult + } // parseCipherInterface handles the decryption in case a cipher key is used diff --git a/telemetry_manager.go b/telemetry_manager.go index c1ff98ac..3efe8e0f 100644 --- a/telemetry_manager.go +++ b/telemetry_manager.go @@ -179,6 +179,35 @@ func telemetryEndpointNameForOperation(t OperationType) string { case PNSignalOperation: endpoint = "sig" break + case PNCreateUserOperation: + fallthrough + case PNGetUsersOperation: + fallthrough + case PNGetUserOperation: + fallthrough + case PNUpdateUserOperation: + fallthrough + case PNDeleteUserOperation: + fallthrough + case PNGetSpaceOperation: + fallthrough + case PNGetSpacesOperation: + fallthrough + case PNCreateSpaceOperation: + fallthrough + case PNDeleteSpaceOperation: + fallthrough + case PNUpdateSpaceOperation: + fallthrough + case PNGetMembershipsOperation: + fallthrough + case PNGetMembersOperation: + fallthrough + case PNManageMembershipsOperation: + fallthrough + case PNManageMembersOperation: + endpoint = "obj" + break default: endpoint = "time" break diff --git a/tests/e2e/grant_test.go b/tests/e2e/grant_test.go index d98a05bc..a3dde9ca 100644 --- a/tests/e2e/grant_test.go +++ b/tests/e2e/grant_test.go @@ -44,8 +44,8 @@ func TestGrantParseLogsForAuthKey(t *testing.T) { //fmt.Printf("Captured: %s", out) s := fmt.Sprintf("%s", out) - // //https://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-e41d50d4-43ce-11e8-a433-9e6b275e7b64?w=1&m=1&channel=ch1,ch2×tamp=1535719943&auth=myAuthKey&pnsdk=PubNub-Go/4.1.3&uuid=pn-621c7b2a-f87c-4362-bd1e-6c6762dfc667&r=1&signature=PntTQe-zBfJa6AvN4bu4u0txG_TOoksHGod7OnijmwM= - // expected := fmt.Sprintf("https://%s/v1/auth/grant/sub-key/%s?&uuid=%sw=1&m=1&channel=ch1,ch2", + // //https://ps.pndsn.com/v2/auth/grant/sub-key/sub-c-e41d50d4-43ce-11e8-a433-9e6b275e7b64?w=1&m=1&channel=ch1,ch2×tamp=1535719943&auth=myAuthKey&pnsdk=PubNub-Go/4.1.3&uuid=pn-621c7b2a-f87c-4362-bd1e-6c6762dfc667&r=1&signature=PntTQe-zBfJa6AvN4bu4u0txG_TOoksHGod7OnijmwM= + // expected := fmt.Sprintf("https://%s/v2/auth/grant/sub-key/%s?&uuid=%sw=1&m=1&channel=ch1,ch2", // pn.Config.Origin, // pn.Config.SubscribeKey, // ) @@ -93,7 +93,7 @@ func TestGrantParseLogsForMultipleAuthKeys(t *testing.T) { s := fmt.Sprintf("%s", out) - //https://ps.pndsn.com/v1/auth/grant/sub-key/sub-c-e41d50d4-43ce-11e8-a433-9e6b275e7b64?m=1&auth=authkey1,authkey2&channel=ch1,ch2×tamp=1535719219&pnsdk=PubNub-Go/4.1.3&uuid=pn-a83164fe-7ecf-42ab-ba14-d2d8e6eabd7a&r=1&w=1&signature=0SkyfvohAq8_0phVi0YhCL4c2ZRSPBVwCwQ9fANvPmM= + //https://ps.pndsn.com/v2/auth/grant/sub-key/sub-c-e41d50d4-43ce-11e8-a433-9e6b275e7b64?m=1&auth=authkey1,authkey2&channel=ch1,ch2×tamp=1535719219&pnsdk=PubNub-Go/4.1.3&uuid=pn-a83164fe-7ecf-42ab-ba14-d2d8e6eabd7a&r=1&w=1&signature=0SkyfvohAq8_0phVi0YhCL4c2ZRSPBVwCwQ9fANvPmM= assert.Contains(s, "auth=authkey1,authkey2") } @@ -208,7 +208,7 @@ func TestGrantMultipleMixed(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "auth=my-auth-key-1%2Cmy-auth-key-2&channel=ch1%2Cch2%2Cch3&channel-group=cg1%2Ccg2%2Ccg3&r=1&m=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"channel-group+auth","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channels":{"ch1":{"auths":{"my-auth-key-1":{"r":1,"w":1,"m":1,"d":0},"my-auth-key-2":{"r":1,"w":1,"m":1,"d":0}}},"ch2":{"auths":{"my-auth-key-1":{"r":1,"w":1,"m":1,"d":0},"my-auth-key-2":{"r":1,"w":1,"m":1,"d":0}}},"ch3":{"auths":{"my-auth-key-1":{"r":1,"w":1,"m":1,"d":0},"my-auth-key-2":{"r":1,"w":1,"m":1,"d":0}}}},"channel-groups":{"cg1":{"auths":{"my-auth-key-1":{"r":1,"w":1,"m":1,"d":0},"my-auth-key-2":{"r":1,"w":1,"m":1,"d":0}}},"cg2":{"auths":{"my-auth-key-1":{"r":1,"w":1,"m":1,"d":0},"my-auth-key-2":{"r":1,"w":1,"m":1,"d":0}}},"cg3":{"auths":{"my-auth-key-1":{"r":1,"w":1,"m":1,"d":0},"my-auth-key-2":{"r":1,"w":1,"m":1,"d":0}}}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "timestamp", "signature"}, @@ -236,7 +236,7 @@ func TestGrantSingleChannel(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "channel=ch1&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"channel","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channels":{"ch1":{"r":1,"w":1,"m":0,"d":0}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, @@ -265,7 +265,7 @@ func TestGrantSingleChannelWithAuth(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "auth=my-pam-key&channel=ch1&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"user","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channel":"ch1","auths":{"my-pam-key":{"r":1,"w":1,"m":0,"d":0}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, @@ -294,7 +294,7 @@ func TestGrantMultipleChannels(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "channel=ch1%2Cch2&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"channel","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channels":{"ch1":{"r":1,"w":1,"m":0,"d":0},"ch2":{"r":1,"w":1,"m":0,"d":0}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, @@ -327,7 +327,7 @@ func TestGrantMultipleChannelsWithAuth(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "auth=my-pam-key&channel=ch1%2Cch2&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"user","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channels":{"ch1":{"auths":{"my-pam-key":{"r":1,"w":1,"m":0,"d":0}}},"ch2":{"auths":{"my-pam-key":{"r":1,"w":1,"m":0,"d":0}}}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, @@ -361,7 +361,7 @@ func TestGrantSingleGroup(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "channel-group=cg1&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"channel-group","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channel-groups":{"cg1":{"r":1,"w":1,"m":0,"d":0}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, @@ -390,7 +390,7 @@ func TestGrantSingleGroupWithAuth(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "auth=my-pam-key&channel-group=cg1&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"channel-group+auth","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channel-groups":"cg1","auths":{"my-pam-key":{"r":1,"w":1,"m":0,"d":0}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, @@ -421,7 +421,7 @@ func TestGrantMultipleGroups(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "channel-group=cg1%2Ccg2&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"channel-group","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channel-groups":{"cg1":{"r":1,"w":1,"m":0,"d":0},"cg2":{"r":1,"w":1,"m":0,"d":0}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, @@ -454,7 +454,7 @@ func TestGrantMultipleGroupsWithAuth(t *testing.T) { interceptor := stubs.NewInterceptor() interceptor.AddStub(&stubs.Stub{ Method: "GET", - Path: fmt.Sprintf("/v1/auth/grant/sub-key/%s", pamConfig.SubscribeKey), + Path: fmt.Sprintf("/v2/auth/grant/sub-key/%s", pamConfig.SubscribeKey), Query: "auth=my-pam-key&channel-group=cg1%2Ccg2&m=0&r=1&w=1&d=0", ResponseBody: `{"message":"Success","payload":{"level":"channel-group+auth","subscribe_key":"sub-c-b9ab9508-43cf-11e8-9967-869954283fb4","ttl":1440,"channel-groups":{"cg1":{"auths":{"my-pam-key":{"r":1,"w":1,"m":0,"d":0}}},"cg2":{"auths":{"my-pam-key":{"r":1,"w":1,"m":0,"d":0}}}}},"service":"Access Manager","status":200}`, IgnoreQueryKeys: []string{"uuid", "pnsdk", "signature", "timestamp"}, diff --git a/tests/e2e/helper.go b/tests/e2e/helper.go index 61316099..83c25632 100644 --- a/tests/e2e/helper.go +++ b/tests/e2e/helper.go @@ -30,13 +30,13 @@ func init() { rand.Seed(time.Now().UnixNano()) config = pubnub.NewConfig() - config.PublishKey = "pub-c-c1648ded-d156-4a2d-9dbb-a23262945fe2" - config.SubscribeKey = "sub-c-c14b8948-7dfe-11e9-aee4-2e27e4d79cf8" + config.PublishKey = "pub-c-3ed95c83-12e6-4cda-9d69-c47ba2abb57e" + config.SubscribeKey = "sub-c-26a73b0a-c3f2-11e9-8b24-569e8a5c3af3" pamConfig = pubnub.NewConfig() - pamConfig.PublishKey = "pub-c-2dea72e4-e0aa-4c85-9411-d75baf7568b7" - pamConfig.SubscribeKey = "sub-c-490a8ac8-7e0e-11e9-84e9-eed29b7b36d8" - pamConfig.SecretKey = "sec-c-MDU3OGY1ZjMtMDUwZS00NTc4LWFhM2ItN2E3NzhmMDVkZmQx" + pamConfig.PublishKey = "pub-c-cdea0ef1-c571-4b72-b43f-ff1dc8aa4c5d" + pamConfig.SubscribeKey = "sub-c-4757f09c-c3f2-11e9-9d00-8a58a5558306" + pamConfig.SecretKey = "sec-c-YTYxNzVjYzctNDY2MS00N2NmLTg2NjYtNGRlNWY1NjMxMDBm" } diff --git a/tests/e2e/objects_test.go b/tests/e2e/objects_test.go new file mode 100644 index 00000000..0c6da37b --- /dev/null +++ b/tests/e2e/objects_test.go @@ -0,0 +1,896 @@ +package e2e + +import ( + "fmt" + //"log" + //"os" + "testing" + "time" + + pubnub "github.com/pubnub/go" + "github.com/stretchr/testify/assert" +) + +func TestObjectsCreateUpdateGetDeleteUser(t *testing.T) { + assert := assert.New(t) + + pn := pubnub.NewPubNub(configCopy()) + + r := GenRandom() + + id := fmt.Sprintf("testuser_%d", r.Intn(99999)) + name := "name" + extid := "extid" + purl := "profileurl" + email := "email" + + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, _, err := pn.CreateUser().Include(incl).ID(id).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() + assert.Nil(err) + assert.Equal(200, res.Status) + assert.Equal(id, res.Data.ID) + assert.Equal(name, res.Data.Name) + assert.Equal(extid, res.Data.ExternalID) + assert.Equal(purl, res.Data.ProfileURL) + assert.Equal(email, res.Data.Email) + assert.NotNil(res.Data.Created) + assert.NotNil(res.Data.Updated) + assert.NotNil(res.Data.ETag) + assert.Equal("b", res.Data.Custom["a"]) + assert.Equal("d", res.Data.Custom["c"]) + + email = "email2" + + res2, _, err2 := pn.UpdateUser().Include(incl).ID(id).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() + assert.Nil(err2) + assert.Equal(200, res2.Status) + assert.Equal(id, res2.Data.ID) + assert.Equal(name, res2.Data.Name) + assert.Equal(extid, res2.Data.ExternalID) + assert.Equal(purl, res2.Data.ProfileURL) + assert.Equal(email, res2.Data.Email) + assert.Equal(res.Data.Created, res2.Data.Created) + assert.NotNil(res2.Data.Updated) + assert.NotNil(res2.Data.ETag) + assert.Equal("b", res2.Data.Custom["a"]) + assert.Equal("d", res2.Data.Custom["c"]) + + res3, _, err3 := pn.GetUser().Include(incl).ID(id).Execute() + assert.Nil(err3) + assert.Equal(200, res3.Status) + assert.Equal(id, res3.Data.ID) + assert.Equal(name, res3.Data.Name) + assert.Equal(extid, res3.Data.ExternalID) + assert.Equal(purl, res3.Data.ProfileURL) + assert.Equal(email, res3.Data.Email) + assert.Equal(res.Data.Created, res3.Data.Created) + assert.Equal(res2.Data.Updated, res3.Data.Updated) + assert.Equal(res2.Data.ETag, res3.Data.ETag) + assert.Equal("b", res3.Data.Custom["a"]) + assert.Equal("d", res3.Data.Custom["c"]) + + //getusers + res6, _, err6 := pn.GetUsers().Include(incl).Limit(100).Count(true).Execute() + assert.Nil(err6) + assert.Equal(200, res6.Status) + assert.True(res6.TotalCount > 0) + found := false + for i := range res6.Data { + if res6.Data[i].ID == id { + assert.Equal(name, res6.Data[i].Name) + assert.Equal(extid, res6.Data[i].ExternalID) + assert.Equal(purl, res6.Data[i].ProfileURL) + assert.Equal(email, res6.Data[i].Email) + assert.Equal(res.Data.Created, res6.Data[i].Created) + assert.Equal(res2.Data.Updated, res6.Data[i].Updated) + assert.Equal(res2.Data.ETag, res6.Data[i].ETag) + assert.Equal("b", res6.Data[i].Custom["a"]) + assert.Equal("d", res6.Data[i].Custom["c"]) + found = true + } + } + assert.True(found) + + //delete + res5, _, err5 := pn.DeleteUser().ID(id).Execute() + assert.Nil(err5) + assert.Equal(200, res5.Status) + assert.Nil(res5.Data) + + //getuser + res4, _, err4 := pn.GetUser().Include(incl).ID(id).Execute() + assert.NotNil(err4) + assert.Nil(res4) + +} + +func TestObjectsCreateUpdateGetDeleteSpace(t *testing.T) { + assert := assert.New(t) + + pn := pubnub.NewPubNub(configCopy()) + + r := GenRandom() + + id := fmt.Sprintf("testspace_%d", r.Intn(99999)) + name := "name" + desc := "desc" + + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, _, err := pn.CreateSpace().Include(incl).ID(id).Name(name).Description(desc).Custom(custom).Execute() + assert.Nil(err) + assert.Equal(200, res.Status) + assert.Equal(id, res.Data.ID) + assert.Equal(name, res.Data.Name) + assert.Equal(desc, res.Data.Description) + assert.NotNil(res.Data.Created) + assert.NotNil(res.Data.Updated) + assert.NotNil(res.Data.ETag) + assert.Equal("b", res.Data.Custom["a"]) + assert.Equal("d", res.Data.Custom["c"]) + + desc = "desc2" + + res2, _, err2 := pn.UpdateSpace().Include(incl).ID(id).Name(name).Description(desc).Custom(custom).Execute() + assert.Nil(err2) + assert.Equal(200, res2.Status) + assert.Equal(id, res2.Data.ID) + assert.Equal(name, res2.Data.Name) + assert.Equal(desc, res2.Data.Description) + assert.Equal(res.Data.Created, res2.Data.Created) + assert.NotNil(res2.Data.Updated) + assert.NotNil(res2.Data.ETag) + assert.Equal("b", res2.Data.Custom["a"]) + assert.Equal("d", res2.Data.Custom["c"]) + + res3, _, err3 := pn.GetSpace().Include(incl).ID(id).Execute() + assert.Nil(err3) + assert.Equal(200, res3.Status) + assert.Equal(id, res3.Data.ID) + assert.Equal(name, res3.Data.Name) + assert.Equal(desc, res3.Data.Description) + assert.Equal(res.Data.Created, res3.Data.Created) + assert.Equal(res2.Data.Updated, res3.Data.Updated) + assert.Equal(res2.Data.ETag, res3.Data.ETag) + assert.Equal("b", res3.Data.Custom["a"]) + assert.Equal("d", res3.Data.Custom["c"]) + + //getusers + res6, _, err6 := pn.GetSpaces().Include(incl).Limit(100).Count(true).Execute() + assert.Nil(err6) + assert.Equal(200, res6.Status) + assert.True(res6.TotalCount > 0) + found := false + for i := range res6.Data { + if res6.Data[i].ID == id { + assert.Equal(name, res6.Data[i].Name) + assert.Equal(desc, res6.Data[i].Description) + assert.Equal(res.Data.Created, res6.Data[i].Created) + assert.Equal(res2.Data.Updated, res6.Data[i].Updated) + assert.Equal(res2.Data.ETag, res6.Data[i].ETag) + assert.Equal("b", res6.Data[i].Custom["a"]) + assert.Equal("d", res6.Data[i].Custom["c"]) + found = true + } + } + assert.True(found) + + //delete + res5, _, err5 := pn.DeleteSpace().ID(id).Execute() + assert.Nil(err5) + assert.Equal(200, res5.Status) + assert.Nil(res5.Data) + + //getuser + res4, _, err4 := pn.GetSpace().Include(incl).ID(id).Execute() + assert.NotNil(err4) + assert.Nil(res4) + +} + +func TestObjectsMemberships(t *testing.T) { + assert := assert.New(t) + + limit := 100 + count := true + + pn := pubnub.NewPubNub(configCopy()) + + r := GenRandom() + + userid := fmt.Sprintf("testuser_%d", r.Intn(99999)) + + name := "name" + extid := "extid" + purl := "profileurl" + email := "email" + + custom := make(map[string]interface{}) + custom["a"] = "b" + custom["c"] = "d" + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + res, _, err := pn.CreateUser().Include(incl).ID(userid).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() + assert.Nil(err) + assert.Equal(200, res.Status) + assert.Equal(userid, res.Data.ID) + assert.Equal(name, res.Data.Name) + assert.Equal(extid, res.Data.ExternalID) + assert.Equal(purl, res.Data.ProfileURL) + assert.Equal(email, res.Data.Email) + assert.NotNil(res.Data.Created) + assert.NotNil(res.Data.Updated) + assert.NotNil(res.Data.ETag) + assert.Equal("b", res.Data.Custom["a"]) + assert.Equal("d", res.Data.Custom["c"]) + + spaceid := fmt.Sprintf("testspace_%d", r.Intn(99999)) + desc := "desc" + custom2 := make(map[string]interface{}) + custom2["a1"] = "b1" + custom2["c1"] = "d1" + + res2, _, err2 := pn.CreateSpace().Include(incl).ID(spaceid).Name(name).Description(desc).Custom(custom2).Execute() + assert.Nil(err2) + assert.Equal(200, res2.Status) + assert.Equal(spaceid, res2.Data.ID) + assert.Equal(name, res2.Data.Name) + assert.Equal(desc, res2.Data.Description) + assert.NotNil(res2.Data.Created) + assert.NotNil(res2.Data.Updated) + assert.NotNil(res2.Data.ETag) + assert.Equal("b1", res2.Data.Custom["a1"]) + assert.Equal("d1", res2.Data.Custom["c1"]) + + userid2 := fmt.Sprintf("testuser_%d", r.Intn(99999)) + + res3, _, err3 := pn.CreateUser().Include(incl).ID(userid2).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(custom).Execute() + assert.Nil(err3) + assert.Equal(200, res3.Status) + + spaceid2 := fmt.Sprintf("testspace_%d", r.Intn(99999)) + + res4, _, err4 := pn.CreateSpace().Include(incl).ID(spaceid2).Name(name).Description(desc).Custom(custom2).Execute() + assert.Nil(err4) + assert.Equal(200, res4.Status) + + inclSm := []pubnub.PNMembersInclude{ + pubnub.PNMembersCustom, + pubnub.PNMembersUser, + pubnub.PNMembersUserCustom, + } + + custom3 := make(map[string]interface{}) + custom3["a3"] = "b3" + custom3["c3"] = "d3" + + in := pubnub.PNMembersInput{ + ID: userid, + Custom: custom3, + } + + inArr := []pubnub.PNMembersInput{ + in, + } + + //Add Space Memberships + + resAdd, _, errAdd := pn.ManageMembers().SpaceID(spaceid).Add(inArr).Update([]pubnub.PNMembersInput{}).Remove([]pubnub.PNMembersRemove{}).Include(inclSm).Limit(limit).Count(count).Execute() + assert.Nil(errAdd) + assert.Equal(200, resAdd.Status) + assert.True(resAdd.TotalCount > 0) + fmt.Println("resAdd-->", resAdd) + found := false + for i := range resAdd.Data { + if resAdd.Data[i].ID == userid { + found = true + assert.Equal(custom3["a3"], resAdd.Data[i].Custom["a3"]) + assert.Equal(custom3["c3"], resAdd.Data[i].Custom["c3"]) + assert.Equal(userid, resAdd.Data[0].User.ID) + assert.Equal(name, resAdd.Data[0].User.Name) + assert.Equal(extid, resAdd.Data[0].User.ExternalID) + assert.Equal(purl, resAdd.Data[0].User.ProfileURL) + assert.Equal(email, resAdd.Data[0].User.Email) + assert.Equal(custom["a"], resAdd.Data[0].User.Custom["a"]) + assert.Equal(custom["c"], resAdd.Data[0].User.Custom["c"]) + } + } + assert.True(found) + + //Update Space Memberships + + custom4 := make(map[string]interface{}) + custom4["a2"] = "b2" + custom4["c2"] = "d2" + + up := pubnub.PNMembersInput{ + ID: userid, + Custom: custom4, + } + + upArr := []pubnub.PNMembersInput{ + up, + } + + resUp, _, errUp := pn.ManageMembers().SpaceID(spaceid).Add([]pubnub.PNMembersInput{}).Update(upArr).Remove([]pubnub.PNMembersRemove{}).Include(inclSm).Limit(limit).Count(count).Execute() + assert.Nil(errUp) + assert.Equal(200, resUp.Status) + assert.True(resUp.TotalCount > 0) + foundUp := false + for i := range resUp.Data { + if resUp.Data[i].ID == userid { + foundUp = true + assert.Equal("b2", resUp.Data[i].Custom["a2"]) + assert.Equal("d2", resUp.Data[i].Custom["c2"]) + assert.Equal(userid, resAdd.Data[0].User.ID) + assert.Equal(name, resAdd.Data[0].User.Name) + assert.Equal(extid, resAdd.Data[0].User.ExternalID) + assert.Equal(purl, resAdd.Data[0].User.ProfileURL) + assert.Equal(email, resAdd.Data[0].User.Email) + assert.Equal(custom["a"], resAdd.Data[0].User.Custom["a"]) + assert.Equal(custom["c"], resAdd.Data[0].User.Custom["c"]) + + } + } + assert.True(foundUp) + + //Get Space Memberships + + inclMemberships := []pubnub.PNMembershipsInclude{ + pubnub.PNMembershipsCustom, + pubnub.PNMembershipsSpace, + pubnub.PNMembershipsSpaceCustom, + } + + resGetMem, _, errGetMem := pn.GetMemberships().UserID(userid).Include(inclMemberships).Limit(limit).Count(count).Execute() + foundGetMem := false + assert.Nil(errGetMem) + for i := range resGetMem.Data { + if resGetMem.Data[i].ID == spaceid { + foundGetMem = true + assert.Equal(name, resGetMem.Data[i].Space.Name) + assert.Equal(desc, resGetMem.Data[i].Space.Description) + assert.Equal("b1", resGetMem.Data[i].Space.Custom["a1"]) + assert.Equal("d1", resGetMem.Data[i].Space.Custom["c1"]) + assert.Equal("b2", resGetMem.Data[i].Custom["a2"]) + assert.Equal("d2", resGetMem.Data[i].Custom["c2"]) + } + } + assert.Equal(200, resGetMem.Status) + assert.True(foundGetMem) + + //Remove Space Memberships + re := pubnub.PNMembersRemove{ + ID: userid, + } + + reArr := []pubnub.PNMembersRemove{ + re, + } + resRem, _, errRem := pn.ManageMembers().SpaceID(spaceid).Add([]pubnub.PNMembersInput{}).Update([]pubnub.PNMembersInput{}).Remove(reArr).Include(inclSm).Limit(limit).Count(count).Execute() + assert.Nil(errRem) + assert.Equal(200, resRem.Status) + foundRem := false + for i := range resRem.Data { + if resRem.Data[i].ID == userid { + foundRem = true + assert.Equal("b2", resUp.Data[i].Custom["a2"]) + assert.Equal("d2", resUp.Data[i].Custom["c2"]) + assert.Equal(userid, resUp.Data[0].User.ID) + assert.Equal(name, resUp.Data[0].User.Name) + assert.Equal(extid, resUp.Data[0].User.ExternalID) + assert.Equal(purl, resUp.Data[0].User.ProfileURL) + assert.Equal(email, resUp.Data[0].User.Email) + assert.Equal(custom["a"], resUp.Data[0].User.Custom["a"]) + assert.Equal(custom["c"], resUp.Data[0].User.Custom["c"]) + + } + } + assert.False(foundRem) + + inMem := pubnub.PNMembershipsInput{ + ID: spaceid2, + Custom: custom3, + } + + inArrMem := []pubnub.PNMembershipsInput{ + inMem, + } + + //Add user memberships + resManageMemAdd, _, errManageMemAdd := pn.ManageMemberships().UserID(userid2).Add(inArrMem).Update([]pubnub.PNMembershipsInput{}).Remove([]pubnub.PNMembershipsRemove{}).Include(inclMemberships).Limit(limit).Count(count).Execute() + fmt.Println("resManageMemAdd -->", resManageMemAdd) + assert.Nil(errManageMemAdd) + assert.Equal(200, resManageMemAdd.Status) + foundManageMembers := false + for i := range resManageMemAdd.Data { + if resManageMemAdd.Data[i].ID == spaceid2 { + assert.Equal(spaceid2, resManageMemAdd.Data[i].Space.ID) + assert.Equal(name, resManageMemAdd.Data[i].Space.Name) + assert.Equal(desc, resManageMemAdd.Data[i].Space.Description) + assert.Equal(custom2["a1"], resManageMemAdd.Data[i].Space.Custom["a1"]) + assert.Equal(custom2["c1"], resManageMemAdd.Data[i].Space.Custom["c1"]) + assert.Equal(custom3["a3"], resManageMemAdd.Data[i].Custom["a3"]) + assert.Equal(custom3["c3"], resManageMemAdd.Data[i].Custom["c3"]) + foundManageMembers = true + } + } + assert.True(foundManageMembers) + + // //Update user memberships + + custom5 := make(map[string]interface{}) + custom5["a5"] = "b5" + custom5["c5"] = "d5" + + upMem := pubnub.PNMembershipsInput{ + ID: spaceid2, + Custom: custom5, + } + + upArrMem := []pubnub.PNMembershipsInput{ + upMem, + } + + resManageMemUp, _, errManageMemUp := pn.ManageMemberships().UserID(userid2).Add([]pubnub.PNMembershipsInput{}).Update(upArrMem).Remove([]pubnub.PNMembershipsRemove{}).Include(inclMemberships).Limit(limit).Count(count).Execute() + fmt.Println("resManageMemUp -->", resManageMemUp) + assert.Nil(errManageMemUp) + assert.Equal(200, resManageMemUp.Status) + foundManageMembersUp := false + for i := range resManageMemUp.Data { + if resManageMemUp.Data[i].ID == spaceid2 { + assert.Equal(spaceid2, resManageMemUp.Data[i].Space.ID) + assert.Equal(name, resManageMemUp.Data[i].Space.Name) + assert.Equal(desc, resManageMemUp.Data[i].Space.Description) + assert.Equal(custom2["a1"], resManageMemAdd.Data[i].Space.Custom["a1"]) + assert.Equal(custom2["c1"], resManageMemAdd.Data[i].Space.Custom["c1"]) + assert.Equal(custom5["a5"], resManageMemUp.Data[i].Custom["a5"]) + assert.Equal(custom5["c5"], resManageMemUp.Data[i].Custom["c5"]) + foundManageMembersUp = true + } + } + assert.True(foundManageMembersUp) + + // //Get members + resGetMembers, _, errGetMembers := pn.GetMembers().SpaceID(spaceid2).Include(inclSm).Limit(limit).Count(count).Execute() + fmt.Println("resGetMembers -->", resGetMembers) + assert.Nil(errGetMembers) + assert.Equal(200, resGetMembers.Status) + foundGetMembers := false + for i := range resGetMembers.Data { + if resGetMembers.Data[i].ID == userid2 { + foundGetMembers = true + assert.Equal(name, resGetMembers.Data[i].User.Name) + assert.Equal(extid, resGetMembers.Data[i].User.ExternalID) + assert.Equal(purl, resGetMembers.Data[i].User.ProfileURL) + assert.Equal(email, resGetMembers.Data[i].User.Email) + assert.Equal(custom["a"], resGetMembers.Data[i].User.Custom["a"]) + assert.Equal(custom["c"], resGetMembers.Data[i].User.Custom["c"]) + assert.Equal(custom5["a5"], resGetMembers.Data[i].Custom["a5"]) + assert.Equal(custom5["c5"], resGetMembers.Data[i].Custom["c5"]) + } + } + assert.True(foundGetMembers) + + // //Remove user memberships + + reMem := pubnub.PNMembershipsRemove{ + ID: spaceid2, + } + + reArrMem := []pubnub.PNMembershipsRemove{ + reMem, + } + resManageMemRem, _, errManageMemRem := pn.ManageMemberships().UserID(userid2).Add([]pubnub.PNMembershipsInput{}).Update([]pubnub.PNMembershipsInput{}).Remove(reArrMem).Include(inclMemberships).Limit(limit).Count(count).Execute() + assert.Nil(errManageMemRem) + assert.Equal(200, resManageMemRem.Status) + + foundManageMemRem := false + for i := range resManageMemRem.Data { + if resManageMemRem.Data[i].ID == spaceid2 { + foundManageMemRem = true + } + } + assert.False(foundManageMemRem) + + //delete + res5, _, err5 := pn.DeleteUser().ID(userid).Execute() + assert.Nil(err5) + assert.Equal(200, res5.Status) + assert.Nil(res5.Data) + + //delete + res6, _, err6 := pn.DeleteSpace().ID(spaceid).Execute() + assert.Nil(err6) + assert.Equal(200, res6.Status) + assert.Nil(res6.Data) + + //delete + res52, _, err52 := pn.DeleteUser().ID(userid2).Execute() + assert.Nil(err52) + assert.Equal(200, res52.Status) + assert.Nil(res52.Data) + + //delete + res62, _, err62 := pn.DeleteSpace().ID(spaceid2).Execute() + assert.Nil(err62) + assert.Equal(200, res62.Status) + assert.Nil(res62.Data) + +} + +func TestObjectsListeners(t *testing.T) { + //Create channel names for Space and User + eventWaitTime := 2 + assert := assert.New(t) + + limit := 100 + count := true + + pn := pubnub.NewPubNub(configCopy()) + pnSub := pubnub.NewPubNub(configCopy()) + + r := GenRandom() + + userid := fmt.Sprintf("testlistuser_%d", r.Intn(99999)) + spaceid := fmt.Sprintf("testlistspace_%d", r.Intn(99999)) + //pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) + //pnSub.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) + + //Subscribe to the channel names + + listener := pubnub.NewListener() + + doneConnected := make(chan bool) + doneUpdateUser := make(chan bool) + doneUpdateSpace := make(chan bool) + doneAddUserToSpace := make(chan bool) + doneAddUserToSpace2 := make(chan bool) + doneUpdateUserMem := make(chan bool) + doneRemoveUserFromSpace := make(chan bool) + doneDeleteUser := make(chan bool) + doneDeleteSpace := make(chan bool) + + go func() { + for { + fmt.Println("Running =--->") + select { + + case status := <-listener.Status: + switch status.Category { + case pubnub.PNConnectedCategory: + doneConnected <- true + default: + fmt.Println(" --- status: ", status) + } + + case userEvent := <-listener.UserEvent: + + fmt.Println(" --- UserEvent: ") + fmt.Println(fmt.Sprintf("%s", userEvent)) + fmt.Println(fmt.Sprintf("userEvent.Channel: %s", userEvent.Channel)) + fmt.Println(fmt.Sprintf("userEvent.SubscribedChannel: %s", userEvent.SubscribedChannel)) + fmt.Println(fmt.Sprintf("userEvent.Event: %s", userEvent.Event)) + fmt.Println(fmt.Sprintf("userEvent.UserID: %s", userEvent.UserID)) + fmt.Println(fmt.Sprintf("userEvent.Description: %s", userEvent.Description)) + fmt.Println(fmt.Sprintf("userEvent.Timestamp: %s", userEvent.Timestamp)) + fmt.Println(fmt.Sprintf("userEvent.Name: %s", userEvent.Name)) + fmt.Println(fmt.Sprintf("userEvent.ExternalID: %s", userEvent.ExternalID)) + fmt.Println(fmt.Sprintf("userEvent.ProfileURL: %s", userEvent.ProfileURL)) + fmt.Println(fmt.Sprintf("userEvent.Email: %s", userEvent.Email)) + fmt.Println(fmt.Sprintf("userEvent.Created: %s", userEvent.Created)) + fmt.Println(fmt.Sprintf("userEvent.Updated: %s", userEvent.Updated)) + fmt.Println(fmt.Sprintf("userEvent.ETag: %s", userEvent.ETag)) + fmt.Println(fmt.Sprintf("userEvent.Custom: %v", userEvent.Custom)) + + if (userEvent.Event == pubnub.PNObjectsEventDelete) && (userEvent.UserID == userid) { + doneDeleteUser <- true + } + if (userEvent.Event == pubnub.PNObjectsEventUpdate) && (userEvent.UserID == userid) { + doneUpdateUser <- true + } + case spaceEvent := <-listener.SpaceEvent: + + fmt.Println(" --- SpaceEvent: ") + fmt.Println(fmt.Sprintf("%s", spaceEvent)) + fmt.Println(fmt.Sprintf("spaceEvent.Channel: %s", spaceEvent.Channel)) + fmt.Println(fmt.Sprintf("spaceEvent.SubscribedChannel: %s", spaceEvent.SubscribedChannel)) + fmt.Println(fmt.Sprintf("spaceEvent.Event: %s", spaceEvent.Event)) + fmt.Println(fmt.Sprintf("spaceEvent.SpaceID: %s", spaceEvent.SpaceID)) + fmt.Println(fmt.Sprintf("spaceEvent.Description: %s", spaceEvent.Description)) + fmt.Println(fmt.Sprintf("spaceEvent.Timestamp: %s", spaceEvent.Timestamp)) + fmt.Println(fmt.Sprintf("spaceEvent.Created: %s", spaceEvent.Created)) + fmt.Println(fmt.Sprintf("spaceEvent.Updated: %s", spaceEvent.Updated)) + fmt.Println(fmt.Sprintf("spaceEvent.ETag: %s", spaceEvent.ETag)) + fmt.Println(fmt.Sprintf("spaceEvent.Custom: %v", spaceEvent.Custom)) + if (spaceEvent.Event == pubnub.PNObjectsEventDelete) && (spaceEvent.SpaceID == spaceid) { + doneDeleteSpace <- true + } + if (spaceEvent.Event == pubnub.PNObjectsEventUpdate) && (spaceEvent.SpaceID == spaceid) { + doneUpdateSpace <- true + } + + case membershipEvent := <-listener.MembershipEvent: + + fmt.Println(" --- MembershipEvent: ") + fmt.Println(fmt.Sprintf("%s", membershipEvent)) + fmt.Println(fmt.Sprintf("membershipEvent.Channel: %s", membershipEvent.Channel)) + fmt.Println(fmt.Sprintf("membershipEvent.SubscribedChannel: %s", membershipEvent.SubscribedChannel)) + fmt.Println(fmt.Sprintf("membershipEvent.Event: %s", membershipEvent.Event)) + fmt.Println(fmt.Sprintf("membershipEvent.SpaceID: %s", membershipEvent.SpaceID)) + fmt.Println(fmt.Sprintf("membershipEvent.UserID: %s", membershipEvent.UserID)) + fmt.Println(fmt.Sprintf("membershipEvent.Description: %s", membershipEvent.Description)) + fmt.Println(fmt.Sprintf("membershipEvent.Timestamp: %s", membershipEvent.Timestamp)) + fmt.Println(fmt.Sprintf("membershipEvent.Custom: %v", membershipEvent.Custom)) + if (membershipEvent.Event == pubnub.PNObjectsEventCreate) && (membershipEvent.SpaceID == spaceid) && (membershipEvent.UserID == userid) && (membershipEvent.Channel == spaceid) { + doneAddUserToSpace <- true + } + if (membershipEvent.Event == pubnub.PNObjectsEventCreate) && (membershipEvent.SpaceID == spaceid) && (membershipEvent.UserID == userid) && ((membershipEvent.Channel == userid) || (membershipEvent.Channel == fmt.Sprintf("pnuser-%s", userid))) { + doneAddUserToSpace2 <- true + } + if (membershipEvent.Event == pubnub.PNObjectsEventUpdate) && (membershipEvent.SpaceID == spaceid) && (membershipEvent.UserID == userid) && (membershipEvent.Channel == spaceid) { + doneUpdateUserMem <- true + } + if (membershipEvent.Event == pubnub.PNObjectsEventDelete) && (membershipEvent.SpaceID == spaceid) && (membershipEvent.UserID == userid) && (membershipEvent.Channel == spaceid) { + doneRemoveUserFromSpace <- true + } + + } + } + + }() + + pnSub.AddListener(listener) + + pnSub.Subscribe().Channels([]string{fmt.Sprintf("pnuser-%s", userid), userid, spaceid}).Execute() + tic := time.NewTicker(time.Duration(eventWaitTime) * time.Second) + select { + case <-doneConnected: + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + } + + name := "name" + extid := "extid" + purl := "profileurl" + email := "email" + desc := "desc" + + customUser := make(map[string]interface{}) + customUser["au"] = "bu" + customUser["cu"] = "du" + + incl := []pubnub.PNUserSpaceInclude{ + pubnub.PNUserSpaceCustom, + } + + //Create User + res, _, err := pn.CreateUser().Include(incl).ID(userid).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(customUser).Execute() + assert.Nil(err) + assert.Equal(200, res.Status) + + //Create Space + customSpace := make(map[string]interface{}) + customSpace["as"] = "bs" + customSpace["cs"] = "ds" + + res4, _, err4 := pn.CreateSpace().Include(incl).ID(spaceid).Name(name).Description(desc).Custom(customSpace).Execute() + assert.Nil(err4) + assert.Equal(200, res4.Status) + + time.Sleep(1 * time.Second) + + //Update User + email = "email2" + + res2, _, err2 := pn.UpdateUser().Include(incl).ID(userid).Name(name).ExternalID(extid).ProfileURL(purl).Email(email).Custom(customUser).Execute() + assert.Nil(err2) + assert.Equal(200, res2.Status) + + //Read event + tic = time.NewTicker(time.Duration(eventWaitTime) * time.Second) + select { + case <-doneUpdateUser: + assert.True(true) + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + } + + time.Sleep(1 * time.Second) + + desc = "desc2" + + //Update Space + res3, _, err3 := pn.UpdateSpace().Include(incl).ID(spaceid).Name(name).Description(desc).Custom(customSpace).Execute() + assert.Nil(err3) + assert.Equal(200, res3.Status) + + //Read event + tic = time.NewTicker(time.Duration(eventWaitTime) * time.Second) + select { + case <-doneUpdateSpace: + assert.True(true) + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + } + + //Add user to space + inclSm := []pubnub.PNMembersInclude{ + pubnub.PNMembersCustom, + pubnub.PNMembersUser, + pubnub.PNMembersUserCustom, + } + + custom3 := make(map[string]interface{}) + custom3["a3"] = "b3" + custom3["c3"] = "d3" + + in := pubnub.PNMembersInput{ + ID: userid, + Custom: custom3, + } + + inArr := []pubnub.PNMembersInput{ + in, + } + time.Sleep(1 * time.Second) + + resAdd, _, errAdd := pn.ManageMembers().SpaceID(spaceid).Add(inArr).Update([]pubnub.PNMembersInput{}).Remove([]pubnub.PNMembersRemove{}).Include(inclSm).Limit(limit).Count(count).Execute() + assert.Nil(errAdd) + assert.Equal(200, resAdd.Status) + + //Read event + tic = time.NewTicker(time.Duration(eventWaitTime) * time.Second) + addUserToSpace := false + addUserToSpace2 := false + + runfor := true + waitforfunc := make(chan bool) + + go func() { + LabelBreak: + for runfor { + + select { + case <-doneAddUserToSpace: + addUserToSpace = true + if addUserToSpace2 { + runfor = false + fmt.Println("break 1") + waitforfunc <- true + break LabelBreak + } + case <-doneAddUserToSpace2: + addUserToSpace2 = true + if addUserToSpace { + runfor = false + fmt.Println("break 2") + waitforfunc <- true + break LabelBreak + } + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + waitforfunc <- true + break LabelBreak + } + + } + + }() + + <-waitforfunc + + assert.True(addUserToSpace && addUserToSpace2) + + //Update user membership + + custom5 := make(map[string]interface{}) + custom5["a5"] = "b5" + custom5["c5"] = "d5" + + upMem := pubnub.PNMembershipsInput{ + ID: spaceid, + Custom: custom5, + } + + upArrMem := []pubnub.PNMembershipsInput{ + upMem, + } + + inclMemberships := []pubnub.PNMembershipsInclude{ + pubnub.PNMembershipsCustom, + pubnub.PNMembershipsSpace, + pubnub.PNMembershipsSpaceCustom, + } + + resManageMemUp, _, errManageMemUp := pn.ManageMemberships().UserID(userid).Add([]pubnub.PNMembershipsInput{}).Update(upArrMem).Remove([]pubnub.PNMembershipsRemove{}).Include(inclMemberships).Limit(limit).Count(count).Execute() + fmt.Println("resManageMemUp -->", resManageMemUp) + assert.Nil(errManageMemUp) + assert.Equal(200, resManageMemUp.Status) + + //Read event + tic = time.NewTicker(time.Duration(eventWaitTime) * time.Second) + select { + case <-doneUpdateUserMem: + assert.True(true) + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + } + + //Remove user from space + reMem := pubnub.PNMembershipsRemove{ + ID: spaceid, + } + + reArrMem := []pubnub.PNMembershipsRemove{ + reMem, + } + resManageMemRem, _, errManageMemRem := pn.ManageMemberships().UserID(userid).Add([]pubnub.PNMembershipsInput{}).Update([]pubnub.PNMembershipsInput{}).Remove(reArrMem).Include(inclMemberships).Limit(limit).Count(count).Execute() + assert.Nil(errManageMemRem) + assert.Equal(200, resManageMemRem.Status) + + //Read event + tic = time.NewTicker(time.Duration(eventWaitTime) * time.Second) + select { + case <-doneRemoveUserFromSpace: + assert.True(true) + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + } + + //Delete user + res52, _, err52 := pn.DeleteUser().ID(userid).Execute() + assert.Nil(err52) + assert.Equal(200, res52.Status) + assert.Nil(res52.Data) + + //Read event + + tic = time.NewTicker(time.Duration(eventWaitTime) * time.Second) + select { + case <-doneDeleteUser: + assert.True(true) + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + } + + //Delete Space + res62, _, err62 := pn.DeleteSpace().ID(spaceid).Execute() + assert.Nil(err62) + assert.Equal(200, res62.Status) + assert.Nil(res62.Data) + + //Read event + + tic = time.NewTicker(time.Duration(eventWaitTime) * time.Second) + select { + case <-doneDeleteSpace: + assert.True(true) + case <-tic.C: + tic.Stop() + assert.Fail("timeout") + } +} diff --git a/tests/e2e/signal_request_test.go b/tests/e2e/signal_request_test.go index 0589437b..29865304 100644 --- a/tests/e2e/signal_request_test.go +++ b/tests/e2e/signal_request_test.go @@ -2,7 +2,7 @@ package e2e import ( "fmt" - "log" + //"log" "net" "os" "testing" @@ -24,7 +24,7 @@ func TestSignal(t *testing.T) { for _, ip := range ips { fmt.Printf("%s IN A %s\n", pn.Config.Origin, ip.String()) } - pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) + // pn.Config.Log = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) pn.Config.SubscribeKey = "demo" pn.Config.PublishKey = "demo" diff --git a/tests/e2e/subscribe_test.go b/tests/e2e/subscribe_test.go index 20adcaa9..b954452b 100644 --- a/tests/e2e/subscribe_test.go +++ b/tests/e2e/subscribe_test.go @@ -1554,9 +1554,6 @@ func TestSubscribe403Error(t *testing.T) { assert.Fail("timeout") } - - assert.Zero(len(pn2.GetSubscribedChannels())) - assert.Zero(len(pn2.GetSubscribedGroups())) } func TestSubscribeSignal(t *testing.T) { diff --git a/utils/crypto_test.go b/utils/crypto_test.go index 745bb0d0..b77a1c43 100644 --- a/utils/crypto_test.go +++ b/utils/crypto_test.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/stretchr/testify/assert" "log" + //"net/url" "os" "testing" "unicode/utf16" @@ -22,6 +23,44 @@ func TestSignSha256(t *testing.T) { assert.Equal("Dq92jnwRTCikdeP2nUs1__gyJthF8NChwbs5aYy2r_I=", res) } +// func TestSignSha256New2(t *testing.T) { +// assert := assert.New(t) +// v := &url.Values{} +// v.Set("PoundsSterling=", "%C2%A313.37") +// v.Set("auth=", "joker") +// v.Set("r=", "1") +// v.Set("timestamp=", "123456789") +// v.Set("ttl=", "60") +// v.Set("w=", "1") + +// d := PreparePamParams(v) + +// signInput := fmt.Sprintf("%s\n%s\n%s\n%s\n", "demo", "demo", "/v2/auth/grant/sub-key/demo", d) + +// res := GetHmacSha256("wMfbo9G0xVUG8yfTfYw5qIdfJkTd7A", signInput) + +// assert.Equal("v2rgQQ1eFzk8omugFV9V1_eKRUvvMv9jyC9Z-L1ogdw=", res) +// } + +// func TestSignSha256New(t *testing.T) { +// assert := assert.New(t) +// v := &url.Values{} +// v.Set("store=", "1") +// v.Set("seqn=", "1") +// v.Set("auth=", "myAuth") +// v.Set("timestamp=", "1535125017") +// v.Set("pnsdk=", "PubNub-Go/4.1.2") +// v.Set("uuid=", "myUuid") + +// d := PreparePamParams(v) + +// signInput := fmt.Sprintf("%s\n%s\n%s\n%s\n", "demoSubscribeKey", "demoPublishKey", "/publish/demoPublishKey/demoSubscribeKey/0/my-channel/0/%22my-message%22", d) + +// res := GetHmacSha256("secretKey", signInput) + +// assert.Equal("Dq92jnwRTCikdeP2nUs1__gyJthF8NChwbs5aYy2r_I=", res) +// } + func TestPad(t *testing.T) { assert := assert.New(t) diff --git a/utils/string_utils.go b/utils/string_utils.go index 94893d32..e6bba88c 100644 --- a/utils/string_utils.go +++ b/utils/string_utils.go @@ -14,7 +14,15 @@ import ( uuid "github.com/satori/go.uuid" ) -// JoinChannels +// EnumArrayToStringArray converts a string enum to an array +func EnumArrayToStringArray(include string) []string { + f := strings.Fields(include) + j := strings.Join(f, ",") + t := strings.Trim(j, "[]") + return strings.Fields(t) +} + +// JoinChannels encodes and joins channels func JoinChannels(channels []string) []byte { if len(channels) == 0 { return []byte(",")