From 45cd054fc6a14c228003380909169b2e93a9ec5f Mon Sep 17 00:00:00 2001 From: Bruno Baia Date: Mon, 19 Mar 2012 23:00:31 +0100 Subject: [PATCH] Added pagination support to IConnectionOperations.GetConnections() --- .../src/Spring.ConsoleQuickStart/Program.cs | 4 +- .../LinkedIn/Api/IConnectionOperations.cs | 50 ++++++++++++++--- .../LinkedIn/Api/Impl/ConnectionTemplate.cs | 53 +++++++++++++++---- .../Impl/Json/LinkedInProfilesDeserializer.cs | 3 +- .../Impl/Json/PaginatedResultDeserializer.cs | 4 +- .../Api/Impl/ConnectionTemplateTests.cs | 34 +++++++++--- .../Social/LinkedIn/Api/Impl/Connections.json | 2 + 7 files changed, 120 insertions(+), 30 deletions(-) diff --git a/examples/Spring.ConsoleQuickStart/src/Spring.ConsoleQuickStart/Program.cs b/examples/Spring.ConsoleQuickStart/src/Spring.ConsoleQuickStart/Program.cs index 0e84885..e7ca5d0 100644 --- a/examples/Spring.ConsoleQuickStart/src/Spring.ConsoleQuickStart/Program.cs +++ b/examples/Spring.ConsoleQuickStart/src/Spring.ConsoleQuickStart/Program.cs @@ -52,7 +52,7 @@ static void Main(string[] args) /* LinkedInProfile profileById = linkedIn.ProfileOperations.GetUserProfileByIdAsync("xO3SEJSVZN").Result; LinkedInProfile profileByPublicUrl = linkedIn.ProfileOperations.GetUserProfileByPublicUrlAsync("http://www.linkedin.com/in/bbaia").Result; - IList connections = linkedIn.ConnectionOperations.GetConnectionsAsync().Result; + LinkedInProfiles connections = linkedIn.ConnectionOperations.GetConnectionsAsync().Result; NetworkStatistics statistics = linkedIn.ConnectionOperations.GetNetworkStatisticsAsync().Result; */ // Consume LinkedIn endpoints that are not covered by the API binding @@ -105,7 +105,7 @@ static void Main(string[] args) /* LinkedInProfile profileById = linkedIn.ProfileOperations.GetUserProfileById("xO3SEJSVZN"); LinkedInProfile profileByPublicUrl = linkedIn.ProfileOperations.GetUserProfileByPublicUrl("http://www.linkedin.com/in/bbaia"); - IList connections = linkedIn.ConnectionOperations.GetConnections(); + LinkedInProfiles connections = linkedIn.ConnectionOperations.GetConnections(); NetworkStatistics statistics = linkedIn.ConnectionOperations.GetNetworkStatistics(); */ // Consume LinkedIn endpoints that are not covered by the API binding diff --git a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/IConnectionOperations.cs b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/IConnectionOperations.cs index be31a8b..05f879a 100644 --- a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/IConnectionOperations.cs +++ b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/IConnectionOperations.cs @@ -38,15 +38,27 @@ namespace Spring.Social.LinkedIn.Api public interface IConnectionOperations { #if NET_4_0 || SILVERLIGHT_5 + /// + /// Asynchronously retrieves up to 500 of the 1st-degree connections from the authenticated user's network. + /// + /// + /// A Task that represents the asynchronous operation that can return + /// a object representing the user's connections. + /// + /// If there is an error while communicating with LinkedIn. + Task GetConnectionsAsync(); + /// /// Asynchronously retrieves the 1st-degree connections from the authenticated user's network. /// + /// The starting location in the result set. Used with count for pagination. + /// The number of connections to return. The maximum value is 500. Used with start for pagination. /// /// A Task that represents the asynchronous operation that can return - /// a object representing the user's connections. + /// a object representing the user's connections. /// /// If there is an error while communicating with LinkedIn. - Task> GetConnectionsAsync(); + Task GetConnectionsAsync(int start, int count); /// /// Asynchronously retrieves network statistics for the authenticated user. @@ -59,14 +71,25 @@ public interface IConnectionOperations Task GetNetworkStatisticsAsync(); #else #if !SILVERLIGHT + /// + /// Retrieves up to 500 of the 1st-degree connections from the authenticated user's network. + /// + /// + /// A object representing the user's connections. + /// + /// If there is an error while communicating with LinkedIn. + LinkedInProfiles GetConnections(); + /// /// Retrieves the 1st-degree connections from the authenticated user's network. /// + /// The starting location in the result set. Used with count for pagination. + /// The number of connections to return. The maximum value is 500. Used with start for pagination. /// - /// A object representing the user's connections. + /// A object representing the user's connections. /// /// If there is an error while communicating with LinkedIn. - IList GetConnections(); + LinkedInProfiles GetConnections(int start, int count); /// /// Retrieves network statistics for the authenticated user. @@ -78,18 +101,33 @@ public interface IConnectionOperations NetworkStatistics GetNetworkStatistics(); #endif + /// + /// Asynchronously retrieves up to 500 of the 1st-degree connections from the authenticated user's network. + /// + /// + /// The Action<> to perform when the asynchronous request completes. + /// Provides a object representing the user's connections. + /// + /// + /// A instance that allows to cancel the asynchronous operation. + /// + /// If there is an error while communicating with LinkedIn. + RestOperationCanceler GetConnectionsAsync(Action> operationCompleted); + /// /// Asynchronously retrieves the 1st-degree connections from the authenticated user's network. /// + /// The starting location in the result set. Used with count for pagination. + /// The number of connections to return. The maximum value is 500. Used with start for pagination. /// /// The Action<> to perform when the asynchronous request completes. - /// Provides a object representing the user's connections. + /// Provides a object representing the user's connections. /// /// /// A instance that allows to cancel the asynchronous operation. /// /// If there is an error while communicating with LinkedIn. - RestOperationCanceler GetConnectionsAsync(Action>> operationCompleted); + RestOperationCanceler GetConnectionsAsync(int start, int count, Action> operationCompleted); /// /// Asynchronously retrieves network statistics for the authenticated user. diff --git a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/ConnectionTemplate.cs b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/ConnectionTemplate.cs index 5f8d0f1..6f1fd07 100644 --- a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/ConnectionTemplate.cs +++ b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/ConnectionTemplate.cs @@ -20,10 +20,14 @@ using System; using System.Collections.Generic; - #if NET_4_0 || SILVERLIGHT_5 using System.Threading.Tasks; #endif +#if SILVERLIGHT +using Spring.Collections.Specialized; +#else +using System.Collections.Specialized; +#endif using Spring.Http; using Spring.Rest.Client; @@ -35,8 +39,11 @@ namespace Spring.Social.LinkedIn.Api.Impl /// /// Robert Drysdale /// Bruno Baia (.NET) - class ConnectionTemplate : IConnectionOperations + class ConnectionTemplate : AbstractLinkedInOperations, IConnectionOperations { + private const string ConnectionsUrl = "people/~/connections:(id,first-name,last-name,headline,industry,site-standard-profile-request,public-profile-url,picture-url,summary)?format=json"; + private const string NetworkStatsUrl = "people/~/network/network-stats?format=json"; + private RestTemplate restTemplate; public ConnectionTemplate(RestTemplate restTemplate) @@ -47,36 +54,60 @@ public ConnectionTemplate(RestTemplate restTemplate) #region IConnectionOperations Members #if NET_4_0 || SILVERLIGHT_5 - public Task> GetConnectionsAsync() + public Task GetConnectionsAsync() + { + return this.restTemplate.GetForObjectAsync(ConnectionsUrl); + } + + public Task GetConnectionsAsync(int start, int count) { - return this.restTemplate.GetForObjectAsync>("people/~/connections:(id,first-name,last-name,headline,industry,site-standard-profile-request,public-profile-url,picture-url,summary)?format=json"); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("start", start.ToString()); + parameters.Add("count", count.ToString()); + return this.restTemplate.GetForObjectAsync(this.BuildUrl(ConnectionsUrl, parameters)); } public Task GetNetworkStatisticsAsync() { - return this.restTemplate.GetForObjectAsync("people/~/network/network-stats?format=json"); + return this.restTemplate.GetForObjectAsync(NetworkStatsUrl); } #else #if !SILVERLIGHT - public IList GetConnections() + public LinkedInProfiles GetConnections() { - return this.restTemplate.GetForObject>("people/~/connections:(id,first-name,last-name,headline,industry,site-standard-profile-request,public-profile-url,picture-url,summary)?format=json"); + return this.restTemplate.GetForObject(ConnectionsUrl); + } + + public LinkedInProfiles GetConnections(int start, int count) + { + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("start", start.ToString()); + parameters.Add("count", count.ToString()); + return this.restTemplate.GetForObject(this.BuildUrl(ConnectionsUrl, parameters)); } public NetworkStatistics GetNetworkStatistics() { - return this.restTemplate.GetForObject("people/~/network/network-stats?format=json"); + return this.restTemplate.GetForObject(NetworkStatsUrl); } #endif - public RestOperationCanceler GetConnectionsAsync(Action>> operationCompleted) + public RestOperationCanceler GetConnectionsAsync(Action> operationCompleted) + { + return this.restTemplate.GetForObjectAsync(ConnectionsUrl, operationCompleted); + } + + public RestOperationCanceler GetConnectionsAsync(int start, int count, Action> operationCompleted) { - return this.restTemplate.GetForObjectAsync>("people/~/connections:(id,first-name,last-name,headline,industry,site-standard-profile-request,public-profile-url,picture-url,summary)?format=json", operationCompleted); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("start", start.ToString()); + parameters.Add("count", count.ToString()); + return this.restTemplate.GetForObjectAsync(this.BuildUrl(ConnectionsUrl, parameters), operationCompleted); } public RestOperationCanceler GetNetworkStatisticsAsync(Action> operationCompleted) { - return this.restTemplate.GetForObjectAsync("people/~/network/network-stats?format=json", operationCompleted); + return this.restTemplate.GetForObjectAsync(NetworkStatsUrl, operationCompleted); } #endif diff --git a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/LinkedInProfilesDeserializer.cs b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/LinkedInProfilesDeserializer.cs index 1c62142..f22fa61 100644 --- a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/LinkedInProfilesDeserializer.cs +++ b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/LinkedInProfilesDeserializer.cs @@ -33,8 +33,7 @@ class LinkedInProfilesDeserializer : PaginatedResultDeserializer { public override object Deserialize(JsonValue json, JsonMapper mapper) { - JsonValue peopleJson = json.GetValue("people"); - + JsonValue peopleJson = json.ContainsName("people") ? json.GetValue("people") : json; LinkedInProfiles profiles = (LinkedInProfiles)base.Deserialize(peopleJson, mapper); profiles.Profiles = mapper.Deserialize>(peopleJson); return profiles; diff --git a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/PaginatedResultDeserializer.cs b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/PaginatedResultDeserializer.cs index 9e943d9..489e51b 100644 --- a/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/PaginatedResultDeserializer.cs +++ b/src/Spring.Social.LinkedIn/Social/LinkedIn/Api/Impl/Json/PaginatedResultDeserializer.cs @@ -34,9 +34,9 @@ public virtual object Deserialize(JsonValue json, JsonMapper mapper) { PaginatedResult paginatedResult = this.CreatePaginatedResult(); - paginatedResult.Start = json.ContainsName("_start") ? json.GetValue("_start") : 0; - paginatedResult.Count = json.ContainsName("_count") ? json.GetValue("_count") : 0; paginatedResult.Total = json.GetValue("_total"); + paginatedResult.Start = json.ContainsName("_start") ? json.GetValue("_start") : 0; + paginatedResult.Count = json.ContainsName("_count") ? json.GetValue("_count") : paginatedResult.Total; return paginatedResult; } diff --git a/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/ConnectionTemplateTests.cs b/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/ConnectionTemplateTests.cs index 2d8d7cb..18f4528 100644 --- a/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/ConnectionTemplateTests.cs +++ b/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/ConnectionTemplateTests.cs @@ -63,24 +63,44 @@ public void GetConnections() .AndRespondWith(JsonResource("Connections"), responseHeaders); #if NET_4_0 || SILVERLIGHT_5 - IList connections = linkedIn.ConnectionOperations.GetConnectionsAsync().Result; + LinkedInProfiles connections = linkedIn.ConnectionOperations.GetConnectionsAsync().Result; #else - IList connections = linkedIn.ConnectionOperations.GetConnections(); + LinkedInProfiles connections = linkedIn.ConnectionOperations.GetConnections(); #endif - Assert.AreEqual(4, connections.Count); - AssertProfile(connections[0], "kR0lnX1ll8", "SpringSource Cofounder", "Keith", "Donald", "Computer Software", + Assert.AreEqual(4, connections.Profiles.Count); + AssertProfile(connections.Profiles[0], "kR0lnX1ll8", "SpringSource Cofounder", "Keith", "Donald", "Computer Software", null, "", null, "http://www.linkedin.com/profile?viewProfile=&key=2526541&authToken=61Sm&authType=name&trk=api*a121026*s129482*", "name:61Sm"); - AssertProfile(connections[1], "VRcwcqPCtP", "GM, SpringSource and SVP, Middleware at VMware", "Rod", "Johnson", "Computer Software", + AssertProfile(connections.Profiles[1], "VRcwcqPCtP", "GM, SpringSource and SVP, Middleware at VMware", "Rod", "Johnson", "Computer Software", null, "", null, "http://www.linkedin.com/profile?viewProfile=&key=210059&authToken=3hU1&authType=name&trk=api*a121026*s129482*", "name:3hU1"); - AssertProfile(connections[2], "Ia7uR1OmDB", "Spring and AOP expert; author AspectJ in Action", "Ramnivas", "Laddad", "Computer Software", + AssertProfile(connections.Profiles[2], "Ia7uR1OmDB", "Spring and AOP expert; author AspectJ in Action", "Ramnivas", "Laddad", "Computer Software", "http://media.linkedin.com/mpr/mprx/0__gnH4Z-585hJSJSu_M6B4RrHCikUf0pu30CB4Rhqg6KwrUI2fUQXnUNVuSXku4j8CYN9cyYH-JuX", "", null, "http://www.linkedin.com/profile?viewProfile=&key=208994&authToken=P5K9&authType=name&trk=api*a121026*s129482*", "name:P5K9"); - AssertProfile(connections[3], "gKEMq4CMdl", "Head of Groovy Development at SpringSource", "Guillaume", "Laforge", "Information Technology and Services", + AssertProfile(connections.Profiles[3], "gKEMq4CMdl", "Head of Groovy Development at SpringSource", "Guillaume", "Laforge", "Information Technology and Services", "http://media.linkedin.com/mpr/mprx/0_CV5yQ4-Er7cqa-ZZhJziQU1WpS3v2qZZhRliQU1Miez51K74apvKHRbB-iTE71MN_JbCWpT7SdWe", "", null, "http://www.linkedin.com/profile?viewProfile=&key=822306&authToken=YmIW&authType=name&trk=api*a121026*s129482*", "name:YmIW"); } + [Test] + public void GetPaginatedConnections() + { + mockServer.ExpectNewRequest() + .AndExpectUri("https://api.linkedin.com/v1/people/~/connections:(id,first-name,last-name,headline,industry,site-standard-profile-request,public-profile-url,picture-url,summary)?format=json&start=0&count=4") + .AndExpectMethod(HttpMethod.GET) + .AndRespondWith(JsonResource("Connections"), responseHeaders); + +#if NET_4_0 || SILVERLIGHT_5 + LinkedInProfiles connections = linkedIn.ConnectionOperations.GetConnectionsAsync(0, 4).Result; +#else + LinkedInProfiles connections = linkedIn.ConnectionOperations.GetConnections(0, 4); +#endif + + Assert.AreEqual(0, connections.Start); + Assert.AreEqual(4, connections.Count); + Assert.AreEqual(243, connections.Total); + Assert.AreEqual(4, connections.Profiles.Count); + } + [Test] public void GetStatistics() { diff --git a/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/Connections.json b/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/Connections.json index 658b9a5..4ce7189 100644 --- a/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/Connections.json +++ b/test/Spring.Social.LinkedIn.Tests/Social/LinkedIn/Api/Impl/Connections.json @@ -91,5 +91,7 @@ "firstName": "Guillaume" } ], + "_start": 0, + "_count": 4, "_total": 243 } \ No newline at end of file