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

rwjdk / TrelloDotNet / 12705056480

10 Jan 2025 07:18AM UTC coverage: 58.22% (-0.2%) from 58.422%
12705056480

push

github

rwjdk
Merge branch 'main' of https://github.com/rwjdk/TrelloDotNet

900 of 1884 branches covered (47.77%)

Branch coverage included in aggregate %.

2344 of 3688 relevant lines covered (63.56%)

47.85 hits per line

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

91.45
/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;
×
22

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

32
        public string Token => _token;
21✔
33

34
        public string ApiKey => _apiKey;
×
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);
307✔
39
            var @object = JsonSerializer.Deserialize<T>(json);
307✔
40
            return @object;
307✔
41
        }
307✔
42

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

53
            return responseContent; //Content is assumed JSON
308✔
54
        }
315✔
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);
251✔
59
            var @object = JsonSerializer.Deserialize<T>(json);
251✔
60
            return @object;
251✔
61
        }
251✔
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
                {
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);
269✔
90
            var response = await _httpClient.PostAsync(uri, null, cancellationToken);
269✔
91
            var content = await response.Content.ReadAsStringAsync();
269✔
92
            if (response.StatusCode != HttpStatusCode.OK)
269✔
93
            {
94
                return await PreformRetryIfNeededOrThrow(uri, content, retry => Post(suffix, cancellationToken, retry, parameters), retryCount, cancellationToken);
31✔
95
            }
96

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

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

117
            return responseContent; //Content is assumed JSON
78✔
118
        }
87✔
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);
15✔
123
            var @object = JsonSerializer.Deserialize<T>(json);
15✔
124
            return @object;
15✔
125
        }
15✔
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);
16✔
130
            var response = await _httpClient.PutAsync(uri, new StringContent(payload, Encoding.UTF8, "application/json"), cancellationToken);
16✔
131
            var responseContent = await response.Content.ReadAsStringAsync();
16✔
132
            if (response.StatusCode != HttpStatusCode.OK)
16!
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
16✔
138
        }
16✔
139

140
        private string FormatExceptionUrlAccordingToClientOptions(string fullUrl)
141
        {
142
            switch (_client.Options.ApiCallExceptionOption)
7!
143
            {
144
                case ApiCallExceptionOption.IncludeUrlAndCredentials:
145
                    return fullUrl;
1✔
146
                case ApiCallExceptionOption.IncludeUrlButMaskCredentials:
147
                    // ReSharper disable StringLiteralTypo
148
                    return fullUrl.Replace($"?key={_apiKey}&token={_token}", "?key=XXXXX&token=XXXXXXXXXX");
5✔
149
                case ApiCallExceptionOption.DoNotIncludeTheUrl:
150
                    return string.Empty.PadLeft(5, 'X');
1✔
151
                default:
152
                    throw new ArgumentOutOfRangeException();
×
153
            }
154
        }
155

156
        internal static StringBuilder GetParametersAsString(QueryParameter[] parameters)
157
        {
158
            StringBuilder parameterString = new StringBuilder();
746✔
159
            foreach (var parameter in parameters)
6,806✔
160
            {
161
                parameterString.Append($"&{parameter.Name}={parameter.GetValueAsApiFormattedString()}");
2,657✔
162
            }
163

164
            return parameterString;
746✔
165
        }
166

167
        private Uri BuildUri(string suffix, params QueryParameter[] parameters)
168
        {
169
            string separator = suffix.Contains("?") ? "&" : "?";
746!
170
            return new Uri($"{BaseUrl}{suffix}{separator}key={_apiKey}&token={_token}" + GetParametersAsString(parameters));
746!
171
        }
172

173
        internal async Task<string> Delete(string suffix, CancellationToken cancellationToken, int retryCount)
174
        {
175
            var uri = BuildUri(suffix);
53✔
176
            var response = await _httpClient.DeleteAsync(uri, cancellationToken);
53✔
177
            var responseContent = await response.Content.ReadAsStringAsync();
53✔
178
            if (response.StatusCode != HttpStatusCode.OK)
53✔
179
            {
180
                return await PreformRetryIfNeededOrThrow(uri, responseContent, retry => Delete(suffix, cancellationToken, retry), retryCount, cancellationToken);
11✔
181
            }
182

183
            return null;
47✔
184
        }
52✔
185

186
        private async Task<string> PreformRetryIfNeededOrThrow(Uri uri, string responseContent, Func<int, Task<string>> toRetry, int retryCount, CancellationToken cancellationToken)
187
        {
188
            if (responseContent.Contains("API_TOKEN_LIMIT_EXCEEDED") && retryCount <= _client.Options.MaxRetryCountForTokenLimitExceeded)
43✔
189
            {
190
                await Task.Delay(TimeSpan.FromSeconds(_client.Options.DelayInSecondsToWaitInTokenLimitExceededRetry), cancellationToken);
36✔
191
                retryCount++;
36✔
192
                return await toRetry(retryCount);
36✔
193
            }
194

195
            throw new TrelloApiException(responseContent, FormatExceptionUrlAccordingToClientOptions(uri.AbsoluteUri)); //Content is assumed Error Message       
7✔
196
        }
36✔
197
    }
198
}
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