31 Commits

Author SHA1 Message Date
swcs b8d211e151 update changelog 2014-08-28 14:15:16 +02:00
swcs c31b771cdb fix datetime conversion issue 2014-08-28 14:14:40 +02:00
swcs e0f96a092c assembly version 2014-07-27 15:16:39 +02:00
swcs 0301bbaf10 LOL 2014-07-27 13:03:33 +02:00
swcs 5266f220e9 fixes #28
Fix StackOverflow bug in PocketItem equality overload
2014-07-27 13:01:53 +02:00
swcs c83971b4cc convert all datetimes to UTC+0 2014-07-21 16:37:30 +02:00
swcs 1567709ace Merge remote-tracking branch 'origin/master' into simple 2014-07-21 16:24:30 +02:00
swcs 342f16fb8e convert timestamps to UTC+-0 (critical fix) 2014-07-21 16:23:59 +02:00
swcs 423be2df5f Merge remote-tracking branch 'origin/master' into simple 2014-07-18 11:58:08 +02:00
swcs 1605740107 fixes #27
Implement WebAuthenticationBroker-view toggle
see: http://getpocket.com/developer/docs/getstarted/windows8
2014-07-18 11:54:47 +02:00
swcs b7b92d87ed sliiim 2014-07-02 15:31:38 +02:00
swcs 01c36a047a flag for caching last HTTP response 2014-07-01 17:10:14 +02:00
swcs 140e645e86 bugfix 2014-06-25 14:41:42 +02:00
swcs 97919cdd18 supi dupi force param 2014-06-18 18:58:43 +02:00
swcs 761d670c65 update docs and changelog 2014-06-17 11:11:41 +02:00
swcs 1ca93bf07f update version 2014-06-17 10:46:00 +02:00
swcs 556e8004f3 fix Action parameter "url", so batch adding is working now 2014-06-17 10:45:24 +02:00
swcs 7cad855f9c return correct response status for "Send" 2014-06-17 10:14:26 +02:00
swcs 366908a070 fix json converter bug 2014-05-27 00:38:48 +02:00
swcs d141c7b255 fix deserializer bug 2014-05-11 23:07:49 +02:00
swcs 8531d20162 make PocketClient disposable 2014-05-11 22:59:06 +02:00
swcs 4f42ed719a separate HttpClient for parser API 2014-05-11 22:54:44 +02:00
swcs b46cc56362 implement ParserAPI func 2014-05-11 22:50:06 +02:00
swcs 500bd1c65d itemID prop for Tags; PocketArticle model 2014-05-11 22:26:08 +02:00
swcs a207ca7ca4 ConvertJsonToList 2014-05-03 18:54:19 +02:00
swcs 07c9f06f75 add IComparable and tostring/gethashcode to PocketItem 2014-05-02 17:59:26 +02:00
swcs a1ff132ba9 change assemblyversion 2014-05-01 18:14:17 +02:00
swcs 82896d6fb4 prevent creation of Lists and use IEnumerable where possible 2014-05-01 17:56:18 +02:00
swcs 36683c7402 add tags as string 2014-04-20 12:36:20 +02:00
swcs 4e5a8ab096 remove internal search and use official by Pocket 2014-04-17 10:26:59 +02:00
swcs 47baa0fd97 update docs 2014-04-05 11:44:03 +02:00
28 changed files with 823 additions and 366 deletions
+33
View File
@@ -1,3 +1,36 @@
### 4.1.6 (2014-08-28)
- Fix DateTime conversion issue (from local to UTC)
### 4.1.5 (2014-07-27)
- Fix StackOverflow bug in PocketItem equality overload (fixes #28)
### 4.1.4 (2014-07-18)
- Implement WebAuthenticationBroker-view toggle
### 4.1.3 (2014-06-25)
- Only dispose response if available
### 4.1.2 (2014-06-18)
- Add force param to login+registration
### 4.1.1 (2014-06-17)
- Fix batch adding bug
### 4.1.0 (2014-06-16)
- Include access to the Article View API
### 4.0.0 (2014-04-03)
- Support for Universal apps (dropped SL and WP7 support)
### 3.2.0 (2014-03-13)
- add raw JSON to each PocketItem (PR #22)
@@ -5,6 +5,7 @@ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Linq;
namespace PocketSharp.WP8.ViewModels
{
@@ -74,7 +75,7 @@ namespace PocketSharp.WP8.ViewModels
try
{
items = await client.Get();
items = (await client.Get()).ToList();
items.ForEach(item =>
{
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Linq;
namespace PocketSharp.Wpf
{
@@ -40,7 +41,7 @@ namespace PocketSharp.Wpf
try
{
items = await client.Get();
items = (await client.Get()).ToList();
items.ForEach(item =>
{
+59 -2
View File
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using System.Linq;
namespace PocketSharp.Tests
{
@@ -47,7 +48,7 @@ namespace PocketSharp.Tests
tweetID: "380051788172632065"
);
List<PocketItem> items = await client.Get();
List<PocketItem> items = (await client.Get()).ToList();
PocketItem itemDesired = null;
items.ForEach(itm =>
@@ -60,9 +61,65 @@ namespace PocketSharp.Tests
Assert.NotNull(itemDesired);
Assert.Equal(itemDesired.ID, item.ID);
Assert.Equal(itemDesired.Tags.Count, 3);
Assert.Equal(itemDesired.Tags.Count(), 3);
itemsToDelete.Add(item.ID);
}
[Fact]
public async Task ItemViaActionsIsAdded()
{
List<PocketAction> actions = new List<PocketAction>();
actions.Add(new PocketAction()
{
Uri = new Uri("http://frontendplay.com/story/4015/string-indexer-for-text-resources-in-nancy"),
Action = "add",
Time = DateTime.Now
});
bool success = await client.SendActions(actions);
Assert.True(success);
IEnumerable<PocketItem> items = await client.Search("in nancy");
Assert.NotNull(items);
Assert.True(items.Count() > 0);
itemsToDelete.Add(items.First().ID);
}
[Fact]
public async Task ItemsViaActionsIsAdded()
{
List<PocketAction> actions = new List<PocketAction>();
actions.Add(new PocketAction()
{
Uri = new Uri("http://frontendplay.com/story/4015/string-indexer-for-text-resources-in-nancy"),
Action = "add",
Time = DateTime.Now
});
actions.Add(new PocketAction()
{
Uri = new Uri("http://frontendplay.com/story/4013/build-a-custom-razor-viewbase-in-nancy"),
Action = "add",
Time = DateTime.Now
});
bool success = await client.SendActions(actions);
Assert.True(success);
IEnumerable<PocketItem> items = await client.Search("in nancy");
Assert.NotNull(items);
Assert.True(items.Count() == 2);
itemsToDelete.AddRange(items.Select(item => item.ID));
}
}
}
+85 -62
View File
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using System.Linq;
namespace PocketSharp.Tests
{
@@ -16,7 +17,7 @@ namespace PocketSharp.Tests
[Fact]
public async Task AreItemsRetrieved()
{
List<PocketItem> items = await client.Get();
List<PocketItem> items = (await client.Get()).ToList();
Assert.True(items.Count > 0);
}
@@ -24,7 +25,7 @@ namespace PocketSharp.Tests
[Fact]
public async Task IsItemRetrievedById()
{
List<PocketItem> items = await client.Get();
List<PocketItem> items = (await client.Get()).ToList();
PocketItem item = items[0];
PocketItem itemDuplicate = await client.Get(item.ID);
@@ -32,37 +33,37 @@ namespace PocketSharp.Tests
Assert.True(item.Uri == itemDuplicate.Uri);
}
[Fact]
public async Task IsItemJsonPopulated()
{
List<PocketItem> items = await client.Get();
string schemaJson = @"{
'description': 'PocketItem',
'type': 'object'
}";
// [Fact]
// public async Task IsItemJsonPopulated()
// {
// List<PocketItem> items = (await client.Get()).ToList();
// string schemaJson = @"{
// 'description': 'PocketItem',
// 'type': 'object'
// }";
JsonSchema schema = JsonSchema.Parse(schemaJson);
foreach (var pocketItem in items)
{
Assert.True(!string.IsNullOrWhiteSpace(pocketItem.Json));
var jObject = JObject.Parse(pocketItem.Json);
Assert.True(jObject.IsValid(schema));
}
}
// JsonSchema schema = JsonSchema.Parse(schemaJson);
// foreach (var pocketItem in items)
// {
// Assert.True(!string.IsNullOrWhiteSpace(pocketItem.Json));
// var jObject = JObject.Parse(pocketItem.Json);
// Assert.True(jObject.IsValid(schema));
// }
// }
[Fact]
public async Task AreFilteredItemsRetrieved()
{
List<PocketItem> items = await client.Get(RetrieveFilter.Favorite);
IEnumerable<PocketItem> items = await client.Get(RetrieveFilter.Favorite);
Assert.True(items.Count > 0);
Assert.True(items.Count() > 0);
}
[Fact]
public async Task RetrieveWithMultipleFilters()
{
List<PocketItem> items = await client.Get(
IEnumerable<PocketItem> items = await client.Get(
state: State.unread,
tag: "pocket",
sort: Sort.title,
@@ -70,29 +71,29 @@ namespace PocketSharp.Tests
count: 2
);
Assert.InRange<int>(items.Count, 0, 2);
Assert.InRange<int>(items.Count(), 0, 2);
}
[Fact]
public async Task ItemContainsUri()
{
List<PocketItem> items = await client.Get(count: 1);
IEnumerable<PocketItem> items = await client.Get(count: 1);
Assert.True(items.Count == 1);
Assert.True(items[0].Uri.ToString().StartsWith("http"));
Assert.True(items.Count() == 1);
Assert.True(items.First().Uri.ToString().StartsWith("http"));
}
[Fact]
public async Task InvalidRetrievalReturnsNoResults()
{
List<PocketItem> items = await client.Get(
IEnumerable<PocketItem> items = await client.Get(
favorite: true,
search: "xoiu987a#;"
);
Assert.False(items.Count > 0);
Assert.False(items.Count() > 0);
PocketItem item = await client.Get("99999999");
@@ -100,48 +101,48 @@ namespace PocketSharp.Tests
items = await client.Get(RetrieveFilter.Video);
Assert.False(items.Count > 0);
Assert.False(items.Count() > 0);
}
[Fact]
public async Task SearchReturnsResult()
{
List<PocketItem> items = await client.Search("pocket");
IEnumerable<PocketItem> items = await client.Search("pocket");
Assert.True(items.Count > 0);
Assert.True(items[0].FullTitle.ToLower().Contains("pocket"));
Assert.True(items.Count() > 0);
Assert.True(items.First().FullTitle.ToLower().Contains("pocket"));
}
[Fact]
public async Task InvalidSearchReturnsNoResult()
{
List<PocketItem> items = await client.Search("adsüasd-opiu2;.398dfyx");
IEnumerable<PocketItem> items = await client.Search("adsüasd-opiu2;.398dfyx");
Assert.False(items.Count > 0);
Assert.False(items.Count() > 0);
}
[Fact]
public async Task RetrieveTagsReturnsResult()
{
List<PocketTag> items = await client.GetTags();
IEnumerable<PocketTag> items = await client.GetTags();
Assert.True(items.Count > 0);
Assert.True(items.Count() > 0);
}
[Fact]
public async Task SearchByTagsReturnsResult()
{
List<PocketItem> items = await client.SearchByTag("pocket");
IEnumerable<PocketItem> items = await client.SearchByTag("pocket");
Assert.True(items.Count == 1);
Assert.True(items.Count() == 1);
bool found = false;
items[0].Tags.ForEach(tag =>
items.First().Tags.ToList().ForEach(tag =>
{
if (tag.Name.Contains("pocket"))
{
@@ -156,9 +157,9 @@ namespace PocketSharp.Tests
[Fact]
public async Task InvalidSearchByTagsReturnsNoResult()
{
List<PocketItem> items = await client.SearchByTag("adsüasd-opiu2;.398dfyx");
IEnumerable<PocketItem> items = await client.SearchByTag("adsüasd-opiu2;.398dfyx");
Assert.False(items.Count > 0);
Assert.False(items.Count() > 0);
}
@@ -183,32 +184,32 @@ namespace PocketSharp.Tests
[Fact]
public async Task IsNewPocketItemListGeneratorWorking()
{
List<PocketItem> items = await client.Get(count: 1, tag: "pocket");
PocketItem item = items[0];
IEnumerable<PocketItem> items = await client.Get(count: 1, tag: "pocket");
PocketItem item = items.First();
Assert.True(item.Tags.Find(i => i.Name == "pocket") != null);
Assert.True(item.Tags.ToList().Find(i => i.Name == "pocket") != null);
}
[Fact]
public async Task IsSinceParameterWorkingOnAddTags()
{
DateTime since = DateTime.UtcNow;
DateTime since = DateTime.Now;
List<PocketItem> items = await client.Get(state: State.all);
PocketItem itemToModify = items[0];
IEnumerable<PocketItem> items = await client.Get(state: State.all);
PocketItem itemToModify = items.First();
Assert.True(items.Count >= 3);
Assert.True(items.Count() >= 3);
items = await client.Get(state: State.all, since: since);
Assert.True(items == null || items.Count == 0);
Assert.True(items == null || items.Count() == 0);
await client.AddTags(itemToModify, new string[] { "pocketsharp" });
items = await client.Get(state: State.all, since: since);
Assert.True(items.Count > 0);
Assert.True(items.Count() > 0);
await client.RemoveTags(itemToModify, new string[] { "pocketsharp" });
}
@@ -217,37 +218,59 @@ namespace PocketSharp.Tests
[Fact]
public async Task IsSinceParameterWorkingOnFavoriteAndArchiveModification()
{
DateTime since = DateTime.UtcNow;
DateTime since = DateTime.Now;//1409221736
List<PocketItem> items = await client.Get(state: State.all);
PocketItem itemToModify = items[0];
IEnumerable<PocketItem> items = await client.Get(state: State.all);
PocketItem itemToModify = items.First();
Assert.True(items.Count >= 3);
Assert.True(items.Count() >= 3);
items = await client.Get(state: State.all, since: since);
Assert.True(items == null || items.Count == 0);
Assert.True(items == null || items.Count() == 0);
await client.Favorite(itemToModify);
items = await client.Get(state: State.all, since: since);
Assert.True(items.Count > 0);
Assert.True(items.Count() > 0);
await client.Unfavorite(itemToModify);
since = DateTime.UtcNow.AddMinutes(-1);
since = DateTime.Now;
await client.Archive(itemToModify);
items = await client.Get(state: State.all, since: since);
Assert.True(items.Count > 0);
Assert.True(items.Count() > 0);
await client.Unarchive(itemToModify);
}
[Fact]
public async Task IsUTCSinceParameterWorking()
{
DateTime since = DateTime.UtcNow;
IEnumerable<PocketItem> items = await client.Get(state: State.all);
PocketItem itemToModify = items.First();
items = await client.Get(state: State.all, since: since);
Assert.True(items == null || items.Count() == 0);
await client.Favorite(itemToModify);
items = await client.Get(state: State.all, since: since);
Assert.True(items.Count() > 0);
await client.Unfavorite(itemToModify);
}
[Fact]
public async Task IsSinceParameterWorkingOnAddAndDelete()
{
@@ -255,7 +278,7 @@ namespace PocketSharp.Tests
PocketItem item = await client.Add(new Uri("http://frontendplay.com"));
List<PocketItem> items = await client.Get(state: State.all, since: since);
IEnumerable<PocketItem> items = await client.Get(state: State.all, since: since);
since = DateTime.UtcNow;
@@ -263,7 +286,7 @@ namespace PocketSharp.Tests
items = await client.Get(state: State.all, since: since);
Assert.True(items.Count == 1 && items[0].IsDeleted);
Assert.True(items.Count() == 1 && items.First().IsDeleted);
}
[Fact]
@@ -271,11 +294,11 @@ namespace PocketSharp.Tests
{
PocketItem item = await client.Add(new Uri("http://de.ign.com/m/feature/21608/die-20-besten-kurzfilme-des-jahres-2013?bust=1"));
List<PocketItem> items = await client.Get(state: State.all);
IEnumerable<PocketItem> items = await client.Get(state: State.all);
Assert.NotNull(item.Uri);
Assert.NotNull(items[0].Uri);
Assert.Equal(item.Uri, items[0].Uri);
Assert.NotNull(items.First().Uri);
Assert.Equal(item.Uri, items.First().Uri);
itemsToDelete.Add(item.ID);
}
+3 -2
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using System.Linq;
namespace PocketSharp.Tests
{
@@ -19,8 +20,8 @@ namespace PocketSharp.Tests
[Fact]
public async Task CheckPreRequestAction()
{
List<PocketItem> items = await client.Get(count: 1);
PocketItem item = items[0];
IEnumerable<PocketItem> items = await client.Get(count: 1);
PocketItem item = items.First();
await client.Favorite(item);
await client.Unfavorite(item);
+4 -4
View File
@@ -21,7 +21,7 @@ namespace PocketSharp.Tests
item = await GetItemById(item.ID);
Assert.True(item.Tags.Count >= 2);
Assert.True(item.Tags.Count() >= 2);
Assert.NotNull(item.Tags.Single<PocketTag>(tag => tag.Name == "test_tag"));
Assert.NotNull(item.Tags.Single<PocketTag>(tag => tag.Name == "test_tag2"));
@@ -44,7 +44,7 @@ namespace PocketSharp.Tests
item = await GetItemById(item.ID);
Assert.True(item.Tags.Count >= 2);
Assert.True(item.Tags.Count() >= 2);
Assert.True(await client.RemoveTags(item));
@@ -63,7 +63,7 @@ namespace PocketSharp.Tests
item = await GetItemById(item.ID);
Assert.Equal(item.Tags.Count, 2);
Assert.Equal(item.Tags.Count(), 2);
Assert.NotNull(item.Tags.SingleOrDefault<PocketTag>(tag => tag.Name == "test_tag"));
Assert.NotNull(item.Tags.SingleOrDefault<PocketTag>(tag => tag.Name == "test_tag2"));
@@ -85,7 +85,7 @@ namespace PocketSharp.Tests
private async Task<PocketItem> GetItemById(string id, bool archive = false)
{
List<PocketItem> items = await client.Get(state: archive ? State.archive : State.unread);
List<PocketItem> items = (await client.Get(state: archive ? State.archive : State.unread)).ToList();
PocketItem itemDesired = null;
items.ForEach(itm =>
+1 -1
View File
@@ -137,7 +137,7 @@ namespace PocketSharp.Tests
private async Task<PocketItem> GetItemById(string id, bool archive = false)
{
List<PocketItem> items = await client.Get(state: archive ? State.archive : State.unread);
List<PocketItem> items = (await client.Get(state: archive ? State.archive : State.unread)).ToList();
PocketItem itemDesired = null;
items.ForEach(itm =>
+34 -20
View File
@@ -30,50 +30,50 @@ namespace PocketSharp.Tests
[Fact]
public async Task Are100ItemsRetrievedProperly()
{
List<PocketItem> items = await client.Get(count: 100, state: State.all);
Assert.True(items.Count == 100);
IEnumerable<PocketItem> items = await client.Get(count: 100, state: State.all);
Assert.True(items.Count() == 100);
}
[Fact]
public async Task Are1000ItemsRetrievedProperly()
{
List<PocketItem> items = await client.Get(count: 1000, state: State.all);
Assert.True(items.Count == 1000);
IEnumerable<PocketItem> items = await client.Get(count: 1000, state: State.all);
Assert.True(items.Count() == 1000);
}
[Fact]
public async Task Are2500ItemsRetrievedProperly()
{
List<PocketItem> items = await client.Get(count: 2500, state: State.all);
Assert.True(items.Count == 2500);
IEnumerable<PocketItem> items = await client.Get(count: 2500, state: State.all);
Assert.True(items.Count() == 2500);
}
[Fact]
public async Task Are5000ItemsRetrievedProperly()
{
List<PocketItem> items = await client.Get(count: 5000, state: State.all);
Assert.True(items.Count == 5000);
IEnumerable<PocketItem> items = await client.Get(count: 5000, state: State.all);
Assert.True(items.Count() == 5000);
}
[Fact]
public async Task IsSearchSuccessfullyOnBigList()
{
List<PocketItem> items = await client.Get(search: "google");
IEnumerable<PocketItem> items = await client.Get(search: "google");
Assert.True(items.Count > 0);
Assert.True(items[0].Title.ToLower().Contains("google") || items[0].Uri.ToString().ToLower().Contains("google"));
Assert.True(items.Count() > 0);
Assert.True(items.First().Title.ToLower().Contains("google") || items.First().Uri.ToString().ToLower().Contains("google"));
}
[Fact]
public async Task IsTagSearchSuccessfullyOnBigList()
{
List<PocketItem> items = await client.SearchByTag(tags[0]);
IEnumerable<PocketItem> items = await client.SearchByTag(tags[0]);
Assert.True(items.Count > 1);
Assert.True(items.Count() > 1);
bool found = false;
items[0].Tags.ForEach(tag =>
items.First().Tags.ToList().ForEach(tag =>
{
if (tag.Name.Contains(tags[0]))
{
@@ -87,7 +87,7 @@ namespace PocketSharp.Tests
[Fact]
public async Task RetrieveTagsReturnsResultOnBigList()
{
List<PocketTag> items = await client.GetTags();
List<PocketTag> items = (await client.GetTags()).ToList();
items.ForEach(tag =>
{
@@ -105,13 +105,27 @@ namespace PocketSharp.Tests
string[] tag;
Random rnd = new Random();
foreach (string url in urls.Skip(offset).Take(count))
var items = urls.Skip(offset).Take(count)
.Select((value, idx) => new { Value = value, Index = idx })
.GroupBy(item => item.Index / 100, item => item.Value)
.Cast<IEnumerable<string>>();
foreach (IEnumerable<string> urlGroup in items)
{
r = rnd.Next(tags.Length);
r2 = rnd.Next(tags.Length);
tag = new string[] { tags[r], tags[r2] };
await client.Add(new Uri("http://" + url), tag);
await Task.WhenAll(urlGroup.Select(url =>
{
r = rnd.Next(tags.Length);
r2 = rnd.Next(tags.Length);
tag = new string[] { tags[r], tags[r2] };
return client.Add(new Uri("http://" + url), tag);
}));
}
}
//[Fact]
//public async Task Fillll()
//{
// await FillAccount(11000, 89999);
//}
}
}
+26 -26
View File
@@ -60,7 +60,32 @@ namespace PocketSharp
RequestCode = requestCode;
}
return new Uri(String.Format(authentificationUri, RequestCode, CallbackUri, isMobileClient ? "1" : "0"));
return new Uri(String.Format(authentificationUri, RequestCode, CallbackUri, isMobileClient ? "1" : "0", "login", useInsideWebAuthenticationBroker ? "1" : "0"));
}
/// <summary>
/// Generate registration URI from requestCode
/// Follow the steps as with GenerateAuthenticationUri, but for unregistered users
/// </summary>
/// <param name="requestCode">The requestCode. If no requestCode is supplied, the property from the PocketClient intialization is used.</param>
/// <returns>A valid URI to redirect the user to.</returns>
/// <exception cref="System.NullReferenceException">Call GetRequestCode() first to receive a request_code</exception>
public Uri GenerateRegistrationUri(string requestCode = null)
{
// check if request code is available
if (RequestCode == null && requestCode == null)
{
throw new NullReferenceException("Call GetRequestCode() first to receive a request_code");
}
// override property with given param if available
if (requestCode != null)
{
RequestCode = requestCode;
}
return new Uri(String.Format(authentificationUri, RequestCode, CallbackUri, isMobileClient ? "1" : "0", "signup", useInsideWebAuthenticationBroker ? "1" : "0"));
}
@@ -99,30 +124,5 @@ namespace PocketSharp
return response;
}
/// <summary>
/// Generate registration URI from requestCode
/// Follow the steps as with GenerateAuthenticationUri, but for unregistered users
/// </summary>
/// <param name="requestCode">The requestCode. If no requestCode is supplied, the property from the PocketClient intialization is used.</param>
/// <returns>A valid URI to redirect the user to.</returns>
/// <exception cref="System.NullReferenceException">Call GetRequestCode() first to receive a request_code</exception>
public Uri GenerateRegistrationUri(string requestCode = null)
{
// check if request code is available
if (RequestCode == null && requestCode == null)
{
throw new NullReferenceException("Call GetRequestCode() first to receive a request_code");
}
// override property with given param if available
if (requestCode != null)
{
RequestCode = requestCode;
}
return new Uri(String.Format("{0}&force=signup", String.Format(authentificationUri, RequestCode, CallbackUri, isMobileClient ? "1" : "0")));
}
}
}
+65 -42
View File
@@ -29,7 +29,7 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
public async Task<List<PocketItem>> Get(
public async Task<IEnumerable<PocketItem>> Get(
State? state = null,
bool? favorite = null,
string tag = null,
@@ -53,14 +53,12 @@ namespace PocketSharp
DetailType = DetailType.complete,
Search = search,
Domain = domain,
Since = since,
Since = since.HasValue ? ((DateTime)since).ToUniversalTime() : since,
Count = count,
Offset = offset
};
Retrieve response = await Request<Retrieve>("get", cancellationToken, parameters.Convert());
return response.Items;
return (await Request<Retrieve>("get", cancellationToken, parameters.Convert())).Items ?? new List<PocketItem>();
}
@@ -74,12 +72,10 @@ namespace PocketSharp
/// <exception cref="PocketException"></exception>
public async Task<PocketItem> Get(string itemID, CancellationToken cancellationToken = default(CancellationToken))
{
List<PocketItem> items = await Get(
return (await Get(
cancellationToken: cancellationToken,
state: State.all
);
return items.SingleOrDefault<PocketItem>(item => item.ID == itemID);
)).SingleOrDefault<PocketItem>(item => item.ID == itemID);
}
@@ -90,7 +86,7 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
public async Task<List<PocketItem>> Get(RetrieveFilter filter, CancellationToken cancellationToken = default(CancellationToken))
public async Task<IEnumerable<PocketItem>> Get(RetrieveFilter filter, CancellationToken cancellationToken = default(CancellationToken))
{
RetrieveParameters parameters = new RetrieveParameters();
@@ -121,9 +117,19 @@ namespace PocketSharp
parameters.DetailType = DetailType.complete;
Retrieve response = await Request<Retrieve>("get", cancellationToken, parameters.Convert());
return (await Request<Retrieve>("get", cancellationToken, parameters.Convert())).Items;
}
return response.Items;
/// <summary>
/// Converts a raw JSON response to a PocketItem list
/// </summary>
/// <param name="itemsJSON">The raw JSON response.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
public IEnumerable<PocketItem> ConvertJsonToList(string itemsJSON)
{
return DeserializeJson<Retrieve>(itemsJSON).Items;
}
@@ -134,18 +140,19 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
public async Task<List<PocketTag>> GetTags(CancellationToken cancellationToken = default(CancellationToken))
public async Task<IEnumerable<PocketTag>> GetTags(CancellationToken cancellationToken = default(CancellationToken))
{
List<PocketItem> items = await Get(
IEnumerable<PocketItem> items = await Get(
cancellationToken: cancellationToken,
state: State.all
);
return items.Where(item => item.Tags != null)
.SelectMany(item => item.Tags)
.GroupBy(item => item.Name)
.Select(item => item.First())
.ToList<PocketTag>();
return items
.Where(item => item.Tags != null)
.SelectMany(item => item.Tags)
.GroupBy(item => item.Name)
.Select(item => item.First())
.ToList<PocketTag>();
}
@@ -156,9 +163,10 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
public async Task<List<PocketItem>> SearchByTag(string tag, CancellationToken cancellationToken = default(CancellationToken))
public async Task<IEnumerable<PocketItem>> SearchByTag(string tag, CancellationToken cancellationToken = default(CancellationToken))
{
return await Get(
state: State.all,
cancellationToken: cancellationToken,
tag: tag
);
@@ -169,37 +177,52 @@ namespace PocketSharp
/// Retrieves items which match the specified search string in title and URI
/// </summary>
/// <param name="searchString">The search string.</param>
/// <param name="searchInUri">if set to <c>true</c> [search in URI].</param>
/// <param name="tag">Filter by tag.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentOutOfRangeException">Search string length has to be a minimum of 2 chars</exception>
/// <exception cref="PocketException"></exception>
public async Task<List<PocketItem>> Search(string searchString, bool searchInUri = true, CancellationToken cancellationToken = default(CancellationToken))
public async Task<IEnumerable<PocketItem>> Search(string searchString, string tag = null, CancellationToken cancellationToken = default(CancellationToken))
{
List<PocketItem> items = await Get(RetrieveFilter.All, cancellationToken);
return Search(items, searchString);
}
/// <summary>
/// Finds the specified search string in title and URI for an available list of items
/// </summary>
/// <param name="availableItems">The available items.</param>
/// <param name="searchString">The search string.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentOutOfRangeException">Search string length has to be a minimum of 2 chars</exception>
/// <exception cref="PocketException"></exception>
public List<PocketItem> Search(List<PocketItem> availableItems, string searchString)
{
if (searchString.Length < 2)
if (String.IsNullOrEmpty(searchString) || searchString.Length < 2)
{
throw new ArgumentOutOfRangeException("Search string length has to be a minimum of 2 chars");
}
return availableItems.Where(item => (
(!String.IsNullOrEmpty(item.FullTitle) && item.FullTitle.ToLower().Contains(searchString))
|| item.Uri.ToString().ToLower().Contains(searchString)
)).ToList();
return await Get(
state: State.all,
search: searchString,
tag: tag,
cancellationToken: cancellationToken
);
}
/// <summary>
/// Retrieves the article content from an URI
/// WARNING:
/// You have to pass the parseUri in the PocketClient ctor for this method to work.
/// This is a private API and can only be used by authenticated users.
/// </summary>
/// <param name="uri">The article URI.</param>
/// <param name="includeImages">Include images into content or use placeholder.</param>
/// <param name="includeVideos">Include videos into content or use placeholder.</param>
/// <param name="forceRefresh">Force refresh of the content (don't use cache).</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
public async Task<PocketArticle> GetArticle(Uri uri, bool includeImages = true, bool includeVideos = true, bool forceRefresh = false, CancellationToken cancellationToken = default(CancellationToken))
{
Dictionary<string, string> parameters = new Dictionary<string, string>()
{
{ "url", uri.OriginalString },
{ "images", includeImages ? "1" : "0" },
{ "videos", includeVideos ? "1" : "0" },
{ "refresh", forceRefresh ? "1" : "0" },
{ "output", "json" }
};
return await Request<PocketArticle>("", cancellationToken, parameters, false, true);
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
using PocketSharp.Models;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -18,7 +19,7 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
public async Task<bool> SendActions(List<PocketAction> actions, CancellationToken cancellationToken = default(CancellationToken))
public async Task<bool> SendActions(IEnumerable<PocketAction> actions, CancellationToken cancellationToken = default(CancellationToken))
{
return await Send(actions, cancellationToken);
}
+31 -13
View File
@@ -127,7 +127,7 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
Task<List<PocketItem>> Get(
Task<IEnumerable<PocketItem>> Get(
State? state = null,
bool? favorite = null,
string tag = null,
@@ -158,7 +158,15 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
Task<List<PocketItem>> Get(RetrieveFilter filter, CancellationToken cancellationToken = default(CancellationToken));
Task<IEnumerable<PocketItem>> Get(RetrieveFilter filter, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Converts a raw JSON response to a PocketItem list
/// </summary>
/// <param name="itemsJSON">The raw JSON response.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
IEnumerable<PocketItem> ConvertJsonToList(string itemsJSON);
/// <summary>
/// Retrieves all available tags.
@@ -167,7 +175,7 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
Task<List<PocketTag>> GetTags(CancellationToken cancellationToken = default(CancellationToken));
Task<IEnumerable<PocketTag>> GetTags(CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Retrieves items by tag
@@ -176,28 +184,33 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
Task<List<PocketItem>> SearchByTag(string tag, CancellationToken cancellationToken = default(CancellationToken));
Task<IEnumerable<PocketItem>> SearchByTag(string tag, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Retrieves items which match the specified search string in title and URI
/// </summary>
/// <param name="searchString">The search string.</param>
/// <param name="searchInUri">if set to <c>true</c> [search in URI].</param>
/// <param name="tag">Filter by tag.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentOutOfRangeException">Search string length has to be a minimum of 2 chars</exception>
/// <exception cref="PocketException"></exception>
Task<List<PocketItem>> Search(string searchString, bool searchInUri = true, CancellationToken cancellationToken = default(CancellationToken));
Task<IEnumerable<PocketItem>> Search(string searchString, string tag = null, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Finds the specified search string in title and URI for an available list of items
/// Retrieves the article content from an URI
/// WARNING:
/// You have to pass the parseUri in the PocketClient ctor for this method to work.
/// This is a private API and can only be used by authenticated users.
/// </summary>
/// <param name="availableItems">The available items.</param>
/// <param name="searchString">The search string.</param>
/// <param name="tag">The article URI.</param>
/// <param name="includeImages">Include images into content or use placeholder.</param>
/// <param name="includeVideos">Include videos into content or use placeholder.</param>
/// <param name="forceRefresh">Force refresh of the content (don't use cache).</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentOutOfRangeException">Search string length has to be a minimum of 2 chars</exception>
/// <exception cref="PocketException"></exception>
List<PocketItem> Search(List<PocketItem> availableItems, string searchString);
Task<PocketArticle> GetArticle(Uri uri, bool includeImages = true, bool includeVideos = true, bool forceRefresh = false, CancellationToken cancellationToken = default(CancellationToken));
#endregion
#region modify methods
@@ -209,7 +222,7 @@ namespace PocketSharp
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
/// <exception cref="PocketException"></exception>
Task<bool> SendActions(List<PocketAction> actions, CancellationToken cancellationToken = default(CancellationToken));
Task<bool> SendActions(IEnumerable<PocketAction> actions, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Archives the specified item.
@@ -422,7 +435,7 @@ namespace PocketSharp
/// <exception cref="PocketException"></exception>
Task<bool> RenameTag(PocketItem item, string oldTag, string newTag, CancellationToken cancellationToken = default(CancellationToken));
#endregion
#region statistics methods
/// <summary>
/// Statistics from the user account.
@@ -442,5 +455,10 @@ namespace PocketSharp
/// <exception cref="PocketException"></exception>
Task<PocketLimits> GetUsageLimits(CancellationToken cancellationToken = default(CancellationToken));
#endregion
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
void Dispose();
}
}
@@ -16,6 +16,6 @@ namespace PocketSharp.Models
/// The actions.
/// </value>
[DataMember(Name = "actions")]
public List<Dictionary<string, string>> Actions { get; set; }
public IEnumerable<Dictionary<string, string>> Actions { get; set; }
}
}
+1 -1
View File
@@ -65,7 +65,7 @@ namespace PocketSharp.Models
// convert DateTime to UNIX timestamp
if (value is DateTime)
{
value = (int)((DateTime)value - new DateTime(1970, 1, 1)).TotalSeconds;
value = (int)((DateTime)value - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
parameterDict.Add(name, value.ToString());
@@ -1,39 +0,0 @@
using System.Runtime.Serialization;
namespace PocketSharp.Models
{
/// <summary>
/// All parameters which can be passed to register a user
/// </summary>
[DataContract]
internal class RegisterParameters : Parameters
{
/// <summary>
/// Gets or sets the username.
/// </summary>
/// <value>
/// The username.
/// </value>
[DataMember(Name = "username")]
public string Username { get; set; }
/// <summary>
/// Gets or sets the E-Mail.
/// </summary>
/// <value>
/// The E-Mail.
/// </value>
[DataMember(Name = "email")]
public string Email { get; set; }
/// <summary>
/// Gets or sets the password.
/// </summary>
/// <value>
/// The password.
/// </value>
[DataMember(Name = "password")]
public string Password { get; set; }
}
}
+3 -2
View File
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
@@ -120,7 +121,7 @@ namespace PocketSharp.Models
if (!String.IsNullOrEmpty(TweetID))
parameters.Add("ref_id", TweetID);
if (Uri != null)
parameters.Add("uri", Uri.ToString());
parameters.Add("url", Uri.OriginalString);
return parameters;
}
+165
View File
@@ -0,0 +1,165 @@
using Newtonsoft.Json;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PocketSharp.Models
{
/// <summary>
/// Article
/// </summary>
[JsonObject]
[ImplementPropertyChanged]
public class PocketArticle
{
/// <summary>
/// Gets or sets the response Code (200 = OK).
/// </summary>
/// <value>
/// The response Code.
/// </value>
[JsonProperty("responseCode")]
public string ResponseCode { get; set; }
/// <summary>
/// Gets or sets the ID.
/// </summary>
/// <value>
/// The ID.
/// </value>
[JsonProperty("resolved_id")]
public string ID { get; set; }
/// <summary>
/// Gets or sets the URI.
/// </summary>
/// <value>
/// The URI.
/// </value>
[JsonProperty("resolvedUrl")]
public Uri Uri { get; set; }
/// <summary>
/// Gets or sets the URI.
/// </summary>
/// <value>
/// The URI.
/// </value>
[JsonProperty("timePublished")]
public DateTime? PublishedTime { get; set; }
/// <summary>
/// Gets or sets the word count.
/// </summary>
/// <value>
/// The word count.
/// </value>
[JsonProperty("wordCount")]
public int WordCount { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is article.
/// </summary>
/// <value>
/// <c>true</c> if this instance is article; otherwise, <c>false</c>.
/// </value>
[JsonProperty("isArticle")]
public bool IsArticle { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is video.
/// </summary>
/// <value>
/// <c>true</c> if this instance is video; otherwise, <c>false</c>.
/// </value>
[JsonProperty("isVideo")]
public bool IsVideo { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is index.
/// </summary>
/// <value>
/// <c>true</c> if this instance is index; otherwise, <c>false</c>.
/// </value>
[JsonProperty("isIndex")]
public bool IsIndex { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance used fallback.
/// </summary>
/// <value>
/// <c>true</c> if this instance used fallback; otherwise, <c>false</c>.
/// </value>
[JsonProperty("usedFallback")]
public bool UsedFallback { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance requires login.
/// </summary>
/// <value>
/// <c>true</c> if this instance requires login; otherwise, <c>false</c>.
/// </value>
[JsonProperty("requiresLogin")]
public bool RequiresLogin { get; set; }
/// <summary>
/// Gets or sets the images.
/// </summary>
/// <value>
/// The images.
/// </value>
[JsonProperty("images")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketImage>))]
public IEnumerable<PocketImage> Images { get; set; }
/// <summary>
/// Gets or sets the videos.
/// </summary>
/// <value>
/// The videos.
/// </value>
[JsonProperty("videos")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketVideo>))]
public IEnumerable<PocketVideo> Videos { get; set; }
/// <summary>
/// Gets or sets the authors.
/// </summary>
/// <value>
/// The authors.
/// </value>
[JsonProperty("authors")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketAuthor>))]
public IEnumerable<PocketAuthor> Authors { get; set; }
/// <summary>
/// Gets or sets the article title.
/// </summary>
/// <value>
/// The Title.
/// </value>
[JsonProperty("title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the Excerpt.
/// </summary>
/// <value>
/// The Excerpt.
/// </value>
[JsonProperty("excerpt")]
public string Excerpt { get; set; }
/// <summary>
/// Gets or sets the Content.
/// </summary>
/// <value>
/// The Content.
/// </value>
[JsonProperty("article")]
public string Content { get; set; }
}
}
+120 -66
View File
@@ -3,6 +3,7 @@ using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace PocketSharp.Models
{
@@ -13,8 +14,7 @@ namespace PocketSharp.Models
[JsonObject]
[ImplementPropertyChanged]
[DebuggerDisplay("Uri = {Uri}, Title = {Title}")]
public class PocketItem
public class PocketItem : IComparable
{
/// <summary>
/// Gets or sets the ID.
@@ -168,15 +168,6 @@ namespace PocketSharp.Models
[JsonProperty("is_article")]
public bool IsArticle { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has image.
/// </summary>
/// <value>
/// <c>true</c> if this instance has image; otherwise, <c>false</c>.
/// </value>
[JsonProperty("has_image")]
private PocketBoolean? _HasImage { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has video.
/// </summary>
@@ -186,21 +177,6 @@ namespace PocketSharp.Models
[JsonProperty("has_video")]
private PocketBoolean? _HasVideo { get; set; }
/// <summary>
/// Gets a value indicating whether [has image].
/// </summary>
/// <value>
/// <c>true</c> if [has image]; otherwise, <c>false</c>.
/// </value>
[JsonIgnore]
public bool HasImage
{
get
{
return _HasImage == PocketBoolean.Yes || _HasImage == PocketBoolean.IsType;
}
}
/// <summary>
/// Gets a value indicating whether [has video].
/// </summary>
@@ -231,21 +207,6 @@ namespace PocketSharp.Models
}
}
/// <summary>
/// Gets a value indicating whether [is image].
/// </summary>
/// <value>
/// <c>true</c> if [is image]; otherwise, <c>false</c>.
/// </value>
[JsonIgnore]
public bool IsImage
{
get
{
return _HasImage == PocketBoolean.IsType;
}
}
/// <summary>
/// Gets or sets the word count.
/// </summary>
@@ -284,23 +245,16 @@ namespace PocketSharp.Models
public DateTime? UpdateTime { get; set; }
/// <summary>
/// Gets or sets the read time.
/// Gets or sets the tags as comma-separated strings.
/// </summary>
/// <value>
/// The read time.
/// The tags.
/// </value>
[JsonProperty("time_read")]
public DateTime? ReadTime { get; set; }
/// <summary>
/// Gets or sets the favorite time.
/// </summary>
/// <value>
/// The favorite time.
/// </value>
[JsonProperty("time_favorited")]
public DateTime? FavoriteTime { get; set; }
[JsonIgnore]
public string TagsString
{
get { return Tags != null ? String.Join(",", Tags.Select(tag => tag.Name)) : ""; }
}
/// <summary>
/// Gets or sets the tags.
@@ -310,7 +264,7 @@ namespace PocketSharp.Models
/// </value>
[JsonProperty("tags")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketTag>))]
public List<PocketTag> Tags { get; set; }
public IEnumerable<PocketTag> Tags { get; set; }
/// <summary>
/// Gets or sets the images.
@@ -320,7 +274,7 @@ namespace PocketSharp.Models
/// </value>
[JsonProperty("images")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketImage>))]
public List<PocketImage> Images { get; set; }
public IEnumerable<PocketImage> Images { get; set; }
/// <summary>
/// Gets or sets the videos.
@@ -330,7 +284,7 @@ namespace PocketSharp.Models
/// </value>
[JsonProperty("videos")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketVideo>))]
public List<PocketVideo> Videos { get; set; }
public IEnumerable<PocketVideo> Videos { get; set; }
/// <summary>
/// Gets or sets the authors.
@@ -340,7 +294,7 @@ namespace PocketSharp.Models
/// </value>
[JsonProperty("authors")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketAuthor>))]
public List<PocketAuthor> Authors { get; set; }
public IEnumerable<PocketAuthor> Authors { get; set; }
/// <summary>
/// Gets the lead image.
@@ -351,16 +305,116 @@ namespace PocketSharp.Models
[JsonIgnore]
public PocketImage LeadImage
{
get { return Images != null && Images.Count > 0 ? Images[0] : null; }
get { return Images != null && Images.Count() > 0 ? Images.First() : null; }
}
/// <summary>
/// Gets and sets the JSON the model was deserialized from
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
/// </summary>
/// <value>
/// Model's original JSON representation
/// </value>
[JsonIgnore]
public string Json { get; set; }
/// <param name="obj">An object to compare with this instance.</param>
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order.
/// </returns>
int IComparable.CompareTo(object obj)
{
PocketItem item = (PocketItem)obj;
if (!AddTime.HasValue)
{
return 1;
}
if (!item.AddTime.HasValue)
{
return -1;
}
return DateTime.Compare(AddTime.Value, item.AddTime.Value);
}
/// <summary>
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
PocketItem item = (PocketItem)obj;
if ((Object)item == null)
{
return false;
}
return ID == item.ID;
}
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="a">A.</param>
/// <param name="b">The b.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator ==(PocketItem a, PocketItem b)
{
if (Object.ReferenceEquals(a, b))
{
return true;
}
PocketItem itemA = (PocketItem)a;
PocketItem itemB = (PocketItem)b;
if ((Object)itemA == null || (Object)itemB == null)
{
return false;
}
return itemA.ID == itemB.ID;
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="a">A.</param>
/// <param name="b">The b.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator !=(PocketItem a, PocketItem b)
{
return !(a == b);
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return ID.GetHashCode();
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
return ID;
}
}
}
+9
View File
@@ -18,5 +18,14 @@ namespace PocketSharp.Models
/// </value>
[JsonProperty("tag")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the item iD.
/// </summary>
/// <value>
/// The name.
/// </value>
[JsonProperty("item_id")]
public string ItemID { get; set; }
}
}
+1 -1
View File
@@ -36,6 +36,6 @@ namespace PocketSharp.Models
/// </value>
[JsonProperty("list")]
[JsonConverter(typeof(ObjectToArrayConverter<PocketItem>))]
public List<PocketItem> Items { get; set; }
public IEnumerable<PocketItem> Items { get; set; }
}
}
+128 -26
View File
@@ -8,19 +8,25 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
namespace PocketSharp
{
/// <summary>
/// PocketClient
/// </summary>
public partial class PocketClient : IPocketClient
public partial class PocketClient : IPocketClient, IDisposable
{
/// <summary>
/// REST client used for the API communication
/// </summary>
protected readonly HttpClient _restClient;
/// <summary>
/// REST client used for the API communication with the Text Parser API
/// </summary>
protected readonly HttpClient _restParserClient;
/// <summary>
/// Caches HTTP headers from last response
/// </summary>
@@ -39,13 +45,23 @@ namespace PocketSharp
/// <summary>
/// The authentification URL
/// </summary>
protected string authentificationUri = "https://getpocket.com/auth/authorize?request_token={0}&redirect_uri={1}&mobile={2}";
protected string authentificationUri = "https://getpocket.com/auth/authorize?request_token={0}&redirect_uri={1}&mobile={2}&force={3}&webauthenticationbroker={4}";
/// <summary>
/// Indicates, whether this client is used for mobile or desktop
/// </summary>
protected bool isMobileClient = true;
/// <summary>
/// Indicates, whether this client is used inside a broker (on Windows 8)
/// </summary>
protected bool useInsideWebAuthenticationBroker = true;
/// <summary>
/// Indicates, whether the last HTTP response is cached
/// </summary>
protected bool cacheHTTPResponseData;
/// <summary>
/// callback URLi for API calls
/// </summary>
@@ -97,18 +113,26 @@ namespace PocketSharp
/// <param name="handler">The HttpMessage handler.</param>
/// <param name="timeout">Request timeout (in seconds).</param>
/// <param name="isMobileClient">Indicates, whether this client is used for mobile or desktop</param>
/// <param name="useInsideWebAuthenticationBroker">Indicates, whether this client is used inside a broker (on Windows 8), see: http://getpocket.com/developer/docs/getstarted/windows8 </param>
/// <param name="parserUri">Enables the wrapper for the private Text Parser API</param>
/// <param name="cacheHTTPResponseData">Caches the last HTTP response in public properties</param>
public PocketClient(
string consumerKey,
string accessCode = null,
string callbackUri = null,
HttpMessageHandler handler = null,
int? timeout = null,
bool isMobileClient = true)
bool isMobileClient = true,
bool useInsideWebAuthenticationBroker = false,
Uri parserUri = null,
bool cacheHTTPResponseData = true)
{
// assign public properties
ConsumerKey = consumerKey;
this.isMobileClient = isMobileClient;
this.useInsideWebAuthenticationBroker = useInsideWebAuthenticationBroker;
this.cacheHTTPResponseData = cacheHTTPResponseData;
// assign access code if submitted
if (accessCode != null)
@@ -122,6 +146,23 @@ namespace PocketSharp
CallbackUri = Uri.EscapeUriString(callbackUri.ToString());
}
// assign text parser if parserUri submitted
if (parserUri != null)
{
_restParserClient = new HttpClient(handler ?? new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
});
_restParserClient.BaseAddress = parserUri;
_restParserClient.DefaultRequestHeaders.Add("Accept", "*/*");
_restParserClient.DefaultRequestHeaders.Add("X-Accept", "application/json");
if (timeout.HasValue)
{
_restParserClient.Timeout = TimeSpan.FromSeconds(timeout.Value);
}
}
// initialize REST client
_restClient = new HttpClient(handler ?? new HttpClientHandler()
{
@@ -158,16 +199,27 @@ namespace PocketSharp
string method,
CancellationToken cancellationToken,
Dictionary<string, string> parameters = null,
bool requireAuth = true) where T : class, new()
bool requireAuth = true,
bool isReaderRequest = false) where T : class, new()
{
if (requireAuth && AccessCode == null)
{
throw new PocketException("SDK error: No access token available. Use authentication first.");
}
// rewrite base if it is a request to the Parser API
if (isReaderRequest)
{
if (_restParserClient == null)
{
throw new PocketException("Please pass the parserUri in the PocketClient ctor.");
}
}
// every single Pocket API endpoint requires HTTP POST data
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, method);
HttpResponseMessage response = null;
string responseString = null;
if (parameters == null)
{
@@ -178,7 +230,7 @@ namespace PocketSharp
parameters.Add("consumer_key", ConsumerKey);
// add access token (necessary for all requests except authentification)
if (AccessCode != null)
if (AccessCode != null && requireAuth)
{
parameters.Add("access_token", AccessCode);
}
@@ -195,30 +247,69 @@ namespace PocketSharp
// make async request
try
{
response = await _restClient.SendAsync(request, cancellationToken);
if (isReaderRequest)
{
response = await _restParserClient.SendAsync(request, cancellationToken);
}
else
{
response = await _restClient.SendAsync(request, cancellationToken);
}
// validate HTTP response
ValidateResponse(response);
// cache headers
if (cacheHTTPResponseData)
{
lastHeaders = response.Headers;
}
// read response
responseString = await response.Content.ReadAsStringAsync();
}
catch (HttpRequestException exc)
{
throw new PocketException(exc.Message, exc);
}
catch (PocketException exc)
{
throw exc;
}
finally
{
request.Dispose();
// validate HTTP response
ValidateResponse(response);
// cache headers
lastHeaders = response.Headers;
// read response
var responseString = await response.Content.ReadAsStringAsync();
if (response != null)
{
response.Dispose();
}
}
// cache response
lastResponseData = responseString;
if (cacheHTTPResponseData)
{
lastResponseData = responseString;
}
responseString = responseString.Replace("[]", "{}");
return DeserializeJson<T>(responseString);
}
/// <summary>
/// Converts JSON to Pocket objects
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json">Raw JSON response</param>
/// <returns></returns>
/// <exception cref="PocketException">Parse error.</exception>
protected T DeserializeJson<T>(string json) where T : class, new()
{
json = json.Replace("[]", "{}");
// deserialize object
T parsedResponse = JsonConvert.DeserializeObject<T>(
responseString,
json,
new JsonSerializerSettings
{
Error = (object sender, ErrorEventArgs args) =>
@@ -245,22 +336,18 @@ namespace PocketSharp
/// </summary>
/// <param name="actionParameters">The action parameters.</param>
/// <returns></returns>
internal async Task<bool> Send(List<PocketAction> actionParameters, CancellationToken cancellationToken)
internal async Task<bool> Send(IEnumerable<PocketAction> actionParameters, CancellationToken cancellationToken)
{
List<Dictionary<string, object>> actionParamList = new List<Dictionary<string, object>>();
foreach (var action in actionParameters)
foreach (PocketAction action in actionParameters)
{
actionParamList.Add(action.Convert());
action.Time = action.Time.HasValue ? ((DateTime)action.Time).ToUniversalTime() : action.Time;
}
Dictionary<string, string> parameters = new Dictionary<string, string>() {{
"actions", JsonConvert.SerializeObject(actionParamList)
"actions", JsonConvert.SerializeObject(actionParameters.Select(action => action.Convert()))
}};
Modify response = await Request<Modify>("send", cancellationToken, parameters);
return response.Status;
return (await Request<Modify>("send", cancellationToken, parameters)).Status;
}
@@ -355,5 +442,20 @@ namespace PocketSharp
return result;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_restClient.Dispose();
lastHeaders = null;
if (_restParserClient != null)
{
_restParserClient.Dispose();
}
}
}
}
+9 -10
View File
@@ -36,7 +36,7 @@
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<NuGetPackageImportStamp>10f8a946</NuGetPackageImportStamp>
<NuGetPackageImportStamp>06ad0893</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -57,13 +57,12 @@
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="Components\Statistics.cs" />
<Compile Include="IPocketClient.cs" />
<Compile Include="Models\Parameters\RegisterParameters.cs" />
<Compile Include="Models\PocketArticle.cs" />
<Compile Include="Models\PocketBoolean.cs" />
<Compile Include="Models\PocketLimits.cs" />
<Compile Include="Models\PocketStatistics.cs" />
@@ -99,20 +98,20 @@
</ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.2\lib\portable-net40+sl5+wp80+win8+monotouch+monoandroid\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.6.0.4\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="PropertyChanged">
<HintPath>..\packages\PropertyChanged.Fody.1.48.0.0\Lib\portable-net4+sl4+wp7+win8+MonoAndroid16+MonoTouch40\PropertyChanged.dll</HintPath>
<HintPath>..\packages\PropertyChanged.Fody.1.48.3\Lib\portable-net4+sl4+wp8+win8+wpa81+MonoAndroid16+MonoTouch40\PropertyChanged.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Http">
<HintPath>..\packages\Microsoft.Net.Http.2.2.19\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.dll</HintPath>
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Extensions">
<HintPath>..\packages\Microsoft.Net.Http.2.2.19\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Extensions.dll</HintPath>
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Primitives">
<HintPath>..\packages\Microsoft.Net.Http.2.2.19\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Primitives.dll</HintPath>
<HintPath>..\packages\Microsoft.Net.Http.2.2.22\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Primitives.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -137,14 +136,14 @@
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\packages\Fody.1.22.1\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.22.1\build\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Fody.1.24.0\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.24.0\build\Fody.targets'))" />
</Target>
<Import Project="..\packages\Fody.1.22.1\build\Fody.targets" Condition="Exists('..\packages\Fody.1.22.1\build\Fody.targets')" />
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
<Import Project="..\packages\Fody.1.24.0\build\Fody.targets" Condition="Exists('..\packages\Fody.1.24.0\build\Fody.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
+2 -2
View File
@@ -22,5 +22,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.0.0")]
[assembly: AssemblyFileVersion("4.0.0")]
[assembly: AssemblyVersion("4.1.6.0")]
[assembly: AssemblyFileVersion("4.1.6.0")]
+24 -15
View File
@@ -32,10 +32,11 @@ namespace PocketSharp
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
DateTime epoc = new DateTime(1970, 1, 1);
var delta = (DateTime)value - epoc;
DateTime date = ((DateTime)value).ToUniversalTime();
DateTime epoc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var delta = date.Subtract(epoc);
writer.WriteValue((long)delta.TotalSeconds);
writer.WriteValue((int)Math.Truncate(delta.TotalSeconds));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
@@ -45,7 +46,12 @@ namespace PocketSharp
return null;
}
return new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(Convert.ToDouble(reader.Value)).ToLocalTime();
if (reader.Value.ToString().StartsWith("-"))
{
return null;
}
return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(Convert.ToDouble(reader.Value));
}
}
@@ -123,22 +129,26 @@ namespace PocketSharp
public class ObjectToArrayConverter<T> : CustomCreationConverter<List<T>> where T : new()
public class ObjectToArrayConverter<T> : CustomCreationConverter<IEnumerable<T>> where T : new()
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject;
List<T> result = new List<T>();
T target;
List<T> results = new List<T>();
// object is an array
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<List<T>>(reader);
return serializer.Deserialize<IEnumerable<T>>(reader);
}
else if (reader.TokenType == JsonToken.Null)
{
return null;
}
else if (reader.TokenType == JsonToken.String)
{
return null;
}
try
{
@@ -152,11 +162,12 @@ namespace PocketSharp
// Populate the object properties
foreach (KeyValuePair<string, JToken> item in jObject)
{
target = serializer.Deserialize<T>(item.Value.CreateReader());
result.Add(target);
results.Add(
serializer.Deserialize<T>(item.Value.CreateReader())
);
}
return result;
return results;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
@@ -164,11 +175,10 @@ namespace PocketSharp
throw new NotImplementedException();
}
public override List<T> Create(Type objectType)
public override IEnumerable<T> Create(Type objectType)
{
return new List<T>();
}
}
@@ -178,11 +188,10 @@ namespace PocketSharp
public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
var jObject = JObject.ReadFrom(reader);
var pocketItem = new PocketItem();
serializer.Populate(jObject.CreateReader(), pocketItem);
pocketItem.Json = jObject.ToString();
//pocketItem.Json = jObject.ToString();
return pocketItem;
}
-15
View File
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.7.0" newVersion="2.6.7.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.7.0" newVersion="2.6.7.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
+5 -5
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Fody" version="1.22.1" targetFramework="portable-net403+sl50+win+wp80" developmentDependency="true" />
<package id="Microsoft.Bcl" version="1.1.7" targetFramework="portable-net403+sl50+win+wpa81+wp80" requireReinstallation="True" />
<package id="Fody" version="1.24.0" targetFramework="portable-net45+win+wpa81+wp80" developmentDependency="true" />
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="portable-net45+win+wpa81+wp80" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="portable-net403+sl50+win+wpa81+wp80" />
<package id="Microsoft.Net.Http" version="2.2.19" targetFramework="portable-net403+sl50+win+wpa81+wp80" />
<package id="Newtonsoft.Json" version="6.0.2" targetFramework="portable-net403+sl50+win+wp80" requireReinstallation="True" />
<package id="PropertyChanged.Fody" version="1.48.0.0" targetFramework="portable-net403+sl50+win+wp80" developmentDependency="true" requireReinstallation="True" />
<package id="Microsoft.Net.Http" version="2.2.22" targetFramework="portable-net45+win+wpa81+wp80" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="portable-net45+win+wpa81+wp80" />
<package id="PropertyChanged.Fody" version="1.48.3" targetFramework="portable-net45+win+wpa81+wp80" developmentDependency="true" />
</packages>
+8 -8
View File
@@ -15,9 +15,8 @@ See [wiki](https://github.com/ceee/PocketSharp/wiki)
## Where's the Article View API?
PocketSharp doesn't include PocketSharp.Reader (an article view implementation) anymore.
**PocketSharp.Reader is now [ReadSharp](https://github.com/ceee/ReadSharp) and hosted without PocketSharp.**
You can either use the open source [ReadSharp](https://github.com/ceee/ReadSharp) parser or if you want to use the official API by Pocket, you have to request access to it.<br>
Afterwards you can use the access information to query the endpoint with PocketSharp. Instructions [here](https://github.com/ceee/PocketSharp/wiki/Article-parser).
---
@@ -49,14 +48,15 @@ Which will output:
## Supported platforms
PocketSharp is a **Portable Class Library**, therefore it's compatible with multiple platforms:
PocketSharp is a **Portable Class Library**, therefore it's compatible with multiple platforms and Universal Apps:
- **.NET** >= 4.5 (including WPF)
- **Silverlight** >= 4
- **Windows Phone** >= 7.5
- **Windows Store**
- **Windows Phone** (Silverlight + WinPRT) >= 8
- **Windows Store** >= 8
- **Xamarin** iOS + Android
- _WP7 and Silverlight are dropped in 4.0, use PocketSharp < 4.0, if you want to support them_
You can find examples for Silverlight 5, WP8 and WPF in the `PocketSharp.Examples` ([@github](https://github.com/ceee/PocketSharp/tree/master/PocketSharp.Examples)) folder.
You can find examples for WP8 and WPF in the `PocketSharp.Examples` ([@github](https://github.com/ceee/PocketSharp/tree/master/PocketSharp.Examples)) folder.
## Dependencies