diff --git a/Habitica.Todoist.Integration.Console/App.config b/Habitica.Todoist.Integration.Console/App.config index 7b7ea32..91a7e94 100644 --- a/Habitica.Todoist.Integration.Console/App.config +++ b/Habitica.Todoist.Integration.Console/App.config @@ -57,6 +57,10 @@ + + + + diff --git a/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj b/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj index 7222040..8c6bf24 100644 --- a/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj +++ b/Habitica.Todoist.Integration.Console/Habitica.Todoist.Integration.Console.csproj @@ -13,6 +13,8 @@ true true + + AnyCPU @@ -34,6 +36,15 @@ 4 + + ..\packages\Microsoft.Azure.Cosmos.Table.1.0.6\lib\netstandard2.0\Microsoft.Azure.Cosmos.Table.dll + + + ..\packages\Microsoft.Azure.DocumentDB.Core.2.1.3\lib\netstandard1.6\Microsoft.Azure.DocumentDB.Core.dll + + + ..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -61,24 +72,121 @@ ..\packages\Microsoft.Extensions.Primitives.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll + + ..\packages\Microsoft.OData.Core.7.5.0\lib\portable-net45+win8+wpa81\Microsoft.OData.Core.dll + + + ..\packages\Microsoft.OData.Edm.7.5.0\lib\portable-net45+win8+wpa81\Microsoft.OData.Edm.dll + + + ..\packages\Microsoft.Spatial.7.5.0\lib\portable-net45+win8+wpa81\Microsoft.Spatial.dll + ..\packages\Microsoft.Bcl.Async.1.0.165\lib\net45\Microsoft.Threading.Tasks.dll ..\packages\Microsoft.Bcl.Async.1.0.165\lib\net45\Microsoft.Threading.Tasks.Extensions.dll + + ..\packages\Microsoft.Win32.Primitives.4.0.1\lib\net46\Microsoft.Win32.Primitives.dll + True + True + + + ..\packages\WindowsAzure.Storage.9.3.3\lib\net45\Microsoft.WindowsAzure.Storage.dll + ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\System.AppContext.4.1.0\lib\net463\System.AppContext.dll + True + True + ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll + + ..\packages\System.Collections.NonGeneric.4.0.1\lib\net46\System.Collections.NonGeneric.dll + True + True + + + ..\packages\System.Collections.Specialized.4.0.1\lib\net46\System.Collections.Specialized.dll + True + True + + + + ..\packages\System.Console.4.0.0\lib\net46\System.Console.dll + True + True + + + ..\packages\System.Diagnostics.DiagnosticSource.4.0.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Diagnostics.TraceSource.4.0.0\lib\net46\System.Diagnostics.TraceSource.dll + True + True + + + ..\packages\System.Diagnostics.Tracing.4.1.0\lib\net462\System.Diagnostics.Tracing.dll + True + True + + + ..\packages\System.Globalization.Calendars.4.0.1\lib\net46\System.Globalization.Calendars.dll + True + True + + + ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll + True + True + + + ..\packages\System.IO.Compression.4.1.0\lib\net46\System.IO.Compression.dll + True + True + + + + ..\packages\System.IO.Compression.ZipFile.4.0.1\lib\net46\System.IO.Compression.ZipFile.dll + True + True + + + ..\packages\System.IO.FileSystem.4.0.1\lib\net46\System.IO.FileSystem.dll + True + True + + + ..\packages\System.IO.FileSystem.Primitives.4.0.1\lib\net46\System.IO.FileSystem.Primitives.dll + True + True + + + ..\packages\System.Linq.4.1.0\lib\net463\System.Linq.dll + True + True + + + ..\packages\System.Linq.Expressions.4.1.0\lib\net463\System.Linq.Expressions.dll + True + True + ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + ..\packages\System.Net.Http.4.1.0\lib\net46\System.Net.Http.dll + True + True + ..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Extensions.dll @@ -86,19 +194,100 @@ ..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Primitives.dll + + ..\packages\System.Net.NameResolution.4.0.0\lib\net46\System.Net.NameResolution.dll + True + True + + + ..\packages\System.Net.NetworkInformation.4.1.0\lib\net46\System.Net.NetworkInformation.dll + True + True + + + ..\packages\System.Net.Security.4.3.2\lib\net46\System.Net.Security.dll + True + True + + + ..\packages\System.Net.Sockets.4.1.0\lib\net46\System.Net.Sockets.dll + True + True + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + ..\packages\System.Reflection.4.1.0\lib\net462\System.Reflection.dll + True + True + + + ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll + True + True + ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.Extensions.4.1.0\lib\net462\System.Runtime.Extensions.dll + True + True + + + ..\packages\System.Runtime.InteropServices.4.1.0\lib\net462\System.Runtime.InteropServices.dll + True + True + + + ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + True + True + + + + ..\packages\System.Runtime.Serialization.Primitives.4.1.1\lib\net46\System.Runtime.Serialization.Primitives.dll + True + True + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll + True + True + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + True + True + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + True + True + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + True + True + + + ..\packages\System.Security.SecureString.4.0.0\lib\net46\System.Security.SecureString.dll + True + True + ..\packages\System.Text.Encodings.Web.4.7.0\lib\netstandard2.0\System.Text.Encodings.Web.dll ..\packages\System.Text.Json.4.7.1\lib\net461\System.Text.Json.dll + + ..\packages\System.Text.RegularExpressions.4.1.0\lib\net463\System.Text.RegularExpressions.dll + True + True + ..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll @@ -110,7 +299,6 @@ - @@ -140,4 +328,11 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/Habitica.Todoist.Integration.Console/Program.cs b/Habitica.Todoist.Integration.Console/Program.cs index 3c246fe..c9527c8 100644 --- a/Habitica.Todoist.Integration.Console/Program.cs +++ b/Habitica.Todoist.Integration.Console/Program.cs @@ -1,4 +1,6 @@ using Habitica.Todoist.Integration.Model.Habitica; +using Habitica.Todoist.Integration.Model.Habitica.Enums; +using Habitica.Todoist.Integration.Model.Storage; using Habitica.Todoist.Integration.Model.Todoist; using Habitica.Todoist.Integration.Services; using Microsoft.Extensions.Configuration; @@ -20,30 +22,79 @@ namespace Habitica.Todoist.Integration.Console private static string habiticaApiUrl => "https://habitica.com/api/v3/"; private static string habiticaUserId => configuration["habitica:userId"]; private static string habiticaApiKey => configuration["habitica:apiKey"]; - private static string todoistApiUrl => "https://api.todoist.com/sync/v8/"; private static string todoistApiKey => configuration["todoist:apiKey"]; + private static string tableStorageConnectionString => configuration["tableStorage:connectionString"]; + private static string giosUserId => "0b6ec4eb-8878-4b9e-8585-7673764a6541"; static void Main(string[] args) { ConfigBuild(); - var todoistClient = new TodoistClientService(todoistApiKey); - var syncResponse = todoistClient.GetUpdatedItems().ConfigureAwait(false).GetAwaiter().GetResult(); - - foreach (var item in syncResponse.Items) - { - var newTask = new Task - { - Text = item.GetCleanContent(), - Type = TaskType.Todo, - Date = item.Due?.ToJavaScriptDateStr(), - Priority = GetHabiticaDifficulty(item.GetDifficulty().GetValueOrDefault()) - }; + //var habiticaClient2 = new HabiticaServiceClient(habiticaUserId, habiticaApiKey); - var habiticaClient = new HabiticaClientService(habiticaUserId, habiticaApiKey); - var task = habiticaClient.CreateUserTask(newTask).ConfigureAwait(false).GetAwaiter().GetResult(); + //var tasks = habiticaClient2.ReadTasks().ConfigureAwait(false).GetAwaiter().GetResult().Data; + //foreach (var task in tasks) + // habiticaClient2.DeleteTask(task.Id).ConfigureAwait(false).GetAwaiter().GetResult(); + + //return; + + // initialize all the clients + var habiticaClient = new HabiticaServiceClient(habiticaUserId, habiticaApiKey); + var todoistClient = new TodoistServiceClient(todoistApiKey); + var tableStorageClient = new TableStorageClient(tableStorageConnectionString); + + // get todoist sync token if available + var syncToken = ""; + try { syncToken = tableStorageClient.Query().Where(x => x.PartitionKey == giosUserId).ToList() + .OrderByDescending(x => x.Timestamp).First().RowKey; } catch { } + + // get all changed items from todoist + var response = todoistClient.GetChangedItems(syncToken).ConfigureAwait(false).GetAwaiter().GetResult(); + var changedItems = response.Items; + + // filter out items by actions + var addItems = changedItems.Where(x => !tableStorageClient.Exists(giosUserId, x.Id) + .ConfigureAwait(false).GetAwaiter().GetResult() && x.Is_deleted == 0).ToList(); + var updateItems = changedItems.Where(x => tableStorageClient.Exists(giosUserId, x.Id) + .ConfigureAwait(false).GetAwaiter().GetResult() && x.Is_deleted == 0 && x.Date_completed == null).ToList(); + var completeItems = changedItems.Where(x => x.Is_deleted == 0 && x.Date_completed != null).ToList(); + var deleteItems = changedItems.Where(x => tableStorageClient.Exists(giosUserId, x.Id) + .ConfigureAwait(false).GetAwaiter().GetResult() && x.Is_deleted == 1).ToList(); + + foreach (var addItem in addItems) + { + var task = habiticaClient.CreateTask(TaskFromTodoistItem(addItem)).ConfigureAwait(false).GetAwaiter().GetResult().Data; + var link = new TodoHabitLink(giosUserId, addItem.Id, task.Id); + + tableStorageClient.InsertOrUpdate(link).ConfigureAwait(false).GetAwaiter().GetResult(); + tableStorageClient.InsertOrUpdate(link.Reverse()).ConfigureAwait(false).GetAwaiter().GetResult(); } + + foreach (var updateItem in updateItems) + { + var habiticaId = tableStorageClient.Query().Where(x => x.PartitionKey == giosUserId && x.RowKey == updateItem.Id) + .ToList().First().HabiticaId; + habiticaClient.UpdateTask(TaskFromTodoistItem(updateItem, habiticaId)).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + foreach (var completeItem in completeItems) + { + var habiticaId = tableStorageClient.Query().Where(x => x.PartitionKey == giosUserId && x.RowKey == completeItem.Id) + .ToList().First().HabiticaId; + habiticaClient.ScoreTask(habiticaId, ScoreAction.Up).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + foreach (var deleteItem in deleteItems) + { + var habiticaId = tableStorageClient.Query().Where(x => x.PartitionKey == giosUserId && x.RowKey == deleteItem.Id) + .ToList().First().HabiticaId; + habiticaClient.DeleteTask(habiticaId).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + // store sync token + var todoistSync = new TodoistSync(giosUserId, response.Sync_token); + tableStorageClient.InsertOrUpdate(todoistSync).ConfigureAwait(false).GetAwaiter().GetResult(); } public static string GetHabiticaDifficulty(int todoistDifficulty) @@ -62,6 +113,21 @@ namespace Habitica.Todoist.Integration.Console return null; } + public static Task TaskFromTodoistItem(Item item, string id = null) + { + var taskTypeStr = Enum.GetName(typeof(TaskType), TaskType.Todo).ToLower(); + var task = new Task + { + Id = id, + Text = item.Content, + Type = taskTypeStr, + Date = item.Due?.ToJavaScriptDateStr(), + Priority = GetHabiticaDifficulty(item.Priority) + }; + + return task; + } + static void ConfigBuild() { new ConfigurationBuilder() diff --git a/Habitica.Todoist.Integration.Console/packages.config b/Habitica.Todoist.Integration.Console/packages.config index ba66f62..727577d 100644 --- a/Habitica.Todoist.Integration.Console/packages.config +++ b/Habitica.Todoist.Integration.Console/packages.config @@ -1,5 +1,8 @@  + + + @@ -13,13 +16,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj b/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj index 6e86919..a0a2fa5 100644 --- a/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj +++ b/Habitica.Todoist.Integration.Model/Habitica.Todoist.Integration.Model.csproj @@ -5,6 +5,7 @@ + diff --git a/Habitica.Todoist.Integration.Model/Habitica/Enums/ScoreAction.cs b/Habitica.Todoist.Integration.Model/Habitica/Enums/ScoreAction.cs new file mode 100644 index 0000000..635efce --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Habitica/Enums/ScoreAction.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Habitica.Enums +{ + public enum ScoreAction + { + Up = 0, + Down = 1 + } +} diff --git a/Habitica.Todoist.Integration.Model/Habitica/Enums/TaskType.cs b/Habitica.Todoist.Integration.Model/Habitica/Enums/TaskType.cs new file mode 100644 index 0000000..8ec5f92 --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Habitica/Enums/TaskType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Habitica.Enums +{ + public enum TaskType + { + Habit = 0, + Daily = 1, + Todo = 2, + Reward = 3 + } +} diff --git a/Habitica.Todoist.Integration.Model/Habitica/Responses/HabiticaResponse.cs b/Habitica.Todoist.Integration.Model/Habitica/Responses/HabiticaResponse.cs new file mode 100644 index 0000000..878f9ca --- /dev/null +++ b/Habitica.Todoist.Integration.Model/Habitica/Responses/HabiticaResponse.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Habitica.Todoist.Integration.Model.Habitica.Responses +{ + public class HabiticaReponse + { + [JsonProperty("success")] + public bool Success { get; set; } + [JsonProperty("data")] + public T Data { get; set; } + [JsonProperty("notifications")] + public object Notifications { get; set; } + } +} diff --git a/Habitica.Todoist.Integration.Model/Habitica/TaskType.cs b/Habitica.Todoist.Integration.Model/Habitica/TaskType.cs deleted file mode 100644 index 1968351..0000000 --- a/Habitica.Todoist.Integration.Model/Habitica/TaskType.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Habitica.Todoist.Integration.Model.Habitica -{ - public static class TaskType - { - public static string Habit = "habit"; - public static string Daily = "daily"; - public static string Todo = "todo"; - public static string Reward = "reward"; - } -} diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoAction.cs b/Habitica.Todoist.Integration.Model/Storage/Enums/TodoAction.cs similarity index 76% rename from Habitica.Todoist.Integration.Model/Storage/TodoAction.cs rename to Habitica.Todoist.Integration.Model/Storage/Enums/TodoAction.cs index 00fff30..a4ffe51 100644 --- a/Habitica.Todoist.Integration.Model/Storage/TodoAction.cs +++ b/Habitica.Todoist.Integration.Model/Storage/Enums/TodoAction.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Habitica.Todoist.Integration.Model.Storage +namespace Habitica.Todoist.Integration.Model.Storage.Enum { public enum TodoAction { diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoApp.cs b/Habitica.Todoist.Integration.Model/Storage/Enums/TodoApp.cs similarity index 72% rename from Habitica.Todoist.Integration.Model/Storage/TodoApp.cs rename to Habitica.Todoist.Integration.Model/Storage/Enums/TodoApp.cs index 0f81a11..c16dbe0 100644 --- a/Habitica.Todoist.Integration.Model/Storage/TodoApp.cs +++ b/Habitica.Todoist.Integration.Model/Storage/Enums/TodoApp.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Habitica.Todoist.Integration.Model.Storage +namespace Habitica.Todoist.Integration.Model.Storage.Enum { public enum TodoApp { diff --git a/Habitica.Todoist.Integration.Model/Storage/HabitTodoLink.cs b/Habitica.Todoist.Integration.Model/Storage/HabitTodoLink.cs index 0d6bb28..8ed54d2 100644 --- a/Habitica.Todoist.Integration.Model/Storage/HabitTodoLink.cs +++ b/Habitica.Todoist.Integration.Model/Storage/HabitTodoLink.cs @@ -1,12 +1,26 @@ -using System; +using Microsoft.Azure.Cosmos.Table; +using System; using System.Collections.Generic; using System.Text; namespace Habitica.Todoist.Integration.Model.Storage { - public class HabitTodoLink + public class HabitTodoLink : TableEntity { - public string HabiticaId { get; set; } + public HabitTodoLink() { } + + public HabitTodoLink(string userId, string habiticaId, string todoistId) + { + PartitionKey = userId; + RowKey = habiticaId; + TodoistId = todoistId; + } + public string TodoistId { get; set; } + + public TodoHabitLink Reverse() + { + return new TodoHabitLink(PartitionKey, TodoistId, RowKey); + } } } diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoChange.cs b/Habitica.Todoist.Integration.Model/Storage/TodoChange.cs index 7e670e4..a7569e1 100644 --- a/Habitica.Todoist.Integration.Model/Storage/TodoChange.cs +++ b/Habitica.Todoist.Integration.Model/Storage/TodoChange.cs @@ -1,12 +1,22 @@ -using System; +using Habitica.Todoist.Integration.Model.Storage.Enum; +using Microsoft.Azure.Cosmos.Table; +using System; using System.Collections.Generic; using System.Text; namespace Habitica.Todoist.Integration.Model.Storage { - public class TodoChange + /* TODO: Rework structure */ + public class TodoChange : TableEntity { - public string Id { get; set; } + public TodoChange() { } + + public TodoChange(string userId, string todoId) + { + PartitionKey = userId; + RowKey = todoId; + } + public TodoApp Application { get; set; } public TodoAction Action { get; set; } public bool Applied { get; set; } diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoHabitLink.cs b/Habitica.Todoist.Integration.Model/Storage/TodoHabitLink.cs index 0394d52..3ac8ddb 100644 --- a/Habitica.Todoist.Integration.Model/Storage/TodoHabitLink.cs +++ b/Habitica.Todoist.Integration.Model/Storage/TodoHabitLink.cs @@ -1,12 +1,26 @@ -using System; +using Microsoft.Azure.Cosmos.Table; +using System; using System.Collections.Generic; using System.Text; namespace Habitica.Todoist.Integration.Model.Storage { - public class TodoHabitLink + public class TodoHabitLink : TableEntity { - public string TodoistId { get; set; } + public TodoHabitLink() { } + + public TodoHabitLink(string userId, string todoistId, string habiticaId) + { + PartitionKey = userId; + RowKey = todoistId; + HabiticaId = habiticaId; + } + public string HabiticaId { get; set; } + + public HabitTodoLink Reverse() + { + return new HabitTodoLink(PartitionKey, HabiticaId, RowKey); + } } } diff --git a/Habitica.Todoist.Integration.Model/Storage/TodoistSync.cs b/Habitica.Todoist.Integration.Model/Storage/TodoistSync.cs index 4264baf..1f925a4 100644 --- a/Habitica.Todoist.Integration.Model/Storage/TodoistSync.cs +++ b/Habitica.Todoist.Integration.Model/Storage/TodoistSync.cs @@ -1,11 +1,18 @@ -using System; +using Microsoft.Azure.Cosmos.Table; +using System; using System.Collections.Generic; using System.Text; namespace Habitica.Todoist.Integration.Model.Storage { - public class TodoistSync + public class TodoistSync : TableEntity { - public string SyncToken { get; set; } + public TodoistSync() { } + + public TodoistSync(string userId, string syncToken) + { + PartitionKey = userId; + RowKey = syncToken; + } } } diff --git a/Habitica.Todoist.Integration.Model/Todoist/Item.cs b/Habitica.Todoist.Integration.Model/Todoist/Item.cs index 215c59d..bad23a4 100644 --- a/Habitica.Todoist.Integration.Model/Todoist/Item.cs +++ b/Habitica.Todoist.Integration.Model/Todoist/Item.cs @@ -6,27 +6,17 @@ namespace Habitica.Todoist.Integration.Model.Todoist { public class Item { + [JsonProperty("id")] + public string Id { get; set; } [JsonProperty("content")] public string Content { get; set; } - [JsonProperty("Id")] - public string Id { get; set; } [JsonProperty("due")] public Due Due { get; set; } + [JsonProperty("priority")] + public int Priority { get; set; } [JsonProperty("is_deleted")] public int Is_deleted { get; set; } [JsonProperty("date_completed")] public string Date_completed { get; set; } - - public int? GetDifficulty() - { - try { return int.Parse(Content.Split('-').Last().Last().ToString()); } catch { } - return null; - } - - public string GetCleanContent() - { - try { return Content.Split('-').First(); } catch { } - return null; - } } } diff --git a/Habitica.Todoist.Integration.Model/Todoist/SyncResponse.cs b/Habitica.Todoist.Integration.Model/Todoist/Responses/SyncResponse.cs similarity index 78% rename from Habitica.Todoist.Integration.Model/Todoist/SyncResponse.cs rename to Habitica.Todoist.Integration.Model/Todoist/Responses/SyncResponse.cs index ffcf01f..e5edd8b 100644 --- a/Habitica.Todoist.Integration.Model/Todoist/SyncResponse.cs +++ b/Habitica.Todoist.Integration.Model/Todoist/Responses/SyncResponse.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Text; -namespace Habitica.Todoist.Integration.Model.Todoist +namespace Habitica.Todoist.Integration.Model.Todoist.Responses { public class SyncResponse { @@ -11,7 +11,7 @@ namespace Habitica.Todoist.Integration.Model.Todoist public string Sync_token { get; set; } [JsonProperty("full_sync")] public bool Full_sync { get; set; } - [JsonProperty("itmes")] + [JsonProperty("items")] public List Items { get; set; } } } diff --git a/Habitica.Todoist.Integration.Services/HabitTodoStorageClient.cs b/Habitica.Todoist.Integration.Services/HabitTodoStorageClient.cs deleted file mode 100644 index cdb7ac4..0000000 --- a/Habitica.Todoist.Integration.Services/HabitTodoStorageClient.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Habitica.Todoist.Integration.Model.Storage; -using Microsoft.Azure.Cosmos.Table; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Habitica.Todoist.Integration.Services -{ - public class HabitTodoStorageClient - { - private CloudStorageAccount storageAccount { get; set; } - - public HabitTodoStorageClient(string connectionString) - { - this.storageAccount = CloudStorageAccount.Parse(connectionString); - } - - //public TodoLink CreateTodoLink(TodoLink todoLink) - //{ - // return null; - //} - } -} diff --git a/Habitica.Todoist.Integration.Services/HabiticaClientService.cs b/Habitica.Todoist.Integration.Services/HabiticaClientService.cs deleted file mode 100644 index 823b80f..0000000 --- a/Habitica.Todoist.Integration.Services/HabiticaClientService.cs +++ /dev/null @@ -1,45 +0,0 @@ -using HabiticaTask = Habitica.Todoist.Integration.Model.Habitica.Task; -using System; -using System.Net; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace Habitica.Todoist.Integration.Services -{ - public class HabiticaClientService - { - private string userId { get; set; } - private string apiKey { get; set; } - - private string baseUrl => "https://habitica.com/api/v3/"; - - public HabiticaClientService(string userId, string apiKey) - { - this.userId = userId; - this.apiKey = apiKey; - } - - public async Task CreateUserTask(HabiticaTask task) - { - using (var client = CreateWebClient()) - { - var request = JsonConvert.SerializeObject(task); - var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/user", "POST", request); - - return JsonConvert.DeserializeObject(json); - } - } - - private WebClient CreateWebClient() - { - var client = new WebClient(); - - client.Headers[HttpRequestHeader.ContentType] = "application/json"; - client.Headers["x-api-user"] = userId; - client.Headers["x-api-key"] = apiKey; - client.Headers["x-client"] = "dotnet-habitica-client"; - - return client; - } - } -} diff --git a/Habitica.Todoist.Integration.Services/HabiticaServiceClient.cs b/Habitica.Todoist.Integration.Services/HabiticaServiceClient.cs new file mode 100644 index 0000000..067becc --- /dev/null +++ b/Habitica.Todoist.Integration.Services/HabiticaServiceClient.cs @@ -0,0 +1,85 @@ +using HabiticaTask = Habitica.Todoist.Integration.Model.Habitica.Task; +using System.Net; +using System.Threading.Tasks; +using Newtonsoft.Json; +using System.Collections.Generic; +using Habitica.Todoist.Integration.Model.Habitica.Responses; +using Habitica.Todoist.Integration.Model.Habitica.Enums; +using System; + +namespace Habitica.Todoist.Integration.Services +{ + public class HabiticaServiceClient + { + private string userId { get; set; } + private string apiKey { get; set; } + + private string baseUrl => "https://habitica.com/api/v3/"; + + public HabiticaServiceClient(string userId, string apiKey) + { + this.userId = userId; + this.apiKey = apiKey; + } + + public async Task> CreateTask(HabiticaTask task) + { + using (var client = CreateWebClient()) + { + var request = JsonConvert.SerializeObject(task); + var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/user", "POST", request); + + return JsonConvert.DeserializeObject>(json); + } + } + + public async Task>> ReadTasks(TaskType taskType = TaskType.Todo) + { + var taskTypeStr = Enum.GetName(taskType.GetType(), taskType).ToLower(); + using (var client = CreateWebClient()) + { + var json = await client.DownloadStringTaskAsync($"{baseUrl}/tasks/user"); + var response = JsonConvert.DeserializeObject>>(json); + response.Data.RemoveAll(x => x.Type != taskTypeStr); + + return response; + } + } + + public async Task UpdateTask(HabiticaTask task) + { + using (var client = CreateWebClient()) + { + var request = JsonConvert.SerializeObject(task); + var json = await client.UploadStringTaskAsync($"{baseUrl}/tasks/{task.Id}", "PUT", request); + + return JsonConvert.DeserializeObject(json); + } + } + + public async Task DeleteTask(string taskId) + { + using (var client = CreateWebClient()) + await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}", "DELETE", ""); + } + + public async Task ScoreTask(string taskId, ScoreAction action) + { + var actionStr = Enum.GetName(action.GetType(), action).ToLower(); + using (var client = CreateWebClient()) + await client.UploadStringTaskAsync($"{baseUrl}/tasks/{taskId}/score/{actionStr}", "POST", ""); + } + + private WebClient CreateWebClient() + { + var client = new WebClient(); + + client.Headers[HttpRequestHeader.ContentType] = "application/json"; + client.Headers["x-api-user"] = userId; + client.Headers["x-api-key"] = apiKey; + client.Headers["x-client"] = "dotnet-habitica-client"; + + return client; + } + } +} diff --git a/Habitica.Todoist.Integration.Services/TableStorageClient.cs b/Habitica.Todoist.Integration.Services/TableStorageClient.cs new file mode 100644 index 0000000..5a95c72 --- /dev/null +++ b/Habitica.Todoist.Integration.Services/TableStorageClient.cs @@ -0,0 +1,83 @@ +using Habitica.Todoist.Integration.Model.Storage; +using Microsoft.Azure.Cosmos.Table; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Habitica.Todoist.Integration.Services +{ + public class TableStorageClient + { + private CloudStorageAccount storageAccount { get; set; } + private CloudTableClient tableClient { get; set; } + + /* TODO: change this so it reads from classes available instead of hardcoding */ + private static List tableNames = new List() + { + "habittodolink", + "todohabitlink", + "todochange", + "todoistsync" + }; + + private Dictionary tables = new Dictionary(); + + public TableStorageClient(string connectionString) + { + storageAccount = CloudStorageAccount.Parse(connectionString); + tableClient = storageAccount.CreateCloudTableClient(new TableClientConfiguration()); + + // initialize all tables to use + foreach (var tableName in tableNames) + { + var table = tableClient.GetTableReference(tableName); + table.CreateIfNotExists(); + tables[tableName] = table; + } + } + + public async Task InsertOrUpdate(T entity) where T : TableEntity, new() + { + var tableName = typeof(T).Name.ToLower(); + var table = tables[tableName]; + + var operation = TableOperation.InsertOrReplace(entity); // TODO: InsertOrReplace vs InsertOrMerge + var result = await table.ExecuteAsync(operation); + + return result.Result as T; + } + + public async Task Exists(string partitionKey, string rowKey) where T : TableEntity, new() + { + var tableName = typeof(T).Name.ToLower(); + var table = tables[tableName]; + + var operation = TableOperation.Retrieve(partitionKey, rowKey); + var result = await table.ExecuteAsync(operation); + + return result.Result != null; + } + + + //public async Task> Read(string partitionKey, string rowKey) where T : TableEntity + //{ + // var tableName = typeof(T).Name.ToLower(); + // var table = tables[tableName]; + + // var operation = TableOperation.Retrieve(partitionKey, rowKey); + // var result = await table.ExecuteAsync(operation); + + // return result.Result != null; + //} + + public TableQuery Query() where T : TableEntity, new() + { + var tableName = typeof(T).Name.ToLower(); + var table = tables[tableName]; + + return table.CreateQuery(); + } + } +} diff --git a/Habitica.Todoist.Integration.Services/TodoistClientService.cs b/Habitica.Todoist.Integration.Services/TodoistServiceClient.cs similarity index 83% rename from Habitica.Todoist.Integration.Services/TodoistClientService.cs rename to Habitica.Todoist.Integration.Services/TodoistServiceClient.cs index c886c10..96da07b 100644 --- a/Habitica.Todoist.Integration.Services/TodoistClientService.cs +++ b/Habitica.Todoist.Integration.Services/TodoistServiceClient.cs @@ -1,4 +1,5 @@ using Habitica.Todoist.Integration.Model.Todoist; +using Habitica.Todoist.Integration.Model.Todoist.Responses; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -8,18 +9,21 @@ using System.Threading.Tasks; namespace Habitica.Todoist.Integration.Services { - public class TodoistClientService + public class TodoistServiceClient { private string apiKey { get; set; } private string baseUrl => "https://api.todoist.com/sync/v8/"; - public TodoistClientService(string apiKey) + public TodoistServiceClient(string apiKey) { this.apiKey = apiKey; } - public async Task GetUpdatedItems(string syncToken = "*") + public async Task GetChangedItems(string syncToken = null) { + if (string.IsNullOrEmpty(syncToken)) + syncToken = "*"; + using (var client = CreateWebClient()) { var body = InitializeRequestBody();