diff --git a/Changelog.md b/Changelog.md index 0ce9f48..2d61ff5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,15 @@ # Changelog *Below is the version history of [TrelloDotNet](https://github.com/rwjdk/TrelloDotNet) (An wrapper of the Trello API)* +## 1.9.2 (12th of October 2023) +#### General +- All internal usage of `UpdateCardAsync` now uses the new 'partial update'-variant described below for better performance and more secure async usage in the Automation Engine + +#### TrelloClient +- Added overload to [`UpdateCardAsync`](https://github.com/rwjdk/TrelloDotNet/wiki/UpdateCardAsync) that instead of a full update with a card can do partial updates with only the parameters you provide + +
+ ## 1.9.1 (11th of October 2023) #### Automation Engine - Added Automation Trigger [`AddChecklistToCardTrigger`](https://github.com/rwjdk/TrelloDotNet/wiki/AddChecklistToCardTrigger) diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddCoverOnCardAction.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddCoverOnCardAction.cs index a097023..373aed1 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddCoverOnCardAction.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddCoverOnCardAction.cs @@ -1,6 +1,10 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; using TrelloDotNet.AutomationEngine.Interface; +using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; using TrelloDotNet.Model.Webhook; namespace TrelloDotNet.AutomationEngine.Model.Actions @@ -40,9 +44,10 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu throw new AutomationException("Could not perform AddCoverOnCardAction as WebhookAction did not involve a Card"); } var trelloClient = webhookAction.TrelloClient; - var card = await webhookAction.Data.Card.GetAsync(); - card.Cover = CardCoverToAdd; - await trelloClient.UpdateCardAsync(card); + await trelloClient.UpdateCardAsync(webhookAction.Data.Card.Id, new List + { + new QueryParameter(CardFieldsType.Cover.GetJsonPropertyName(), JsonSerializer.Serialize(CardCoverToAdd)) + }); processingResult.AddToLog($"Updated Card '{webhookAction.Data.Card.Name}' with new Cover"); processingResult.ActionsExecuted++; } diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddLabelsToCardAction.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddLabelsToCardAction.cs index a8ff57e..b30030b 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddLabelsToCardAction.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddLabelsToCardAction.cs @@ -2,6 +2,9 @@ using System.Linq; using System.Threading.Tasks; using TrelloDotNet.AutomationEngine.Interface; +using TrelloDotNet.Control; +using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; using TrelloDotNet.Model.Webhook; namespace TrelloDotNet.AutomationEngine.Model.Actions @@ -77,7 +80,10 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu if (updateNeeded) { - await trelloClient.UpdateCardAsync(card); + await trelloClient.UpdateCardAsync(card.Id, new List() + { + new QueryParameter(CardFieldsType.LabelIds.GetJsonPropertyName(), card.LabelIds) + }); processingResult.AddToLog($"Added labels '{string.Join(",", LabelIds)}' to card '{webhookAction.Data.Card.Name}'"); processingResult.ActionsExecuted++; } diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddMembersToCardAction.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddMembersToCardAction.cs index ff70f95..e059598 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddMembersToCardAction.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/AddMembersToCardAction.cs @@ -2,6 +2,9 @@ using System.Linq; using System.Threading.Tasks; using TrelloDotNet.AutomationEngine.Interface; +using TrelloDotNet.Control; +using TrelloDotNet.Model.Options; +using TrelloDotNet.Model; using TrelloDotNet.Model.Webhook; namespace TrelloDotNet.AutomationEngine.Model.Actions @@ -75,7 +78,10 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu if (updateNeeded) { - await trelloClient.UpdateCardAsync(card); + await trelloClient.UpdateCardAsync(card.Id, new List() + { + new QueryParameter(CardFieldsType.MemberIds.GetJsonPropertyName(), card.MemberIds) + }); processingResult.AddToLog($"Added members '{string.Join(",", MemberIds)}' to card '{webhookAction.Data.Card.Name}'"); processingResult.ActionsExecuted++; } diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/ISetCardFieldValue.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/ISetCardFieldValue.cs index 3e7c865..3c38166 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/ISetCardFieldValue.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/ISetCardFieldValue.cs @@ -13,5 +13,12 @@ public interface ISetCardFieldValue /// The Card to set the values on /// If card was modified (aka need to be updated against the API) bool SetIfNeeded(Card card); + + /// + /// Get a Query Parameter representing the change (or null if not needed) + /// + /// Card to apply the change to + /// Query parameter of null + QueryParameter GetQueryParameter(Card card); } } \ No newline at end of file diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCardDataAction.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCardDataAction.cs index a98febf..225613a 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCardDataAction.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCardDataAction.cs @@ -3,8 +3,10 @@ using System.Linq; using System.Threading.Tasks; using TrelloDotNet.AutomationEngine.Interface; +using TrelloDotNet.Control; using TrelloDotNet.Model; using TrelloDotNet.Model.Actions; +using TrelloDotNet.Model.Options; using TrelloDotNet.Model.Webhook; namespace TrelloDotNet.AutomationEngine.Model.Actions @@ -41,7 +43,7 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu throw new AutomationException("Could not perform RemoveCardDataAction as WebhookAction did not involve a Card"); } var card = await webhookAction.Data.Card.GetAsync(); - bool updateNeeded = false; + var queryParametersToUpdate = new List(); foreach (var dataType in DataToRemove) { switch (dataType) @@ -50,49 +52,49 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu if (card.Start != null) { card.Start = null; - updateNeeded = true; + queryParametersToUpdate.Add(new QueryParameter(CardFieldsType.Start.GetJsonPropertyName(), (DateTimeOffset?)null)); } break; case RemoveCardDataType.DueDate: if (card.Due != null) { card.Due = null; - updateNeeded = true; + queryParametersToUpdate.Add(new QueryParameter(CardFieldsType.Due.GetJsonPropertyName(), (DateTimeOffset?)null)); } break; case RemoveCardDataType.DueComplete: if (card.DueComplete) { card.DueComplete = false; - updateNeeded = true; + queryParametersToUpdate.Add(new QueryParameter(CardFieldsType.DueComplete.GetJsonPropertyName(), false)); } break; case RemoveCardDataType.Description: if (!string.IsNullOrWhiteSpace(card.Description)) { card.Description = null; - updateNeeded = true; + queryParametersToUpdate.Add(new QueryParameter(CardFieldsType.Description.GetJsonPropertyName(), string.Empty)); } break; case RemoveCardDataType.AllLabels: if (card.LabelIds.Any()) { card.LabelIds = new List(); - updateNeeded = true; + queryParametersToUpdate.Add(new QueryParameter(CardFieldsType.LabelIds.GetJsonPropertyName(), new List())); } break; case RemoveCardDataType.AllMembers: if (card.MemberIds.Any()) { card.MemberIds = new List(); - updateNeeded = true; + queryParametersToUpdate.Add(new QueryParameter(CardFieldsType.MemberIds.GetJsonPropertyName(), new List())); } break; case RemoveCardDataType.Cover: if (card.Cover != null && (card.Cover.Color != null || card.Cover.BackgroundImageId != null)) { card.Cover = null; - updateNeeded = true; + queryParametersToUpdate.Add(new QueryParameter(CardFieldsType.Cover.GetJsonPropertyName(), (string)null)); } break; case RemoveCardDataType.AllChecklists: @@ -128,10 +130,10 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu } } - if (updateNeeded) + if (queryParametersToUpdate.Any()) { processingResult.ActionsExecuted++; - await webhookAction.TrelloClient.UpdateCardAsync(card); + await webhookAction.TrelloClient.UpdateCardAsync(card.Id, queryParametersToUpdate); } else { diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCoverFromCardAction.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCoverFromCardAction.cs index 3e7ac84..72f4ec7 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCoverFromCardAction.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/RemoveCoverFromCardAction.cs @@ -1,5 +1,9 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; using TrelloDotNet.AutomationEngine.Interface; +using TrelloDotNet.Control; +using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; using TrelloDotNet.Model.Webhook; namespace TrelloDotNet.AutomationEngine.Model.Actions @@ -25,9 +29,10 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu throw new AutomationException("Could not perform RemoveCoverFromCardAction as WebhookAction did not involve a Card"); } var trelloClient = webhookAction.TrelloClient; - var card = await webhookAction.Data.Card.GetAsync(); - card.Cover = null; - await trelloClient.UpdateCardAsync(card); + await trelloClient.UpdateCardAsync(webhookAction.Data.Card.Id, new List + { + new QueryParameter(CardFieldsType.Cover.GetJsonPropertyName(), (string)null) + }); processingResult.AddToLog($"Removed Cover from Card '{webhookAction.Data.Card.Name}'"); processingResult.ActionsExecuted++; } diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDescriptionFieldValue.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDescriptionFieldValue.cs index 191da88..62ca02c 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDescriptionFieldValue.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDescriptionFieldValue.cs @@ -1,5 +1,7 @@ using System; +using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; namespace TrelloDotNet.AutomationEngine.Model.Actions { @@ -42,6 +44,12 @@ public bool SetIfNeeded(Card card) return false; } + /// + public QueryParameter GetQueryParameter(Card card) + { + return SetIfNeeded(card) ? new QueryParameter(CardFieldsType.Description.GetJsonPropertyName(), card.Description) : null; + } + /// /// Constructor /// diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueCompleteFieldValue.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueCompleteFieldValue.cs index fff606e..4732839 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueCompleteFieldValue.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueCompleteFieldValue.cs @@ -1,4 +1,6 @@ -using TrelloDotNet.Model; +using TrelloDotNet.Control; +using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; namespace TrelloDotNet.AutomationEngine.Model.Actions { @@ -23,6 +25,12 @@ public bool SetIfNeeded(Card card) return true; } + /// + public QueryParameter GetQueryParameter(Card card) + { + return SetIfNeeded(card) ? new QueryParameter(CardFieldsType.DueComplete.GetJsonPropertyName(), card.DueComplete) : null; + } + /// /// Constructor /// diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueFieldValue.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueFieldValue.cs index bdd1fef..aaa1fb9 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueFieldValue.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardDueFieldValue.cs @@ -1,5 +1,7 @@ using System; +using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; namespace TrelloDotNet.AutomationEngine.Model.Actions { @@ -44,6 +46,12 @@ public bool SetIfNeeded(Card card) return false; } + /// + public QueryParameter GetQueryParameter(Card card) + { + return SetIfNeeded(card) ? new QueryParameter(CardFieldsType.Due.GetJsonPropertyName(), card.Due) : null; + } + /// /// Constructor /// diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardNameFieldValue.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardNameFieldValue.cs index 4ae7a40..cd3d428 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardNameFieldValue.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardNameFieldValue.cs @@ -1,5 +1,7 @@ using System; +using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; namespace TrelloDotNet.AutomationEngine.Model.Actions { @@ -44,6 +46,12 @@ public bool SetIfNeeded(Card card) return false; } + /// + public QueryParameter GetQueryParameter(Card card) + { + return SetIfNeeded(card) ? new QueryParameter(CardFieldsType.Name.GetJsonPropertyName(), card.Name) : null; + } + /// /// Constructor /// diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardStartFieldValue.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardStartFieldValue.cs index 57baeb9..ba68c42 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardStartFieldValue.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetCardStartFieldValue.cs @@ -1,5 +1,7 @@ using System; +using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; namespace TrelloDotNet.AutomationEngine.Model.Actions { @@ -43,6 +45,12 @@ public bool SetIfNeeded(Card card) return false; } + /// + public QueryParameter GetQueryParameter(Card card) + { + return SetIfNeeded(card) ? new QueryParameter(CardFieldsType.Start.GetJsonPropertyName(), card.Start) : null; + } + /// /// Constructor /// diff --git a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetFieldsOnCardAction.cs b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetFieldsOnCardAction.cs index 89b54bf..3283be0 100644 --- a/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetFieldsOnCardAction.cs +++ b/TrelloDotNet/TrelloDotNet/AutomationEngine/Model/Actions/SetFieldsOnCardAction.cs @@ -1,5 +1,8 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using TrelloDotNet.AutomationEngine.Interface; +using TrelloDotNet.Model; using TrelloDotNet.Model.Webhook; namespace TrelloDotNet.AutomationEngine.Model.Actions @@ -35,19 +38,21 @@ public async Task PerformActionAsync(WebhookAction webhookAction, ProcessingResu throw new AutomationException("Could not perform SetFieldsOnCardAction as WebhookAction did not involve a Card"); } var card = await webhookAction.Data.Card.GetAsync(); - bool updateNeeded = false; + var queryParametersToAdd = new List(); foreach (var fieldValue in FieldValues) { - if (fieldValue.SetIfNeeded(card)) + QueryParameter queryParameter = fieldValue.GetQueryParameter(card); + if (queryParameter != null) { - updateNeeded = true; + queryParametersToAdd.Add(queryParameter); } + } - if (updateNeeded) + if (queryParametersToAdd.Any()) { processingResult.ActionsExecuted++; - await webhookAction.TrelloClient.UpdateCardAsync(card); + await webhookAction.TrelloClient.UpdateCardAsync(card.Id, queryParametersToAdd); } else { diff --git a/TrelloDotNet/TrelloDotNet/Control/QueryParametersBuilder.cs b/TrelloDotNet/TrelloDotNet/Control/QueryParametersBuilder.cs index a1995ec..16993f8 100644 --- a/TrelloDotNet/TrelloDotNet/Control/QueryParametersBuilder.cs +++ b/TrelloDotNet/TrelloDotNet/Control/QueryParametersBuilder.cs @@ -62,7 +62,7 @@ internal QueryParameter[] GetViaQueryParameterAttributes(T instance) else if (updateablePropertyType == typeof(List)) { var list = (List)rawValue; - parameters.Add(list == null ? new QueryParameter(jsonPropertyName.Name, string.Empty) : new QueryParameter(jsonPropertyName.Name, string.Join(",", list))); + parameters.Add(list == null ? new QueryParameter(jsonPropertyName.Name, string.Empty) : new QueryParameter(jsonPropertyName.Name, list)); } else if (updateablePropertyType.BaseType == typeof(Enum)) { diff --git a/TrelloDotNet/TrelloDotNet/Model/QueryParameter.cs b/TrelloDotNet/TrelloDotNet/Model/QueryParameter.cs index cd38e42..5c398a8 100644 --- a/TrelloDotNet/TrelloDotNet/Model/QueryParameter.cs +++ b/TrelloDotNet/TrelloDotNet/Model/QueryParameter.cs @@ -1,6 +1,10 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Text.Json; +using TrelloDotNet.Control; +using TrelloDotNet.Model.Options; namespace TrelloDotNet.Model { @@ -31,6 +35,18 @@ public QueryParameter(string name, string value) Type = QueryParameterType.String; _valueAsObject = value; } + + /// + /// Constructor + /// + /// Name of the Parameter + /// Value of the Parameter + public QueryParameter(string name, List value) + { + Name = name; + Type = QueryParameterType.String; + _valueAsObject = value != null ? string.Join(",", value) : null; + } /// /// Constructor @@ -107,5 +123,10 @@ public string GetValueAsApiFormattedString() throw new ArgumentOutOfRangeException(); } } + + internal string GetRawStringValue() + { + return _valueAsObject?.ToString(); + } } } \ No newline at end of file diff --git a/TrelloDotNet/TrelloDotNet/TrelloClient.Cards.cs b/TrelloDotNet/TrelloDotNet/TrelloClient.Cards.cs index 4b98027..068d7f5 100644 --- a/TrelloDotNet/TrelloDotNet/TrelloClient.Cards.cs +++ b/TrelloDotNet/TrelloDotNet/TrelloClient.Cards.cs @@ -4,8 +4,11 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using TrelloDotNet.AutomationEngine.Model; +using TrelloDotNet.AutomationEngine.Model.Actions; using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; using TrelloDotNet.Model.Options.GetCardOptions; namespace TrelloDotNet @@ -54,25 +57,35 @@ public async Task ReOpenCardAsync(string cardId, CancellationToken cancell public async Task UpdateCardAsync(Card cardWithChanges, CancellationToken cancellationToken = default) { var parameters = _queryParametersBuilder.GetViaQueryParameterAttributes(cardWithChanges).ToList(); + CardCover cardCover = cardWithChanges.Cover; + var payload = GeneratePayloadForCoverUpdate(cardCover, parameters); + + return await _apiRequestController.PutWithJsonPayload($"{UrlPaths.Cards}/{cardWithChanges.Id}", cancellationToken, payload, parameters.ToArray()); + } + + private static string GeneratePayloadForCoverUpdate(CardCover cardCover, List parameters) + { //Special code for Cover string payload = string.Empty; - if (cardWithChanges.Cover == null) + if (cardCover == null) { //Remove cover parameters.Add(new QueryParameter("cover", "")); } else { - cardWithChanges.Cover.PrepareForAddUpdate(); - if (cardWithChanges.Cover.Color != null || cardWithChanges.Cover.BackgroundImageId != null) + cardCover.PrepareForAddUpdate(); + if (cardCover.Color != null || cardCover.BackgroundImageId != null) { - parameters.Remove(parameters.First(x => x.Name == "idAttachmentCover")); //This parameter can't be there while a cover is added + QueryParameter queryParameter = parameters.FirstOrDefault(x => x.Name == "idAttachmentCover"); + if (queryParameter != null) + { + parameters.Remove(queryParameter); //This parameter can't be there while a cover is added + } } - - payload = $"{{\"cover\":{JsonSerializer.Serialize(cardWithChanges.Cover)}}}"; + payload = $"{{\"cover\":{JsonSerializer.Serialize(cardCover)}}}"; } - - return await _apiRequestController.PutWithJsonPayload($"{UrlPaths.Cards}/{cardWithChanges.Id}", cancellationToken, payload, parameters.ToArray()); + return payload; } /// @@ -247,10 +260,11 @@ public async Task> GetCardsForMemberAsync(string memberId, GetCardOpt /// Cancellation Token public async Task SetDueDateOnCardAsync(string cardId, DateTimeOffset dueDate, bool dueComplete = false, CancellationToken cancellationToken = default) { - var card = await GetCardAsync(cardId, cancellationToken); - card.Due = dueDate; - card.DueComplete = dueComplete; - return await UpdateCardAsync(card, cancellationToken); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.Due.GetJsonPropertyName(), dueDate), + new QueryParameter(CardFieldsType.DueComplete.GetJsonPropertyName(), dueComplete) + }, cancellationToken); } /// @@ -261,9 +275,10 @@ public async Task SetDueDateOnCardAsync(string cardId, DateTimeOffset dueD /// Cancellation Token public async Task SetStartDateOnCardAsync(string cardId, DateTimeOffset startDate, CancellationToken cancellationToken = default) { - var card = await GetCardAsync(cardId, cancellationToken); - card.Start = startDate; - return await UpdateCardAsync(card, cancellationToken); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.Start.GetJsonPropertyName(), startDate) + }, cancellationToken); } /// @@ -276,11 +291,12 @@ public async Task SetStartDateOnCardAsync(string cardId, DateTimeOffset st /// Cancellation Token public async Task SetStartDateAndDueDateOnCardAsync(string cardId, DateTimeOffset startDate, DateTimeOffset dueDate, bool dueComplete = false, CancellationToken cancellationToken = default) { - var card = await GetCardAsync(cardId, cancellationToken); - card.Start = startDate; - card.Due = dueDate; - card.DueComplete = dueComplete; - return await UpdateCardAsync(card, cancellationToken); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.Start.GetJsonPropertyName(), startDate), + new QueryParameter(CardFieldsType.Due.GetJsonPropertyName(), dueDate), + new QueryParameter(CardFieldsType.DueComplete.GetJsonPropertyName(), dueComplete), + }, cancellationToken); } /// @@ -292,9 +308,31 @@ public async Task SetStartDateAndDueDateOnCardAsync(string cardId, DateTim /// public async Task MoveCardToListAsync(string cardId, string newListId, CancellationToken cancellationToken = default) { - Card card = await GetCardAsync(cardId, cancellationToken); - card.ListId = newListId; - return await UpdateCardAsync(card, cancellationToken); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.ListId.GetJsonPropertyName(), newListId) + }, cancellationToken); + } + + /// + /// Update one or more specific fields on a card (compared to a full update of all fields with UpdateCard) + /// + /// Id of the Card + /// The Specific Parameters to set + /// CancellationToken + public async Task UpdateCardAsync(string cardId, List parameters, CancellationToken cancellationToken = default) + { + QueryParameter coverParameter = parameters.FirstOrDefault(x => x.Name == "cover"); + if (coverParameter != null && !string.IsNullOrWhiteSpace(coverParameter.GetRawStringValue())) + { + parameters.Remove(coverParameter); + CardCover cover = JsonSerializer.Deserialize(coverParameter.GetRawStringValue()); + var payload = GeneratePayloadForCoverUpdate(cover, parameters); + return await _apiRequestController.PutWithJsonPayload($"{UrlPaths.Cards}/{cardId}", cancellationToken, payload, parameters.ToArray()); + } + + //Special Cover Card + return await _apiRequestController.Put($"{UrlPaths.Cards}/{cardId}", cancellationToken, parameters.ToArray()); } } } \ No newline at end of file diff --git a/TrelloDotNet/TrelloDotNet/TrelloClient.Labels.cs b/TrelloDotNet/TrelloDotNet/TrelloClient.Labels.cs index 213c481..1517439 100644 --- a/TrelloDotNet/TrelloDotNet/TrelloClient.Labels.cs +++ b/TrelloDotNet/TrelloDotNet/TrelloClient.Labels.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; namespace TrelloDotNet { @@ -44,11 +45,11 @@ public async Task AddLabelsToCardAsync(string cardId, params string[] labe /// Add a Label to a Card /// /// Id of the Card - /// Cancellation Token + /// Cancellation Token /// One or more Ids of Labels to add - public async Task AddLabelsToCardAsync(string cardId, CancellationToken cancellation = default, params string[] labelIdsToAdd) + public async Task AddLabelsToCardAsync(string cardId, CancellationToken cancellationToken = default, params string[] labelIdsToAdd) { - var card = await GetCardAsync(cardId, cancellation); + var card = await GetCardAsync(cardId, cancellationToken); var missing = labelIdsToAdd.Where(x => !card.LabelIds.Contains(x)).ToList(); if (missing.Count == 0) @@ -58,7 +59,10 @@ public async Task AddLabelsToCardAsync(string cardId, CancellationToken ca //Need update card.LabelIds.AddRange(missing); - return await UpdateCardAsync(card, cancellation); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.LabelIds.GetJsonPropertyName(), card.LabelIds) + }, cancellationToken); } /// @@ -88,7 +92,10 @@ public async Task RemoveLabelsFromCardAsync(string cardId, CancellationTok //Need update card.LabelIds = card.LabelIds.Except(toRemove).ToList(); - return await UpdateCardAsync(card, cancellationToken); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.LabelIds.GetJsonPropertyName(), card.LabelIds) + }, cancellationToken); } /// @@ -98,15 +105,10 @@ public async Task RemoveLabelsFromCardAsync(string cardId, CancellationTok /// Cancellation Token public async Task RemoveAllLabelsFromCardAsync(string cardId, CancellationToken cancellationToken = default) { - var card = await GetCardAsync(cardId, cancellationToken); - if (card.LabelIds.Any()) + return await UpdateCardAsync(cardId, new List { - //Need update - card.LabelIds = new List(); - return await UpdateCardAsync(card, cancellationToken); - } - - return card; + new QueryParameter(CardFieldsType.LabelIds.GetJsonPropertyName(), new List()) + }, cancellationToken); } /// diff --git a/TrelloDotNet/TrelloDotNet/TrelloClient.Members.cs b/TrelloDotNet/TrelloDotNet/TrelloClient.Members.cs index c0d6420..a6abdee 100644 --- a/TrelloDotNet/TrelloDotNet/TrelloClient.Members.cs +++ b/TrelloDotNet/TrelloDotNet/TrelloClient.Members.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using TrelloDotNet.Control; using TrelloDotNet.Model; +using TrelloDotNet.Model.Options; namespace TrelloDotNet { @@ -70,7 +71,10 @@ public async Task AddMembersToCardAsync(string cardId, CancellationToken c //Need update card.MemberIds.AddRange(missing); - return await UpdateCardAsync(card, cancellationToken); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.MemberIds.GetJsonPropertyName(), card.MemberIds) + }, cancellationToken); } /// @@ -87,11 +91,11 @@ public async Task RemoveMembersFromCardAsync(string cardId, params string[ /// Remove one or more Members from a Card /// /// Id of the Card - /// Cancellation Token + /// Cancellation Token /// One or more Ids of Members to remove - public async Task RemoveMembersFromCardAsync(string cardId, CancellationToken cancellation = default, params string[] memberIdsToRemove) + public async Task RemoveMembersFromCardAsync(string cardId, CancellationToken cancellationToken = default, params string[] memberIdsToRemove) { - var card = await GetCardAsync(cardId, cancellation); + var card = await GetCardAsync(cardId, cancellationToken); var toRemove = memberIdsToRemove.Where(x => card.MemberIds.Contains(x)).ToList(); if (toRemove.Count == 0) { @@ -100,7 +104,10 @@ public async Task RemoveMembersFromCardAsync(string cardId, CancellationTo //Need update card.MemberIds = card.MemberIds.Except(toRemove).ToList(); - return await UpdateCardAsync(card, cancellation); + return await UpdateCardAsync(cardId, new List + { + new QueryParameter(CardFieldsType.MemberIds.GetJsonPropertyName(), card.MemberIds) + }, cancellationToken); } /// @@ -110,15 +117,10 @@ public async Task RemoveMembersFromCardAsync(string cardId, CancellationTo /// Cancellation Token public async Task RemoveAllMembersFromCardAsync(string cardId, CancellationToken cancellationToken = default) { - var card = await GetCardAsync(cardId, cancellationToken); - if (card.MemberIds.Any()) + return await UpdateCardAsync(cardId, new List { - //Need update - card.MemberIds = new List(); - return await UpdateCardAsync(card, cancellationToken); - } - - return card; + new QueryParameter(CardFieldsType.MemberIds.GetJsonPropertyName(), new List()) + }, cancellationToken); } /// diff --git a/TrelloDotNet/TrelloDotNet/TrelloDotNet.csproj b/TrelloDotNet/TrelloDotNet/TrelloDotNet.csproj index ad0b813..8d45200 100644 --- a/TrelloDotNet/TrelloDotNet/TrelloDotNet.csproj +++ b/TrelloDotNet/TrelloDotNet/TrelloDotNet.csproj @@ -13,7 +13,7 @@ https://github.com/rwjdk/TrelloDotNet/wiki trello.png trello api rest restful dotNet wrapper webhook automation - 1.9.1 + 1.9.2 RWJDK MIT https://github.com/rwjdk/TrelloDotNet/blob/main/Changelog.md