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

rwjdk / TrelloDotNet / 13617669023

02 Mar 2025 05:20PM UTC coverage: 49.905% (+4.0%) from 45.939%
13617669023

push

github

rwjdk
Fixes + Increase new Filter Test Coverage

1297 of 3263 branches covered (39.75%)

Branch coverage included in aggregate %.

26 of 34 new or added lines in 1 file covered. (76.47%)

34 existing lines in 7 files now uncovered.

2903 of 5153 relevant lines covered (56.34%)

53.77 hits per line

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

93.08
/src/TrelloDotNet/Control/ApiRequestController.cs
1
using System;
2
using System.Net;
3
using System.Net.Http;
4
using System.Net.Http.Headers;
5
using System.Text;
6
using System.Text.Json;
7
using System.Threading;
8
using System.Threading.Tasks;
9
using TrelloDotNet.Model;
10

11
namespace TrelloDotNet.Control
12
{
13
    internal class ApiRequestController
14
    {
15
        private const string BaseUrl = "https://api.trello.com/1/";
16
        private readonly HttpClient _httpClient;
17
        private readonly string _apiKey;
18
        private readonly string _token;
19
        private readonly TrelloClient _client;
20

21
        internal HttpClient HttpClient => _httpClient;
3✔
22

23
        internal ApiRequestController(HttpClient httpClient, string apiKey, string token, TrelloClient client)
206✔
24
        {
25
            _httpClient = httpClient;
206✔
26
            _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
206✔
27
            _apiKey = apiKey;
206✔
28
            _token = token;
206✔
29
            _client = client;
206✔
30
        }
206✔
31

32
        public string Token => _token;
27✔
33

34
        public string ApiKey => _apiKey;
1✔
35

36
        internal async Task<T> Get<T>(string suffix, CancellationToken cancellationToken, params QueryParameter[] parameters)
37
        {
38
            string json = await Get(suffix, cancellationToken, 0, parameters);
523✔
39
            var @object = JsonSerializer.Deserialize<T>(json);
523✔
40
            return @object;
523✔
41
        }
523✔
42

43
        internal async Task<string> Get(string suffix, CancellationToken cancellationToken, int retryCount, params QueryParameter[] parameters)
44
        {
45
            var uri = BuildUri(suffix, parameters);
569✔
46
            var response = await _httpClient.GetAsync(uri, cancellationToken);
569✔
47
            var responseContent = await response.Content.ReadAsStringAsync();
569✔
48
            if (response.StatusCode != HttpStatusCode.OK)
569✔
49
            {
50
                return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => Get(suffix, cancellationToken, retry, parameters), retryCount, cancellationToken);
86✔
51
            }
52

53
            return responseContent; //Content is assumed JSON
524✔
54
        }
565✔
55

56
        internal async Task<T> Post<T>(string suffix, CancellationToken cancellationToken, params QueryParameter[] parameters)
57
        {
58
            string json = await Post(suffix, cancellationToken, 0, parameters);
290✔
59
            var @object = JsonSerializer.Deserialize<T>(json);
290✔
60
            return @object;
290✔
61
        }
290✔
62

63
        internal async Task<T> PostWithAttachmentFileUpload<T>(string suffix, AttachmentFileUpload attachmentFile, CancellationToken cancellationToken, params QueryParameter[] parameters)
64
        {
65
            string json = await PostWithAttachmentFileUpload(suffix, attachmentFile, cancellationToken, 0, parameters);
1✔
66
            var @object = JsonSerializer.Deserialize<T>(json);
1✔
67
            return @object;
1✔
68
        }
1✔
69

70
        internal async Task<string> PostWithAttachmentFileUpload(string suffix, AttachmentFileUpload attachmentFile, CancellationToken cancellationToken, int retryCount, params QueryParameter[] parameters)
71
        {
72
            var uri = BuildUri(suffix, parameters);
1✔
73
            using (var multipartFormContent = new MultipartFormDataContent())
1✔
74
            {
75
                multipartFormContent.Add(new StreamContent(attachmentFile.Stream), name: "file", fileName: attachmentFile.Filename);
1✔
76
                var response = await _httpClient.PostAsync(uri, multipartFormContent, cancellationToken);
1✔
77
                var responseContent = await response.Content.ReadAsStringAsync();
1✔
78
                if (response.StatusCode != HttpStatusCode.OK)
1!
79
                {
UNCOV
80
                    return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => PostWithAttachmentFileUpload(suffix, attachmentFile, cancellationToken, retry, parameters), retryCount, cancellationToken);
×
81
                }
82

83
                return responseContent; //Content is assumed JSON
1✔
84
            }
85
        }
1✔
86

87
        internal async Task<string> Post(string suffix, CancellationToken cancellationToken, int retryCount, params QueryParameter[] parameters)
88
        {
89
            var uri = BuildUri(suffix, parameters);
312✔
90
            var response = await _httpClient.PostAsync(uri, null, cancellationToken);
312✔
91
            var content = await response.Content.ReadAsStringAsync();
312✔
92
            if (response.StatusCode != HttpStatusCode.OK)
312✔
93
            {
94
                return await PreformRetryIfNeededOrThrow(uri, content, retry => Post(suffix, cancellationToken, retry, parameters), retryCount, cancellationToken);
39✔
95
            }
96

97
            return content; //Content is assumed JSON
292✔
98
        }
311✔
99

100
        internal async Task<T> Put<T>(string suffix, CancellationToken cancellationToken, params QueryParameter[] parameters)
101
        {
102
            string json = await Put(suffix, cancellationToken, 0, parameters);
87✔
103
            var @object = JsonSerializer.Deserialize<T>(json);
87✔
104
            return @object;
87✔
105
        }
87✔
106

107
        internal async Task<string> Put(string suffix, CancellationToken cancellationToken, int retryCount, params QueryParameter[] parameters)
108
        {
109
            var uri = BuildUri(suffix, parameters);
98✔
110
            var response = await _httpClient.PutAsync(uri, null, cancellationToken);
98✔
111
            var responseContent = await response.Content.ReadAsStringAsync();
98✔
112
            if (response.StatusCode != HttpStatusCode.OK)
98✔
113
            {
114
                return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => Put(suffix, cancellationToken, retry, parameters), retryCount, cancellationToken);
13✔
115
            }
116

117
            return responseContent; //Content is assumed JSON
91✔
118
        }
97✔
119

120
        internal async Task<T> PutWithJsonPayload<T>(string suffix, CancellationToken cancellationToken, string payload, params QueryParameter[] parameters)
121
        {
122
            string json = await PutWithJsonPayload(suffix, cancellationToken, payload, 0, parameters);
10✔
123
            var @object = JsonSerializer.Deserialize<T>(json);
10✔
124
            return @object;
10✔
125
        }
10✔
126

127
        internal async Task<string> PutWithJsonPayload(string suffix, CancellationToken cancellationToken, string payload, int retryCount, params QueryParameter[] parameters)
128
        {
129
            var uri = BuildUri(suffix, parameters);
11✔
130
            var response = await _httpClient.PutAsync(uri, new StringContent(payload, Encoding.UTF8, "application/json"), cancellationToken);
11✔
131
            var responseContent = await response.Content.ReadAsStringAsync();
11✔
132
            if (response.StatusCode != HttpStatusCode.OK)
11!
133
            {
134
                return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => PutWithJsonPayload(suffix, cancellationToken, payload, retry, parameters), retryCount, cancellationToken);
×
135
            }
136

137
            return responseContent; //Content is assumed JSON
11✔
138
        }
11✔
139

140
        internal async Task<T> PostWithJsonPayload<T>(string suffix, CancellationToken cancellationToken, string payload, params QueryParameter[] parameters)
141
        {
142
            string json = await PostWithJsonPayload(suffix, cancellationToken, payload, 0, parameters);
1✔
143
            var @object = JsonSerializer.Deserialize<T>(json);
1✔
144
            return @object;
1✔
145
        }
1✔
146

147
        internal async Task<string> PostWithJsonPayload(string suffix, CancellationToken cancellationToken, string payload, int retryCount, params QueryParameter[] parameters)
148
        {
149
            var uri = BuildUri(suffix, parameters);
1✔
150
            var response = await _httpClient.PostAsync(uri, new StringContent(payload, Encoding.UTF8, "application/json"), cancellationToken);
1✔
151
            var responseContent = await response.Content.ReadAsStringAsync();
1✔
152
            if (response.StatusCode != HttpStatusCode.OK)
1!
153
            {
154
                return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => PostWithJsonPayload(suffix, cancellationToken, payload, retry, parameters), retryCount, cancellationToken);
×
155
            }
156

157
            return responseContent; //Content is assumed JSON
1✔
158
        }
1✔
159

160
        private string FormatExceptionUrlAccordingToClientOptions(string fullUrl)
161
        {
162
            switch (_client.Options.ApiCallExceptionOption)
7!
163
            {
164
                case ApiCallExceptionOption.IncludeUrlAndCredentials:
165
                    return fullUrl;
1✔
166
                case ApiCallExceptionOption.IncludeUrlButMaskCredentials:
167
                    // ReSharper disable StringLiteralTypo
168
                    return fullUrl.Replace($"?key={_apiKey}&token={_token}", "?key=XXXXX&token=XXXXXXXXXX");
5✔
169
                case ApiCallExceptionOption.DoNotIncludeTheUrl:
170
                    return string.Empty.PadLeft(5, 'X');
1✔
171
                default:
172
                    throw new ArgumentOutOfRangeException();
×
173
            }
174
        }
175

176
        internal static StringBuilder GetParametersAsString(QueryParameter[] parameters)
177
        {
178
            StringBuilder parameterString = new StringBuilder();
1,069✔
179
            foreach (var parameter in parameters)
11,136✔
180
            {
181
                parameterString.Append($"&{parameter.Name}={parameter.GetValueAsApiFormattedString()}");
4,499✔
182
            }
183

184
            return parameterString;
1,069✔
185
        }
186

187
        private Uri BuildUri(string suffix, params QueryParameter[] parameters)
188
        {
189
            string separator = suffix.Contains("?") ? "&" : "?";
1,068✔
190
            return new Uri($"{BaseUrl}{suffix}{separator}key={_apiKey}&token={_token}" + GetParametersAsString(parameters));
1,068!
191
        }
192

193
        internal async Task<string> Delete(string suffix, CancellationToken cancellationToken, int retryCount)
194
        {
195
            var uri = BuildUri(suffix);
76✔
196
            var response = await _httpClient.DeleteAsync(uri, cancellationToken);
76✔
197
            var responseContent = await response.Content.ReadAsStringAsync();
76✔
198
            if (response.StatusCode != HttpStatusCode.OK)
76✔
199
            {
200
                return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => Delete(suffix, cancellationToken, retry), retryCount, cancellationToken);
41✔
201
            }
202

203
            return null;
55✔
204
        }
75✔
205

206
        private async Task<string> PreformRetryIfNeededOrThrow(Uri uri, string responseContent, Func<int, Task<string>> toRetry, int retryCount, CancellationToken cancellationToken)
207
        {
208
            if (responseContent.Contains("API_TOKEN_LIMIT_EXCEEDED") && retryCount <= _client.Options.MaxRetryCountForTokenLimitExceeded)
93✔
209
            {
210
                await Task.Delay(TimeSpan.FromSeconds(_client.Options.DelayInSecondsToWaitInTokenLimitExceededRetry), cancellationToken);
86✔
211
                retryCount++;
86✔
212
                return await toRetry(retryCount);
86✔
213
            }
214

215
            throw new TrelloApiException(responseContent, FormatExceptionUrlAccordingToClientOptions(uri.AbsoluteUri)); //Content is assumed Error Message       
7✔
216
        }
86✔
217
    }
218
}
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

© 2026 Coveralls, Inc