• 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

88.46
/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)
1,291✔
24
        {
25
            _httpClient = httpClient;
1,291✔
26
            _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
1,291✔
27
            _apiKey = apiKey;
1,291✔
28
            _token = token;
1,291✔
29
            _client = client;
1,291✔
30
        }
1,291✔
31

32
        public string Token => _token;
35✔
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);
850✔
39
            var @object = JsonSerializer.Deserialize<T>(json);
850✔
40
            return @object;
850✔
41
        }
850✔
42

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

53
            return responseContent; //Content is assumed JSON
865✔
54
        }
865✔
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);
453✔
59
            var @object = JsonSerializer.Deserialize<T>(json);
453✔
60
            return @object;
453✔
61
        }
453✔
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);
2✔
66
            var @object = JsonSerializer.Deserialize<T>(json);
2✔
67
            return @object;
2✔
68
        }
2✔
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);
2✔
73
            using (var multipartFormContent = new MultipartFormDataContent())
2✔
74
            {
75
                multipartFormContent.Add(new StreamContent(attachmentFile.Stream), name: "file", fileName: attachmentFile.Filename);
2✔
76
                var response = await _httpClient.PostAsync(uri, multipartFormContent, cancellationToken);
2✔
77
                var responseContent = await response.Content.ReadAsStringAsync();
2✔
78
                if (response.StatusCode != HttpStatusCode.OK)
2!
79
                {
80
                    return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => PostWithAttachmentFileUpload(suffix, attachmentFile, cancellationToken, retry, parameters), retryCount, cancellationToken);
×
81
                }
82

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

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

97
            return content; //Content is assumed JSON
456✔
98
        }
456✔
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);
139✔
103
            var @object = JsonSerializer.Deserialize<T>(json);
139✔
104
            return @object;
139✔
105
        }
139✔
106

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

117
            return responseContent; //Content is assumed JSON
144✔
118
        }
144✔
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);
13✔
123
            var @object = JsonSerializer.Deserialize<T>(json);
13✔
124
            return @object;
13✔
125
        }
13✔
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);
31✔
130
            var response = await _httpClient.PutAsync(uri, new StringContent(payload, Encoding.UTF8, "application/json"), cancellationToken);
31✔
131
            var responseContent = await response.Content.ReadAsStringAsync();
31✔
132
            if (response.StatusCode != HttpStatusCode.OK)
31!
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
31✔
138
        }
31✔
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,598✔
179
            foreach (var parameter in parameters)
19,212✔
180
            {
181
                parameterString.Append($"&{parameter.Name}={parameter.GetValueAsApiFormattedString()}");
8,008✔
182
            }
183

184
            return parameterString;
1,598✔
185
        }
186

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

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

203
            return null;
81✔
204
        }
81✔
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)
7!
209
            {
UNCOV
210
                await Task.Delay(TimeSpan.FromSeconds(_client.Options.DelayInSecondsToWaitInTokenLimitExceededRetry), cancellationToken);
×
UNCOV
211
                retryCount++;
×
UNCOV
212
                return await toRetry(retryCount);
×
213
            }
214

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

© 2025 Coveralls, Inc