{"action":"created","comment":{"url":"https://api.github.com/repos/ably/ably-dotnet/pulls/comments/176849389","pull_request_review_id":106631257,"id":176849389,"diff_hunk":"@@ -75,145 +156,172 @@ private static TokenParams CreateTokenParams(Capability capability, TimeSpan? tt\n         [Theory]\n         [ProtocolData]\n         [Trait(\"spec\", \"RSA4b\")]\n-        public async Task AuthToken_WithMeansToRenew_WhenTokenExpired_ShouldRetry_WhenRetryFails_RaiseError(Protocol protocol)\n+        public async Task WithApiKey_WhenTokenExpired_ShouldRetry_WhenRetryFails_ShouldRaiseError(Protocol protocol)\n         {\n-            var authClient = await GetRestClient(protocol);\n-\n-            // create a tokenDetails that is about to expire\n-            var almostExpiredToken = await authClient.Auth.RequestTokenAsync(new TokenParams { ClientId = \"123\", Ttl = TimeSpan.FromMilliseconds(2000) }, null);\n+            var helper = new RSA4Helper(this);\n \n-            // Fool the client it is a valid token\n-            almostExpiredToken.Expires = DateTimeOffset.UtcNow.AddHours(1);\n-\n-            var testLogger = new TestLogger(\"Handling UnAuthorized Error, attmepting to Re-authorize and repeat request.\");\n-\n-            // create a realtime instance with no mean to renew the token\n-            var restClient = await GetRestClient(protocol, (options) =>\n+            var restClient = await GetRestClient(protocol);\n+            await restClient.Auth.AuthorizeAsync(new TokenParams\n             {\n-                options.TokenDetails = almostExpiredToken;\n-                options.ClientId = \"123\";\n-                options.Key = string.Empty;\n-                options.AutoConnect = false;\n-                options.AuthCallback = null;\n-                options.AuthUrl = new Uri(\"http://localhost:12345/invalid-uri\");\n-                options.Logger = testLogger;\n+                Ttl = TimeSpan.FromMilliseconds(1000)\n             });\n \n-            // wait for the token to expire\n-            await Task.Delay(TimeSpan.FromMilliseconds(2100));\n+            // when an HTTP request is made return a response with a 500 status, causing retry to fail\n+            restClient.ExecuteHttpRequest = helper.AblyResponseWith500Status;\n+\n+            // this realtime client will have a key for the sandbox, thus a means to renew\n+            var realtimeClient = await GetRealtimeClient(protocol, (options, _) => { options.TokenDetails = restClient.Options.TokenDetails; }, options => restClient);\n \n+            realtimeClient.RestClient.ExecuteHttpRequest = helper.AblyResponseWith500Status;\n+            await realtimeClient.WaitForState(ConnectionState.Connected);\n+            var channel = realtimeClient.Channels.Get(\"random\");\n+\n+            // wait for the token to expire and then publish\n+            await Task.Delay(TimeSpan.FromMilliseconds(2000));\n             try\n             {\n-                var channel = restClient.Channels.Get(\"random\");\n                 channel.Publish(\"event\", \"data\");\n-                throw new Exception(\"Unexpected success, preceeding code should have failed\");\n+                throw new Exception(\"Unexpected success, channel.Publish() should have thrown an AblyException\");\n             }\n-            catch (AblyException e)\n+            catch (Exception e)\n             {\n-                e.ErrorInfo.Code.Should().Be(80019);\n+                (e is AblyException).Should().BeTrue(\"should be an Ably Exception\");\n             }\n \n-            // check the retry code was run\n-            testLogger.MessageSeen.Should().BeTrue();\n-\n-            // only once\n-            testLogger.SeenCount.Should().Be(1);\n+            helper.Requests.Count.Should().Be(1);\n+            helper.Requests[0].Url.EndsWith(\"requestToken\").Should().BeTrue();\n         }\n \n         [Theory]\n         [ProtocolData]\n-        [Trait(\"spec\", \"RSA4c\")]\n-        [Trait(\"spec\", \"RSA4c1\")]\n-        [Trait(\"spec\", \"RSA4c3\")]\n-        public async Task AuthToken_WhenAuthUrlFails_ShouldRaiseError(Protocol protocol)\n+        [Trait(\"spec\", \"RSA4b\")]\n+        public async Task WithAuthCallback_WhenTokenExpired_ShouldRetry_WhenRetryFails_ShouldRaiseError(Protocol protocol)\n         {\n-            var client = await GetRealtimeClient(protocol);\n-            client.Connect();\n+            // create a short lived token\n+            var authRestClient = await GetRestClient(protocol);\n+            var token = await authRestClient.Auth.RequestTokenAsync(new TokenParams\n+            {\n+                Ttl = TimeSpan.FromMilliseconds(1000)\n+            });\n+\n+            // create a realtime client with the token and provide an auth callback that will throw an exception\n+            var realtimeClient = await GetRealtimeClient(protocol, (options, _) =>\n+            {\n+                options.TokenDetails = token;\n+                options.AuthCallback = tokenParams => throw new Exception(\"AuthCallback failed\");\n+            });\n \n-            // wait until connected\n-            await client.WaitForState();\n+            await realtimeClient.WaitForState(ConnectionState.Connected);\n+            var channel = realtimeClient.Channels.Get(\"random\");\n+\n+            // wait for the token to expire and then try to publish\n+            await Task.Delay(TimeSpan.FromMilliseconds(2000));\n \n-            // set a bogus AuthUrl\n-            client.Options.AuthUrl = new Uri(\"http://localhost:8910\");\n             try\n             {\n-                client.Auth.RequestToken();\n-                throw new Exception(\"Unexpected success\");\n+                channel.Publish(\"event\", \"data\");\n+                throw new Exception(\"Unexpected success, channel.Publish() should have thrown an AblyException\");\n             }\n-            catch (AblyException e)\n+            catch (Exception e)\n             {\n-                e.ErrorInfo.Code.Should().Be(80019);\n+                (e is AblyException).Should().BeTrue(\"should be an Ably Exception\");\n             }\n-\n-            // RSA4c3 should still be connected\n-            client.Connection.State.Should().Be(ConnectionState.Connected);\n         }\n \n         [Theory]\n         [ProtocolData]\n-        [Trait(\"spec\", \"RSA4c\")]\n-        [Trait(\"spec\", \"RSA4c1\")]\n-        [Trait(\"spec\", \"RSA4c3\")]\n-        public async Task AuthToken_WhenAuthCallbackFails_ShouldRaiseError(Protocol protocol)\n+        [Trait(\"spec\", \"RSA4b\")]\n+        public async Task WithAuthUrl_WhenTokenExpired_ShouldRetry_WhenRetryFails_ShouldRaiseError(Protocol protocol)\n         {\n-            var client = await GetRealtimeClient(protocol);\n-            client.Connect();\n+            var authRestClient = await GetRestClient(protocol);\n+            var token = await authRestClient.Auth.RequestTokenAsync(new TokenParams\n+            {\n+                Ttl = TimeSpan.FromMilliseconds(1000)\n+            });\n \n-            // wait until connected\n-            await client.WaitForState();\n+            // this realtime client will have a key for the sandbox, thus a means to renew\n+            var realtimeClient = await GetRealtimeClient(protocol, (options, _) =>\n+            {\n+                options.TokenDetails = token;\n+                options.LogLevel = LogLevel.Debug;\n+                options.AuthUrl = new Uri(_invalidAuthUrl);\n+            });\n \n-            client.Options.TokenDetails = new TokenDetails();\n-            client.Options.Key = string.Empty;\n-            client.Options.AuthCallback = tokenParams => throw new Exception(\"Force error in test\");\n+            await realtimeClient.WaitForState(ConnectionState.Connected);\n+            var channel = realtimeClient.Channels.Get(\"random\");\n \n+            // wait for the token to expire and then publish\n+            await Task.Delay(TimeSpan.FromMilliseconds(2000));\n             try\n             {\n-                client.Auth.RequestToken();\n-                throw new Exception(\"Unexpected success\");\n-            }\n-            catch (AblyException e)\n-            {\n-                e.ErrorInfo.Code.Should().Be(80019);\n+                channel.Publish(\"event\", \"data\");\n+                throw new Exception(\"Unexpected success, channel.Publish() should have thrown an AblyException\");\n             }\n             catch (Exception e)\n             {\n-                throw e;\n+                (e is AblyException).Should().BeTrue(\"should be an AblyException\");\n             }\n-\n-            // RSA4c3 should still be connected\n-            client.Connection.State.Should().Be(ConnectionState.Connected);\n         }\n \n+        /*\n+           (RSA4c) If an attempt by the realtime client library to authenticate is made using the authUrl or authCallback,\n+           and the request to authUrl fails (unless RSA4d applies), the callback authCallback results in an error\n+           (unless RSA4d applies), an attempt to exchange a TokenRequest for a TokenDetails results in an error\n+           (unless RSA4d applies), the provided token is in an invalid format, or the attempt times out after realtimeRequestTimeout, then:\n+\n+           (RSA4c1) An ErrorInfo with code 80019 and description of the underlying failure should be emitted\n+           with the state change, in the errorReason and/or in the callback as appropriate\n+\n+           (RSA4c2) If the connection is CONNECTING, then the connection attempt should be treated as unsuccessful,\n+           and as such the connection should transition to the DISCONNECTED or SUSPENDED state as defined in RTN14 and RTN15\n+\n+           (RSA4c3)If the connection is CONNECTED, then the connection should remain CONNECTED\n+\n+\n+            (RSA4d) If a request by a realtime client to an authUrl results in an HTTP 403 response,\n+            or any of an authUrl request, an authCallback, or a request to Ably to exchange a TokenRequest\n+            for a TokenDetails result in an ErrorInfo with statusCode 403, then the client library\n+            should transition to the FAILED state, with the connection errorReason should be set to\n+            the ErrorInfo (or where there is none, as for a 403 authUrl response with no body, an ErrorInfo with code 40300 and an appropriate message)\n+         */\n+\n         [Theory]\n         [ProtocolData]\n         [Trait(\"spec\", \"RSA4c\")]\n         [Trait(\"spec\", \"RSA4c1\")]\n-        [Trait(\"spec\", \"RSA4c3\")]\n-        public async Task AuthToken_WhenTokenIsInvalid_ShouldRaiseError(Protocol protocol)\n+        [Trait(\"spec\", \"RSA4c2\")]\n+        public async Task AuthToken_WithConnectingRealtimeClient_WhenAuthUrlFails_ShouldRaiseError(Protocol protocol)\n         {\n-            var client = await GetRealtimeClient(protocol);\n-            client.Connect();\n-\n-            // wait until connected\n-            await client.WaitForState();\n+            throw new Exception(\"WIP test stub\");\n+        }\n \n-            // have the auth callback return an invalid token\n-#pragma warning disable 1998\n-            client.Options.AuthCallback = async tokenParams => \"invalid_token\";\n-#pragma warning restore 1998\n+        [Theory]\n+        [ProtocolData]\n+        [Trait(\"spec\", \"RSA4c\")]\n+        [Trait(\"spec\", \"RSA4c1\")]\n+        [Trait(\"spec\", \"RSA4c2\")]\n+        public async Task AuthToken_WithConnectingRealtimeClient_WhenAuthCallbackFails_ShouldRaiseError(Protocol protocol)\n+        {\n+            throw new Exception(\"WIP test stub\");\n+        }\n \n-            try\n-            {\n-                client.Auth.RequestToken();\n-                throw new Exception(\"Unexpected success\");\n-            }\n-            catch (AblyException e)\n-            {\n-                e.ErrorInfo.Code.Should().Be(80019);\n-            }\n+        [Theory]\n+        [ProtocolData]\n+        [Trait(\"spec\", \"RSA4c\")]\n+        [Trait(\"spec\", \"RSA4c1\")]\n+        [Trait(\"spec\", \"RSA4c3\")]\n+        public async Task AuthToken_WithConnectedRealtimeClient_WhenAuthUrlFails_ShouldRaiseError(Protocol protocol)\n+        {\n+            throw new Exception(\"WIP test stub\");\n+        }\n \n-            // RSA4c3 should still be connected\n-            client.Connection.State.Should().Be(ConnectionState.Connected);\n+        [Theory]\n+        [ProtocolData]\n+        [Trait(\"spec\", \"RSA4c\")]\n+        [Trait(\"spec\", \"RSA4c1\")]\n+        [Trait(\"spec\", \"RSA4c3\")]\n+        public async Task AuthToken_WithConnectedRealtimeClient_WhenAuthCallbackFails_ShouldRaiseError(Protocol protocol)\n+        {","path":"src/IO.Ably.Tests.Shared/AuthTests/AuthSandboxSpecs.cs","position":406,"original_position":406,"commit_id":"c3c5f7704f276c250022e75f1cbc24614286a507","original_commit_id":"5729865a260e46dff5c4dcbd9e625be52bc2c317","user":{"login":"withakay","id":249723,"avatar_url":"https://avatars0.githubusercontent.com/u/249723?v=4","gravatar_id":"","url":"https://api.github.com/users/withakay","html_url":"https://github.com/withakay","followers_url":"https://api.github.com/users/withakay/followers","following_url":"https://api.github.com/users/withakay/following{/other_user}","gists_url":"https://api.github.com/users/withakay/gists{/gist_id}","starred_url":"https://api.github.com/users/withakay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/withakay/subscriptions","organizations_url":"https://api.github.com/users/withakay/orgs","repos_url":"https://api.github.com/users/withakay/repos","events_url":"https://api.github.com/users/withakay/events{/privacy}","received_events_url":"https://api.github.com/users/withakay/received_events","type":"User","site_admin":false},"body":"fixed.","created_at":"2018-03-23T20:01:25Z","updated_at":"2018-03-23T20:01:25Z","html_url":"https://github.com/ably/ably-dotnet/pull/201#discussion_r176849389","pull_request_url":"https://api.github.com/repos/ably/ably-dotnet/pulls/201","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/ably/ably-dotnet/pulls/comments/176849389"},"html":{"href":"https://github.com/ably/ably-dotnet/pull/201#discussion_r176849389"},"pull_request":{"href":"https://api.github.com/repos/ably/ably-dotnet/pulls/201"}},"in_reply_to_id":176789313},"pull_request":{"url":"https://api.github.com/repos/ably/ably-dotnet/pulls/201","id":176555903,"html_url":"https://github.com/ably/ably-dotnet/pull/201","diff_url":"https://github.com/ably/ably-dotnet/pull/201.diff","patch_url":"https://github.com/ably/ably-dotnet/pull/201.patch","issue_url":"https://api.github.com/repos/ably/ably-dotnet/issues/201","number":201,"state":"open","locked":false,"title":"RSA4a+b","user":{"login":"withakay","id":249723,"avatar_url":"https://avatars0.githubusercontent.com/u/249723?v=4","gravatar_id":"","url":"https://api.github.com/users/withakay","html_url":"https://github.com/withakay","followers_url":"https://api.github.com/users/withakay/followers","following_url":"https://api.github.com/users/withakay/following{/other_user}","gists_url":"https://api.github.com/users/withakay/gists{/gist_id}","starred_url":"https://api.github.com/users/withakay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/withakay/subscriptions","organizations_url":"https://api.github.com/users/withakay/orgs","repos_url":"https://api.github.com/users/withakay/repos","events_url":"https://api.github.com/users/withakay/events{/privacy}","received_events_url":"https://api.github.com/users/withakay/received_events","type":"User","site_admin":false},"body":"I have reworked tests for RSA4a and RSA4b and replaced the inadequate tests for RSA4c with stubs for the time being. I have put these on a new branch to (hopefully) make it easier to review (let me know if you think there is a better way).\r\n\r\nI would love to get some feed back on this, make sure I am aligning with the spec","created_at":"2018-03-21T17:36:02Z","updated_at":"2018-03-23T20:01:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"10734494a409975e391ed69fe104c71e9a783c6b","assignee":null,"assignees":[],"requested_reviewers":[{"login":"mattheworiordan","id":43789,"avatar_url":"https://avatars3.githubusercontent.com/u/43789?v=4","gravatar_id":"","url":"https://api.github.com/users/mattheworiordan","html_url":"https://github.com/mattheworiordan","followers_url":"https://api.github.com/users/mattheworiordan/followers","following_url":"https://api.github.com/users/mattheworiordan/following{/other_user}","gists_url":"https://api.github.com/users/mattheworiordan/gists{/gist_id}","starred_url":"https://api.github.com/users/mattheworiordan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattheworiordan/subscriptions","organizations_url":"https://api.github.com/users/mattheworiordan/orgs","repos_url":"https://api.github.com/users/mattheworiordan/repos","events_url":"https://api.github.com/users/mattheworiordan/events{/privacy}","received_events_url":"https://api.github.com/users/mattheworiordan/received_events","type":"User","site_admin":false},{"login":"paddybyers","id":1137574,"avatar_url":"https://avatars2.githubusercontent.com/u/1137574?v=4","gravatar_id":"","url":"https://api.github.com/users/paddybyers","html_url":"https://github.com/paddybyers","followers_url":"https://api.github.com/users/paddybyers/followers","following_url":"https://api.github.com/users/paddybyers/following{/other_user}","gists_url":"https://api.github.com/users/paddybyers/gists{/gist_id}","starred_url":"https://api.github.com/users/paddybyers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/paddybyers/subscriptions","organizations_url":"https://api.github.com/users/paddybyers/orgs","repos_url":"https://api.github.com/users/paddybyers/repos","events_url":"https://api.github.com/users/paddybyers/events{/privacy}","received_events_url":"https://api.github.com/users/paddybyers/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/ably/ably-dotnet/pulls/201/commits","review_comments_url":"https://api.github.com/repos/ably/ably-dotnet/pulls/201/comments","review_comment_url":"https://api.github.com/repos/ably/ably-dotnet/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ably/ably-dotnet/issues/201/comments","statuses_url":"https://api.github.com/repos/ably/ably-dotnet/statuses/c3c5f7704f276c250022e75f1cbc24614286a507","head":{"label":"ably:RSA4a+b","ref":"RSA4a+b","sha":"c3c5f7704f276c250022e75f1cbc24614286a507","user":{"login":"ably","id":1859245,"avatar_url":"https://avatars0.githubusercontent.com/u/1859245?v=4","gravatar_id":"","url":"https://api.github.com/users/ably","html_url":"https://github.com/ably","followers_url":"https://api.github.com/users/ably/followers","following_url":"https://api.github.com/users/ably/following{/other_user}","gists_url":"https://api.github.com/users/ably/gists{/gist_id}","starred_url":"https://api.github.com/users/ably/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ably/subscriptions","organizations_url":"https://api.github.com/users/ably/orgs","repos_url":"https://api.github.com/users/ably/repos","events_url":"https://api.github.com/users/ably/events{/privacy}","received_events_url":"https://api.github.com/users/ably/received_events","type":"Organization","site_admin":false},"repo":{"id":27377333,"name":"ably-dotnet","full_name":"ably/ably-dotnet","owner":{"login":"ably","id":1859245,"avatar_url":"https://avatars0.githubusercontent.com/u/1859245?v=4","gravatar_id":"","url":"https://api.github.com/users/ably","html_url":"https://github.com/ably","followers_url":"https://api.github.com/users/ably/followers","following_url":"https://api.github.com/users/ably/following{/other_user}","gists_url":"https://api.github.com/users/ably/gists{/gist_id}","starred_url":"https://api.github.com/users/ably/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ably/subscriptions","organizations_url":"https://api.github.com/users/ably/orgs","repos_url":"https://api.github.com/users/ably/repos","events_url":"https://api.github.com/users/ably/events{/privacy}","received_events_url":"https://api.github.com/users/ably/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ably/ably-dotnet","description":".NET client for Ably real-time messaging service","fork":false,"url":"https://api.github.com/repos/ably/ably-dotnet","forks_url":"https://api.github.com/repos/ably/ably-dotnet/forks","keys_url":"https://api.github.com/repos/ably/ably-dotnet/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ably/ably-dotnet/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ably/ably-dotnet/teams","hooks_url":"https://api.github.com/repos/ably/ably-dotnet/hooks","issue_events_url":"https://api.github.com/repos/ably/ably-dotnet/issues/events{/number}","events_url":"https://api.github.com/repos/ably/ably-dotnet/events","assignees_url":"https://api.github.com/repos/ably/ably-dotnet/assignees{/user}","branches_url":"https://api.github.com/repos/ably/ably-dotnet/branches{/branch}","tags_url":"https://api.github.com/repos/ably/ably-dotnet/tags","blobs_url":"https://api.github.com/repos/ably/ably-dotnet/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ably/ably-dotnet/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ably/ably-dotnet/git/refs{/sha}","trees_url":"https://api.github.com/repos/ably/ably-dotnet/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ably/ably-dotnet/statuses/{sha}","languages_url":"https://api.github.com/repos/ably/ably-dotnet/languages","stargazers_url":"https://api.github.com/repos/ably/ably-dotnet/stargazers","contributors_url":"https://api.github.com/repos/ably/ably-dotnet/contributors","subscribers_url":"https://api.github.com/repos/ably/ably-dotnet/subscribers","subscription_url":"https://api.github.com/repos/ably/ably-dotnet/subscription","commits_url":"https://api.github.com/repos/ably/ably-dotnet/commits{/sha}","git_commits_url":"https://api.github.com/repos/ably/ably-dotnet/git/commits{/sha}","comments_url":"https://api.github.com/repos/ably/ably-dotnet/comments{/number}","issue_comment_url":"https://api.github.com/repos/ably/ably-dotnet/issues/comments{/number}","contents_url":"https://api.github.com/repos/ably/ably-dotnet/contents/{+path}","compare_url":"https://api.github.com/repos/ably/ably-dotnet/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ably/ably-dotnet/merges","archive_url":"https://api.github.com/repos/ably/ably-dotnet/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ably/ably-dotnet/downloads","issues_url":"https://api.github.com/repos/ably/ably-dotnet/issues{/number}","pulls_url":"https://api.github.com/repos/ably/ably-dotnet/pulls{/number}","milestones_url":"https://api.github.com/repos/ably/ably-dotnet/milestones{/number}","notifications_url":"https://api.github.com/repos/ably/ably-dotnet/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ably/ably-dotnet/labels{/name}","releases_url":"https://api.github.com/repos/ably/ably-dotnet/releases{/id}","deployments_url":"https://api.github.com/repos/ably/ably-dotnet/deployments","created_at":"2014-12-01T11:56:18Z","updated_at":"2018-03-12T16:07:21Z","pushed_at":"2018-03-23T20:00:49Z","git_url":"git://github.com/ably/ably-dotnet.git","ssh_url":"git@github.com:ably/ably-dotnet.git","clone_url":"https://github.com/ably/ably-dotnet.git","svn_url":"https://github.com/ably/ably-dotnet","homepage":null,"size":17068,"stargazers_count":12,"watchers_count":12,"language":"C#","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":11,"mirror_url":null,"archived":false,"open_issues_count":35,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":11,"open_issues":35,"watchers":12,"default_branch":"master"}},"base":{"label":"ably:feature/RSC7b+RSA4a+RSA4c1+RSA4c3","ref":"feature/RSC7b+RSA4a+RSA4c1+RSA4c3","sha":"a59a2edeab8771a9f0505b8c892f610b6464d66c","user":{"login":"ably","id":1859245,"avatar_url":"https://avatars0.githubusercontent.com/u/1859245?v=4","gravatar_id":"","url":"https://api.github.com/users/ably","html_url":"https://github.com/ably","followers_url":"https://api.github.com/users/ably/followers","following_url":"https://api.github.com/users/ably/following{/other_user}","gists_url":"https://api.github.com/users/ably/gists{/gist_id}","starred_url":"https://api.github.com/users/ably/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ably/subscriptions","organizations_url":"https://api.github.com/users/ably/orgs","repos_url":"https://api.github.com/users/ably/repos","events_url":"https://api.github.com/users/ably/events{/privacy}","received_events_url":"https://api.github.com/users/ably/received_events","type":"Organization","site_admin":false},"repo":{"id":27377333,"name":"ably-dotnet","full_name":"ably/ably-dotnet","owner":{"login":"ably","id":1859245,"avatar_url":"https://avatars0.githubusercontent.com/u/1859245?v=4","gravatar_id":"","url":"https://api.github.com/users/ably","html_url":"https://github.com/ably","followers_url":"https://api.github.com/users/ably/followers","following_url":"https://api.github.com/users/ably/following{/other_user}","gists_url":"https://api.github.com/users/ably/gists{/gist_id}","starred_url":"https://api.github.com/users/ably/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ably/subscriptions","organizations_url":"https://api.github.com/users/ably/orgs","repos_url":"https://api.github.com/users/ably/repos","events_url":"https://api.github.com/users/ably/events{/privacy}","received_events_url":"https://api.github.com/users/ably/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ably/ably-dotnet","description":".NET client for Ably real-time messaging service","fork":false,"url":"https://api.github.com/repos/ably/ably-dotnet","forks_url":"https://api.github.com/repos/ably/ably-dotnet/forks","keys_url":"https://api.github.com/repos/ably/ably-dotnet/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ably/ably-dotnet/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ably/ably-dotnet/teams","hooks_url":"https://api.github.com/repos/ably/ably-dotnet/hooks","issue_events_url":"https://api.github.com/repos/ably/ably-dotnet/issues/events{/number}","events_url":"https://api.github.com/repos/ably/ably-dotnet/events","assignees_url":"https://api.github.com/repos/ably/ably-dotnet/assignees{/user}","branches_url":"https://api.github.com/repos/ably/ably-dotnet/branches{/branch}","tags_url":"https://api.github.com/repos/ably/ably-dotnet/tags","blobs_url":"https://api.github.com/repos/ably/ably-dotnet/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ably/ably-dotnet/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ably/ably-dotnet/git/refs{/sha}","trees_url":"https://api.github.com/repos/ably/ably-dotnet/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ably/ably-dotnet/statuses/{sha}","languages_url":"https://api.github.com/repos/ably/ably-dotnet/languages","stargazers_url":"https://api.github.com/repos/ably/ably-dotnet/stargazers","contributors_url":"https://api.github.com/repos/ably/ably-dotnet/contributors","subscribers_url":"https://api.github.com/repos/ably/ably-dotnet/subscribers","subscription_url":"https://api.github.com/repos/ably/ably-dotnet/subscription","commits_url":"https://api.github.com/repos/ably/ably-dotnet/commits{/sha}","git_commits_url":"https://api.github.com/repos/ably/ably-dotnet/git/commits{/sha}","comments_url":"https://api.github.com/repos/ably/ably-dotnet/comments{/number}","issue_comment_url":"https://api.github.com/repos/ably/ably-dotnet/issues/comments{/number}","contents_url":"https://api.github.com/repos/ably/ably-dotnet/contents/{+path}","compare_url":"https://api.github.com/repos/ably/ably-dotnet/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ably/ably-dotnet/merges","archive_url":"https://api.github.com/repos/ably/ably-dotnet/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ably/ably-dotnet/downloads","issues_url":"https://api.github.com/repos/ably/ably-dotnet/issues{/number}","pulls_url":"https://api.github.com/repos/ably/ably-dotnet/pulls{/number}","milestones_url":"https://api.github.com/repos/ably/ably-dotnet/milestones{/number}","notifications_url":"https://api.github.com/repos/ably/ably-dotnet/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ably/ably-dotnet/labels{/name}","releases_url":"https://api.github.com/repos/ably/ably-dotnet/releases{/id}","deployments_url":"https://api.github.com/repos/ably/ably-dotnet/deployments","created_at":"2014-12-01T11:56:18Z","updated_at":"2018-03-12T16:07:21Z","pushed_at":"2018-03-23T20:00:49Z","git_url":"git://github.com/ably/ably-dotnet.git","ssh_url":"git@github.com:ably/ably-dotnet.git","clone_url":"https://github.com/ably/ably-dotnet.git","svn_url":"https://github.com/ably/ably-dotnet","homepage":null,"size":17068,"stargazers_count":12,"watchers_count":12,"language":"C#","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":11,"mirror_url":null,"archived":false,"open_issues_count":35,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":11,"open_issues":35,"watchers":12,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ably/ably-dotnet/pulls/201"},"html":{"href":"https://github.com/ably/ably-dotnet/pull/201"},"issue":{"href":"https://api.github.com/repos/ably/ably-dotnet/issues/201"},"comments":{"href":"https://api.github.com/repos/ably/ably-dotnet/issues/201/comments"},"review_comments":{"href":"https://api.github.com/repos/ably/ably-dotnet/pulls/201/comments"},"review_comment":{"href":"https://api.github.com/repos/ably/ably-dotnet/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ably/ably-dotnet/pulls/201/commits"},"statuses":{"href":"https://api.github.com/repos/ably/ably-dotnet/statuses/c3c5f7704f276c250022e75f1cbc24614286a507"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"withakay"}}