diff --git a/src/Todoist.Net.Tests/Models/DurationTests.cs b/src/Todoist.Net.Tests/Models/DurationTests.cs new file mode 100644 index 0000000..976aede --- /dev/null +++ b/src/Todoist.Net.Tests/Models/DurationTests.cs @@ -0,0 +1,54 @@ +using System; + +using Todoist.Net.Models; +using Todoist.Net.Tests.Extensions; +using Xunit; + +namespace Todoist.Net.Tests.Models +{ + [Trait(Constants.TraitName, Constants.UnitTraitValue)] + public class DurationTests + { + [Fact] + public void AmountAssignment_InvalidValue_ThrowsException() + { + Duration duration; + + Assert.Throws(() => + duration = new Duration(0, DurationUnit.Minute)); + + duration = new Duration(15, DurationUnit.Minute); + + Assert.Throws(() => + duration.Amount = -5); + } + + [Fact] + public void UnitAssignment_InvalidValue_ThrowsException() + { + Duration duration; + + Assert.Throws(() => + duration = new Duration(15, null)); + + duration = new Duration(15, DurationUnit.Minute); + + Assert.Throws(() => + duration.Unit = null); + } + + [Fact] + public void TimeValueEvaluation_Success() + { + var duration = new Duration(15, DurationUnit.Minute); + + Assert.Equal(TimeSpan.FromMinutes(15), duration.TimeValue); + + duration.Amount = 3; + duration.Unit = DurationUnit.Day; + + Assert.Equal(TimeSpan.FromDays(3), duration.TimeValue); + } + + } +} diff --git a/src/Todoist.Net.Tests/Services/ItemsServiceTests.cs b/src/Todoist.Net.Tests/Services/ItemsServiceTests.cs index 5a88b2e..68eba51 100644 --- a/src/Todoist.Net.Tests/Services/ItemsServiceTests.cs +++ b/src/Todoist.Net.Tests/Services/ItemsServiceTests.cs @@ -197,5 +197,37 @@ public void CreateNewItem_DueDateIsLocal_DueDateNotChanged() client.Items.DeleteAsync(item.Id).Wait(); } + + + [Fact] + [Trait(Constants.TraitName, Constants.IntegrationPremiumTraitValue)] + public void CreateItemClearDurationAndDelete_Success() + { + var client = TodoistClientFactory.Create(_outputHelper); + + var item = new Item("duration task") + { + DueDate = new DueDate("22 Dec 2021 at 9:15", language: Language.English), + Duration = new Duration(45, DurationUnit.Minute) + }; + client.Items.AddAsync(item).Wait(); + + var itemInfo = client.Items.GetAsync(item.Id).Result; + + Assert.True(itemInfo.Item.Content == item.Content); + Assert.Equal("2021-12-22T09:15:00", itemInfo.Item.DueDate.StringDate); + + Assert.Equal(item.Duration.Amount, itemInfo.Item.Duration.Amount); + Assert.Equal(item.Duration.Unit, itemInfo.Item.Duration.Unit); + + itemInfo.Item.Duration = null; + client.Items.UpdateAsync(itemInfo.Item).Wait(); + + itemInfo = client.Items.GetAsync(item.Id).Result; + Assert.Null(itemInfo.Item.Duration); + + client.Items.DeleteAsync(item.Id).Wait(); + } + } } diff --git a/src/Todoist.Net/Models/Duration.cs b/src/Todoist.Net/Models/Duration.cs new file mode 100644 index 0000000..e96c72f --- /dev/null +++ b/src/Todoist.Net/Models/Duration.cs @@ -0,0 +1,92 @@ +using System; + +using Newtonsoft.Json; + +namespace Todoist.Net.Models +{ + /// + /// Represents durations for tasks. + /// + public class Duration + { + + private int _amount; + private DurationUnit _unit; + + /// + /// Initializes a new instance of the class. + /// + /// The time amount. + /// The time unit. + public Duration(int amount, DurationUnit unit) + { + Amount = amount; + Unit = unit; + } + + internal Duration() + { + } + + /// + /// Gets or sets the duration time amount. + /// + /// + /// Must be a positive integer (greater than zero). + /// + /// + /// The time amount. + /// + /// Duration amount must be greater than zero." + [JsonProperty("amount")] + public int Amount + { + get => _amount; + set => _amount = value > 0 + ? value + : throw new ArgumentOutOfRangeException(nameof(Amount), "Duration amount must be greater than zero."); + } + + /// + /// Gets or sets the duration time unit. + /// + /// + /// Either minute or day. + /// + /// + /// The duration unit. + /// + /// Unit + [JsonProperty("unit")] + public DurationUnit Unit + { + get => _unit; + set => _unit = value ?? throw new ArgumentNullException(nameof(Unit)); + } + + + /// + /// Gets the value of the duration as a object. + /// + /// + /// The value of the duration. + /// + [JsonIgnore] + public TimeSpan TimeValue + { + get + { + switch (Unit) + { + case var _ when Unit == DurationUnit.Minute: + return TimeSpan.FromMinutes(Amount); + case var _ when Unit == DurationUnit.Day: + return TimeSpan.FromDays(Amount); + default: + throw new NotImplementedException(); + } + } + } + + } +} diff --git a/src/Todoist.Net/Models/DurationUnit.cs b/src/Todoist.Net/Models/DurationUnit.cs new file mode 100644 index 0000000..234e9aa --- /dev/null +++ b/src/Todoist.Net/Models/DurationUnit.cs @@ -0,0 +1,31 @@ +namespace Todoist.Net.Models +{ + /// + /// Represents a duration unit. + /// + /// + public class DurationUnit : StringEnum + { + /// + /// Initializes a new instance of the class. + /// + /// The value. + private DurationUnit(string value) + : base(value) + { + } + + /// + /// Gets the minute unit. + /// + /// The minute unit. + public static DurationUnit Minute { get; } = new DurationUnit("minute"); + + /// + /// Gets the day unit. + /// + /// The day unit. + public static DurationUnit Day { get; } = new DurationUnit("day"); + + } +} diff --git a/src/Todoist.Net/Models/Item.cs b/src/Todoist.Net/Models/Item.cs index 57bb914..c3a7b84 100644 --- a/src/Todoist.Net/Models/Item.cs +++ b/src/Todoist.Net/Models/Item.cs @@ -103,6 +103,18 @@ internal Item() [JsonProperty("due")] public DueDate DueDate { get; set; } + /// + /// Gets or sets the duration. + /// + /// + /// Durations are only available for Todoist Premium users. + /// + /// + /// The duration. + /// + [JsonProperty("duration", NullValueHandling = NullValueHandling.Include)] + public Duration Duration { get; set; } + /// /// Gets a value indicating whether this instance is checked. ///