From 9f68d1282c9cdcbd73d988b6f2f7d4b44b1395d7 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 22 Sep 2024 15:24:42 +0100 Subject: [PATCH] feat: Migrate to API v9 Fixes #47 --- Program.cs | 8 +++--- Toggl/Project.cs | 2 +- Toggl/Query.cs | 72 +++++++++++++++++++++------------------------- Toggl/TimeEntry.cs | 6 ++-- 4 files changed, 41 insertions(+), 47 deletions(-) diff --git a/Program.cs b/Program.cs index 2e658e1..e689476 100644 --- a/Program.cs +++ b/Program.cs @@ -99,11 +99,11 @@ static async Task GetMatchingProject(Query query, string projectNameOrI static async Task FormatTimer(Query query, TimeEntry timer) { - var project = await query.GetProject(timer.pid); - var duration = TimeSpan.FromSeconds(DateTimeOffset.Now.ToUnixTimeSeconds() + timer.duration); - var timeRange = timer.stop.Year == 1 ? + var project = timer.project_id.HasValue ? await query.GetProject(timer.workspace_id, timer.project_id.Value) : null; + var duration = DateTimeOffset.Now - timer.start; + var timeRange = timer.stop == null ? $"{timer.start.ToString("yyyy-MM-dd HH:mm")}-now ({duration.ToString("hh\\:mm")})" : - $"{timer.start.ToString("yyyy-MM-dd HH:mm")}-{timer.stop.TimeOfDay.ToString("hh\\:mm")} ({(timer.stop - timer.start).ToString("hh\\:mm")})"; + $"{timer.start.ToString("yyyy-MM-dd HH:mm")}-{timer.stop.Value.TimeOfDay.ToString("hh\\:mm")} ({(timer.stop.Value - timer.start).ToString("hh\\:mm")})"; return $"{timeRange} - {project?.name ?? "(none)"} - {timer.description} [{(timer.tags == null ? "" : string.Join(", ", timer.tags))}]"; } diff --git a/Toggl/Project.cs b/Toggl/Project.cs index c9fa4c5..d975856 100644 --- a/Toggl/Project.cs +++ b/Toggl/Project.cs @@ -3,7 +3,7 @@ namespace Toggl_CLI.Toggl public class Project { public uint id; - public uint wid; + public uint workspace_id; public string name; } } diff --git a/Toggl/Query.cs b/Toggl/Query.cs index f8b78a2..0ae61f8 100644 --- a/Toggl/Query.cs +++ b/Toggl/Query.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Net; using System.Net.Http; @@ -21,7 +22,7 @@ public TogglException(string message, Exception inner) : base(message, inner) } } - const string Endpoint = "https://api.track.toggl.com/api/v8/"; + const string Endpoint = "https://api.track.toggl.com/api/v9/"; const string UserAgent = "Toggl-CLI/1.0"; readonly string Token; @@ -80,6 +81,11 @@ internal async Task Put(string type, JObject content) return await Send(HttpMethod.Put, type, new StringContent(JsonConvert.SerializeObject(content), Encoding.UTF8, "application/json")); } + internal async Task Patch(string type, JObject content) + { + return await Send(HttpMethod.Patch, type, new StringContent(JsonConvert.SerializeObject(content), Encoding.UTF8, "application/json")); + } + internal async Task GetCached(Dictionary cache, T key, Func> generator) { if (!cache.ContainsKey(key)) @@ -96,10 +102,7 @@ public async Task> GetWorkspaces() public async Task> GetProjects() { - return (await GetWorkspaces()) - .Select(async workspace => await GetProjects(workspace)) - .SelectMany(projects => projects.Result) - .ToList(); + return (await Get($"me/projects")).ToObject>(); } public async Task> GetProjects(Workspace workspace) @@ -107,82 +110,73 @@ public async Task> GetProjects(Workspace workspace) return (await Get($"workspaces/{workspace.id}/projects")).ToObject>(); } - public async Task GetProject(uint projectId) + public async Task GetProject(uint workspaceId, uint projectId) { if (projectId == 0) { return null; } - return await GetCached(ProjectCache, projectId, async () => (await Get($"projects/{projectId}"))["data"].ToObject()); + return await GetCached(ProjectCache, projectId, async () => (await Get($"workspaces/{workspaceId}/projects/{projectId}")).ToObject()); } public async Task> GetRecentTimers() { - return (await Get("time_entries")).ToObject>(); + return (await Get("me/time_entries")).ToObject>().Reverse().ToImmutableList(); } public async Task GetCurrentTimer() { - return (await Get("time_entries/current"))["data"].ToObject(); + return (await Get("me/time_entries/current")).ToObject(); } public async Task SetCurrentTimerProject(Project project) { var timer = await GetCurrentTimer(); - await Put($"time_entries/{timer.id}", new JObject( - new JProperty("time_entry", new JObject( - new JProperty("pid", project.id) - )) + await Put($"workspaces/{timer.workspace_id}/time_entries/{timer.id}", new JObject( + new JProperty("project_id", project.id) )); } public async Task SetCurrentTimerDescription(string description) { var timer = await GetCurrentTimer(); - await Put($"time_entries/{timer.id}", new JObject( - new JProperty("time_entry", new JObject( - new JProperty("description", description) - )) + await Put($"workspaces/{timer.workspace_id}/time_entries/{timer.id}", new JObject( + new JProperty("description", description) )); } public async Task SetCurrentTimerTags(IReadOnlyList tags) { var timer = await GetCurrentTimer(); - await Put($"time_entries/{timer.id}", new JObject( - new JProperty("time_entry", new JObject( - new JProperty("tags", JArray.FromObject(tags)) - )) + await Put($"workspaces/{timer.workspace_id}/time_entries/{timer.id}", new JObject( + new JProperty("tags", JArray.FromObject(tags)) )); } public async Task StartTimer(Project project, string description, IReadOnlyList tags) { - var response = await Post("time_entries/start", new JObject( - new JProperty("time_entry", project != null ? - new JObject( - new JProperty("pid", project.id), - new JProperty("description", description), - new JProperty("tags", JArray.FromObject(tags)), - new JProperty("created_with", UserAgent) - ) : - new JObject( - new JProperty("description", description), - new JProperty("tags", JArray.FromObject(tags)), - new JProperty("created_with", UserAgent) - ) + var workspaceId = project?.workspace_id ?? (await GetWorkspaces()).First().id; + var startTime = DateTimeOffset.Now; + var response = await Post($"workspaces/{workspaceId}/time_entries", + new JObject( + new JProperty("workspace_id", workspaceId), + new JProperty("project_id", project?.id), + new JProperty("start", startTime), + new JProperty("duration", -1), + new JProperty("description", description), + new JProperty("tags", JArray.FromObject(tags)), + new JProperty("created_with", UserAgent) ) - )); - return response["data"].ToObject(); + ); + return response.ToObject(); } public async Task StopTimer() { - var response = await Get("time_entries/current"); - var currentTimer = response["data"].ToObject(); + var currentTimer = await GetCurrentTimer(); if (currentTimer != null) { - await Put($"time_entries/{currentTimer["id"]}/stop", new JObject()); + await Patch($"workspaces/{currentTimer.workspace_id}/time_entries/{currentTimer.id}/stop", new JObject()); return true; } return false; diff --git a/Toggl/TimeEntry.cs b/Toggl/TimeEntry.cs index 8716dbb..456b7b3 100644 --- a/Toggl/TimeEntry.cs +++ b/Toggl/TimeEntry.cs @@ -6,10 +6,10 @@ namespace Toggl_CLI.Toggl public class TimeEntry { public uint id; - public uint wid; - public uint pid; + public uint workspace_id; + public uint? project_id; public DateTimeOffset start; - public DateTimeOffset stop; + public DateTimeOffset? stop; public int duration; public string description; public IReadOnlyList tags;