Skip to content

Commit abacb62

Browse files
committed
modify retry
1 parent 078dbc1 commit abacb62

247 files changed

Lines changed: 5654 additions & 3675 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

maker/api.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::collections::HashSet;
55
<% end %><% if fields.present? %>use crate::fields::{<%= fields.map{|it| "#{it[:name].make_field()}::#{it[:name].ucc}"}.join(", ") %>};
66
<% end %><% if refs.present? %>use crate::responses::{<%= refs.map{|it| "#{it}::#{it.ucc}"}.join(", ") %>};
77
<% end %>use reqwest::RequestBuilder;
8-
use crate::{error::Error, headers::Headers, api::{apply_options, execute_twitter, Authentication, make_url, TwapiOptions}};
8+
use crate::{error::Error, headers::Headers, api::{execute_twitter, Authentication, make_url, TwapiOptions}};
99

1010
const URL: &str = "<%= yml[:url] %>";
1111

@@ -28,7 +28,7 @@ impl Api {
2828
.form(&form_parameters)<% end %><% if bodies.present? %>
2929
.json(&self.body)<% end %>
3030
;
31-
authentication.execute(apply_options(builder, &self.twapi_options), "<%= yml[:method].upcase %>", &url, <%= queries.present? ? :"&query_parameters.iter().map(|it| (it.0, it.1.as_str())).collect::<Vec<_>>()" : "&[]" %>)
31+
authentication.execute(builder, "<%= yml[:method].upcase %>", &url, <%= queries.present? ? :"&query_parameters.iter().map(|it| (it.0, it.1.as_str())).collect::<Vec<_>>()" : "&[]" %>)
3232
}
3333

3434
pub async fn execute(&self, authentication: &impl Authentication) -> Result<(Response, Headers), Error> {

src/api.rs

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::time::Duration;
22

3-
use reqwest::RequestBuilder;
3+
use reqwest::{RequestBuilder, StatusCode};
44
use serde::de::DeserializeOwned;
5+
use tokio::time::sleep;
56

67
use crate::{
78
error::{Error, TwitterError},
@@ -116,6 +117,9 @@ pub fn setup_prefix_url(url: &str) {
116117
pub struct TwapiOptions {
117118
pub prefix_url: Option<String>,
118119
pub timeout: Option<Duration>,
120+
pub try_count: Option<u32>,
121+
pub retry_interval_duration: Option<Duration>,
122+
pub retryable_status_codes: Option<Vec<u16>>,
119123
}
120124

121125
pub(crate) fn make_url(twapi_options: &Option<TwapiOptions>, post_url: &str) -> String {
@@ -177,45 +181,70 @@ impl Authentication for BearerAuthentication {
177181
}
178182
}
179183

180-
pub async fn execute_twitter<T>(f: impl Fn() -> RequestBuilder, twapi_options: &Option<TwapiOptions>) -> Result<(T, Headers), Error>
184+
pub async fn execute_twitter<T>(
185+
f: impl Fn() -> RequestBuilder,
186+
twapi_options: &Option<TwapiOptions>,
187+
) -> Result<(T, Headers), Error>
181188
where
182189
T: DeserializeOwned,
183190
{
184-
let response = f().send().await?;
185-
let status_code = response.status();
186-
let header = response.headers();
187-
let headers = Headers::new(header);
191+
let mut count = 0;
192+
#[allow(unused_assignments)]
193+
let mut last_error: Option<Error> = None;
194+
let default_retryable_status_codes: Vec<u16> = vec![StatusCode::INTERNAL_SERVER_ERROR.as_u16()];
195+
let retryable_status_codes = twapi_options
196+
.as_ref()
197+
.and_then(|options| options.retryable_status_codes.as_ref())
198+
.unwrap_or(default_retryable_status_codes.as_ref());
199+
let retryable_interval_duration = twapi_options
200+
.as_ref()
201+
.and_then(|options| options.retry_interval_duration)
202+
.unwrap_or(Duration::from_millis(100));
203+
204+
loop {
205+
let mut builder = f();
206+
if let Some(timeout) = twapi_options.as_ref().and_then(|options| options.timeout) {
207+
builder = builder.timeout(timeout);
208+
}
188209

189-
println!("status_code: {:?}", status_code);
210+
let response = builder.send().await?;
211+
let status_code = response.status();
212+
let header = response.headers();
213+
let headers = Headers::new(header);
190214

191-
if status_code.is_success() {
192-
Ok((response.json::<T>().await?, headers))
193-
} else {
194-
let text = response.text().await?;
195-
println!("text: {:?}", text);
196-
match serde_json::from_str(&text) {
197-
Ok(value) => Err(Error::Twitter(
198-
TwitterError::new(&value, status_code),
199-
value,
200-
Box::new(headers),
201-
)),
202-
Err(err) => Err(Error::Other(
203-
format!("{:?}:{}", err, text),
204-
Some(status_code),
205-
)),
215+
if status_code.is_success() {
216+
return Ok((response.json::<T>().await?, headers));
217+
} else {
218+
let text = response.text().await?;
219+
last_error = Some(match serde_json::from_str(&text) {
220+
Ok(value) => Error::Twitter(
221+
TwitterError::new(&value, status_code),
222+
value,
223+
Box::new(headers),
224+
),
225+
Err(err) => Error::Other(format!("{:?}:{}", err, text), Some(status_code)),
226+
});
227+
}
228+
229+
if count
230+
>= twapi_options
231+
.as_ref()
232+
.and_then(|options| options.try_count)
233+
.unwrap_or(0)
234+
{
235+
break;
206236
}
237+
238+
if !retryable_status_codes.contains(&status_code.as_u16()) {
239+
break;
240+
}
241+
242+
sleep(retryable_interval_duration * 2_u32.pow(count)).await;
243+
count += 1;
207244
}
208-
}
209245

210-
pub(crate) fn apply_options(
211-
client: RequestBuilder,
212-
options: &Option<TwapiOptions>,
213-
) -> RequestBuilder {
214-
let Some(options) = options else {
215-
return client;
216-
};
217-
let Some(timeout) = options.timeout else {
218-
return client;
219-
};
220-
client.timeout(timeout)
246+
Err(last_error.unwrap_or(Error::Other(
247+
"Retry Over last_error is None".to_string(),
248+
None,
249+
)))
221250
}

src/api/delete_2_lists_id.rs

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
use serde::{Serialize, Deserialize};
2-
use crate::responses::{errors::Errors};
1+
use crate::responses::errors::Errors;
2+
use crate::{
3+
api::{Authentication, TwapiOptions, execute_twitter, make_url},
4+
error::Error,
5+
headers::Headers,
6+
};
37
use reqwest::RequestBuilder;
4-
use crate::{error::Error, headers::Headers, api::{apply_options, execute_twitter, Authentication, make_url, TwapiOptions}};
8+
use serde::{Deserialize, Serialize};
59

610
const URL: &str = "/2/lists/:id";
711

8-
9-
10-
11-
1212
#[derive(Debug, Clone, Default)]
1313
pub struct Api {
1414
id: String,
@@ -22,58 +22,61 @@ impl Api {
2222
..Default::default()
2323
}
2424
}
25-
26-
25+
2726
pub fn twapi_options(mut self, value: TwapiOptions) -> Self {
2827
self.twapi_options = Some(value);
2928
self
3029
}
3130

3231
pub fn build(&self, authentication: &impl Authentication) -> RequestBuilder {
33-
3432
let client = reqwest::Client::new();
3533
let url = make_url(&self.twapi_options, &URL.replace(":id", &self.id));
36-
let builder = client
37-
.delete(&url)
38-
;
39-
authentication.execute(apply_options(builder, &self.twapi_options), "DELETE", &url, &[])
34+
let builder = client.delete(&url);
35+
authentication.execute(builder, "DELETE", &url, &[])
4036
}
4137

42-
pub async fn execute(&self, authentication: &impl Authentication) -> Result<(Response, Headers), Error> {
38+
pub async fn execute(
39+
&self,
40+
authentication: &impl Authentication,
41+
) -> Result<(Response, Headers), Error> {
4342
execute_twitter(|| self.build(authentication), &self.twapi_options).await
4443
}
4544
}
4645

47-
48-
4946
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
5047
pub struct Response {
5148
#[serde(skip_serializing_if = "Option::is_none")]
52-
pub data: Option<Data>,
49+
pub data: Option<Data>,
5350
#[serde(skip_serializing_if = "Option::is_none")]
54-
pub errors: Option<Vec<Errors>>,
51+
pub errors: Option<Vec<Errors>>,
5552
#[serde(flatten)]
5653
pub extra: std::collections::HashMap<String, serde_json::Value>,
5754
}
5855

5956
impl Response {
6057
pub fn is_empty_extra(&self) -> bool {
61-
let res = self.extra.is_empty() &&
62-
self.data.as_ref().map(|it| it.is_empty_extra()).unwrap_or(true) &&
63-
self.errors.as_ref().map(|it| it.iter().all(|item| item.is_empty_extra())).unwrap_or(true);
58+
let res = self.extra.is_empty()
59+
&& self
60+
.data
61+
.as_ref()
62+
.map(|it| it.is_empty_extra())
63+
.unwrap_or(true)
64+
&& self
65+
.errors
66+
.as_ref()
67+
.map(|it| it.iter().all(|item| item.is_empty_extra()))
68+
.unwrap_or(true);
6469
if !res {
65-
println!("Response {:?}", self.extra);
70+
println!("Response {:?}", self.extra);
6671
}
6772
res
6873
}
6974
}
7075

71-
72-
7376
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
7477
pub struct Data {
7578
#[serde(skip_serializing_if = "Option::is_none")]
76-
pub deleted: Option<bool>,
79+
pub deleted: Option<bool>,
7780
#[serde(flatten)]
7881
pub extra: std::collections::HashMap<String, serde_json::Value>,
7982
}
@@ -82,7 +85,7 @@ impl Data {
8285
pub fn is_empty_extra(&self) -> bool {
8386
let res = self.extra.is_empty();
8487
if !res {
85-
println!("Data {:?}", self.extra);
88+
println!("Data {:?}", self.extra);
8689
}
8790
res
8891
}

src/api/delete_2_lists_id_members_user_id.rs

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
use serde::{Serialize, Deserialize};
1+
use crate::{
2+
api::{Authentication, TwapiOptions, execute_twitter, make_url},
3+
error::Error,
4+
headers::Headers,
5+
};
26
use reqwest::RequestBuilder;
3-
use crate::{error::Error, headers::Headers, api::{apply_options, execute_twitter, Authentication, make_url, TwapiOptions}};
7+
use serde::{Deserialize, Serialize};
48

59
const URL: &str = "/2/lists/:id/members/:user_id";
610

7-
8-
9-
10-
1111
#[derive(Debug, Clone, Default)]
1212
pub struct Api {
1313
id: String,
@@ -23,55 +23,58 @@ impl Api {
2323
..Default::default()
2424
}
2525
}
26-
27-
26+
2827
pub fn twapi_options(mut self, value: TwapiOptions) -> Self {
2928
self.twapi_options = Some(value);
3029
self
3130
}
3231

3332
pub fn build(&self, authentication: &impl Authentication) -> RequestBuilder {
34-
3533
let client = reqwest::Client::new();
36-
let url = make_url(&self.twapi_options, &URL.replace(":id", &self.id).replace(":user_id", &self.user_id));
37-
let builder = client
38-
.delete(&url)
39-
;
40-
authentication.execute(apply_options(builder, &self.twapi_options), "DELETE", &url, &[])
34+
let url = make_url(
35+
&self.twapi_options,
36+
&URL.replace(":id", &self.id)
37+
.replace(":user_id", &self.user_id),
38+
);
39+
let builder = client.delete(&url);
40+
authentication.execute(builder, "DELETE", &url, &[])
4141
}
4242

43-
pub async fn execute(&self, authentication: &impl Authentication) -> Result<(Response, Headers), Error> {
43+
pub async fn execute(
44+
&self,
45+
authentication: &impl Authentication,
46+
) -> Result<(Response, Headers), Error> {
4447
execute_twitter(|| self.build(authentication), &self.twapi_options).await
4548
}
4649
}
4750

48-
49-
5051
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
5152
pub struct Response {
5253
#[serde(skip_serializing_if = "Option::is_none")]
53-
pub data: Option<Data>,
54+
pub data: Option<Data>,
5455
#[serde(flatten)]
5556
pub extra: std::collections::HashMap<String, serde_json::Value>,
5657
}
5758

5859
impl Response {
5960
pub fn is_empty_extra(&self) -> bool {
60-
let res = self.extra.is_empty() &&
61-
self.data.as_ref().map(|it| it.is_empty_extra()).unwrap_or(true);
61+
let res = self.extra.is_empty()
62+
&& self
63+
.data
64+
.as_ref()
65+
.map(|it| it.is_empty_extra())
66+
.unwrap_or(true);
6267
if !res {
63-
println!("Response {:?}", self.extra);
68+
println!("Response {:?}", self.extra);
6469
}
6570
res
6671
}
6772
}
6873

69-
70-
7174
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
7275
pub struct Data {
7376
#[serde(skip_serializing_if = "Option::is_none")]
74-
pub is_member: Option<bool>,
77+
pub is_member: Option<bool>,
7578
#[serde(flatten)]
7679
pub extra: std::collections::HashMap<String, serde_json::Value>,
7780
}
@@ -80,7 +83,7 @@ impl Data {
8083
pub fn is_empty_extra(&self) -> bool {
8184
let res = self.extra.is_empty();
8285
if !res {
83-
println!("Data {:?}", self.extra);
86+
println!("Data {:?}", self.extra);
8487
}
8588
res
8689
}

0 commit comments

Comments
 (0)