1use crate::dto::*;
2use crate::error::{map_status_error, try_parse_api_error, Result, WebmasterApiError};
3use reqwest::{Client, Response};
4use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
5use serde_json::json;
6use tracing::instrument;
7
8#[derive(Debug, Clone)]
13pub struct BingWebmasterClient {
14 client: ClientWithMiddleware,
15 base_url: String,
16 api_key: String,
17}
18
19impl BingWebmasterClient {
20 async fn handle_response<T: for<'de> serde::Deserialize<'de>>(
22 &self,
23 response: Response,
24 ) -> Result<T> {
25 let status = response.status();
26
27 if status.is_success() {
28 let response_text = response.text().await?;
29
30 println!("{}", &response_text);
31
32 match serde_json::from_str::<ResponseWrapper<T>>(&response_text) {
34 Ok(wrapper) => Ok(wrapper.d),
35 Err(json_err) => {
36 match serde_json::from_str::<T>(&response_text) {
38 Ok(data) => Ok(data),
39 Err(_) => {
40 if let Some((error_code, message)) = try_parse_api_error(&response_text)
42 {
43 Err(WebmasterApiError::api_error(
44 error_code,
45 message,
46 Some(status.as_u16()),
47 ))
48 } else {
49 Err(WebmasterApiError::invalid_response(format!(
50 "Failed to parse response: {}",
51 json_err
52 )))
53 }
54 }
55 }
56 }
57 }
58 } else {
59 let response_text = response.text().await.unwrap_or_default();
60
61 if let Some((error_code, message)) = try_parse_api_error(&response_text) {
63 Err(WebmasterApiError::api_error(
64 error_code,
65 message,
66 Some(status.as_u16()),
67 ))
68 } else {
69 Err(map_status_error(status, response_text))
70 }
71 }
72 }
73
74 async fn handle_void_response(&self, response: Response) -> Result<()> {
76 let status = response.status();
77
78 if status.is_success() {
79 Ok(())
80 } else {
81 let response_text = response.text().await.unwrap_or_default();
82
83 if let Some((error_code, message)) = try_parse_api_error(&response_text) {
85 Err(WebmasterApiError::api_error(
86 error_code,
87 message,
88 Some(status.as_u16()),
89 ))
90 } else {
91 Err(map_status_error(status, response_text))
92 }
93 }
94 }
95
96 pub fn new(api_key: String) -> Self {
97 let client = ClientBuilder::new(Client::new()).build();
98 Self {
99 client,
100 base_url: "https://ssl.bing.com/webmaster/api.svc".to_string(),
101 api_key,
102 }
103 }
104
105 pub fn with_base_url(api_key: String, base_url: String) -> Self {
111 let client = ClientBuilder::new(Client::new()).build();
112 Self {
113 client,
114 base_url,
115 api_key,
116 }
117 }
118
119 pub fn with_middleware(
141 api_key: String,
142 base_url: Option<String>,
143 client_builder: ClientBuilder,
144 ) -> Self {
145 Self {
146 client: client_builder.build(),
147 base_url: base_url
148 .unwrap_or_else(|| "https://ssl.bing.com/webmaster/api.svc".to_string()),
149 api_key,
150 }
151 }
152
153 #[instrument(skip(self))]
156 pub async fn add_blocked_url(&self, site_url: &str, blocked_url: &BlockedUrl) -> Result<()> {
157 let url = format!(
158 "{}/json/AddBlockedUrl?apikey={}",
159 self.base_url, self.api_key
160 );
161 let body = json!({
162 "siteUrl": site_url,
163 "blockedUrl": blocked_url
164 });
165
166 let response = self
167 .client
168 .post(&url)
169 .header("Content-Type", "application/json; charset=utf-8")
170 .body(serde_json::to_string(&body)?)
171 .send()
172 .await?;
173
174 self.handle_void_response(response).await
175 }
176
177 #[instrument(skip(self))]
178 pub async fn add_connected_page(&self, site_url: &str, master_url: &str) -> Result<()> {
179 let url = format!(
180 "{}/json/AddConnectedPage?apikey={}",
181 self.base_url, self.api_key
182 );
183 let body = json!({
184 "siteUrl": site_url,
185 "masterUrl": master_url
186 });
187
188 let response = self
189 .client
190 .post(&url)
191 .header("Content-Type", "application/json; charset=utf-8")
192 .body(serde_json::to_string(&body)?)
193 .send()
194 .await?;
195
196 self.handle_void_response(response).await
197 }
198
199 #[instrument(skip(self))]
200 pub async fn add_country_region_settings(
201 &self,
202 site_url: &str,
203 settings: &CountryRegionSettings,
204 ) -> Result<()> {
205 let url = format!(
206 "{}/json/AddCountryRegionSettings?apikey={}",
207 self.base_url, self.api_key
208 );
209 let body = json!({
210 "siteUrl": site_url,
211 "settings": settings
212 });
213
214 let response = self
215 .client
216 .post(&url)
217 .header("Content-Type", "application/json; charset=utf-8")
218 .body(serde_json::to_string(&body)?)
219 .send()
220 .await?;
221
222 self.handle_void_response(response).await
223 }
224
225 #[instrument(skip(self))]
226 pub async fn add_deep_link_block(
227 &self,
228 site_url: &str,
229 market: &str,
230 search_url: &str,
231 deep_link_url: &str,
232 ) -> Result<()> {
233 let url = format!(
234 "{}/json/AddDeepLinkBlock?apikey={}",
235 self.base_url, self.api_key
236 );
237 let body = json!({
238 "siteUrl": site_url,
239 "market": market,
240 "searchUrl": search_url,
241 "deepLinkUrl": deep_link_url
242 });
243
244 let response = self
245 .client
246 .post(&url)
247 .header("Content-Type", "application/json; charset=utf-8")
248 .body(serde_json::to_string(&body)?)
249 .send()
250 .await?;
251
252 self.handle_void_response(response).await
253 }
254
255 #[instrument(skip(self))]
256 pub async fn add_query_parameter(&self, site_url: &str, query_parameter: &str) -> Result<()> {
257 let url = format!(
258 "{}/json/AddQueryParameter?apikey={}",
259 self.base_url, self.api_key
260 );
261 let body = json!({
262 "siteUrl": site_url,
263 "queryParameter": query_parameter
264 });
265
266 let response = self
267 .client
268 .post(&url)
269 .header("Content-Type", "application/json; charset=utf-8")
270 .body(serde_json::to_string(&body)?)
271 .send()
272 .await?;
273
274 self.handle_void_response(response).await
275 }
276
277 #[instrument(skip(self))]
278 pub async fn add_site(&self, site_url: &str) -> Result<()> {
279 let url = format!("{}/json/AddSite?apikey={}", self.base_url, self.api_key);
280 let body = json!({
281 "siteUrl": site_url
282 });
283
284 let response = self
285 .client
286 .post(&url)
287 .header("Content-Type", "application/json; charset=utf-8")
288 .body(serde_json::to_string(&body)?)
289 .send()
290 .await?;
291
292 self.handle_void_response(response).await
293 }
294
295 #[instrument(skip(self))]
296 pub async fn add_site_roles(
297 &self,
298 site_url: &str,
299 delegated_url: &str,
300 user_email: &str,
301 authentication_code: &str,
302 is_administrator: bool,
303 is_read_only: bool,
304 ) -> Result<()> {
305 let url = format!(
306 "{}/json/AddSiteRoles?apikey={}",
307 self.base_url, self.api_key
308 );
309 let body = json!({
310 "siteUrl": site_url,
311 "delegatedUrl": delegated_url,
312 "userEmail": user_email,
313 "authenticationCode": authentication_code,
314 "isAdministrator": is_administrator,
315 "isReadOnly": is_read_only
316 });
317
318 let response = self
319 .client
320 .post(&url)
321 .header("Content-Type", "application/json; charset=utf-8")
322 .body(serde_json::to_string(&body)?)
323 .send()
324 .await?;
325
326 self.handle_void_response(response).await
327 }
328
329 #[instrument(skip(self))]
330 pub async fn enable_disable_query_parameter(
331 &self,
332 site_url: &str,
333 parameter: &str,
334 enabled: bool,
335 ) -> Result<()> {
336 let url = format!(
337 "{}/json/EnableDisableQueryParameter?apikey={}",
338 self.base_url, self.api_key
339 );
340 let body = json!({
341 "siteUrl": site_url,
342 "queryParameter": parameter,
343 "isEnabled": enabled
344 });
345
346 let response = self
347 .client
348 .post(&url)
349 .header("Content-Type", "application/json; charset=utf-8")
350 .body(serde_json::to_string(&body)?)
351 .send()
352 .await?;
353
354 self.handle_void_response(response).await
355 }
356
357 #[instrument(skip(self))]
358 pub async fn fetch_url(&self, site_url: &str, url: &str) -> Result<()> {
359 let api_url = format!("{}/json/FetchUrl?apikey={}", self.base_url, self.api_key);
360 let body = json!({
361 "siteUrl": site_url,
362 "url": url
363 });
364
365 let response = self
366 .client
367 .post(&api_url)
368 .header("Content-Type", "application/json; charset=utf-8")
369 .body(serde_json::to_string(&body)?)
370 .send()
371 .await?;
372
373 self.handle_void_response(response).await
374 }
375
376 #[instrument(skip(self))]
385 pub async fn submit_content(
386 &self,
387 site_url: &str,
388 url: &str,
389 http_message: &str,
390 structured_data: &str,
391 dynamic_serving: i32,
392 ) -> Result<()> {
393 let api_url = format!(
394 "{}/json/SubmitContent?apikey={}",
395 self.base_url, self.api_key
396 );
397 let body = json!({
398 "siteUrl": site_url,
399 "url": url,
400 "httpMessage": http_message,
401 "structuredData": structured_data,
402 "dynamicServing": dynamic_serving
403 });
404
405 let response = self
406 .client
407 .post(&api_url)
408 .header("Content-Type", "application/json; charset=utf-8")
409 .body(serde_json::to_string(&body)?)
410 .send()
411 .await?;
412
413 self.handle_void_response(response).await
414 }
415
416 #[instrument(skip(self))]
417 pub async fn submit_url(&self, site_url: &str, url: &str) -> Result<()> {
418 let api_url = format!("{}/json/SubmitUrl?apikey={}", self.base_url, self.api_key);
419 let body = json!({
420 "siteUrl": site_url,
421 "url": url
422 });
423
424 let response = self
425 .client
426 .post(&api_url)
427 .header("Content-Type", "application/json; charset=utf-8")
428 .body(serde_json::to_string(&body)?)
429 .send()
430 .await?;
431
432 self.handle_void_response(response).await
433 }
434
435 #[instrument(skip(self))]
436 pub async fn submit_url_batch(&self, site_url: &str, url_list: &[String]) -> Result<()> {
437 let api_url = format!(
438 "{}/json/SubmitUrlBatch?apikey={}",
439 self.base_url, self.api_key
440 );
441 let body = json!({
442 "siteUrl": site_url,
443 "urlList": url_list
444 });
445
446 let response = self
447 .client
448 .post(&api_url)
449 .header("Content-Type", "application/json; charset=utf-8")
450 .body(serde_json::to_string(&body)?)
451 .send()
452 .await?;
453
454 self.handle_void_response(response).await
455 }
456
457 #[instrument(skip(self))]
460 pub async fn get_crawl_issues(&self, site_url: &str) -> Result<Vec<UrlWithCrawlIssues>> {
461 let url = format!(
462 "{}/json/GetCrawlIssues?apikey={}&siteUrl={}",
463 self.base_url,
464 self.api_key,
465 urlencoding::encode(site_url)
466 );
467
468 let response = self.client.get(&url).send().await?;
469
470 self.handle_response(response).await
471 }
472
473 #[instrument(skip(self))]
474 pub async fn get_page_stats(&self, site_url: &str) -> Result<Vec<QueryStats>> {
475 let url = format!(
476 "{}/json/GetPageStats?apikey={}&siteUrl={}",
477 self.base_url,
478 self.api_key,
479 urlencoding::encode(site_url)
480 );
481
482 let response = self.client.get(&url).send().await?;
483
484 self.handle_response(response).await
485 }
486
487 #[instrument(skip(self))]
488 pub async fn get_connected_pages(&self, site_url: &str) -> Result<Vec<Site>> {
489 let url = format!(
490 "{}/json/GetConnectedPages?apikey={}&siteUrl={}",
491 self.base_url,
492 self.api_key,
493 urlencoding::encode(site_url)
494 );
495
496 let response = self.client.get(&url).send().await?;
497
498 self.handle_response(response).await
499 }
500
501 #[instrument(skip(self))]
502 pub async fn get_query_parameters(&self, site_url: &str) -> Result<Vec<QueryParameter>> {
503 let url = format!(
504 "{}/json/GetQueryParameters?apikey={}&siteUrl={}",
505 self.base_url,
506 self.api_key,
507 urlencoding::encode(site_url)
508 );
509
510 let response = self.client.get(&url).send().await?;
511
512 self.handle_response(response).await
513 }
514
515 #[instrument(skip(self))]
516 pub async fn get_crawl_stats(&self, site_url: &str) -> Result<Vec<CrawlStats>> {
517 let url = format!(
518 "{}/json/GetCrawlStats?apikey={}&siteUrl={}",
519 self.base_url,
520 self.api_key,
521 urlencoding::encode(site_url)
522 );
523
524 let response = self.client.get(&url).send().await?;
525
526 self.handle_response(response).await
527 }
528
529 #[instrument(skip(self))]
530 pub async fn verify_site(&self, site_url: &str) -> Result<bool> {
531 let url = format!("{}/json/VerifySite?apikey={}", self.base_url, self.api_key);
532 let body = json!({
533 "siteUrl": site_url
534 });
535
536 let response = self
537 .client
538 .post(&url)
539 .header("Content-Type", "application/json; charset=utf-8")
540 .body(serde_json::to_string(&body)?)
541 .send()
542 .await?;
543
544 self.handle_response(response).await
545 }
546
547 #[instrument(skip(self))]
548 pub async fn get_content_submission_quota(
549 &self,
550 site_url: &str,
551 ) -> Result<ContentSubmissionQuota> {
552 let url = format!(
553 "{}/json/GetContentSubmissionQuota?apikey={}&siteUrl={}",
554 self.base_url,
555 self.api_key,
556 urlencoding::encode(site_url)
557 );
558
559 let response = self.client.get(&url).send().await?;
560
561 self.handle_response(response).await
562 }
563
564 #[instrument(skip(self))]
565 pub async fn get_blocked_urls(&self, site_url: &str) -> Result<Vec<BlockedUrl>> {
566 let url = format!(
567 "{}/json/GetBlockedUrls?apikey={}&siteUrl={}",
568 self.base_url,
569 self.api_key,
570 urlencoding::encode(site_url)
571 );
572
573 let response = self.client.get(&url).send().await?;
574
575 self.handle_response(response).await
576 }
577
578 #[instrument(skip(self))]
579 pub async fn get_children_url_info(
580 &self,
581 site_url: &str,
582 url: &str,
583 page: u16,
584 filter_properties: &FilterProperties,
585 ) -> Result<Vec<UrlInfo>> {
586 let api_url = format!(
587 "{}/json/GetChildrenUrlInfo?apikey={}",
588 self.base_url, self.api_key
589 );
590
591 let body = serde_json::json!({
592 "siteUrl": site_url,
593 "url": url,
594 "page": page,
595 "filterProperties": filter_properties
596 });
597
598 let response = self
599 .client
600 .post(&api_url)
601 .header("Content-Type", "application/json; charset=utf-8")
602 .body(serde_json::to_string(&body)?)
603 .send()
604 .await?;
605
606 self.handle_response(response).await
607 }
608
609 #[instrument(skip(self))]
610 pub async fn get_children_url_traffic_info(
611 &self,
612 site_url: &str,
613 url: &str,
614 page: u16,
615 ) -> Result<Vec<UrlTrafficInfo>> {
616 let api_url = format!(
617 "{}/json/GetChildrenUrlTrafficInfo?apikey={}&siteUrl={}&url={}&page={}",
618 self.base_url,
619 self.api_key,
620 urlencoding::encode(site_url),
621 urlencoding::encode(url),
622 page
623 );
624
625 let response = self.client.get(&api_url).send().await?;
626
627 self.handle_response(response).await
628 }
629
630 #[instrument(skip(self))]
631 pub async fn get_country_region_settings(
632 &self,
633 site_url: &str,
634 ) -> Result<Vec<CountryRegionSettings>> {
635 let url = format!(
636 "{}/json/GetCountryRegionSettings?apikey={}&siteUrl={}",
637 self.base_url,
638 self.api_key,
639 urlencoding::encode(site_url)
640 );
641
642 let response = self.client.get(&url).send().await?;
643
644 self.handle_response(response).await
645 }
646
647 #[instrument(skip(self))]
648 pub async fn get_crawl_settings(&self, site_url: &str) -> Result<CrawlSettings> {
649 let url = format!(
650 "{}/json/GetCrawlSettings?apikey={}&siteUrl={}",
651 self.base_url,
652 self.api_key,
653 urlencoding::encode(site_url)
654 );
655
656 let response = self.client.get(&url).send().await?;
657
658 self.handle_response(response).await
659 }
660
661 #[instrument(skip(self))]
662 pub async fn get_deep_link(&self, site_url: &str, url: &str) -> Result<DeepLink> {
663 let api_url = format!(
664 "{}/json/GetDeepLink?apikey={}&siteUrl={}&url={}",
665 self.base_url,
666 self.api_key,
667 urlencoding::encode(site_url),
668 urlencoding::encode(url)
669 );
670
671 let response = self.client.get(&api_url).send().await?;
672
673 self.handle_response(response).await
674 }
675
676 #[instrument(skip(self))]
677 pub async fn get_deep_link_algo_urls(&self, site_url: &str) -> Result<Vec<DeepLinkAlgoUrl>> {
678 let url = format!(
679 "{}/json/GetDeepLinkAlgoUrls?apikey={}&siteUrl={}",
680 self.base_url,
681 self.api_key,
682 urlencoding::encode(site_url)
683 );
684
685 let response = self.client.get(&url).send().await?;
686
687 self.handle_response(response).await
688 }
689
690 #[instrument(skip(self))]
691 pub async fn get_deep_link_blocks(&self, site_url: &str) -> Result<Vec<DeepLinkBlock>> {
692 let url = format!(
693 "{}/json/GetDeepLinkBlocks?apikey={}&siteUrl={}",
694 self.base_url,
695 self.api_key,
696 urlencoding::encode(site_url)
697 );
698
699 let response = self.client.get(&url).send().await?;
700
701 self.handle_response(response).await
702 }
703
704 #[instrument(skip(self))]
705 pub async fn get_feed_details(&self, site_url: &str, feed_url: &str) -> Result<Vec<Feed>> {
706 let api_url = format!(
707 "{}/json/GetFeedDetails?apikey={}&siteUrl={}&feedUrl={}",
708 self.base_url,
709 self.api_key,
710 urlencoding::encode(site_url),
711 urlencoding::encode(feed_url)
712 );
713
714 let response = self.client.get(&api_url).send().await?;
715
716 self.handle_response(response).await
717 }
718
719 #[instrument(skip(self))]
720 pub async fn get_feeds(&self, site_url: &str) -> Result<Vec<Feed>> {
721 let url = format!(
722 "{}/json/GetFeeds?apikey={}&siteUrl={}",
723 self.base_url,
724 self.api_key,
725 urlencoding::encode(site_url)
726 );
727
728 let response = self.client.get(&url).send().await?;
729
730 self.handle_response(response).await
731 }
732
733 #[instrument(skip(self))]
734 pub async fn get_fetched_url_details(
735 &self,
736 site_url: &str,
737 url: &str,
738 ) -> Result<FetchedUrlDetails> {
739 let api_url = format!(
740 "{}/json/GetFetchedUrlDetails?apikey={}&siteUrl={}&url={}",
741 self.base_url,
742 self.api_key,
743 urlencoding::encode(site_url),
744 urlencoding::encode(url)
745 );
746
747 let response = self.client.get(&api_url).send().await?;
748
749 self.handle_response(response).await
750 }
751
752 #[instrument(skip(self))]
753 pub async fn get_fetched_urls(&self, site_url: &str) -> Result<Vec<FetchedUrl>> {
754 let url = format!(
755 "{}/json/GetFetchedUrls?apikey={}&siteUrl={}",
756 self.base_url,
757 self.api_key,
758 urlencoding::encode(site_url)
759 );
760
761 let response = self.client.get(&url).send().await?;
762
763 self.handle_response(response).await
764 }
765
766 #[instrument(skip(self))]
767 pub async fn get_keyword(
768 &self,
769 query: &str,
770 country: &str,
771 language: &str,
772 start_date: chrono::DateTime<chrono::Utc>,
773 end_date: chrono::DateTime<chrono::Utc>,
774 ) -> Result<Vec<Keyword>> {
775 let api_url = format!(
776 "{}/json/GetKeyword?apikey={}&q={}&country={}&language={}&startDate={}&endDate={}",
777 self.base_url,
778 self.api_key,
779 urlencoding::encode(query),
780 urlencoding::encode(country),
781 urlencoding::encode(language),
782 start_date.format("%Y-%m-%dT%H:%M:%S"),
783 end_date.format("%Y-%m-%dT%H:%M:%S")
784 );
785
786 let response = self.client.get(&api_url).send().await?;
787
788 self.handle_response(response).await
789 }
790
791 #[instrument(skip(self))]
792 pub async fn get_keyword_stats(
793 &self,
794 query: &str,
795 country: &str,
796 language: &str,
797 ) -> Result<Vec<KeywordStats>> {
798 let api_url = format!(
799 "{}/json/GetKeywordStats?apikey={}&q={}&country={}&language={}",
800 self.base_url,
801 self.api_key,
802 urlencoding::encode(query),
803 urlencoding::encode(country),
804 urlencoding::encode(language)
805 );
806
807 let response = self.client.get(&api_url).send().await?;
808
809 self.handle_response(response).await
810 }
811
812 #[instrument(skip(self))]
813 pub async fn get_link_counts(&self, site_url: &str, page: i16) -> Result<LinkCounts> {
814 let url = format!(
815 "{}/json/GetLinkCounts?apikey={}&siteUrl={}&page={}",
816 self.base_url,
817 self.api_key,
818 urlencoding::encode(site_url),
819 page
820 );
821
822 let response = self.client.get(&url).send().await?;
823
824 self.handle_response(response).await
825 }
826
827 #[instrument(skip(self))]
828 pub async fn get_page_query_stats(
829 &self,
830 site_url: &str,
831 page_url: &str,
832 ) -> Result<Vec<QueryStats>> {
833 let api_url = format!(
834 "{}/json/GetPageQueryStats?apikey={}&siteUrl={}&page={}",
835 self.base_url,
836 self.api_key,
837 urlencoding::encode(site_url),
838 urlencoding::encode(page_url)
839 );
840
841 let response = self.client.get(&api_url).send().await?;
842
843 self.handle_response(response).await
844 }
845
846 #[instrument(skip(self))]
847 pub async fn get_query_page_detail_stats(
848 &self,
849 site_url: &str,
850 query: &str,
851 page_url: &str,
852 ) -> Result<Vec<DetailedQueryStats>> {
853 let api_url = format!(
854 "{}/json/GetQueryPageDetailStats?apikey={}&siteUrl={}&query={}&page={}",
855 self.base_url,
856 self.api_key,
857 urlencoding::encode(site_url),
858 urlencoding::encode(query),
859 urlencoding::encode(page_url)
860 );
861
862 let response = self.client.get(&api_url).send().await?;
863
864 self.handle_response(response).await
865 }
866
867 #[instrument(skip(self))]
868 pub async fn get_query_page_stats(
869 &self,
870 site_url: &str,
871 query: &str,
872 ) -> Result<Vec<QueryStats>> {
873 let api_url = format!(
874 "{}/json/GetQueryPageStats?apikey={}&siteUrl={}&query={}",
875 self.base_url,
876 self.api_key,
877 urlencoding::encode(site_url),
878 urlencoding::encode(query)
879 );
880
881 let response = self.client.get(&api_url).send().await?;
882
883 self.handle_response(response).await
884 }
885
886 #[instrument(skip(self))]
887 pub async fn get_query_stats(&self, site_url: &str) -> Result<Vec<QueryStats>> {
888 let url = format!(
889 "{}/json/GetQueryStats?apikey={}&siteUrl={}",
890 self.base_url,
891 self.api_key,
892 urlencoding::encode(site_url)
893 );
894
895 let response = self.client.get(&url).send().await?;
896
897 self.handle_response(response).await
898 }
899
900 #[instrument(skip(self))]
901 pub async fn get_query_traffic_stats(
902 &self,
903 site_url: &str,
904 query: &str,
905 ) -> Result<Vec<RankAndTrafficStats>> {
906 let api_url = format!(
907 "{}/json/GetQueryTrafficStats?apikey={}&siteUrl={}&query={}",
908 self.base_url,
909 self.api_key,
910 urlencoding::encode(site_url),
911 urlencoding::encode(query)
912 );
913
914 let response = self.client.get(&api_url).send().await?;
915
916 self.handle_response(response).await
917 }
918
919 #[instrument(skip(self))]
920 pub async fn get_rank_and_traffic_stats(
921 &self,
922 site_url: &str,
923 ) -> Result<Vec<RankAndTrafficStats>> {
924 let url = format!(
925 "{}/json/GetRankAndTrafficStats?apikey={}&siteUrl={}",
926 self.base_url,
927 self.api_key,
928 urlencoding::encode(site_url)
929 );
930
931 let response = self.client.get(&url).send().await?;
932
933 self.handle_response(response).await
934 }
935
936 #[instrument(skip(self))]
937 pub async fn get_site_moves(&self, site_url: &str) -> Result<Vec<SiteMove>> {
938 let url = format!(
939 "{}/json/GetSiteMoves?apikey={}&siteUrl={}",
940 self.base_url,
941 self.api_key,
942 urlencoding::encode(site_url)
943 );
944
945 let response = self.client.get(&url).send().await?;
946
947 self.handle_response(response).await
948 }
949
950 #[instrument(skip(self))]
951 pub async fn get_site_roles(
952 &self,
953 site_url: &str,
954 include_all_subdomains: bool,
955 ) -> Result<Vec<SiteRoles>> {
956 let url = format!(
957 "{}/json/GetSiteRoles?apikey={}&siteUrl={}&includeAllSubdomains={}",
958 self.base_url,
959 self.api_key,
960 urlencoding::encode(site_url),
961 include_all_subdomains
962 );
963
964 let response = self.client.get(&url).send().await?;
965
966 self.handle_response(response).await
967 }
968
969 #[instrument(skip(self))]
970 pub async fn get_url_info(&self, site_url: &str, url: &str) -> Result<UrlInfo> {
971 let api_url = format!(
972 "{}/json/GetUrlInfo?apikey={}&siteUrl={}&url={}",
973 self.base_url,
974 self.api_key,
975 urlencoding::encode(site_url),
976 urlencoding::encode(url)
977 );
978
979 let response = self.client.get(&api_url).send().await?;
980
981 self.handle_response(response).await
982 }
983
984 #[instrument(skip(self))]
985 pub async fn get_url_links(
986 &self,
987 site_url: &str,
988 link: &str,
989 page: i16,
990 ) -> Result<LinkDetails> {
991 let api_url = format!(
992 "{}/json/GetUrlLinks?apikey={}&siteUrl={}&link={}&page={}",
993 self.base_url,
994 self.api_key,
995 urlencoding::encode(site_url),
996 urlencoding::encode(link),
997 page
998 );
999
1000 let response = self.client.get(&api_url).send().await?;
1001
1002 self.handle_response(response).await
1003 }
1004
1005 #[instrument(skip(self))]
1006 pub async fn get_url_submission_quota(&self, site_url: &str) -> Result<UrlSubmissionQuota> {
1007 let url = format!(
1008 "{}/json/GetUrlSubmissionQuota?apikey={}&siteUrl={}",
1009 self.base_url,
1010 self.api_key,
1011 urlencoding::encode(site_url)
1012 );
1013
1014 let response = self.client.get(&url).send().await?;
1015
1016 self.handle_response(response).await
1017 }
1018
1019 #[instrument(skip(self))]
1020 pub async fn get_url_traffic_info(&self, site_url: &str, url: &str) -> Result<UrlTrafficInfo> {
1021 let api_url = format!(
1022 "{}/json/GetUrlTrafficInfo?apikey={}&siteUrl={}&url={}",
1023 self.base_url,
1024 self.api_key,
1025 urlencoding::encode(site_url),
1026 urlencoding::encode(url)
1027 );
1028
1029 let response = self.client.get(&api_url).send().await?;
1030
1031 self.handle_response(response).await
1032 }
1033
1034 #[instrument(skip(self))]
1035 pub async fn get_user_sites(&self) -> Result<Vec<Site>> {
1036 let url = format!(
1037 "{}/json/GetUserSites?apikey={}",
1038 self.base_url, self.api_key
1039 );
1040
1041 let response = self.client.get(&url).send().await?;
1042
1043 self.handle_response(response).await
1044 }
1045
1046 #[instrument(skip(self))]
1049 pub async fn remove_blocked_url(&self, site_url: &str, blocked_url: &BlockedUrl) -> Result<()> {
1050 let url = format!(
1051 "{}/json/RemoveBlockedUrl?apikey={}",
1052 self.base_url, self.api_key
1053 );
1054 let body = json!({
1055 "siteUrl": site_url,
1056 "blockedUrl": blocked_url
1057 });
1058
1059 let response = self
1060 .client
1061 .post(&url)
1062 .header("Content-Type", "application/json; charset=utf-8")
1063 .body(serde_json::to_string(&body)?)
1064 .send()
1065 .await?;
1066
1067 self.handle_void_response(response).await
1068 }
1069
1070 #[instrument(skip(self))]
1071 pub async fn remove_country_region_settings(
1072 &self,
1073 site_url: &str,
1074 settings: &CountryRegionSettings,
1075 ) -> Result<()> {
1076 let url = format!(
1077 "{}/json/RemoveCountryRegionSettings?apikey={}",
1078 self.base_url, self.api_key
1079 );
1080 let body = json!({
1081 "siteUrl": site_url,
1082 "settings": settings
1083 });
1084
1085 let response = self
1086 .client
1087 .post(&url)
1088 .header("Content-Type", "application/json; charset=utf-8")
1089 .body(serde_json::to_string(&body)?)
1090 .send()
1091 .await?;
1092
1093 self.handle_void_response(response).await
1094 }
1095
1096 #[instrument(skip(self))]
1097 pub async fn remove_deep_link_block(
1098 &self,
1099 site_url: &str,
1100 market: &str,
1101 search_url: &str,
1102 deep_link_url: &str,
1103 ) -> Result<()> {
1104 let url = format!(
1105 "{}/json/RemoveDeepLinkBlock?apikey={}",
1106 self.base_url, self.api_key
1107 );
1108 let body = json!({
1109 "siteUrl": site_url,
1110 "market": market,
1111 "searchUrl": search_url,
1112 "deepLinkUrl": deep_link_url
1113 });
1114
1115 let response = self
1116 .client
1117 .post(&url)
1118 .header("Content-Type", "application/json; charset=utf-8")
1119 .body(serde_json::to_string(&body)?)
1120 .send()
1121 .await?;
1122
1123 self.handle_void_response(response).await
1124 }
1125
1126 #[instrument(skip(self))]
1127 pub async fn remove_feed(&self, site_url: &str, feed_url: &str) -> Result<()> {
1128 let url = format!("{}/json/RemoveFeed?apikey={}", self.base_url, self.api_key);
1129 let body = json!({
1130 "siteUrl": site_url,
1131 "feedUrl": feed_url
1132 });
1133
1134 let response = self
1135 .client
1136 .post(&url)
1137 .header("Content-Type", "application/json; charset=utf-8")
1138 .body(serde_json::to_string(&body)?)
1139 .send()
1140 .await?;
1141
1142 self.handle_void_response(response).await
1143 }
1144
1145 #[instrument(skip(self))]
1146 pub async fn remove_query_parameter(
1147 &self,
1148 site_url: &str,
1149 query_parameter: &str,
1150 ) -> Result<()> {
1151 let url = format!(
1152 "{}/json/RemoveQueryParameter?apikey={}",
1153 self.base_url, self.api_key
1154 );
1155 let body = json!({
1156 "siteUrl": site_url,
1157 "queryParameter": query_parameter
1158 });
1159
1160 let response = self
1161 .client
1162 .post(&url)
1163 .header("Content-Type", "application/json; charset=utf-8")
1164 .body(serde_json::to_string(&body)?)
1165 .send()
1166 .await?;
1167
1168 self.handle_void_response(response).await
1169 }
1170
1171 #[instrument(skip(self))]
1172 pub async fn remove_site(&self, site_url: &str) -> Result<()> {
1173 let url = format!("{}/json/RemoveSite?apikey={}", self.base_url, self.api_key);
1174 let body = json!({
1175 "siteUrl": site_url
1176 });
1177
1178 let response = self
1179 .client
1180 .post(&url)
1181 .header("Content-Type", "application/json; charset=utf-8")
1182 .body(serde_json::to_string(&body)?)
1183 .send()
1184 .await?;
1185
1186 self.handle_void_response(response).await
1187 }
1188
1189 #[instrument(skip(self))]
1190 pub async fn remove_site_role(&self, site_url: &str, site_roles: &SiteRoles) -> Result<()> {
1191 let url = format!(
1192 "{}/json/RemoveSiteRole?apikey={}",
1193 self.base_url, self.api_key
1194 );
1195 let body = json!({
1196 "siteUrl": site_url,
1197 "siteRoles": site_roles
1198 });
1199
1200 let response = self
1201 .client
1202 .post(&url)
1203 .header("Content-Type", "application/json; charset=utf-8")
1204 .body(serde_json::to_string(&body)?)
1205 .send()
1206 .await?;
1207
1208 self.handle_void_response(response).await
1209 }
1210
1211 #[instrument(skip(self))]
1214 pub async fn save_crawl_settings(
1215 &self,
1216 site_url: &str,
1217 crawl_settings: &CrawlSettings,
1218 ) -> Result<()> {
1219 let url = format!(
1220 "{}/json/SaveCrawlSettings?apikey={}",
1221 self.base_url, self.api_key
1222 );
1223 let body = json!({
1224 "siteUrl": site_url,
1225 "crawlSettings": crawl_settings
1226 });
1227
1228 let response = self
1229 .client
1230 .post(&url)
1231 .header("Content-Type", "application/json; charset=utf-8")
1232 .body(serde_json::to_string(&body)?)
1233 .send()
1234 .await?;
1235
1236 self.handle_void_response(response).await
1237 }
1238
1239 #[instrument(skip(self))]
1240 pub async fn submit_feed(&self, site_url: &str, feed_url: &str) -> Result<()> {
1241 let url = format!("{}/json/SubmitFeed?apikey={}", self.base_url, self.api_key);
1242 let body = json!({
1243 "siteUrl": site_url,
1244 "feedUrl": feed_url
1245 });
1246
1247 let response = self
1248 .client
1249 .post(&url)
1250 .header("Content-Type", "application/json; charset=utf-8")
1251 .body(serde_json::to_string(&body)?)
1252 .send()
1253 .await?;
1254
1255 self.handle_void_response(response).await
1256 }
1257
1258 #[instrument(skip(self))]
1259 pub async fn submit_site_move(
1260 &self,
1261 site_url: &str,
1262 site_move_settings: &SiteMoveSettings,
1263 ) -> Result<()> {
1264 let url = format!(
1265 "{}/json/SubmitSiteMove?apikey={}",
1266 self.base_url, self.api_key
1267 );
1268 let body = json!({
1269 "siteUrl": site_url,
1270 "siteMoveSettings": site_move_settings
1271 });
1272
1273 let response = self
1274 .client
1275 .post(&url)
1276 .header("Content-Type", "application/json; charset=utf-8")
1277 .body(serde_json::to_string(&body)?)
1278 .send()
1279 .await?;
1280
1281 self.handle_void_response(response).await
1282 }
1283
1284 #[instrument(skip(self))]
1285 pub async fn update_deep_link(
1286 &self,
1287 site_url: &str,
1288 market: &str,
1289 search_url: &str,
1290 deep_link_weight: &DeepLinkWeight,
1291 ) -> Result<()> {
1292 let url = format!(
1293 "{}/json/UpdateDeepLink?apikey={}",
1294 self.base_url, self.api_key
1295 );
1296 let body = json!({
1297 "siteUrl": site_url,
1298 "market": market,
1299 "searchUrl": search_url,
1300 "deepLinkWeight": deep_link_weight
1301 });
1302
1303 let response = self
1304 .client
1305 .post(&url)
1306 .header("Content-Type", "application/json; charset=utf-8")
1307 .body(serde_json::to_string(&body)?)
1308 .send()
1309 .await?;
1310
1311 self.handle_void_response(response).await
1312 }
1313}