• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rwjdk / TrelloDotNet / 15348388346

30 May 2025 01:56PM UTC coverage: 80.419% (-0.07%) from 80.488%
15348388346

push

github

rwjdk
WIP

2534 of 3447 branches covered (73.51%)

Branch coverage included in aggregate %.

0 of 1 new or added line in 1 file covered. (0.0%)

4 existing lines in 1 file now uncovered.

4460 of 5250 relevant lines covered (84.95%)

102.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.91
/src/TrelloDotNet/TrelloClient.Cards.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Globalization;
4
using System.Linq;
5
using System.Text.Json;
6
using System.Threading;
7
using System.Threading.Tasks;
8
using TrelloDotNet.Control;
9
using TrelloDotNet.Model;
10
using TrelloDotNet.Model.Options;
11
using TrelloDotNet.Model.Options.AddCardFromTemplateOptions;
12
using TrelloDotNet.Model.Options.AddCardOptions;
13
using TrelloDotNet.Model.Options.AddCardToInboxOptions;
14
using TrelloDotNet.Model.Options.CopyCardOptions;
15
using TrelloDotNet.Model.Options.GetCardOptions;
16
using TrelloDotNet.Model.Options.GetInboxCardOptions;
17
using TrelloDotNet.Model.Options.MirrorCardOptions;
18
using TrelloDotNet.Model.Options.MoveCardToBoardOptions;
19
using TrelloDotNet.Model.Options.MoveCardToListOptions;
20

21
// ReSharper disable UnusedMember.Global
22

23
namespace TrelloDotNet
24
{
25
    public partial class TrelloClient
26
    {
27
        /// <summary>
28
        /// Creates a new card in a specified list with the provided options.
29
        /// </summary>
30
        /// <param name="options">Options for the new card, such as name, list ID, description, dates, checklists, attachments, labels, members, custom fields, and cover. <see cref="AddCardOptions"/></param>
31
        /// <param name="cancellationToken">Cancellation Token</param>
32
        /// <returns>The created <see cref="Card"/> object with all requested properties set.</returns>
33
        public async Task<Card> AddCardAsync(AddCardOptions options, CancellationToken cancellationToken = default)
34
        {
35
            if (options == null)
129!
36
            {
37
                throw new ArgumentNullException(nameof(options), $"{nameof(AddCardOptions)} cannot be null");
×
38
            }
39

40
            if (string.IsNullOrWhiteSpace(options.ListId))
129!
41
            {
42
                throw new TrelloApiException("No ListId provided in options (Mandatory)", string.Empty);
×
43
            }
44

45
            var input = new Card(options.ListId, options.Name, options.Description)
129✔
46
            {
129✔
47
                IsTemplate = options.IsTemplate,
129✔
48
                DueComplete = options.DueComplete
129✔
49
            };
129✔
50

51
            if (options.Start.HasValue)
129✔
52
            {
53
                input.Start = options.Start.Value;
16✔
54
            }
55

56
            if (options.Due.HasValue)
129✔
57
            {
58
                input.Due = options.Due.Value;
23✔
59
            }
60

61
            if (options.Position.HasValue)
129!
62
            {
63
                input.Position = options.Position.Value;
×
64
            }
65
            else
66
            {
67
                input.NamedPosition = options.NamedPosition;
129✔
68
            }
69

70
            if (options.LabelIds != null)
129✔
71
            {
72
                input.LabelIds = options.LabelIds.Distinct().ToList();
129✔
73
            }
74

75
            if (options.MemberIds != null)
129✔
76
            {
77
                input.MemberIds = options.MemberIds.Distinct().ToList();
129✔
78
            }
79

80
            QueryParameter[] parameters = _queryParametersBuilder.GetViaQueryParameterAttributes(input);
129✔
81

82
            _queryParametersBuilder.AdjustForNamedPosition(parameters, input.NamedPosition);
129✔
83
            Card addedCard = await _apiRequestController.Post<Card>($"{UrlPaths.Cards}", cancellationToken, parameters);
129✔
84

85
            bool needGet = false;
129✔
86
            var getCardOptions = new GetCardOptions();
129✔
87
            if (options.Checklists != null)
129✔
88
            {
89
                needGet = true;
129✔
90
                getCardOptions.IncludeChecklists = true;
129✔
91
                foreach (Checklist checklist in options.Checklists)
262✔
92
                {
93
                    checklist.NamedPosition = NamedPosition.Bottom;
2✔
94
                    await AddChecklistAsync(addedCard.Id, checklist, cancellationToken: cancellationToken);
2✔
95
                }
96
            }
97

98
            if (options.AttachmentFileUploads != null)
129✔
99
            {
100
                needGet = true;
129✔
101
                getCardOptions.IncludeAttachments = GetCardOptionsIncludeAttachments.True;
129✔
102
                foreach (AttachmentFileUpload fileUpload in options.AttachmentFileUploads)
260✔
103
                {
104
                    await AddAttachmentToCardAsync(addedCard.Id, fileUpload, cancellationToken: cancellationToken);
1✔
105
                }
106
            }
107

108
            if (options.AttachmentUrlLinks != null)
129✔
109
            {
110
                needGet = true;
129✔
111
                getCardOptions.IncludeAttachments = GetCardOptionsIncludeAttachments.True;
129✔
112
                foreach (AttachmentUrlLink urlLink in options.AttachmentUrlLinks)
260✔
113
                {
114
                    await AddAttachmentToCardAsync(addedCard.Id, urlLink, cancellationToken: cancellationToken);
1✔
115
                }
116
            }
117

118
            if (options.Cover != null)
129✔
119
            {
120
                needGet = true;
2✔
121
                await AddCoverToCardAsync(addedCard.Id, options.Cover, cancellationToken);
2✔
122
            }
123

124
            // ReSharper disable once InvertIf
125
            if (options.CustomFields != null)
129✔
126
            {
127
                needGet = true;
129✔
128
                getCardOptions.IncludeCustomFieldItems = true;
129✔
129
                foreach (var customField in options.CustomFields)
270✔
130
                {
131
                    switch (customField.Field.Type)
6✔
132
                    {
133
                        case CustomFieldType.Checkbox:
134
                            await UpdateCustomFieldValueOnCardAsync(addedCard.Id, customField.Field, (bool)customField.Value, cancellationToken);
1✔
135
                            break;
1✔
136
                        case CustomFieldType.Date:
137
                            await UpdateCustomFieldValueOnCardAsync(addedCard.Id, customField.Field, (DateTimeOffset)customField.Value, cancellationToken);
1✔
138
                            break;
1✔
139
                        case CustomFieldType.List:
140
                            await UpdateCustomFieldValueOnCardAsync(addedCard.Id, customField.Field, (CustomFieldOption)customField.Value, cancellationToken);
1✔
141
                            break;
1✔
142
                        case CustomFieldType.Number:
143
                            switch (customField.Value)
2✔
144
                            {
145
                                case int intValue:
146
                                    await UpdateCustomFieldValueOnCardAsync(addedCard.Id, customField.Field, intValue, cancellationToken);
1✔
147
                                    break;
1✔
148
                                case decimal decimalValue:
149
                                    await UpdateCustomFieldValueOnCardAsync(addedCard.Id, customField.Field, decimalValue, cancellationToken);
1✔
150
                                    break;
1✔
151
                            }
152

153
                            break;
154
                        case CustomFieldType.Text:
155
                        default:
156
                            await UpdateCustomFieldValueOnCardAsync(addedCard.Id, customField.Field, (string)customField.Value, cancellationToken);
1✔
157
                            break;
158
                    }
159
                }
160
            }
161

162
            return needGet ? await GetCardAsync(addedCard.Id, getCardOptions, cancellationToken) : addedCard;
129!
163
        }
129✔
164

165
        /// <summary>
166
        /// Creates a new card in the inbox list of the member who owns the Trello token, using the provided options.
167
        /// </summary>
168
        /// <param name="options">Options for the new inbox card, such as name, description, dates, checklists, attachments, and cover. <see cref="AddCardToInboxOptions"/></param>
169
        /// <param name="cancellationToken">CancellationToken</param>
170
        /// <returns>The created <see cref="Card"/> object in the inbox list.</returns>
171
        public async Task<Card> AddCardToInboxAsync(AddCardToInboxOptions options, CancellationToken cancellationToken = default)
172
        {
173
            if (options == null)
×
174
            {
175
                throw new ArgumentNullException(nameof(options), $"{nameof(AddCardToInboxOptions)} cannot be null");
×
176
            }
177

178
            TokenMemberInbox inbox = await GetTokenMemberInboxAsync(cancellationToken);
×
179
            if (inbox == null)
×
180
            {
181
                throw new TrelloApiException("Could not find your inbox", string.Empty);
×
182
            }
183

184
            var input = new Card(inbox.ListId, options.Name, options.Description);
×
185
            if (options.Start.HasValue)
×
186
            {
187
                input.Start = options.Start.Value;
×
188
            }
189

190
            if (options.Due.HasValue)
×
191
            {
192
                input.Due = options.Due.Value;
×
193
            }
194

195
            input.DueComplete = options.DueComplete;
×
196

197
            if (options.Position.HasValue)
×
198
            {
199
                input.Position = options.Position.Value;
×
200
            }
201
            else
202
            {
203
                input.NamedPosition = options.NamedPosition;
×
204
            }
205

206
            QueryParameter[] parameters = _queryParametersBuilder.GetViaQueryParameterAttributes(input);
×
207
            Card addedCard = await _apiRequestController.Post<Card>($"{UrlPaths.Cards}", cancellationToken, parameters);
×
208

209
            if (options.Checklists != null)
×
210
            {
211
                foreach (Checklist checklist in options.Checklists)
×
212
                {
213
                    checklist.NamedPosition = NamedPosition.Bottom;
×
214
                    await AddChecklistAsync(addedCard.Id, checklist, cancellationToken: cancellationToken);
×
215
                }
216
            }
217

218
            if (options.AttachmentFileUploads != null)
×
219
            {
220
                foreach (AttachmentFileUpload fileUpload in options.AttachmentFileUploads)
×
221
                {
222
                    await AddAttachmentToCardAsync(addedCard.Id, fileUpload, cancellationToken: cancellationToken);
×
223
                }
224
            }
225

226
            if (options.AttachmentUrlLinks != null)
×
227
            {
228
                foreach (AttachmentUrlLink urlLink in options.AttachmentUrlLinks)
×
229
                {
230
                    await AddAttachmentToCardAsync(addedCard.Id, urlLink, cancellationToken: cancellationToken);
×
231
                }
232
            }
233

234
            if (options.Cover != null)
×
235
            {
236
                await AddCoverToCardAsync(addedCard.Id, options.Cover, cancellationToken);
×
237
            }
238

239
            return addedCard;
×
240
        }
×
241

242
        /// <summary>
243
        /// Creates a new card based on a template card, copying selected properties and content from the template.
244
        /// </summary>
245
        /// <param name="options">Options for creating the card from a template, including template card ID, target list, name, position, and what to keep. <see cref="AddCardFromTemplateOptions"/></param>
246
        /// <param name="cancellationToken">Cancellation Token</param>
247
        /// <returns>The newly created <see cref="Card"/> based on the template.</returns>
248
        public async Task<Card> AddCardFromTemplateAsync(AddCardFromTemplateOptions options, CancellationToken cancellationToken = default)
249
        {
250
            if (options == null)
1!
251
            {
252
                throw new ArgumentNullException(nameof(options), $"{nameof(AddCardFromTemplateOptions)} cannot be null");
×
253
            }
254

255
            var nameOnNewCard = !string.IsNullOrWhiteSpace(options.Name)
1!
256
                ? options.Name
1✔
257
                : (await GetCardAsync(options.SourceTemplateCardId, new GetCardOptions
1✔
258
                {
1✔
259
                    CardFields = new CardFields(CardFieldsType.Name)
1✔
260
                }, cancellationToken)).Name;
1✔
261

262
            string position = "bottom";
1✔
263

264
            if (options.Position.HasValue)
1!
265
            {
266
                position = options.Position.Value.ToString(CultureInfo.InvariantCulture);
×
267
            }
268

269
            if (options.NamedPosition.HasValue)
1✔
270
            {
271
                position = options.NamedPosition.Value == NamedPosition.Bottom ? "bottom" : "top";
1!
272
            }
273

274
            string keepFromSource = "all";
1✔
275
            if (options.Keep.HasValue)
1✔
276
            {
277
                AddCardFromTemplateOptionsToKeep keep = options.Keep.Value;
1✔
278

279
                if (keep.HasFlag(AddCardFromTemplateOptionsToKeep.All))
1!
280
                {
281
                    keepFromSource = "all";
1✔
282
                }
283
                else
284
                {
285
                    var keepStrings = new List<string>();
×
286
                    var enumValues = Enum.GetValues(typeof(AddCardFromTemplateOptionsToKeep)).Cast<AddCardFromTemplateOptionsToKeep>().ToList();
×
287
                    foreach (AddCardFromTemplateOptionsToKeep toKeep in enumValues.Where(x => x != AddCardFromTemplateOptionsToKeep.All))
×
288
                    {
289
                        if (keep.HasFlag(toKeep))
×
290
                        {
291
                            keepStrings.Add(toKeep.GetJsonPropertyName());
×
292
                        }
293
                    }
294

295
                    keepFromSource = string.Join(",", keepStrings);
×
296
                }
297
            }
298

299
            QueryParameter[] parameters =
1✔
300
            {
1✔
301
                new QueryParameter("name", nameOnNewCard),
1✔
302
                new QueryParameter("idList", options.TargetListId),
1✔
303
                new QueryParameter("pos", position),
1✔
304
                new QueryParameter("idCardSource", options.SourceTemplateCardId),
1✔
305
                new QueryParameter("keepFromSource", keepFromSource)
1✔
306
            };
1✔
307
            return await _apiRequestController.Post<Card>($"{UrlPaths.Cards}", cancellationToken, parameters);
1✔
308
        }
1✔
309

310
        /// <summary>
311
        /// Creates a copy of an existing card, with options to specify which properties to keep or override.
312
        /// </summary>
313
        /// <param name="options">Options for copying the card, including source card ID, target list, name, position, and what to keep. <see cref="CopyCardOptions"/></param>
314
        /// <param name="cancellationToken">Cancellation Token</param>
315
        /// <returns>The copied <see cref="Card"/>.</returns>
316
        public async Task<Card> CopyCardAsync(CopyCardOptions options, CancellationToken cancellationToken = default)
317
        {
318
            if (options == null)
1!
319
            {
320
                throw new ArgumentNullException(nameof(options), $"{nameof(CopyCardOptions)} cannot be null");
×
321
            }
322

323
            var nameOnNewCard = !string.IsNullOrWhiteSpace(options.Name)
1!
324
                ? options.Name
1✔
325
                : (await GetCardAsync(options.SourceCardId, new GetCardOptions
1✔
326
                {
1✔
327
                    CardFields = new CardFields(CardFieldsType.Name)
1✔
328
                }, cancellationToken)).Name;
1✔
329

330
            string position = "bottom";
1✔
331

332
            if (options.Position.HasValue)
1!
333
            {
334
                position = options.Position.Value.ToString(CultureInfo.InvariantCulture);
×
335
            }
336

337
            if (options.NamedPosition.HasValue)
1✔
338
            {
339
                position = options.NamedPosition.Value == NamedPosition.Bottom ? "bottom" : "top";
1!
340
            }
341

342
            string keepFromSource = "all";
1✔
343
            if (options.Keep.HasValue)
1✔
344
            {
345
                CopyCardOptionsToKeep keep = options.Keep.Value;
1✔
346

347
                if (keep.HasFlag(CopyCardOptionsToKeep.All))
1!
348
                {
349
                    keepFromSource = "all";
×
350
                }
351
                else
352
                {
353
                    var keepStrings = new List<string>();
1✔
354
                    var enumValues = Enum.GetValues(typeof(CopyCardOptionsToKeep)).Cast<CopyCardOptionsToKeep>().ToList();
1✔
355
                    foreach (CopyCardOptionsToKeep toKeep in enumValues.Where(x => x != CopyCardOptionsToKeep.All))
30✔
356
                    {
357
                        if (keep.HasFlag(toKeep))
9✔
358
                        {
359
                            keepStrings.Add(toKeep.GetJsonPropertyName());
3✔
360
                        }
361
                    }
362

363
                    keepFromSource = string.Join(",", keepStrings);
1✔
364
                }
365
            }
366

367
            QueryParameter[] parameters =
1✔
368
            {
1✔
369
                new QueryParameter("name", nameOnNewCard),
1✔
370
                new QueryParameter("idList", options.TargetListId),
1✔
371
                new QueryParameter("pos", position),
1✔
372
                new QueryParameter("idCardSource", options.SourceCardId),
1✔
373
                new QueryParameter("keepFromSource", keepFromSource)
1✔
374
            };
1✔
375
            return await _apiRequestController.Post<Card>($"{UrlPaths.Cards}", cancellationToken, parameters);
1✔
376
        }
1✔
377

378
        /// <summary>
379
        /// Creates a mirror card in a target list, linking it to the source card for synchronization.
380
        /// </summary>
381
        /// <param name="options">Options for mirroring the card, including source card ID, target list, and position. <see cref="MirrorCardOptions"/></param>
382
        /// <param name="cancellationToken">Cancellation Token</param>
383
        /// <returns>The created mirror <see cref="Card"/>.</returns>
384
        public async Task<Card> MirrorCardAsync(MirrorCardOptions options, CancellationToken cancellationToken = default)
385
        {
386
            if (options == null)
1!
387
            {
388
                throw new ArgumentNullException(nameof(options), $"{nameof(MirrorCardOptions)} cannot be null");
×
389
            }
390

391
            //Get Source-Card as we need the ShortUrl to make the Card Mirror magic Happen
392
            Card sourceCard = await GetCardAsync(options.SourceCardId, new GetCardOptions
1✔
393
            {
1✔
394
                CardFields = new CardFields(CardFieldsType.ShortUrl)
1✔
395
            }, cancellationToken);
1✔
396

397
            string position = "bottom";
1✔
398

399
            if (options.Position.HasValue)
1!
400
            {
401
                position = options.Position.Value.ToString(CultureInfo.InvariantCulture);
×
402
            }
403

404
            if (options.NamedPosition.HasValue)
1✔
405
            {
406
                position = options.NamedPosition.Value == NamedPosition.Bottom ? "bottom" : "top";
1!
407
            }
408

409
            var parameters = new List<QueryParameter>
1✔
410
            {
1✔
411
                new QueryParameter("idList", options.TargetListId),
1✔
412
                new QueryParameter("name", sourceCard.ShortUrl),
1✔
413
                new QueryParameter("isTemplate", false),
1✔
414
                new QueryParameter("closed", false),
1✔
415
                new QueryParameter("pos", position),
1✔
416
                new QueryParameter("cardRole", "mirror"),
1✔
417
            };
1✔
418

419
            var result = await _apiRequestController.Post<Card>($"{UrlPaths.Cards}", cancellationToken, parameters.ToArray());
1✔
420
            return result;
1✔
421
        }
1✔
422

423
        /// <summary>
424
        /// Archives (closes) the specified card, removing it from the active list but not deleting it permanently.
425
        /// </summary>
426
        /// <param name="cardId">The ID of the card to archive.</param>
427
        /// <param name="cancellationToken">Cancellation Token</param>
428
        /// <returns>The archived <see cref="Card"/>.</returns>
429
        public async Task<Card> ArchiveCardAsync(string cardId, CancellationToken cancellationToken = default)
430
        {
431
            return await _apiRequestController.Put<Card>($"{UrlPaths.Cards}/{cardId}", cancellationToken, new QueryParameter("closed", true));
1✔
432
        }
1✔
433

434
        /// <summary>
435
        /// Reopens an archived card, making it active and visible on the board again.
436
        /// </summary>
437
        /// <param name="cardId">The ID of the card to reopen.</param>
438
        /// <param name="cancellationToken">Cancellation Token</param>
439
        /// <returns>The reopened <see cref="Card"/>.</returns>
440
        public async Task<Card> ReOpenCardAsync(string cardId, CancellationToken cancellationToken = default)
441
        {
442
            return await _apiRequestController.Put<Card>($"{UrlPaths.Cards}/{cardId}", cancellationToken, new QueryParameter("closed", false));
1✔
443
        }
1✔
444

445
        /// <summary>
446
        /// Updates one or more specific fields on a card, such as name, description, dates, members, labels, or cover.
447
        /// </summary>
448
        /// <param name="cardId">The ID of the card to update.</param>
449
        /// <param name="valuesToUpdate">A list of updates to apply to the card. <see cref="CardUpdate"/></param>
450
        /// <param name="cancellationToken">CancellationToken</param>
451
        /// <returns>The updated <see cref="Card"/>.</returns>
452
        public async Task<Card> UpdateCardAsync(string cardId, List<CardUpdate> valuesToUpdate, CancellationToken cancellationToken = default)
453
        {
454
            var parameters = valuesToUpdate.Select(x => x.ToQueryParameter()).ToList();
207✔
455
            QueryParameter coverParameter = parameters.FirstOrDefault(x => x.Name == "cover");
187✔
456
            if (coverParameter != null && !string.IsNullOrWhiteSpace(coverParameter.GetRawStringValue()))
67✔
457
            {
458
                //Special Cover Card
459
                parameters.Remove(coverParameter);
5✔
460
                CardCover cover = JsonSerializer.Deserialize<CardCover>(coverParameter.GetRawStringValue());
5✔
461
                var payload = GeneratePayloadForCoverUpdate(cover, parameters);
5✔
462
                return await _apiRequestController.PutWithJsonPayload<Card>($"{UrlPaths.Cards}/{cardId}", cancellationToken, payload, parameters.ToArray());
5✔
463
            }
464

465
            return await _apiRequestController.Put<Card>($"{UrlPaths.Cards}/{cardId}", cancellationToken, parameters.ToArray());
62✔
466
        }
67✔
467

468
        /// <summary>
469
        /// Archives all cards in the specified list.
470
        /// </summary>
471
        /// <param name="listId">The ID of the list whose cards should be archived.</param>
472
        /// <param name="cancellationToken">Cancellation Token</param>
473
        public async Task ArchiveAllCardsInListAsync(string listId, CancellationToken cancellationToken = default)
474
        {
475
            await _apiRequestController.Post<List>($"{UrlPaths.Lists}/{listId}/archiveAllCards", cancellationToken);
1✔
476
        }
1✔
477

478
        /// <summary>
479
        /// Moves all cards from one list to another list (on same or other Board)
480
        /// </summary>
481
        /// <param name="currentListId">The ID of the list whose cards should be moved.</param>
482
        /// <param name="newListId">The ID of the destination list.</param>
483
        /// <param name="cancellationToken">Cancellation Token</param>
484
        public async Task MoveAllCardsInListAsync(string currentListId, string newListId, CancellationToken cancellationToken = default)
485
        {
486
            var newList = await GetListAsync(newListId, cancellationToken); //Get the new list's BoardId so the user do not need to provide it.
1✔
487
            await _apiRequestController.Post($"{UrlPaths.Lists}/{currentListId}/moveAllCards", cancellationToken,
1✔
488
                0,
1✔
489
                new QueryParameter("idBoard", newList.BoardId),
1✔
490
                new QueryParameter("idList", newListId)
1✔
491
            );
1✔
492
        }
1✔
493

494
        /// <summary>
495
        /// Permanently deletes a card. <b>Warning: This action cannot be undone. Use <see cref="ArchiveCardAsync"/> for non-permanent removal.</b>
496
        /// </summary>
497
        /// <param name="cardId">The ID of the card to delete.</param>
498
        /// <param name="cancellationToken">Cancellation Token</param>
499
        public async Task DeleteCardAsync(string cardId, CancellationToken cancellationToken = default)
500
        {
501
            await _apiRequestController.Delete($"{UrlPaths.Cards}/{cardId}", cancellationToken, 0);
4✔
502
        }
4✔
503

504
        /// <summary>
505
        /// Retrieves a card by its ID, including all default properties.
506
        /// </summary>
507
        /// <param name="cardId">The ID of the card to retrieve.</param>
508
        /// <param name="cancellationToken">Cancellation Token</param>
509
        /// <returns>The requested <see cref="Card"/>.</returns>
510
        public async Task<Card> GetCardAsync(string cardId, CancellationToken cancellationToken = default)
511
        {
512
            return await _apiRequestController.Get<Card>(GetUrlBuilder.GetCard(cardId), cancellationToken);
68✔
513
        }
68✔
514

515
        /// <summary>
516
        /// Retrieves a card by its ID, with options to include or exclude specific fields and nested data.
517
        /// </summary>
518
        /// <param name="cardId">The ID of the card to retrieve.</param>
519
        /// <param name="options">Options specifying which fields and nested data to include. <see cref="GetCardOptions"/></param>
520
        /// <param name="cancellationToken">Cancellation Token</param>
521
        /// <returns>The requested <see cref="Card"/>.</returns>
522
        public async Task<Card> GetCardAsync(string cardId, GetCardOptions options, CancellationToken cancellationToken = default)
523
        {
524
            if (options == null)
198!
525
            {
526
                throw new ArgumentNullException(nameof(options), $"{nameof(GetCardOptions)} cannot be null");
×
527
            }
528

529
            return await _apiRequestController.Get<Card>(GetUrlBuilder.GetCard(cardId), cancellationToken, options.GetParameters(false));
198✔
530
        }
198✔
531

532
        /// <summary>
533
        /// Retrieves all open cards on all un-archived lists on a board.
534
        /// </summary>
535
        /// <param name="boardId">The ID of the board (long or short form).</param>
536
        /// <param name="cancellationToken">CancellationToken</param>
537
        /// <returns>A list of <see cref="Card"/> objects on the board.</returns>
538
        public async Task<List<Card>> GetCardsOnBoardAsync(string boardId, CancellationToken cancellationToken = default)
539
        {
540
            return await _apiRequestController.Get<List<Card>>(GetUrlBuilder.GetCardsOnBoard(boardId), cancellationToken);
18✔
541
        }
18✔
542

543
        /// <summary>
544
        /// Retrieves all cards on a board, with options to filter, include archived cards, and specify which fields to include.
545
        /// </summary>
546
        /// <param name="boardId">The ID of the board (long or short form).</param>
547
        /// <param name="options">Options specifying which cards and fields to include. <see cref="GetCardOptions"/></param>
548
        /// <param name="cancellationToken">CancellationToken</param>
549
        /// <returns>A list of <see cref="Card"/> objects on the board.</returns>
550
        public async Task<List<Card>> GetCardsOnBoardAsync(string boardId, GetCardOptions options, CancellationToken cancellationToken = default)
551
        {
552
            if (options == null)
251!
553
            {
554
                throw new ArgumentNullException(nameof(options), $"{nameof(GetCardOptions)} cannot be null");
×
555
            }
556

557
            options.AdjustFieldsBasedOnSelectedOptions();
251✔
558
            if (options.IncludeList)
251✔
559
            {
560
                if (options.CardFields != null && !options.CardFields.Fields.Contains("idList"))
25✔
561
                {
562
                    options.CardFields.Fields = options.CardFields.Fields.Union(new List<string>
1✔
563
                    {
1✔
564
                        "idList"
1✔
565
                    }).ToArray();
1✔
566
                }
567
            }
568

569
            if (options.IncludeBoard)
251✔
570
            {
571
                if (options.CardFields != null && !options.CardFields.Fields.Contains("idBoard"))
1✔
572
                {
573
                    options.CardFields.Fields = options.CardFields.Fields.Union(new List<string>
1✔
574
                    {
1✔
575
                        "idBoard"
1✔
576
                    }).ToArray();
1✔
577
                }
578
            }
579

580
            List<Card> cards;
581
            if (options.Filter.HasValue)
251✔
582
            {
583
                cards = await _apiRequestController.Get<List<Card>>($"{GetUrlBuilder.GetCardsOnBoard(boardId)}/{options.Filter.Value.GetJsonPropertyName()}", cancellationToken, options.GetParameters(true));
2✔
584
            }
585
            else
586
            {
587
                cards = await _apiRequestController.Get<List<Card>>(GetUrlBuilder.GetCardsOnBoard(boardId), cancellationToken, options.GetParameters(true));
249✔
588
            }
589

590
            if (options.IncludeList)
251✔
591
            {
592
                var lists = await GetListsOnBoardAsync(boardId, cancellationToken);
25✔
593
                foreach (Card card in cards)
254✔
594
                {
595
                    card.List = lists.FirstOrDefault(x => x.Id == card.ListId);
559✔
596
                }
597
            }
598

599
            if (options.IncludeBoard)
251✔
600
            {
601
                var board = await GetBoardAsync(boardId, cancellationToken);
1✔
602
                foreach (Card card in cards)
62✔
603
                {
604
                    card.Board = board;
30✔
605
                }
606
            }
607

608
            cards = FilterCards(cards, options.FilterConditions);
251✔
609
            return OrderCards(cards, options.OrderBy);
245✔
610
        }
245✔
611

612
        /// <summary>
613
        /// Retrieves all open cards on a specific list.
614
        /// </summary>
615
        /// <param name="listId">The ID of the list.</param>
616
        /// <param name="cancellationToken">Cancellation Token</param>
617
        /// <returns>A list of <see cref="Card"/> objects in the list.</returns>
618
        public async Task<List<Card>> GetCardsInListAsync(string listId, CancellationToken cancellationToken = default)
619
        {
620
            return await _apiRequestController.Get<List<Card>>(GetUrlBuilder.GetCardsInList(listId), cancellationToken);
13✔
621
        }
13✔
622

623
        /// <summary>
624
        /// Retrieves all open cards on a specific list, with options to include board and list details.
625
        /// </summary>
626
        /// <param name="listId">The ID of the list.</param>
627
        /// <param name="options">Options specifying which fields and nested data to include. <see cref="GetCardOptions"/></param>
628
        /// <param name="cancellationToken">Cancellation Token</param>
629
        /// <returns>A list of <see cref="Card"/> objects in the list.</returns>
630
        public async Task<List<Card>> GetCardsInListAsync(string listId, GetCardOptions options, CancellationToken cancellationToken = default)
631
        {
632
            if (options == null)
1!
633
            {
634
                throw new ArgumentNullException(nameof(options), $"{nameof(GetCardOptions)} cannot be null");
×
635
            }
636

637
            options.AdjustFieldsBasedOnSelectedOptions();
1✔
638
            if (options.IncludeBoard)
1✔
639
            {
640
                if (options.CardFields != null && !options.CardFields.Fields.Contains("idBoard"))
1✔
641
                {
642
                    options.CardFields.Fields = options.CardFields.Fields.Union(new List<string>
1✔
643
                    {
1✔
644
                        "idBoard"
1✔
645
                    }).ToArray();
1✔
646
                }
647
            }
648

649
            var cards = await _apiRequestController.Get<List<Card>>(GetUrlBuilder.GetCardsInList(listId), cancellationToken, options.GetParameters(true));
1✔
650
            if (options.IncludeList)
1✔
651
            {
652
                var list = await GetListAsync(listId, cancellationToken);
1✔
653
                foreach (Card card in cards)
8✔
654
                {
655
                    card.ListId = listId;
3✔
656
                    card.List = list;
3✔
657
                }
658
            }
659

660
            if (options.IncludeBoard && cards.Count > 0)
1✔
661
            {
662
                var board = await GetBoardAsync(cards[0].BoardId, cancellationToken);
1✔
663
                foreach (Card card in cards)
8✔
664
                {
665
                    card.Board = board;
3✔
666
                }
667
            }
668

669
            cards = FilterCards(cards, options.FilterConditions);
1✔
670
            return OrderCards(cards, options.OrderBy);
1✔
671
        }
1✔
672

673
        /// <summary>
674
        /// Retrieves all cards in the inbox of the current member.
675
        /// </summary>
676
        /// <param name="cancellationToken">CancellationToken</param>
677
        /// <returns>A list of <see cref="Card"/> objects in the inbox.</returns>
678
        public async Task<List<Card>> GetCardsInInboxAsync(CancellationToken cancellationToken = default)
679
        {
680
            TokenMemberInbox inbox = await GetTokenMemberInboxAsync(cancellationToken);
×
681
            if (inbox == null)
×
682
            {
683
                throw new TrelloApiException("Could not find your inbox", string.Empty);
×
684
            }
685

686
            return await GetCardsOnBoardAsync(inbox.BoardId, cancellationToken);
×
687
        }
×
688

689
        /// <summary>
690
        /// Retrieves all cards in the inbox of the current member, with options to include specific fields and nested data.
691
        /// </summary>
692
        /// <param name="options">Options specifying which fields and nested data to include. <see cref="GetInboxCardOptions"/></param>
693
        /// <param name="cancellationToken">CancellationToken</param>
694
        /// <returns>A list of <see cref="Card"/> objects in the inbox.</returns>
695
        public async Task<List<Card>> GetCardsInInboxAsync(GetInboxCardOptions options, CancellationToken cancellationToken = default)
696
        {
697
            if (options == null)
×
698
            {
699
                throw new ArgumentNullException(nameof(options), $"{nameof(GetInboxCardOptions)} cannot be null");
×
700
            }
701

702
            TokenMemberInbox inbox = await GetTokenMemberInboxAsync(cancellationToken);
×
703
            if (inbox == null)
×
704
            {
705
                throw new TrelloApiException("Could not find your inbox", string.Empty);
×
706
            }
707

708
            GetCardOptions getCardOptions = options.ToCardOptions();
×
709
            var cards = await GetCardsOnBoardAsync(inbox.BoardId, getCardOptions, cancellationToken);
×
710
            cards = FilterCards(cards, getCardOptions.FilterConditions);
×
711
            return OrderCards(cards, getCardOptions.OrderBy);
×
712
        }
×
713

714
        /// <summary>
715
        /// Retrieves all cards assigned to a member across multiple boards.
716
        /// </summary>
717
        /// <param name="memberId">The ID of the member.</param>
718
        /// <param name="cancellationToken">Cancellation Token</param>
719
        /// <returns>A list of <see cref="Card"/> objects assigned to the member.</returns>
720
        public async Task<List<Card>> GetCardsForMemberAsync(string memberId, CancellationToken cancellationToken = default)
721
        {
722
            return await _apiRequestController.Get<List<Card>>(GetUrlBuilder.GetCardsForMember(memberId), cancellationToken);
1✔
723
        }
1✔
724

725
        /// <summary>
726
        /// Retrieves all cards assigned to a member across multiple boards, with options to include specific fields and nested data.
727
        /// </summary>
728
        /// <param name="memberId">The ID of the member.</param>
729
        /// <param name="options">Options specifying which fields and nested data to include. <see cref="GetCardOptions"/></param>
730
        /// <param name="cancellationToken">Cancellation Token</param>
731
        /// <returns>A list of <see cref="Card"/> objects assigned to the member.</returns>
732
        public async Task<List<Card>> GetCardsForMemberAsync(string memberId, GetCardOptions options, CancellationToken cancellationToken = default)
733
        {
734
            if (options == null)
1!
735
            {
736
                throw new ArgumentNullException(nameof(options), $"{nameof(GetCardOptions)} cannot be null");
×
737
            }
738

739
            options.AdjustFieldsBasedOnSelectedOptions();
1✔
740
            if (options.IncludeList)
1✔
741
            {
742
                if (options.CardFields != null && !options.CardFields.Fields.Contains("idList"))
1✔
743
                {
744
                    options.CardFields.Fields = options.CardFields.Fields.Union(new List<string>
1✔
745
                    {
1✔
746
                        "idList"
1✔
747
                    }).ToArray();
1✔
748
                }
749

750
                if (options.CardFields != null && !options.CardFields.Fields.Contains("idBoard"))
1✔
751
                {
752
                    options.CardFields.Fields = options.CardFields.Fields.Union(new List<string>
1✔
753
                    {
1✔
754
                        "idBoard"
1✔
755
                    }).ToArray();
1✔
756
                }
757
            }
758

759
            if (options.IncludeBoard)
1✔
760
            {
761
                if (options.CardFields != null && !options.CardFields.Fields.Contains("idBoard"))
1!
762
                {
763
                    options.CardFields.Fields = options.CardFields.Fields.Union(new List<string>
×
764
                    {
×
765
                        "idBoard"
×
766
                    }).ToArray();
×
767
                }
768
            }
769

770
            var cards = await _apiRequestController.Get<List<Card>>(GetUrlBuilder.GetCardsForMember(memberId), cancellationToken, options.GetParameters(true));
1✔
771
            if (options.IncludeList)
1✔
772
            {
773
                var boardsToGetListsFor = cards.Select(x => x.BoardId).Distinct().ToArray();
12✔
774
                List<List> lists = new List<List>();
1✔
775
                foreach (var boardId in boardsToGetListsFor)
16✔
776
                {
777
                    lists.AddRange(await GetListsOnBoardAsync(boardId, cancellationToken));
7✔
778
                }
779

780
                foreach (Card card in cards)
24✔
781
                {
782
                    card.List = lists.FirstOrDefault(x => x.Id == card.ListId);
213✔
783
                }
784
            }
1✔
785

786
            if (options.IncludeBoard)
1✔
787
            {
788
                var boardIds = cards.Select(x => x.BoardId).Distinct().ToList();
12✔
789
                var boards = await GetBoardsAsync(boardIds, cancellationToken);
1✔
790
                foreach (Card card in cards)
24✔
791
                {
792
                    card.Board = boards.FirstOrDefault(x => x.Id == card.BoardId);
67✔
793
                }
794
            }
795

796
            cards = FilterCards(cards, options.FilterConditions);
1✔
797
            return OrderCards(cards, options.OrderBy);
1✔
798
        }
1✔
799

800
        /// <summary>
801
        /// Sets the due date and completion status on a card.
802
        /// </summary>
803
        /// <param name="cardId">The ID of the card.</param>
804
        /// <param name="dueDate">The due date to set (in UTC).</param>
805
        /// <param name="dueComplete">Whether the card is complete.</param>
806
        /// <param name="cancellationToken">Cancellation Token</param>
807
        /// <returns>The updated <see cref="Card"/>.</returns>
808
        public async Task<Card> SetDueDateOnCardAsync(string cardId, DateTimeOffset dueDate, bool dueComplete = false, CancellationToken cancellationToken = default)
809
        {
810
            return await UpdateCardAsync(cardId, new List<CardUpdate>
1✔
811
            {
1✔
812
                CardUpdate.DueDate(dueDate),
1✔
813
                CardUpdate.DueComplete(dueComplete)
1✔
814
            }, cancellationToken);
1✔
815
        }
1✔
816

817
        /// <summary>
818
        /// Sets the start date on a card.
819
        /// </summary>
820
        /// <param name="cardId">The ID of the card.</param>
821
        /// <param name="startDate">The start date to set (in UTC).</param>
822
        /// <param name="cancellationToken">Cancellation Token</param>
823
        /// <returns>The updated <see cref="Card"/>.</returns>
824
        public async Task<Card> SetStartDateOnCardAsync(string cardId, DateTimeOffset startDate, CancellationToken cancellationToken = default)
825
        {
826
            return await UpdateCardAsync(cardId, new List<CardUpdate>
1✔
827
            {
1✔
828
                CardUpdate.StartDate(startDate)
1✔
829
            }, cancellationToken);
1✔
830
        }
1✔
831

832
        /// <summary>
833
        /// Sets both the start and due dates, and completion status, on a card.
834
        /// </summary>
835
        /// <param name="cardId">The ID of the card.</param>
836
        /// <param name="startDate">The start date to set (in UTC).</param>
837
        /// <param name="dueDate">The due date to set (in UTC).</param>
838
        /// <param name="dueComplete">Whether the card is complete.</param>
839
        /// <param name="cancellationToken">Cancellation Token</param> 
840
        /// <returns>The updated <see cref="Card"/>.</returns>
841
        public async Task<Card> SetStartDateAndDueDateOnCardAsync(string cardId, DateTimeOffset startDate, DateTimeOffset dueDate, bool dueComplete = false, CancellationToken cancellationToken = default)
842
        {
843
            return await UpdateCardAsync(cardId, new List<CardUpdate>
1✔
844
            {
1✔
845
                CardUpdate.StartDate(startDate),
1✔
846
                CardUpdate.DueDate(dueDate),
1✔
847
                CardUpdate.DueComplete(dueComplete)
1✔
848
            }, cancellationToken);
1✔
849
        }
1✔
850

851
        /// <summary>
852
        /// Moves a card to a new list on the same board.
853
        /// </summary>
854
        /// <param name="cardId">The ID of the card to move.</param>
855
        /// <param name="newListId">The ID of the destination list.</param>
856
        /// <param name="cancellationToken">Cancellation Token</param>
857
        /// <returns>The updated <see cref="Card"/> in the new list.</returns>
858
        public async Task<Card> MoveCardToListAsync(string cardId, string newListId, CancellationToken cancellationToken = default)
859
        {
860
            return await UpdateCardAsync(cardId, new List<CardUpdate>
1✔
861
            {
1✔
862
                CardUpdate.List(newListId)
1✔
863
            }, cancellationToken);
1✔
864
        }
1✔
865

866
        /// <summary>
867
        /// Moves a card to a new list on the same board, with additional options for position.
868
        /// </summary>
869
        /// <param name="cardId">The ID of the card to move.</param>
870
        /// <param name="newListId">The ID of the destination list.</param>
871
        /// <param name="options">Options for the move, such as position in the new list. <see cref="MoveCardToListOptions"/></param>
872
        /// <param name="cancellationToken">Cancellation Token</param>
873
        /// <returns>The updated <see cref="Card"/> in the new list.</returns>
874
        public async Task<Card> MoveCardToListAsync(string cardId, string newListId, MoveCardToListOptions options, CancellationToken cancellationToken = default)
875
        {
876
            if (options == null)
1!
877
            {
878
                throw new ArgumentNullException(nameof(options), $"{nameof(MoveCardToListOptions)} cannot be null");
×
879
            }
880

881
            var parameters = new List<CardUpdate> { CardUpdate.List(newListId) };
1✔
882
            if (options.NamedPositionOnNewList.HasValue)
1!
883
            {
884
                parameters.Add(CardUpdate.Position(options.NamedPositionOnNewList.Value));
1✔
885
            }
886
            else if (options.PositionOnNewList.HasValue)
×
887
            {
888
                parameters.Add(CardUpdate.Position(options.PositionOnNewList.Value));
×
889
            }
890

891
            return await UpdateCardAsync(cardId, parameters, cancellationToken);
1✔
892
        }
1✔
893

894
        /// <summary>
895
        /// Moves a card to the top of its current list.
896
        /// </summary>
897
        /// <param name="cardId">The ID of the card to move.</param>
898
        /// <param name="cancellationToken">Cancellation Token</param>
899
        /// <returns>The updated <see cref="Card"/> at the top of the list.</returns>
900
        public async Task<Card> MoveCardToTopOfCurrentListAsync(string cardId, CancellationToken cancellationToken = default)
901
        {
902
            return await UpdateCardAsync(cardId, new List<CardUpdate>
1✔
903
            {
1✔
904
                CardUpdate.Position(NamedPosition.Top)
1✔
905
            }, cancellationToken);
1✔
906
        }
1✔
907

908
        /// <summary>
909
        /// Moves a card to the bottom of its current list.
910
        /// </summary>
911
        /// <param name="cardId">The ID of the card to move.</param>
912
        /// <param name="cancellationToken">Cancellation Token</param>
913
        /// <returns>The updated <see cref="Card"/> at the bottom of the list.</returns>
914
        public async Task<Card> MoveCardToBottomOfCurrentListAsync(string cardId, CancellationToken cancellationToken = default)
915
        {
916
            return await UpdateCardAsync(cardId, new List<CardUpdate>
1✔
917
            {
1✔
918
                CardUpdate.Position(NamedPosition.Bottom)
1✔
919
            }, cancellationToken);
1✔
920
        }
1✔
921

922
        /// <summary>
923
        /// Moves a card to another board, with options for list, members, labels, and removal of due/start dates.
924
        /// </summary>
925
        /// <param name="cardId">The ID of the card to move.</param>
926
        /// <param name="newBoardId">The ID of the destination board.</param>
927
        /// <param name="options">Additional Options for the move like what list the card should end up on the new board and what happens to labels and members</param>
928
        /// <param name="cancellationToken">Cancellation Token</param>
929
        /// <returns>The updated <see cref="Card"/> on the new board.</returns>
930
        public async Task<Card> MoveCardToBoardAsync(string cardId, string newBoardId, MoveCardToBoardOptions options, CancellationToken cancellationToken = default)
931
        {
932
            if (options == null)
5!
933
            {
NEW
934
                throw new ArgumentNullException(nameof(options), "You need to pass an options object to confirm the various options that are involved with moving a card between boards");
×
935
            }
936

937
            List<CardUpdate> parameters = new List<CardUpdate> { CardUpdate.Board(newBoardId) };
5✔
938
            var newListId = options.NewListId;
5✔
939
            if (string.IsNullOrWhiteSpace(newListId))
5!
940
            {
941
                //No list specified, so we need to find the first list on the board
942
                newListId = (await GetListsOnBoardAsync(newBoardId, cancellationToken)).OrderBy(x => x.Position).FirstOrDefault()?.Id;
×
943
            }
944

945
            parameters.Add(CardUpdate.List(newListId));
5✔
946

947
            if (options.NamedPositionOnNewList.HasValue)
5!
948
            {
949
                parameters.Add(CardUpdate.Position((options.NamedPositionOnNewList.Value)));
5✔
950
            }
951
            else if (options.PositionOnNewList.HasValue)
×
952
            {
953
                parameters.Add(CardUpdate.Position(options.PositionOnNewList.Value));
×
954
            }
955

956
            Card card = await GetCardAsync(cardId, new GetCardOptions
5✔
957
            {
5✔
958
                CardFields = new CardFields(CardFieldsType.MemberIds, CardFieldsType.LabelIds, CardFieldsType.Labels)
5✔
959
            }, cancellationToken);
5✔
960

961
            switch (options.MemberOptions)
5!
962
            {
963
                case MoveCardToBoardOptionsMemberOptions.KeepMembersAlsoOnNewBoardAndRemoveRest:
964
                    var existingMemberIdsOnNewBoard = (await GetMembersOfBoardAsync(newBoardId, cancellationToken)).Select(x => x.Id);
6✔
965
                    card.MemberIds = card.MemberIds.Intersect(existingMemberIdsOnNewBoard).ToList();
3✔
966
                    break;
3✔
967
                case MoveCardToBoardOptionsMemberOptions.RemoveAllMembersOnCard:
968
                    card.MemberIds.Clear();
2✔
969
                    break;
2✔
970
                default:
971
                    throw new ArgumentOutOfRangeException();
×
972
            }
973

974
            if (card.LabelIds.Any())
5✔
975
            {
976
                card.LabelIds.Clear();
5✔
977
                switch (options.LabelOptions)
5!
978
                {
979
                    case MoveCardToBoardOptionsLabelOptions.MigrateToLabelsOfSameNameAndColorAndCreateMissing:
980
                    {
981
                        var existingLabels = await GetLabelsOfBoardAsync(newBoardId, cancellationToken);
2✔
982
                        foreach (Label cardLabel in card.Labels)
28✔
983
                        {
984
                            Label existingLabel = existingLabels.FirstOrDefault(x => x.Name == cardLabel.Name && x.Color == cardLabel.Color);
84!
985
                            if (existingLabel != null)
12!
986
                            {
987
                                card.LabelIds.Add(existingLabel.Id);
×
988
                            }
989
                            else
990
                            {
991
                                //Label need to be added
992
                                Label newLabel = await AddLabelAsync(new Label(newBoardId, cardLabel.Name, cardLabel.Color), cancellationToken);
12✔
993
                                card.LabelIds.Add(newLabel.Id);
12✔
994
                            }
995
                        }
996

997
                        break;
2✔
998
                    }
999
                    case MoveCardToBoardOptionsLabelOptions.MigrateToLabelsOfSameNameAndColorAndRemoveMissing:
1000
                    {
1001
                        var existingLabels = await GetLabelsOfBoardAsync(newBoardId, cancellationToken);
1✔
1002
                        foreach (Label cardLabel in card.Labels)
14✔
1003
                        {
1004
                            Label existingLabel = existingLabels.FirstOrDefault(x => x.Name == cardLabel.Name && x.Color == cardLabel.Color);
42!
1005
                            if (existingLabel != null)
6!
1006
                            {
1007
                                card.LabelIds.Add(existingLabel.Id);
×
1008
                            }
1009
                        }
1010

1011
                        break;
1012
                    }
1013
                    case MoveCardToBoardOptionsLabelOptions.MigrateToLabelsOfSameNameAndCreateMissing:
1014
                    {
1015
                        var existingLabels = await GetLabelsOfBoardAsync(newBoardId, cancellationToken);
1✔
1016
                        foreach (Label cardLabel in card.Labels)
14✔
1017
                        {
1018
                            Label existingLabel = existingLabels.FirstOrDefault(x => x.Name == cardLabel.Name);
42✔
1019
                            if (existingLabel != null)
6!
1020
                            {
1021
                                card.LabelIds.Add(existingLabel.Id);
×
1022
                            }
1023
                            else
1024
                            {
1025
                                //Label need to be added
1026
                                Label newLabel = await AddLabelAsync(new Label(newBoardId, cardLabel.Name, cardLabel.Color), cancellationToken);
6✔
1027
                                card.LabelIds.Add(newLabel.Id);
6✔
1028
                            }
1029
                        }
1030

1031
                        break;
1✔
1032
                    }
1033
                    case MoveCardToBoardOptionsLabelOptions.MigrateToLabelsOfSameNameAndRemoveMissing:
1034
                    {
1035
                        var existingLabels = await GetLabelsOfBoardAsync(newBoardId, cancellationToken);
×
1036
                        foreach (Label cardLabel in card.Labels)
×
1037
                        {
1038
                            Label existingLabel = existingLabels.FirstOrDefault(x => x.Name == cardLabel.Name);
×
1039
                            if (existingLabel != null)
×
1040
                            {
1041
                                card.LabelIds.Add(existingLabel.Id);
×
1042
                            }
1043
                        }
1044

1045
                        break;
1046
                    }
1047
                    case MoveCardToBoardOptionsLabelOptions.RemoveAllLabelsOnCard:
1048
                        //No more Work needed
1049
                        break;
1050
                    default:
1051
                        throw new ArgumentOutOfRangeException();
×
1052
                }
1053
            }
1054

1055
            parameters.Add(CardUpdate.Labels(card.LabelIds.Distinct().ToList()));
5✔
1056
            parameters.Add(CardUpdate.Members(card.MemberIds.Distinct().ToList()));
5✔
1057

1058
            if (options.RemoveDueDate)
5✔
1059
            {
1060
                parameters.Add(CardUpdate.DueDate(null));
4✔
1061
            }
1062

1063
            if (options.RemoveStartDate)
5✔
1064
            {
1065
                parameters.Add(CardUpdate.StartDate(null));
4✔
1066
            }
1067

1068
            return await UpdateCardAsync(cardId, parameters, cancellationToken);
5✔
1069
        }
5✔
1070

1071
        private static string GeneratePayloadForCoverUpdate(CardCover cardCover, List<QueryParameter> parameters)
1072
        {
1073
            //Special code for Cover
1074
            string payload = string.Empty;
5✔
1075
            if (cardCover == null)
5✔
1076
            {
1077
                //Remove cover
1078
                parameters.Add(new QueryParameter("cover", ""));
2✔
1079
            }
1080
            else
1081
            {
1082
                cardCover.PrepareForAddUpdate();
3✔
1083
                if (cardCover.Color != null || cardCover.BackgroundImageId != null)
3!
1084
                {
1085
                    QueryParameter queryParameter = parameters.FirstOrDefault(x => x.Name == "idAttachmentCover");
27✔
1086
                    if (queryParameter != null)
3!
1087
                    {
1088
                        parameters.Remove(queryParameter); //This parameter can't be there while a cover is added
×
1089
                    }
1090
                }
1091

1092
                payload = $"{{\"cover\":{JsonSerializer.Serialize(cardCover)}}}";
3✔
1093
            }
1094

1095
            return payload;
5✔
1096
        }
1097
    }
1098
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc