1pub mod account;
2pub mod api_credits;
3pub mod billing;
4pub mod bulk;
5pub mod chains;
6pub mod endpoint_metrics;
7pub mod endpoint_rate_limits;
8pub mod endpoint_security;
9pub mod endpoint_urls;
10pub mod endpoints;
11pub mod logs;
12pub mod tags;
13pub mod teams;
14pub mod tooling_access;
15pub mod usage;
16
17pub use account::{AccountInfo, AccountInfoResponse, AccountSubscription};
18pub use api_credits::{ApiCredit, GetApiCreditsResponse};
19pub use billing::{
20 Invoice, InvoiceLine, ListInvoicesData, ListInvoicesResponse, ListPaymentsData,
21 ListPaymentsResponse, Payment,
22};
23pub use bulk::{
24 BulkAddTagData, BulkAddTagRequest, BulkAddTagResponse, BulkOperationResult, BulkRemoveTagData,
25 BulkRemoveTagRequest, BulkRemoveTagResponse, BulkTag, BulkUpdateEndpointStatusData,
26 BulkUpdateEndpointStatusRequest, BulkUpdateEndpointStatusResponse,
27};
28pub use chains::{Chain, ChainNetwork, ListChainsResponse};
29pub use endpoint_metrics::{
30 EndpointMetric, GetAccountMetricsRequest, GetAccountMetricsResponse, GetEndpointMetricsRequest,
31 GetEndpointMetricsResponse,
32};
33pub use endpoint_rate_limits::{
34 CreateMethodRateLimitRequest, CreateMethodRateLimitResponse, GetMethodRateLimitsData,
35 GetMethodRateLimitsResponse, GetRateLimitsData, GetRateLimitsResponse, MethodRateLimiter,
36 RateLimitEntry, RateLimitSettings, UpdateMethodRateLimitRequest, UpdateMethodRateLimitResponse,
37 UpdateRateLimitsRequest,
38};
39pub use endpoint_security::{
40 CreateDomainMaskRequest, CreateIpRequest, CreateJwtRequest,
41 CreateOrUpdateIpCustomHeaderRequest, CreateOrUpdateIpCustomHeaderResponse,
42 CreateReferrerRequest, CreateRequestFilterData, CreateRequestFilterRequest,
43 CreateRequestFilterResponse, DeleteBoolResponse, GetSecurityOptionsResponse,
44 IpCustomHeaderData, SecurityOption, SecurityOptionsUpdate, UpdateRequestFilterRequest,
45 UpdateSecurityOptionsRequest, UpdateSecurityOptionsResponse,
46};
47pub use endpoint_urls::{EndpointUrl, GetEndpointUrlsData, GetEndpointUrlsResponse};
48pub use endpoints::{
49 CreateEndpointRequest, CreateEndpointResponse, CreateTagRequest, Endpoint, EndpointDomainMask,
50 EndpointIp, EndpointIpCustomHeaderOption, EndpointJwt, EndpointRateLimits, EndpointReferrer,
51 EndpointRequestFilter, EndpointSecurity, EndpointSecurityOptions, EndpointTag, EndpointToken,
52 GetEndpointSecurityResponse, GetEndpointsRequest, GetEndpointsResponse, Pagination,
53 ShowEndpointResponse, SingleEndpoint, UpdateEndpointRequest, UpdateEndpointStatusRequest,
54 UpdateEndpointStatusResponse,
55};
56pub use logs::{
57 EndpointLog, GetEndpointLogsRequest, GetEndpointLogsResponse, GetLogDetailsResponse, LogDetails,
58};
59pub use tags::{
60 AccountTag, DeleteAccountTagData, DeleteAccountTagResponse, ListTagsData, ListTagsResponse,
61 RenameTagRequest, RenameTagResponse,
62};
63pub use teams::{
64 CreateTeamData, CreateTeamRequest, CreateTeamResponse, DeleteTeamData, DeleteTeamResponse,
65 GetTeamResponse, InviteTeamMemberRequest, InviteTeamMemberResponse, ListTeamEndpointsResponse,
66 ListTeamsResponse, RemoveTeamMemberRequest, RemoveTeamMemberResponse, ResendTeamInviteResponse,
67 TeamDetail, TeamEndpoint, TeamMessageData, TeamSummary, TeamUser, UpdateTeamEndpointsData,
68 UpdateTeamEndpointsRequest, UpdateTeamEndpointsResponse,
69};
70pub use tooling_access::ToolingAccessStatus;
71
72pub use usage::{
73 ChainUsage, EndpointUsage, GetUsageByChainResponse, GetUsageByEndpointResponse,
74 GetUsageByMethodResponse, GetUsageByTagResponse, GetUsageRequest, GetUsageResponse,
75 MethodUsage, TagUsage, UsageByChainData, UsageByEndpointData, UsageByMethodData,
76 UsageByTagData, UsageData,
77};
78
79use crate::{config::AdminConfig, errors::SdkError, SdkConfig};
80
81const ADMIN_BASE_URL: &str = "https://api.quicknode.com/v0/";
82
83pub(crate) struct ResolvedAdminConfig {
84 pub(crate) base_url: reqwest::Url,
85}
86
87impl ResolvedAdminConfig {
88 pub(crate) fn from_config(config: Option<&AdminConfig>) -> Result<Self, SdkError> {
89 let url_str = config
90 .and_then(|a| a.base_url.as_deref())
91 .unwrap_or(ADMIN_BASE_URL);
92 let mut base_url = reqwest::Url::parse(url_str)?;
93 if !base_url.path().ends_with('/') {
94 base_url.set_path(&format!("{}/", base_url.path()));
95 }
96 Ok(Self { base_url })
97 }
98}
99
100#[derive(Debug, Clone)]
103pub struct AdminApiClient {
104 config: SdkConfig,
105}
106
107impl AdminApiClient {
108 pub fn new(config: SdkConfig) -> Self {
109 Self { config }
110 }
111
112 pub async fn get_endpoints(
118 &self,
119 params: &GetEndpointsRequest,
120 ) -> Result<GetEndpointsResponse, SdkError> {
121 let url = self.config.admin().base_url.join("endpoints")?;
122 let query = endpoints_query(params);
126 let resp = self
127 .config
128 .http_client()
129 .get(url)
130 .query(&query)
131 .send()
132 .await
133 .map_err(SdkError::Http)?;
134
135 let status = resp.status();
136 let body = resp.text().await.map_err(SdkError::Http)?;
137
138 if !status.is_success() {
139 return Err(SdkError::Api { status, body });
140 }
141 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
142 }
143
144 pub async fn create_endpoint(
149 &self,
150 params: &CreateEndpointRequest,
151 ) -> Result<CreateEndpointResponse, SdkError> {
152 let url = self.config.admin().base_url.join("endpoints")?;
153 let resp = self
154 .config
155 .http_client()
156 .post(url)
157 .json(params)
158 .send()
159 .await
160 .map_err(SdkError::Http)?;
161
162 let status = resp.status();
163 let body = resp.text().await.map_err(SdkError::Http)?;
164
165 if !status.is_success() {
166 return Err(SdkError::Api { status, body });
167 }
168 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
169 }
170
171 pub async fn show_endpoint(&self, id: &str) -> Result<ShowEndpointResponse, SdkError> {
173 let url = self
174 .config
175 .admin()
176 .base_url
177 .join(&format!("endpoints/{}", id))?;
178 let resp = self
179 .config
180 .http_client()
181 .get(url)
182 .send()
183 .await
184 .map_err(SdkError::Http)?;
185
186 let status = resp.status();
187 let body = resp.text().await.map_err(SdkError::Http)?;
188
189 if !status.is_success() {
190 return Err(SdkError::Api { status, body });
191 }
192 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
193 }
194
195 pub async fn update_endpoint(
198 &self,
199 id: &str,
200 params: &UpdateEndpointRequest,
201 ) -> Result<(), SdkError> {
202 let url = self
203 .config
204 .admin()
205 .base_url
206 .join(&format!("endpoints/{}", id))?;
207 let resp = self
208 .config
209 .http_client()
210 .patch(url)
211 .json(params)
212 .send()
213 .await
214 .map_err(SdkError::Http)?;
215
216 let status = resp.status();
217 let body = resp.text().await.map_err(SdkError::Http)?;
218
219 if !status.is_success() {
220 return Err(SdkError::Api { status, body });
221 }
222 Ok(())
223 }
224
225 pub async fn archive_endpoint(&self, id: &str) -> Result<(), SdkError> {
228 let url = self
229 .config
230 .admin()
231 .base_url
232 .join(&format!("endpoints/{}", id))?;
233 let resp = self
234 .config
235 .http_client()
236 .delete(url)
237 .send()
238 .await
239 .map_err(SdkError::Http)?;
240
241 let status = resp.status();
242 let body = resp.text().await.map_err(SdkError::Http)?;
243
244 if !status.is_success() {
245 return Err(SdkError::Api { status, body });
246 }
247 Ok(())
248 }
249
250 pub async fn update_endpoint_status(
253 &self,
254 id: &str,
255 params: &UpdateEndpointStatusRequest,
256 ) -> Result<UpdateEndpointStatusResponse, SdkError> {
257 let url = self
258 .config
259 .admin()
260 .base_url
261 .join(&format!("endpoints/{}/status", id))?;
262 let resp = self
263 .config
264 .http_client()
265 .patch(url)
266 .json(params)
267 .send()
268 .await
269 .map_err(SdkError::Http)?;
270
271 let status = resp.status();
272 let body = resp.text().await.map_err(SdkError::Http)?;
273
274 if !status.is_success() {
275 return Err(SdkError::Api { status, body });
276 }
277 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
278 }
279
280 pub async fn create_tag(&self, id: &str, params: &CreateTagRequest) -> Result<(), SdkError> {
283 let url = self
284 .config
285 .admin()
286 .base_url
287 .join(&format!("endpoints/{}/tags", id))?;
288 let resp = self
289 .config
290 .http_client()
291 .post(url)
292 .json(params)
293 .send()
294 .await
295 .map_err(SdkError::Http)?;
296
297 let status = resp.status();
298 let body = resp.text().await.map_err(SdkError::Http)?;
299
300 if !status.is_success() {
301 return Err(SdkError::Api { status, body });
302 }
303 Ok(())
304 }
305
306 pub async fn delete_tag(&self, id: &str, tag_id: &str) -> Result<(), SdkError> {
308 let url = self
309 .config
310 .admin()
311 .base_url
312 .join(&format!("endpoints/{}/tags/{}", id, tag_id))?;
313 let resp = self
314 .config
315 .http_client()
316 .delete(url)
317 .send()
318 .await
319 .map_err(SdkError::Http)?;
320
321 let status = resp.status();
322 let body = resp.text().await.map_err(SdkError::Http)?;
323
324 if !status.is_success() {
325 return Err(SdkError::Api { status, body });
326 }
327 Ok(())
328 }
329
330 pub async fn list_teams(&self) -> Result<ListTeamsResponse, SdkError> {
333 let url = self.config.admin().base_url.join("teams")?;
334 let resp = self
335 .config
336 .http_client()
337 .get(url)
338 .send()
339 .await
340 .map_err(SdkError::Http)?;
341
342 let status = resp.status();
343 let body = resp.text().await.map_err(SdkError::Http)?;
344
345 if !status.is_success() {
346 return Err(SdkError::Api { status, body });
347 }
348 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
349 }
350
351 pub async fn create_team(
354 &self,
355 params: &CreateTeamRequest,
356 ) -> Result<CreateTeamResponse, SdkError> {
357 let url = self.config.admin().base_url.join("teams")?;
358 let resp = self
359 .config
360 .http_client()
361 .post(url)
362 .json(params)
363 .send()
364 .await
365 .map_err(SdkError::Http)?;
366
367 let status = resp.status();
368 let body = resp.text().await.map_err(SdkError::Http)?;
369
370 if !status.is_success() {
371 return Err(SdkError::Api { status, body });
372 }
373 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
374 }
375
376 pub async fn get_team(&self, id: i64) -> Result<GetTeamResponse, SdkError> {
379 let url = self
380 .config
381 .admin()
382 .base_url
383 .join(&format!("teams/{}", id))?;
384 let resp = self
385 .config
386 .http_client()
387 .get(url)
388 .send()
389 .await
390 .map_err(SdkError::Http)?;
391
392 let status = resp.status();
393 let body = resp.text().await.map_err(SdkError::Http)?;
394
395 if !status.is_success() {
396 return Err(SdkError::Api { status, body });
397 }
398 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
399 }
400
401 pub async fn delete_team(&self, id: i64) -> Result<DeleteTeamResponse, SdkError> {
404 let url = self
405 .config
406 .admin()
407 .base_url
408 .join(&format!("teams/{}", id))?;
409 let resp = self
410 .config
411 .http_client()
412 .delete(url)
413 .send()
414 .await
415 .map_err(SdkError::Http)?;
416
417 let status = resp.status();
418 let body = resp.text().await.map_err(SdkError::Http)?;
419
420 if !status.is_success() {
421 return Err(SdkError::Api { status, body });
422 }
423 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
424 }
425
426 pub async fn list_team_endpoints(
429 &self,
430 id: i64,
431 ) -> Result<ListTeamEndpointsResponse, SdkError> {
432 let url = self
433 .config
434 .admin()
435 .base_url
436 .join(&format!("teams/{}/endpoints", id))?;
437 let resp = self
438 .config
439 .http_client()
440 .get(url)
441 .send()
442 .await
443 .map_err(SdkError::Http)?;
444
445 let status = resp.status();
446 let body = resp.text().await.map_err(SdkError::Http)?;
447
448 if !status.is_success() {
449 return Err(SdkError::Api { status, body });
450 }
451 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
452 }
453
454 pub async fn update_team_endpoints(
458 &self,
459 id: i64,
460 params: &UpdateTeamEndpointsRequest,
461 ) -> Result<UpdateTeamEndpointsResponse, SdkError> {
462 let url = self
463 .config
464 .admin()
465 .base_url
466 .join(&format!("teams/{}/endpoints", id))?;
467 let resp = self
468 .config
469 .http_client()
470 .patch(url)
471 .json(params)
472 .send()
473 .await
474 .map_err(SdkError::Http)?;
475
476 let status = resp.status();
477 let body = resp.text().await.map_err(SdkError::Http)?;
478
479 if !status.is_success() {
480 return Err(SdkError::Api { status, body });
481 }
482 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
483 }
484
485 pub async fn invite_team_member(
489 &self,
490 id: i64,
491 params: &InviteTeamMemberRequest,
492 ) -> Result<InviteTeamMemberResponse, SdkError> {
493 let url = self
494 .config
495 .admin()
496 .base_url
497 .join(&format!("teams/{}/members", id))?;
498 let resp = self
499 .config
500 .http_client()
501 .post(url)
502 .json(params)
503 .send()
504 .await
505 .map_err(SdkError::Http)?;
506
507 let status = resp.status();
508 let body = resp.text().await.map_err(SdkError::Http)?;
509
510 if !status.is_success() {
511 return Err(SdkError::Api { status, body });
512 }
513 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
514 }
515
516 pub async fn remove_team_member(
518 &self,
519 id: i64,
520 user_id: i64,
521 params: &RemoveTeamMemberRequest,
522 ) -> Result<RemoveTeamMemberResponse, SdkError> {
523 let url = self
524 .config
525 .admin()
526 .base_url
527 .join(&format!("teams/{}/members/{}", id, user_id))?;
528 let resp = self
529 .config
530 .http_client()
531 .delete(url)
532 .json(params)
533 .send()
534 .await
535 .map_err(SdkError::Http)?;
536
537 let status = resp.status();
538 let body = resp.text().await.map_err(SdkError::Http)?;
539
540 if !status.is_success() {
541 return Err(SdkError::Api { status, body });
542 }
543 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
544 }
545
546 pub async fn resend_team_invite(
549 &self,
550 id: i64,
551 user_id: i64,
552 ) -> Result<ResendTeamInviteResponse, SdkError> {
553 let url = self
554 .config
555 .admin()
556 .base_url
557 .join(&format!("teams/{}/members/{}/resend_invite", id, user_id))?;
558 let resp = self
559 .config
560 .http_client()
561 .post(url)
562 .send()
563 .await
564 .map_err(SdkError::Http)?;
565
566 let status = resp.status();
567 let body = resp.text().await.map_err(SdkError::Http)?;
568
569 if !status.is_success() {
570 return Err(SdkError::Api { status, body });
571 }
572 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
573 }
574
575 pub async fn get_usage(&self, params: &GetUsageRequest) -> Result<GetUsageResponse, SdkError> {
579 let url = self.config.admin().base_url.join("usage/rpc")?;
580 let resp = self
581 .config
582 .http_client()
583 .get(url)
584 .query(params)
585 .send()
586 .await
587 .map_err(SdkError::Http)?;
588
589 let status = resp.status();
590 let body = resp.text().await.map_err(SdkError::Http)?;
591
592 if !status.is_success() {
593 return Err(SdkError::Api { status, body });
594 }
595 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
596 }
597
598 pub async fn get_usage_by_endpoint(
602 &self,
603 params: &GetUsageRequest,
604 ) -> Result<GetUsageByEndpointResponse, SdkError> {
605 let url = self.config.admin().base_url.join("usage/rpc/by-endpoint")?;
606 let resp = self
607 .config
608 .http_client()
609 .get(url)
610 .query(params)
611 .send()
612 .await
613 .map_err(SdkError::Http)?;
614
615 let status = resp.status();
616 let body = resp.text().await.map_err(SdkError::Http)?;
617
618 if !status.is_success() {
619 return Err(SdkError::Api { status, body });
620 }
621 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
622 }
623
624 pub async fn get_usage_by_method(
628 &self,
629 params: &GetUsageRequest,
630 ) -> Result<GetUsageByMethodResponse, SdkError> {
631 let url = self.config.admin().base_url.join("usage/rpc/by-method")?;
632 let resp = self
633 .config
634 .http_client()
635 .get(url)
636 .query(params)
637 .send()
638 .await
639 .map_err(SdkError::Http)?;
640
641 let status = resp.status();
642 let body = resp.text().await.map_err(SdkError::Http)?;
643
644 if !status.is_success() {
645 return Err(SdkError::Api { status, body });
646 }
647 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
648 }
649
650 pub async fn get_usage_by_chain(
653 &self,
654 params: &GetUsageRequest,
655 ) -> Result<GetUsageByChainResponse, SdkError> {
656 let url = self.config.admin().base_url.join("usage/rpc/by-chain")?;
657 let resp = self
658 .config
659 .http_client()
660 .get(url)
661 .query(params)
662 .send()
663 .await
664 .map_err(SdkError::Http)?;
665
666 let status = resp.status();
667 let body = resp.text().await.map_err(SdkError::Http)?;
668
669 if !status.is_success() {
670 return Err(SdkError::Api { status, body });
671 }
672 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
673 }
674
675 pub async fn get_endpoint_logs(
680 &self,
681 id: &str,
682 params: &GetEndpointLogsRequest,
683 ) -> Result<GetEndpointLogsResponse, SdkError> {
684 let url = self
685 .config
686 .admin()
687 .base_url
688 .join(&format!("endpoints/{}/logs", id))?;
689 let resp = self
690 .config
691 .http_client()
692 .get(url)
693 .query(params)
694 .send()
695 .await
696 .map_err(SdkError::Http)?;
697
698 let status = resp.status();
699 let body = resp.text().await.map_err(SdkError::Http)?;
700
701 if !status.is_success() {
702 return Err(SdkError::Api { status, body });
703 }
704 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
705 }
706
707 pub async fn get_log_details(
711 &self,
712 id: &str,
713 request_id: &str,
714 ) -> Result<GetLogDetailsResponse, SdkError> {
715 let url = self
716 .config
717 .admin()
718 .base_url
719 .join(&format!("endpoints/{}/log_details", id))?;
720 let resp = self
721 .config
722 .http_client()
723 .get(url)
724 .query(&[("request_id", request_id)])
725 .send()
726 .await
727 .map_err(SdkError::Http)?;
728
729 let status = resp.status();
730 let body = resp.text().await.map_err(SdkError::Http)?;
731
732 if !status.is_success() {
733 return Err(SdkError::Api { status, body });
734 }
735 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
736 }
737
738 pub async fn get_security_options(
741 &self,
742 id: &str,
743 ) -> Result<GetSecurityOptionsResponse, SdkError> {
744 let url = self
745 .config
746 .admin()
747 .base_url
748 .join(&format!("endpoints/{}/security_options", id))?;
749 let resp = self
750 .config
751 .http_client()
752 .get(url)
753 .send()
754 .await
755 .map_err(SdkError::Http)?;
756
757 let status = resp.status();
758 let body = resp.text().await.map_err(SdkError::Http)?;
759
760 if !status.is_success() {
761 return Err(SdkError::Api { status, body });
762 }
763 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
764 }
765
766 pub async fn update_security_options(
771 &self,
772 id: &str,
773 params: &UpdateSecurityOptionsRequest,
774 ) -> Result<UpdateSecurityOptionsResponse, SdkError> {
775 let url = self
776 .config
777 .admin()
778 .base_url
779 .join(&format!("endpoints/{}/security_options", id))?;
780 let resp = self
781 .config
782 .http_client()
783 .patch(url)
784 .json(params)
785 .send()
786 .await
787 .map_err(SdkError::Http)?;
788
789 let status = resp.status();
790 let body = resp.text().await.map_err(SdkError::Http)?;
791
792 if !status.is_success() {
793 return Err(SdkError::Api { status, body });
794 }
795 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
796 }
797
798 pub async fn create_token(&self, id: &str) -> Result<(), SdkError> {
800 let url = self
801 .config
802 .admin()
803 .base_url
804 .join(&format!("endpoints/{}/security/tokens", id))?;
805 let resp = self
806 .config
807 .http_client()
808 .post(url)
809 .send()
810 .await
811 .map_err(SdkError::Http)?;
812
813 let status = resp.status();
814 let body = resp.text().await.map_err(SdkError::Http)?;
815
816 if !status.is_success() {
817 return Err(SdkError::Api { status, body });
818 }
819 Ok(())
820 }
821
822 pub async fn delete_token(
824 &self,
825 id: &str,
826 token_id: &str,
827 ) -> Result<DeleteBoolResponse, SdkError> {
828 let url = self
829 .config
830 .admin()
831 .base_url
832 .join(&format!("endpoints/{}/security/tokens/{}", id, token_id))?;
833 let resp = self
834 .config
835 .http_client()
836 .delete(url)
837 .send()
838 .await
839 .map_err(SdkError::Http)?;
840
841 let status = resp.status();
842 let body = resp.text().await.map_err(SdkError::Http)?;
843
844 if !status.is_success() {
845 return Err(SdkError::Api { status, body });
846 }
847 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
848 }
849
850 pub async fn create_referrer(
853 &self,
854 id: &str,
855 params: &CreateReferrerRequest,
856 ) -> Result<(), SdkError> {
857 let url = self
858 .config
859 .admin()
860 .base_url
861 .join(&format!("endpoints/{}/security/referrers", id))?;
862 let resp = self
863 .config
864 .http_client()
865 .post(url)
866 .json(params)
867 .send()
868 .await
869 .map_err(SdkError::Http)?;
870
871 let status = resp.status();
872 let body = resp.text().await.map_err(SdkError::Http)?;
873
874 if !status.is_success() {
875 return Err(SdkError::Api { status, body });
876 }
877 Ok(())
878 }
879
880 pub async fn delete_referrer(
882 &self,
883 id: &str,
884 referrer_id: &str,
885 ) -> Result<DeleteBoolResponse, SdkError> {
886 let url = self.config.admin().base_url.join(&format!(
887 "endpoints/{}/security/referrers/{}",
888 id, referrer_id
889 ))?;
890 let resp = self
891 .config
892 .http_client()
893 .delete(url)
894 .send()
895 .await
896 .map_err(SdkError::Http)?;
897
898 let status = resp.status();
899 let body = resp.text().await.map_err(SdkError::Http)?;
900
901 if !status.is_success() {
902 return Err(SdkError::Api { status, body });
903 }
904 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
905 }
906
907 pub async fn create_ip(&self, id: &str, params: &CreateIpRequest) -> Result<(), SdkError> {
909 let url = self
910 .config
911 .admin()
912 .base_url
913 .join(&format!("endpoints/{}/security/ips", id))?;
914 let resp = self
915 .config
916 .http_client()
917 .post(url)
918 .json(params)
919 .send()
920 .await
921 .map_err(SdkError::Http)?;
922
923 let status = resp.status();
924 let body = resp.text().await.map_err(SdkError::Http)?;
925
926 if !status.is_success() {
927 return Err(SdkError::Api { status, body });
928 }
929 Ok(())
930 }
931
932 pub async fn delete_ip(&self, id: &str, ip_id: &str) -> Result<DeleteBoolResponse, SdkError> {
934 let url = self
935 .config
936 .admin()
937 .base_url
938 .join(&format!("endpoints/{}/security/ips/{}", id, ip_id))?;
939 let resp = self
940 .config
941 .http_client()
942 .delete(url)
943 .send()
944 .await
945 .map_err(SdkError::Http)?;
946
947 let status = resp.status();
948 let body = resp.text().await.map_err(SdkError::Http)?;
949
950 if !status.is_success() {
951 return Err(SdkError::Api { status, body });
952 }
953 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
954 }
955
956 pub async fn create_domain_mask(
960 &self,
961 id: &str,
962 params: &CreateDomainMaskRequest,
963 ) -> Result<(), SdkError> {
964 let url = self
965 .config
966 .admin()
967 .base_url
968 .join(&format!("endpoints/{}/security/domain_masks", id))?;
969 let resp = self
970 .config
971 .http_client()
972 .post(url)
973 .json(params)
974 .send()
975 .await
976 .map_err(SdkError::Http)?;
977
978 let status = resp.status();
979 let body = resp.text().await.map_err(SdkError::Http)?;
980
981 if !status.is_success() {
982 return Err(SdkError::Api { status, body });
983 }
984 Ok(())
985 }
986
987 pub async fn delete_domain_mask(
989 &self,
990 id: &str,
991 domain_mask_id: &str,
992 ) -> Result<DeleteBoolResponse, SdkError> {
993 let url = self.config.admin().base_url.join(&format!(
994 "endpoints/{}/security/domain_masks/{}",
995 id, domain_mask_id
996 ))?;
997 let resp = self
998 .config
999 .http_client()
1000 .delete(url)
1001 .send()
1002 .await
1003 .map_err(SdkError::Http)?;
1004
1005 let status = resp.status();
1006 let body = resp.text().await.map_err(SdkError::Http)?;
1007
1008 if !status.is_success() {
1009 return Err(SdkError::Api { status, body });
1010 }
1011 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1012 }
1013
1014 pub async fn create_jwt(&self, id: &str, params: &CreateJwtRequest) -> Result<(), SdkError> {
1017 let url = self
1018 .config
1019 .admin()
1020 .base_url
1021 .join(&format!("endpoints/{}/security/jwts", id))?;
1022 let resp = self
1023 .config
1024 .http_client()
1025 .post(url)
1026 .json(params)
1027 .send()
1028 .await
1029 .map_err(SdkError::Http)?;
1030
1031 let status = resp.status();
1032 let body = resp.text().await.map_err(SdkError::Http)?;
1033
1034 if !status.is_success() {
1035 return Err(SdkError::Api { status, body });
1036 }
1037 Ok(())
1038 }
1039
1040 pub async fn delete_jwt(&self, id: &str, jwt_id: &str) -> Result<(), SdkError> {
1043 let url = self
1044 .config
1045 .admin()
1046 .base_url
1047 .join(&format!("endpoints/{}/security/jwts/{}", id, jwt_id))?;
1048 let resp = self
1049 .config
1050 .http_client()
1051 .delete(url)
1052 .send()
1053 .await
1054 .map_err(SdkError::Http)?;
1055
1056 let status = resp.status();
1057 let body = resp.text().await.map_err(SdkError::Http)?;
1058
1059 if !status.is_success() {
1060 return Err(SdkError::Api { status, body });
1061 }
1062 Ok(())
1063 }
1064
1065 pub async fn create_request_filter(
1069 &self,
1070 id: &str,
1071 params: &CreateRequestFilterRequest,
1072 ) -> Result<CreateRequestFilterResponse, SdkError> {
1073 let url = self
1074 .config
1075 .admin()
1076 .base_url
1077 .join(&format!("endpoints/{}/security/request_filters", id))?;
1078 let resp = self
1079 .config
1080 .http_client()
1081 .post(url)
1082 .json(params)
1083 .send()
1084 .await
1085 .map_err(SdkError::Http)?;
1086
1087 let status = resp.status();
1088 let body = resp.text().await.map_err(SdkError::Http)?;
1089
1090 if !status.is_success() {
1091 return Err(SdkError::Api { status, body });
1092 }
1093 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1094 }
1095
1096 pub async fn update_request_filter(
1099 &self,
1100 id: &str,
1101 request_filter_id: &str,
1102 params: &UpdateRequestFilterRequest,
1103 ) -> Result<(), SdkError> {
1104 let url = self.config.admin().base_url.join(&format!(
1105 "endpoints/{}/security/request_filters/{}",
1106 id, request_filter_id
1107 ))?;
1108 let resp = self
1109 .config
1110 .http_client()
1111 .put(url)
1112 .json(params)
1113 .send()
1114 .await
1115 .map_err(SdkError::Http)?;
1116
1117 let status = resp.status();
1118 let body = resp.text().await.map_err(SdkError::Http)?;
1119
1120 if !status.is_success() {
1121 return Err(SdkError::Api { status, body });
1122 }
1123 Ok(())
1124 }
1125
1126 pub async fn delete_request_filter(
1129 &self,
1130 id: &str,
1131 request_filter_id: &str,
1132 ) -> Result<(), SdkError> {
1133 let url = self.config.admin().base_url.join(&format!(
1134 "endpoints/{}/security/request_filters/{}",
1135 id, request_filter_id
1136 ))?;
1137 let resp = self
1138 .config
1139 .http_client()
1140 .delete(url)
1141 .send()
1142 .await
1143 .map_err(SdkError::Http)?;
1144
1145 let status = resp.status();
1146 let body = resp.text().await.map_err(SdkError::Http)?;
1147
1148 if !status.is_success() {
1149 return Err(SdkError::Api { status, body });
1150 }
1151 Ok(())
1152 }
1153
1154 pub async fn enable_multichain(&self, id: &str) -> Result<(), SdkError> {
1157 let url = self
1158 .config
1159 .admin()
1160 .base_url
1161 .join(&format!("endpoints/{}/enable_multichain", id))?;
1162 let resp = self
1163 .config
1164 .http_client()
1165 .post(url)
1166 .send()
1167 .await
1168 .map_err(SdkError::Http)?;
1169
1170 let status = resp.status();
1171 let body = resp.text().await.map_err(SdkError::Http)?;
1172
1173 if !status.is_success() {
1174 return Err(SdkError::Api { status, body });
1175 }
1176 Ok(())
1177 }
1178
1179 pub async fn disable_multichain(&self, id: &str) -> Result<(), SdkError> {
1181 let url = self
1182 .config
1183 .admin()
1184 .base_url
1185 .join(&format!("endpoints/{}/disable_multichain", id))?;
1186 let resp = self
1187 .config
1188 .http_client()
1189 .post(url)
1190 .send()
1191 .await
1192 .map_err(SdkError::Http)?;
1193
1194 let status = resp.status();
1195 let body = resp.text().await.map_err(SdkError::Http)?;
1196
1197 if !status.is_success() {
1198 return Err(SdkError::Api { status, body });
1199 }
1200 Ok(())
1201 }
1202
1203 pub async fn create_or_update_ip_custom_header(
1208 &self,
1209 id: &str,
1210 params: &CreateOrUpdateIpCustomHeaderRequest,
1211 ) -> Result<CreateOrUpdateIpCustomHeaderResponse, SdkError> {
1212 let url = self
1213 .config
1214 .admin()
1215 .base_url
1216 .join(&format!("endpoints/{}/ip_custom_header", id))?;
1217 let resp = self
1218 .config
1219 .http_client()
1220 .patch(url)
1221 .json(params)
1222 .send()
1223 .await
1224 .map_err(SdkError::Http)?;
1225
1226 let status = resp.status();
1227 let body = resp.text().await.map_err(SdkError::Http)?;
1228
1229 if !status.is_success() {
1230 return Err(SdkError::Api { status, body });
1231 }
1232 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1233 }
1234
1235 pub async fn delete_ip_custom_header(&self, id: &str) -> Result<DeleteBoolResponse, SdkError> {
1237 let url = self
1238 .config
1239 .admin()
1240 .base_url
1241 .join(&format!("endpoints/{}/ip_custom_header", id))?;
1242 let resp = self
1243 .config
1244 .http_client()
1245 .delete(url)
1246 .send()
1247 .await
1248 .map_err(SdkError::Http)?;
1249
1250 let status = resp.status();
1251 let body = resp.text().await.map_err(SdkError::Http)?;
1252
1253 if !status.is_success() {
1254 return Err(SdkError::Api { status, body });
1255 }
1256 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1257 }
1258
1259 pub async fn get_method_rate_limits(
1262 &self,
1263 id: &str,
1264 ) -> Result<GetMethodRateLimitsResponse, SdkError> {
1265 let url = self
1266 .config
1267 .admin()
1268 .base_url
1269 .join(&format!("endpoints/{}/method-rate-limits", id))?;
1270 let resp = self
1271 .config
1272 .http_client()
1273 .get(url)
1274 .send()
1275 .await
1276 .map_err(SdkError::Http)?;
1277
1278 let status = resp.status();
1279 let body = resp.text().await.map_err(SdkError::Http)?;
1280
1281 if !status.is_success() {
1282 return Err(SdkError::Api { status, body });
1283 }
1284 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1285 }
1286
1287 pub async fn create_method_rate_limit(
1291 &self,
1292 id: &str,
1293 params: &CreateMethodRateLimitRequest,
1294 ) -> Result<CreateMethodRateLimitResponse, SdkError> {
1295 let url = self
1296 .config
1297 .admin()
1298 .base_url
1299 .join(&format!("endpoints/{}/method-rate-limits", id))?;
1300 let resp = self
1301 .config
1302 .http_client()
1303 .post(url)
1304 .json(params)
1305 .send()
1306 .await
1307 .map_err(SdkError::Http)?;
1308
1309 let status = resp.status();
1310 let body = resp.text().await.map_err(SdkError::Http)?;
1311
1312 if !status.is_success() {
1313 return Err(SdkError::Api { status, body });
1314 }
1315 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1316 }
1317
1318 pub async fn update_method_rate_limit(
1321 &self,
1322 id: &str,
1323 method_rate_limit_id: &str,
1324 params: &UpdateMethodRateLimitRequest,
1325 ) -> Result<UpdateMethodRateLimitResponse, SdkError> {
1326 let url = self.config.admin().base_url.join(&format!(
1327 "endpoints/{}/method-rate-limits/{}",
1328 id, method_rate_limit_id
1329 ))?;
1330 let resp = self
1331 .config
1332 .http_client()
1333 .patch(url)
1334 .json(params)
1335 .send()
1336 .await
1337 .map_err(SdkError::Http)?;
1338
1339 let status = resp.status();
1340 let body = resp.text().await.map_err(SdkError::Http)?;
1341
1342 if !status.is_success() {
1343 return Err(SdkError::Api { status, body });
1344 }
1345 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1346 }
1347
1348 pub async fn delete_method_rate_limit(
1350 &self,
1351 id: &str,
1352 method_rate_limit_id: &str,
1353 ) -> Result<(), SdkError> {
1354 let url = self.config.admin().base_url.join(&format!(
1355 "endpoints/{}/method-rate-limits/{}",
1356 id, method_rate_limit_id
1357 ))?;
1358 let resp = self
1359 .config
1360 .http_client()
1361 .delete(url)
1362 .send()
1363 .await
1364 .map_err(SdkError::Http)?;
1365
1366 let status = resp.status();
1367 let body = resp.text().await.map_err(SdkError::Http)?;
1368
1369 if !status.is_success() {
1370 return Err(SdkError::Api { status, body });
1371 }
1372 Ok(())
1373 }
1374
1375 pub async fn update_rate_limits(
1381 &self,
1382 id: &str,
1383 params: &UpdateRateLimitsRequest,
1384 ) -> Result<(), SdkError> {
1385 let url = self
1386 .config
1387 .admin()
1388 .base_url
1389 .join(&format!("endpoints/{}/rate-limits", id))?;
1390 let resp = self
1391 .config
1392 .http_client()
1393 .patch(url)
1394 .json(params)
1395 .send()
1396 .await
1397 .map_err(SdkError::Http)?;
1398
1399 let status = resp.status();
1400 let body = resp.text().await.map_err(SdkError::Http)?;
1401
1402 if !status.is_success() {
1403 return Err(SdkError::Api { status, body });
1404 }
1405 Ok(())
1406 }
1407
1408 pub async fn get_rate_limits(&self, id: &str) -> Result<GetRateLimitsResponse, SdkError> {
1413 let url = self
1414 .config
1415 .admin()
1416 .base_url
1417 .join(&format!("endpoints/{}/rate-limits", id))?;
1418 let resp = self
1419 .config
1420 .http_client()
1421 .get(url)
1422 .send()
1423 .await
1424 .map_err(SdkError::Http)?;
1425
1426 let status = resp.status();
1427 let body = resp.text().await.map_err(SdkError::Http)?;
1428
1429 if !status.is_success() {
1430 return Err(SdkError::Api { status, body });
1431 }
1432 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1433 }
1434
1435 pub async fn delete_rate_limit_override(
1439 &self,
1440 id: &str,
1441 override_id: &str,
1442 ) -> Result<(), SdkError> {
1443 let url = self
1444 .config
1445 .admin()
1446 .base_url
1447 .join(&format!("endpoints/{}/rate-limits/{}", id, override_id))?;
1448 let resp = self
1449 .config
1450 .http_client()
1451 .delete(url)
1452 .send()
1453 .await
1454 .map_err(SdkError::Http)?;
1455
1456 let status = resp.status();
1457 let body = resp.text().await.map_err(SdkError::Http)?;
1458
1459 if !status.is_success() {
1460 return Err(SdkError::Api { status, body });
1461 }
1462 Ok(())
1463 }
1464
1465 pub async fn get_endpoint_urls(&self, id: &str) -> Result<GetEndpointUrlsResponse, SdkError> {
1470 let url = self
1471 .config
1472 .admin()
1473 .base_url
1474 .join(&format!("endpoints/{}/urls", id))?;
1475 let resp = self
1476 .config
1477 .http_client()
1478 .get(url)
1479 .send()
1480 .await
1481 .map_err(SdkError::Http)?;
1482
1483 let status = resp.status();
1484 let body = resp.text().await.map_err(SdkError::Http)?;
1485
1486 if !status.is_success() {
1487 return Err(SdkError::Api { status, body });
1488 }
1489 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1490 }
1491
1492 pub async fn get_endpoint_metrics(
1496 &self,
1497 id: &str,
1498 params: &GetEndpointMetricsRequest,
1499 ) -> Result<GetEndpointMetricsResponse, SdkError> {
1500 let url = self
1501 .config
1502 .admin()
1503 .base_url
1504 .join(&format!("endpoints/{}/metrics", id))?;
1505 let resp = self
1506 .config
1507 .http_client()
1508 .get(url)
1509 .query(params)
1510 .send()
1511 .await
1512 .map_err(SdkError::Http)?;
1513
1514 let status = resp.status();
1515 let body = resp.text().await.map_err(SdkError::Http)?;
1516
1517 if !status.is_success() {
1518 return Err(SdkError::Api { status, body });
1519 }
1520 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1521 }
1522
1523 pub async fn get_account_metrics(
1527 &self,
1528 params: &GetAccountMetricsRequest,
1529 ) -> Result<GetAccountMetricsResponse, SdkError> {
1530 let url = self.config.admin().base_url.join("metrics")?;
1531 let resp = self
1532 .config
1533 .http_client()
1534 .get(url)
1535 .query(params)
1536 .send()
1537 .await
1538 .map_err(SdkError::Http)?;
1539
1540 let status = resp.status();
1541 let body = resp.text().await.map_err(SdkError::Http)?;
1542
1543 if !status.is_success() {
1544 return Err(SdkError::Api { status, body });
1545 }
1546 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1547 }
1548
1549 pub async fn list_chains(&self) -> Result<ListChainsResponse, SdkError> {
1552 let url = self.config.admin().base_url.join("chains")?;
1553 let resp = self
1554 .config
1555 .http_client()
1556 .get(url)
1557 .send()
1558 .await
1559 .map_err(SdkError::Http)?;
1560
1561 let status = resp.status();
1562 let body = resp.text().await.map_err(SdkError::Http)?;
1563
1564 if !status.is_success() {
1565 return Err(SdkError::Api { status, body });
1566 }
1567 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1568 }
1569
1570 pub async fn account_info(&self) -> Result<AccountInfoResponse, SdkError> {
1573 let url = self.config.admin().base_url.join("account/info")?;
1574 let resp = self
1575 .config
1576 .http_client()
1577 .get(url)
1578 .send()
1579 .await
1580 .map_err(SdkError::Http)?;
1581
1582 let status = resp.status();
1583 let body = resp.text().await.map_err(SdkError::Http)?;
1584
1585 if !status.is_success() {
1586 return Err(SdkError::Api { status, body });
1587 }
1588 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1589 }
1590
1591 pub async fn get_api_credits(&self, chain: &str) -> Result<GetApiCreditsResponse, SdkError> {
1597 let url = self
1598 .config
1599 .admin()
1600 .base_url
1601 .join(&format!("api-credits/{}", chain))?;
1602 let resp = self
1603 .config
1604 .http_client()
1605 .get(url)
1606 .send()
1607 .await
1608 .map_err(SdkError::Http)?;
1609
1610 let status = resp.status();
1611 let body = resp.text().await.map_err(SdkError::Http)?;
1612
1613 if !status.is_success() {
1614 return Err(SdkError::Api { status, body });
1615 }
1616 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1617 }
1618
1619 pub async fn list_invoices(&self) -> Result<ListInvoicesResponse, SdkError> {
1623 let url = self.config.admin().base_url.join("billing/invoices")?;
1624 let resp = self
1625 .config
1626 .http_client()
1627 .get(url)
1628 .send()
1629 .await
1630 .map_err(SdkError::Http)?;
1631
1632 let status = resp.status();
1633 let body = resp.text().await.map_err(SdkError::Http)?;
1634
1635 if !status.is_success() {
1636 return Err(SdkError::Api { status, body });
1637 }
1638 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1639 }
1640
1641 pub async fn list_payments(&self) -> Result<ListPaymentsResponse, SdkError> {
1644 let url = self.config.admin().base_url.join("billing/payments")?;
1645 let resp = self
1646 .config
1647 .http_client()
1648 .get(url)
1649 .send()
1650 .await
1651 .map_err(SdkError::Http)?;
1652
1653 let status = resp.status();
1654 let body = resp.text().await.map_err(SdkError::Http)?;
1655
1656 if !status.is_success() {
1657 return Err(SdkError::Api { status, body });
1658 }
1659 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1660 }
1661
1662 pub async fn bulk_update_endpoint_status(
1666 &self,
1667 params: &BulkUpdateEndpointStatusRequest,
1668 ) -> Result<BulkUpdateEndpointStatusResponse, SdkError> {
1669 if params.ids.is_empty() {
1670 return Err(SdkError::Config(
1671 "bulk_update_endpoint_status requires at least one id".into(),
1672 ));
1673 }
1674 let url = self.config.admin().base_url.join("endpoints/bulk/status")?;
1675 let resp = self
1676 .config
1677 .http_client()
1678 .post(url)
1679 .json(params)
1680 .send()
1681 .await
1682 .map_err(SdkError::Http)?;
1683
1684 let status = resp.status();
1685 let body = resp.text().await.map_err(SdkError::Http)?;
1686
1687 if !status.is_success() {
1688 return Err(SdkError::Api { status, body });
1689 }
1690 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1691 }
1692
1693 pub async fn bulk_add_tag(
1697 &self,
1698 params: &BulkAddTagRequest,
1699 ) -> Result<BulkAddTagResponse, SdkError> {
1700 if params.ids.is_empty() {
1701 return Err(SdkError::Config(
1702 "bulk_add_tag requires at least one id".into(),
1703 ));
1704 }
1705 let url = self.config.admin().base_url.join("endpoints/bulk/tags")?;
1706 let resp = self
1707 .config
1708 .http_client()
1709 .post(url)
1710 .json(params)
1711 .send()
1712 .await
1713 .map_err(SdkError::Http)?;
1714
1715 let status = resp.status();
1716 let body = resp.text().await.map_err(SdkError::Http)?;
1717
1718 if !status.is_success() {
1719 return Err(SdkError::Api { status, body });
1720 }
1721 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1722 }
1723
1724 pub async fn bulk_remove_tag(
1727 &self,
1728 params: &BulkRemoveTagRequest,
1729 ) -> Result<BulkRemoveTagResponse, SdkError> {
1730 if params.ids.is_empty() {
1734 return Err(SdkError::Config(
1735 "bulk_remove_tag requires at least one id".into(),
1736 ));
1737 }
1738 let url = self.config.admin().base_url.join("endpoints/bulk/tags")?;
1739 let resp = self
1740 .config
1741 .http_client()
1742 .delete(url)
1743 .json(params)
1744 .send()
1745 .await
1746 .map_err(SdkError::Http)?;
1747
1748 let status = resp.status();
1749 let body = resp.text().await.map_err(SdkError::Http)?;
1750
1751 if !status.is_success() {
1752 return Err(SdkError::Api { status, body });
1753 }
1754 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1755 }
1756
1757 pub async fn list_tags(&self) -> Result<ListTagsResponse, SdkError> {
1760 let url = self.config.admin().base_url.join("endpoints/tags")?;
1761 let resp = self
1762 .config
1763 .http_client()
1764 .get(url)
1765 .send()
1766 .await
1767 .map_err(SdkError::Http)?;
1768
1769 let status = resp.status();
1770 let body = resp.text().await.map_err(SdkError::Http)?;
1771
1772 if !status.is_success() {
1773 return Err(SdkError::Api { status, body });
1774 }
1775 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1776 }
1777
1778 pub async fn rename_tag(
1781 &self,
1782 id: i32,
1783 params: &RenameTagRequest,
1784 ) -> Result<RenameTagResponse, SdkError> {
1785 let url = self
1786 .config
1787 .admin()
1788 .base_url
1789 .join(&format!("endpoints/tags/{}", id))?;
1790 let resp = self
1791 .config
1792 .http_client()
1793 .patch(url)
1794 .json(params)
1795 .send()
1796 .await
1797 .map_err(SdkError::Http)?;
1798
1799 let status = resp.status();
1800 let body = resp.text().await.map_err(SdkError::Http)?;
1801
1802 if !status.is_success() {
1803 return Err(SdkError::Api { status, body });
1804 }
1805 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1806 }
1807
1808 pub async fn delete_account_tag(&self, id: i32) -> Result<DeleteAccountTagResponse, SdkError> {
1813 let url = self
1814 .config
1815 .admin()
1816 .base_url
1817 .join(&format!("endpoints/tags/{}", id))?;
1818 let resp = self
1819 .config
1820 .http_client()
1821 .delete(url)
1822 .send()
1823 .await
1824 .map_err(SdkError::Http)?;
1825
1826 let status = resp.status();
1827 let body = resp.text().await.map_err(SdkError::Http)?;
1828
1829 if !status.is_success() {
1830 return Err(SdkError::Api { status, body });
1831 }
1832 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1833 }
1834
1835 pub async fn get_usage_by_tag(
1839 &self,
1840 params: &GetUsageRequest,
1841 ) -> Result<GetUsageByTagResponse, SdkError> {
1842 let url = self.config.admin().base_url.join("usage/rpc/by-tag")?;
1843 let resp = self
1844 .config
1845 .http_client()
1846 .get(url)
1847 .query(params)
1848 .send()
1849 .await
1850 .map_err(SdkError::Http)?;
1851
1852 let status = resp.status();
1853 let body = resp.text().await.map_err(SdkError::Http)?;
1854
1855 if !status.is_success() {
1856 return Err(SdkError::Api { status, body });
1857 }
1858 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1859 }
1860
1861 pub async fn get_endpoint_security(
1866 &self,
1867 id: &str,
1868 ) -> Result<GetEndpointSecurityResponse, SdkError> {
1869 let url = self
1870 .config
1871 .admin()
1872 .base_url
1873 .join(&format!("endpoints/{}/security", id))?;
1874 let resp = self
1875 .config
1876 .http_client()
1877 .get(url)
1878 .send()
1879 .await
1880 .map_err(SdkError::Http)?;
1881
1882 let status = resp.status();
1883 let body = resp.text().await.map_err(SdkError::Http)?;
1884
1885 if !status.is_success() {
1886 return Err(SdkError::Api { status, body });
1887 }
1888 serde_json::from_str(&body).map_err(|source| SdkError::Decode { source, body })
1889 }
1890}
1891
1892fn endpoints_query(params: &GetEndpointsRequest) -> Vec<(&'static str, String)> {
1893 let mut q: Vec<(&'static str, String)> = Vec::new();
1894 if let Some(v) = params.limit {
1895 q.push(("limit", v.to_string()));
1896 }
1897 if let Some(v) = params.offset {
1898 q.push(("offset", v.to_string()));
1899 }
1900 if let Some(ref v) = params.search {
1901 q.push(("search", v.clone()));
1902 }
1903 if let Some(ref v) = params.sort_by {
1904 q.push(("sort_by", v.clone()));
1905 }
1906 if let Some(ref v) = params.sort_direction {
1907 q.push(("sort_direction", v.clone()));
1908 }
1909 if let Some(ref list) = params.networks {
1910 for item in list {
1911 q.push(("networks[]", item.clone()));
1912 }
1913 }
1914 if let Some(ref list) = params.statuses {
1915 for item in list {
1916 q.push(("statuses[]", item.clone()));
1917 }
1918 }
1919 if let Some(ref list) = params.labels {
1920 for item in list {
1921 q.push(("labels[]", item.clone()));
1922 }
1923 }
1924 if let Some(v) = params.dedicated {
1925 q.push(("dedicated", v.to_string()));
1926 }
1927 if let Some(v) = params.is_flat_rate {
1928 q.push(("is_flat_rate", v.to_string()));
1929 }
1930 if let Some(ref list) = params.tag_ids {
1931 for item in list {
1932 q.push(("tag_ids[]", item.to_string()));
1933 }
1934 }
1935 if let Some(ref list) = params.tag_labels {
1936 for item in list {
1937 q.push(("tag_labels[]", item.clone()));
1938 }
1939 }
1940 q
1941}
1942
1943#[cfg(test)]
1944#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
1945mod tests {
1946 use super::*;
1947 use crate::{AdminConfig, QuicknodeSdk, SdkFullConfig};
1948 use wiremock::matchers::{method, path, query_param};
1949 use wiremock::{Mock, MockServer, ResponseTemplate};
1950
1951 fn make_sdk(base_url: String) -> QuicknodeSdk {
1952 QuicknodeSdk::new(&SdkFullConfig {
1953 api_key: Some("test-key".to_string()),
1954 http: None,
1955 admin: Some(AdminConfig {
1956 base_url: Some(base_url),
1957 }),
1958 streams: None,
1959 webhooks: None,
1960 kvstore: None,
1961 sql: None,
1962 rpc: None,
1963 })
1964 .unwrap()
1965 }
1966
1967 #[tokio::test]
1968 async fn get_endpoints_success() {
1969 let server = MockServer::start().await;
1970
1971 Mock::given(method("GET"))
1972 .and(path("/endpoints"))
1973 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
1974 "data": [
1975 {
1976 "id": "abc123",
1977 "name": "aged-intensive-patron",
1978 "label": "My Endpoint",
1979 "status": "active",
1980 "chain": "ethereum",
1981 "network": "mainnet",
1982 "is_dedicated": false,
1983 "is_flat_rate": true,
1984 "http_url": "https://example.quicknode.pro/abc123",
1985 "wss_url": null,
1986 "tags": []
1987 }
1988 ],
1989 "pagination": {
1990 "total": 1,
1991 "limit": 20,
1992 "offset": 0
1993 },
1994 "error": null
1995 })))
1996 .mount(&server)
1997 .await;
1998
1999 let sdk = make_sdk(format!("{}/", server.uri()));
2000 let resp = sdk
2001 .admin
2002 .get_endpoints(&GetEndpointsRequest::default())
2003 .await
2004 .unwrap();
2005
2006 assert_eq!(resp.data.len(), 1);
2007 assert_eq!(resp.data[0].id, "abc123");
2008 assert_eq!(resp.data[0].name, "aged-intensive-patron");
2009 assert_eq!(resp.data[0].status, "active");
2010 assert_eq!(resp.data[0].chain, "ethereum");
2011 assert!(!resp.data[0].is_dedicated);
2012 assert!(resp.data[0].is_flat_rate);
2013 let pagination = resp.pagination.expect("pagination present");
2014 assert_eq!(pagination.total, 1);
2015 assert_eq!(pagination.limit, 20);
2016 assert_eq!(pagination.offset, 0);
2017 }
2018
2019 #[tokio::test]
2020 async fn get_endpoints_sends_search_and_filter_params() {
2021 let server = MockServer::start().await;
2022
2023 Mock::given(method("GET"))
2024 .and(path("/endpoints"))
2025 .and(query_param("search", "intensive"))
2026 .and(query_param("networks[]", "mainnet"))
2027 .and(query_param("statuses[]", "active"))
2028 .and(query_param("dedicated", "true"))
2029 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2030 "data": [],
2031 "error": null
2032 })))
2033 .mount(&server)
2034 .await;
2035
2036 let sdk = make_sdk(format!("{}/", server.uri()));
2037 let params = GetEndpointsRequest {
2038 search: Some("intensive".to_string()),
2039 networks: Some(vec!["mainnet".to_string()]),
2040 statuses: Some(vec!["active".to_string()]),
2041 dedicated: Some(true),
2042 ..Default::default()
2043 };
2044 let resp = sdk.admin.get_endpoints(¶ms).await.unwrap();
2045
2046 assert_eq!(resp.data.len(), 0);
2047 }
2048
2049 #[tokio::test]
2050 async fn get_endpoints_api_error() {
2051 let server = MockServer::start().await;
2052
2053 Mock::given(method("GET"))
2054 .and(path("/endpoints"))
2055 .respond_with(ResponseTemplate::new(401).set_body_string("Unauthorized"))
2056 .mount(&server)
2057 .await;
2058
2059 let sdk = make_sdk(format!("{}/", server.uri()));
2060 let err = sdk
2061 .admin
2062 .get_endpoints(&GetEndpointsRequest::default())
2063 .await
2064 .unwrap_err();
2065
2066 match err {
2067 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 401),
2068 other => panic!("expected SdkError::Api, got {:?}", other),
2069 }
2070 }
2071
2072 #[tokio::test]
2073 async fn get_endpoints_sends_query_params() {
2074 let server = MockServer::start().await;
2075
2076 Mock::given(method("GET"))
2077 .and(path("/endpoints"))
2078 .and(query_param("limit", "10"))
2079 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2080 "data": [],
2081 "error": null
2082 })))
2083 .mount(&server)
2084 .await;
2085
2086 let sdk = make_sdk(format!("{}/", server.uri()));
2087 let params = GetEndpointsRequest {
2088 limit: Some(10),
2089 ..Default::default()
2090 };
2091 let resp = sdk.admin.get_endpoints(¶ms).await.unwrap();
2092
2093 assert_eq!(resp.data.len(), 0);
2094 }
2095
2096 #[tokio::test]
2097 async fn get_endpoints_base_url_without_trailing_slash() {
2098 let server = MockServer::start().await;
2099
2100 Mock::given(method("GET"))
2101 .and(path("/endpoints"))
2102 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2103 "data": [],
2104 "error": null
2105 })))
2106 .mount(&server)
2107 .await;
2108
2109 let base_url_no_slash = server.uri();
2110 let sdk = make_sdk(base_url_no_slash);
2111 let resp = sdk
2112 .admin
2113 .get_endpoints(&GetEndpointsRequest::default())
2114 .await
2115 .unwrap();
2116
2117 assert_eq!(resp.data.len(), 0);
2118 }
2119
2120 #[tokio::test]
2121 async fn create_endpoint_success() {
2122 let server = MockServer::start().await;
2123
2124 Mock::given(method("POST"))
2125 .and(path("/endpoints"))
2126 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2127 "data": {
2128 "id": "ep123",
2129 "label": null,
2130 "status": "active",
2131 "chain": "ethereum",
2132 "network": "mainnet",
2133 "http_url": "https://example.quicknode.pro/ep123",
2134 "wss_url": null,
2135 "security": {
2136 "options": { "tokens": true, "jwts": false, "domainMasks": false, "ips": false, "referrers": false, "requestFilters": false },
2137 "tokens": [{"id": "tok1", "token": "abc123"}],
2138 "jwts": null,
2139 "referrers": null,
2140 "domain_masks": null,
2141 "ips": null,
2142 "request_filters": null
2143 },
2144 "rate_limits": null,
2145 "tags": []
2146 },
2147 "error": null
2148 })))
2149 .mount(&server)
2150 .await;
2151
2152 let sdk = make_sdk(format!("{}/", server.uri()));
2153 let resp = sdk
2154 .admin
2155 .create_endpoint(&CreateEndpointRequest::default())
2156 .await
2157 .unwrap();
2158
2159 assert_eq!(resp.data.id, "ep123");
2160 assert_eq!(resp.data.chain, "ethereum");
2161 assert_eq!(resp.data.network, "mainnet");
2162 let security = resp.data.security.unwrap();
2163 assert!(security.tokens.unwrap().len() == 1);
2164 assert!(security.jwts.is_none());
2165 }
2166
2167 #[tokio::test]
2168 async fn create_endpoint_api_error() {
2169 let server = MockServer::start().await;
2170
2171 Mock::given(method("POST"))
2172 .and(path("/endpoints"))
2173 .respond_with(ResponseTemplate::new(400).set_body_string("Bad Request"))
2174 .mount(&server)
2175 .await;
2176
2177 let sdk = make_sdk(format!("{}/", server.uri()));
2178 let err = sdk
2179 .admin
2180 .create_endpoint(&CreateEndpointRequest::default())
2181 .await
2182 .unwrap_err();
2183
2184 match err {
2185 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 400),
2186 other => panic!("expected SdkError::Api, got {:?}", other),
2187 }
2188 }
2189
2190 #[tokio::test]
2191 async fn create_endpoint_sends_body() {
2192 use wiremock::matchers::body_json;
2193
2194 let server = MockServer::start().await;
2195
2196 Mock::given(method("POST"))
2197 .and(path("/endpoints"))
2198 .and(body_json(serde_json::json!({
2199 "chain": "solana",
2200 "network": "mainnet-beta"
2201 })))
2202 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2203 "data": {
2204 "id": "ep456",
2205 "label": null,
2206 "status": "active",
2207 "chain": "solana",
2208 "network": "mainnet-beta",
2209 "http_url": "https://example.quicknode.pro/ep456",
2210 "wss_url": null,
2211 "security": null,
2212 "rate_limits": null,
2213 "tags": []
2214 },
2215 "error": null
2216 })))
2217 .mount(&server)
2218 .await;
2219
2220 let sdk = make_sdk(format!("{}/", server.uri()));
2221 let params = CreateEndpointRequest {
2222 chain: Some("solana".to_string()),
2223 network: Some("mainnet-beta".to_string()),
2224 };
2225 let resp = sdk.admin.create_endpoint(¶ms).await.unwrap();
2226
2227 assert_eq!(resp.data.id, "ep456");
2228 assert_eq!(resp.data.chain, "solana");
2229 }
2230
2231 #[tokio::test]
2232 async fn show_endpoint_success() {
2233 let server = MockServer::start().await;
2234
2235 Mock::given(method("GET"))
2236 .and(path("/endpoints/ep123"))
2237 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2238 "data": {
2239 "id": "ep123",
2240 "label": null,
2241 "status": "active",
2242 "chain": "ethereum",
2243 "network": "mainnet",
2244 "http_url": "https://example.quicknode.pro/ep123",
2245 "wss_url": null,
2246 "security": null,
2247 "rate_limits": null,
2248 "tags": []
2249 },
2250 "error": null
2251 })))
2252 .mount(&server)
2253 .await;
2254
2255 let sdk = make_sdk(format!("{}/", server.uri()));
2256 let resp = sdk.admin.show_endpoint("ep123").await.unwrap();
2257 assert_eq!(resp.data.unwrap().id, "ep123");
2258 }
2259
2260 #[tokio::test]
2261 async fn show_endpoint_api_error() {
2262 let server = MockServer::start().await;
2263
2264 Mock::given(method("GET"))
2265 .and(path("/endpoints/ep123"))
2266 .respond_with(ResponseTemplate::new(404).set_body_string("Not Found"))
2267 .mount(&server)
2268 .await;
2269
2270 let sdk = make_sdk(format!("{}/", server.uri()));
2271 let err = sdk.admin.show_endpoint("ep123").await.unwrap_err();
2272 match err {
2273 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 404),
2274 other => panic!("expected SdkError::Api, got {:?}", other),
2275 }
2276 }
2277
2278 #[tokio::test]
2279 async fn update_endpoint_success() {
2280 let server = MockServer::start().await;
2281
2282 Mock::given(method("PATCH"))
2283 .and(path("/endpoints/ep123"))
2284 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2285 .mount(&server)
2286 .await;
2287
2288 let sdk = make_sdk(format!("{}/", server.uri()));
2289 sdk.admin
2290 .update_endpoint(
2291 "ep123",
2292 &UpdateEndpointRequest {
2293 label: Some("New Name".to_string()),
2294 },
2295 )
2296 .await
2297 .unwrap();
2298 }
2299
2300 #[tokio::test]
2301 async fn archive_endpoint_success() {
2302 let server = MockServer::start().await;
2303
2304 Mock::given(method("DELETE"))
2305 .and(path("/endpoints/ep123"))
2306 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2307 .mount(&server)
2308 .await;
2309
2310 let sdk = make_sdk(format!("{}/", server.uri()));
2311 sdk.admin.archive_endpoint("ep123").await.unwrap();
2312 }
2313
2314 #[tokio::test]
2315 async fn update_endpoint_status_success() {
2316 let server = MockServer::start().await;
2317
2318 Mock::given(method("PATCH"))
2319 .and(path("/endpoints/ep123/status"))
2320 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2321 "data": "paused",
2322 "error": null
2323 })))
2324 .mount(&server)
2325 .await;
2326
2327 let sdk = make_sdk(format!("{}/", server.uri()));
2328 let resp = sdk
2329 .admin
2330 .update_endpoint_status(
2331 "ep123",
2332 &UpdateEndpointStatusRequest {
2333 status: "paused".to_string(),
2334 },
2335 )
2336 .await
2337 .unwrap();
2338 assert_eq!(resp.data.unwrap(), "paused");
2339 }
2340
2341 #[tokio::test]
2342 async fn create_tag_success() {
2343 let server = MockServer::start().await;
2344
2345 Mock::given(method("POST"))
2346 .and(path("/endpoints/ep123/tags"))
2347 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2348 .mount(&server)
2349 .await;
2350
2351 let sdk = make_sdk(format!("{}/", server.uri()));
2352 sdk.admin
2353 .create_tag(
2354 "ep123",
2355 &CreateTagRequest {
2356 label: Some("my-tag".to_string()),
2357 },
2358 )
2359 .await
2360 .unwrap();
2361 }
2362
2363 #[tokio::test]
2364 async fn delete_tag_success() {
2365 let server = MockServer::start().await;
2366
2367 Mock::given(method("DELETE"))
2368 .and(path("/endpoints/ep123/tags/tag456"))
2369 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2370 .mount(&server)
2371 .await;
2372
2373 let sdk = make_sdk(format!("{}/", server.uri()));
2374 sdk.admin.delete_tag("ep123", "tag456").await.unwrap();
2375 }
2376
2377 #[tokio::test]
2378 async fn get_usage_success() {
2379 let server = MockServer::start().await;
2380
2381 Mock::given(method("GET"))
2382 .and(path("/usage/rpc"))
2383 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2384 "data": {
2385 "credits_used": 5000,
2386 "credits_remaining": 95000,
2387 "limit": 100000,
2388 "overages": null,
2389 "start_time": 1700000000,
2390 "end_time": 1702592000
2391 },
2392 "error": null
2393 })))
2394 .mount(&server)
2395 .await;
2396
2397 let sdk = make_sdk(format!("{}/", server.uri()));
2398 let resp = sdk
2399 .admin
2400 .get_usage(&GetUsageRequest::default())
2401 .await
2402 .unwrap();
2403 assert_eq!(resp.data.unwrap().credits_used, 5000);
2404 }
2405
2406 #[tokio::test]
2407 async fn get_usage_by_endpoint_success() {
2408 let server = MockServer::start().await;
2409
2410 Mock::given(method("GET"))
2411 .and(path("/usage/rpc/by-endpoint"))
2412 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2413 "data": {
2414 "endpoints": [{"name": "ep1", "chain": "eth", "network": "mainnet", "status": "active", "credits_used": 100, "label": null, "methods_breakdown": [], "requests": 50}],
2415 "start_time": 1700000000,
2416 "end_time": 1702592000
2417 },
2418 "error": null
2419 })))
2420 .mount(&server)
2421 .await;
2422
2423 let sdk = make_sdk(format!("{}/", server.uri()));
2424 let resp = sdk
2425 .admin
2426 .get_usage_by_endpoint(&GetUsageRequest::default())
2427 .await
2428 .unwrap();
2429 assert_eq!(resp.data.unwrap().endpoints.len(), 1);
2430 }
2431
2432 #[tokio::test]
2433 async fn get_usage_by_chain_success() {
2434 let server = MockServer::start().await;
2435
2436 Mock::given(method("GET"))
2437 .and(path("/usage/rpc/by-chain"))
2438 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2439 "data": {
2440 "chains": [{"name": "ethereum", "credits_used": 1000}],
2441 "start_time": 1700000000,
2442 "end_time": 1702592000
2443 },
2444 "error": null
2445 })))
2446 .mount(&server)
2447 .await;
2448
2449 let sdk = make_sdk(format!("{}/", server.uri()));
2450 let resp = sdk
2451 .admin
2452 .get_usage_by_chain(&GetUsageRequest::default())
2453 .await
2454 .unwrap();
2455 assert_eq!(resp.data.unwrap().chains[0].name, "ethereum");
2456 }
2457
2458 #[tokio::test]
2459 async fn get_endpoint_logs_success() {
2460 let server = MockServer::start().await;
2461
2462 Mock::given(method("GET"))
2463 .and(path("/endpoints/ep123/logs"))
2464 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2465 "data": [
2466 {
2467 "timestamp": "2025-04-29T12:39:25.543Z",
2468 "method": "eth_call",
2469 "network": "mainnet",
2470 "http_method": "POST",
2471 "status": 200,
2472 "error_code": null,
2473 "url": "/",
2474 "request_id": "abc-123",
2475 "details": null
2476 }
2477 ],
2478 "next_at": null
2479 })))
2480 .mount(&server)
2481 .await;
2482
2483 let sdk = make_sdk(format!("{}/", server.uri()));
2484 let params = GetEndpointLogsRequest {
2485 from: "2025-04-29T00:00:00Z".to_string(),
2486 to: "2025-04-29T23:59:59Z".to_string(),
2487 ..Default::default()
2488 };
2489 let resp = sdk.admin.get_endpoint_logs("ep123", ¶ms).await.unwrap();
2490 assert_eq!(resp.data.len(), 1);
2491 assert_eq!(resp.data[0].method.as_deref(), Some("eth_call"));
2492 }
2493
2494 #[tokio::test]
2495 async fn get_log_details_success() {
2496 let server = MockServer::start().await;
2497
2498 Mock::given(method("GET"))
2499 .and(path("/endpoints/ep123/log_details"))
2500 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2501 "data": {
2502 "request": "{\"method\":\"eth_call\"}",
2503 "response": "{\"result\":\"0x1\"}"
2504 }
2505 })))
2506 .mount(&server)
2507 .await;
2508
2509 let sdk = make_sdk(format!("{}/", server.uri()));
2510 let resp = sdk.admin.get_log_details("ep123", "abc-123").await.unwrap();
2511 assert!(resp.data.is_some());
2512 }
2513
2514 #[tokio::test]
2515 async fn get_security_options_success() {
2516 let server = MockServer::start().await;
2517
2518 Mock::given(method("GET"))
2519 .and(path("/endpoints/ep123/security_options"))
2520 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2521 "data": [{"option": "tokens", "status": "enabled", "value": null}],
2522 "error": null
2523 })))
2524 .mount(&server)
2525 .await;
2526
2527 let sdk = make_sdk(format!("{}/", server.uri()));
2528 let resp = sdk.admin.get_security_options("ep123").await.unwrap();
2529 assert_eq!(resp.data.len(), 1);
2530 assert_eq!(resp.data[0].option, "tokens");
2531 }
2532
2533 #[tokio::test]
2534 async fn update_security_options_success() {
2535 let server = MockServer::start().await;
2536
2537 Mock::given(method("PATCH"))
2538 .and(path("/endpoints/ep123/security_options"))
2539 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2540 "data": [{"option": "tokens", "status": "disabled", "value": null}],
2541 "error": null
2542 })))
2543 .mount(&server)
2544 .await;
2545
2546 let sdk = make_sdk(format!("{}/", server.uri()));
2547 let params = UpdateSecurityOptionsRequest {
2548 options: SecurityOptionsUpdate {
2549 tokens: Some("disabled".to_string()),
2550 ..Default::default()
2551 },
2552 };
2553 let resp = sdk
2554 .admin
2555 .update_security_options("ep123", ¶ms)
2556 .await
2557 .unwrap();
2558 assert_eq!(resp.data[0].status, "disabled");
2559 }
2560
2561 #[tokio::test]
2562 async fn create_token_success() {
2563 let server = MockServer::start().await;
2564
2565 Mock::given(method("POST"))
2566 .and(path("/endpoints/ep123/security/tokens"))
2567 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2568 .mount(&server)
2569 .await;
2570
2571 let sdk = make_sdk(format!("{}/", server.uri()));
2572 sdk.admin.create_token("ep123").await.unwrap();
2573 }
2574
2575 #[tokio::test]
2576 async fn delete_token_success() {
2577 let server = MockServer::start().await;
2578
2579 Mock::given(method("DELETE"))
2580 .and(path("/endpoints/ep123/security/tokens/tok1"))
2581 .respond_with(
2582 ResponseTemplate::new(200)
2583 .set_body_json(serde_json::json!({"data": true, "error": null})),
2584 )
2585 .mount(&server)
2586 .await;
2587
2588 let sdk = make_sdk(format!("{}/", server.uri()));
2589 let resp = sdk.admin.delete_token("ep123", "tok1").await.unwrap();
2590 assert_eq!(resp.data, Some(true));
2591 }
2592
2593 #[tokio::test]
2594 async fn create_referrer_success() {
2595 let server = MockServer::start().await;
2596
2597 Mock::given(method("POST"))
2598 .and(path("/endpoints/ep123/security/referrers"))
2599 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2600 .mount(&server)
2601 .await;
2602
2603 let sdk = make_sdk(format!("{}/", server.uri()));
2604 sdk.admin
2605 .create_referrer(
2606 "ep123",
2607 &CreateReferrerRequest {
2608 referrer: "example.com".to_string(),
2609 },
2610 )
2611 .await
2612 .unwrap();
2613 }
2614
2615 #[tokio::test]
2616 async fn enable_disable_multichain_success() {
2617 let server = MockServer::start().await;
2618
2619 Mock::given(method("POST"))
2620 .and(path("/endpoints/ep123/enable_multichain"))
2621 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2622 .mount(&server)
2623 .await;
2624
2625 Mock::given(method("POST"))
2626 .and(path("/endpoints/ep123/disable_multichain"))
2627 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2628 .mount(&server)
2629 .await;
2630
2631 let sdk = make_sdk(format!("{}/", server.uri()));
2632 sdk.admin.enable_multichain("ep123").await.unwrap();
2633 sdk.admin.disable_multichain("ep123").await.unwrap();
2634 }
2635
2636 #[tokio::test]
2637 async fn create_or_update_ip_custom_header_success() {
2638 let server = MockServer::start().await;
2639
2640 Mock::given(method("PATCH"))
2641 .and(path("/endpoints/ep123/ip_custom_header"))
2642 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2643 "data": {"header_name": "CF-Connecting-IP"},
2644 "error": null
2645 })))
2646 .mount(&server)
2647 .await;
2648
2649 let sdk = make_sdk(format!("{}/", server.uri()));
2650 let params = CreateOrUpdateIpCustomHeaderRequest {
2651 header_name: "CF-Connecting-IP".to_string(),
2652 };
2653 let resp = sdk
2654 .admin
2655 .create_or_update_ip_custom_header("ep123", ¶ms)
2656 .await
2657 .unwrap();
2658 assert_eq!(resp.data.unwrap().header_name, "CF-Connecting-IP");
2659 }
2660
2661 #[tokio::test]
2662 async fn get_method_rate_limits_success() {
2663 let server = MockServer::start().await;
2664
2665 Mock::given(method("GET"))
2666 .and(path("/endpoints/ep123/method-rate-limits"))
2667 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2668 "data": {
2669 "rate_limiters": [
2670 {"id": "rl1", "interval": "second", "methods": ["eth_call"], "rate": 10, "status": "enabled", "created": "2024-01-01T00:00:00Z"}
2671 ]
2672 },
2673 "error": null
2674 })))
2675 .mount(&server)
2676 .await;
2677
2678 let sdk = make_sdk(format!("{}/", server.uri()));
2679 let resp = sdk.admin.get_method_rate_limits("ep123").await.unwrap();
2680 assert_eq!(resp.data.unwrap().rate_limiters.len(), 1);
2681 }
2682
2683 #[tokio::test]
2684 async fn create_method_rate_limit_success() {
2685 let server = MockServer::start().await;
2686
2687 Mock::given(method("POST"))
2688 .and(path("/endpoints/ep123/method-rate-limits"))
2689 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2690 "data": {"id": "rl1", "interval": "second", "methods": ["eth_call"], "rate": 10, "status": "enabled", "created": "2024-01-01T00:00:00Z"},
2691 "error": null
2692 })))
2693 .mount(&server)
2694 .await;
2695
2696 let sdk = make_sdk(format!("{}/", server.uri()));
2697 let params = CreateMethodRateLimitRequest {
2698 interval: "second".to_string(),
2699 methods: vec!["eth_call".to_string()],
2700 rate: 10,
2701 };
2702 let resp = sdk
2703 .admin
2704 .create_method_rate_limit("ep123", ¶ms)
2705 .await
2706 .unwrap();
2707 assert_eq!(resp.data.unwrap().id, "rl1");
2708 }
2709
2710 #[tokio::test]
2711 async fn update_method_rate_limit_success() {
2712 let server = MockServer::start().await;
2713
2714 Mock::given(method("PATCH"))
2715 .and(path("/endpoints/ep123/method-rate-limits/rl1"))
2716 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2717 "data": {"id": "rl1", "interval": "day", "methods": ["eth_call"], "rate": 30, "status": "enabled", "created": "2024-01-01T00:00:00Z"},
2718 "error": null
2719 })))
2720 .mount(&server)
2721 .await;
2722
2723 let sdk = make_sdk(format!("{}/", server.uri()));
2724 let params = UpdateMethodRateLimitRequest {
2725 rate: Some(30),
2726 ..Default::default()
2727 };
2728 let resp = sdk
2729 .admin
2730 .update_method_rate_limit("ep123", "rl1", ¶ms)
2731 .await
2732 .unwrap();
2733 assert_eq!(resp.data.unwrap().rate, 30);
2734 }
2735
2736 #[tokio::test]
2737 async fn delete_method_rate_limit_success() {
2738 let server = MockServer::start().await;
2739
2740 Mock::given(method("DELETE"))
2741 .and(path("/endpoints/ep123/method-rate-limits/rl1"))
2742 .respond_with(
2743 ResponseTemplate::new(200)
2744 .set_body_json(serde_json::json!({"data": "deleted", "error": null})),
2745 )
2746 .mount(&server)
2747 .await;
2748
2749 let sdk = make_sdk(format!("{}/", server.uri()));
2750 sdk.admin
2751 .delete_method_rate_limit("ep123", "rl1")
2752 .await
2753 .unwrap();
2754 }
2755
2756 #[tokio::test]
2757 async fn update_rate_limits_success() {
2758 let server = MockServer::start().await;
2759
2760 Mock::given(method("PATCH"))
2761 .and(path("/endpoints/ep123/rate-limits"))
2762 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({})))
2763 .mount(&server)
2764 .await;
2765
2766 let sdk = make_sdk(format!("{}/", server.uri()));
2767 let params = UpdateRateLimitsRequest {
2768 rate_limits: RateLimitSettings {
2769 rps: Some(100),
2770 rpm: None,
2771 rpd: None,
2772 },
2773 };
2774 sdk.admin
2775 .update_rate_limits("ep123", ¶ms)
2776 .await
2777 .unwrap();
2778 }
2779
2780 #[tokio::test]
2781 async fn get_rate_limits_success() {
2782 let server = MockServer::start().await;
2783
2784 Mock::given(method("GET"))
2785 .and(path("/endpoints/ep123/rate-limits"))
2786 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2787 "data": {
2788 "rate_limits": [
2789 {"bucket": "rps", "rate_limit": 100, "source": "plan_default"},
2790 {"bucket": "rpm", "rate_limit": 6000, "source": "user_override", "id": "ovr-1"}
2791 ]
2792 },
2793 "error": null
2794 })))
2795 .mount(&server)
2796 .await;
2797
2798 let sdk = make_sdk(format!("{}/", server.uri()));
2799 let resp = sdk.admin.get_rate_limits("ep123").await.unwrap();
2800 let rows = resp.data.unwrap().rate_limits;
2801 assert_eq!(rows.len(), 2);
2802 assert_eq!(rows[0].source, "plan_default");
2803 assert!(rows[0].id.is_none());
2804 assert_eq!(rows[1].source, "user_override");
2805 assert_eq!(rows[1].rate_limit, 6000);
2806 assert_eq!(rows[1].id.as_deref(), Some("ovr-1"));
2807 }
2808
2809 #[tokio::test]
2810 async fn get_rate_limits_api_error() {
2811 let server = MockServer::start().await;
2812
2813 Mock::given(method("GET"))
2814 .and(path("/endpoints/missing/rate-limits"))
2815 .respond_with(ResponseTemplate::new(404).set_body_string("not found"))
2816 .mount(&server)
2817 .await;
2818
2819 let sdk = make_sdk(format!("{}/", server.uri()));
2820 let err = sdk.admin.get_rate_limits("missing").await.unwrap_err();
2821 match err {
2822 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 404),
2823 other => panic!("expected SdkError::Api, got {:?}", other),
2824 }
2825 }
2826
2827 #[tokio::test]
2828 async fn delete_rate_limit_override_success() {
2829 let server = MockServer::start().await;
2830
2831 Mock::given(method("DELETE"))
2832 .and(path("/endpoints/ep123/rate-limits/ovr-1"))
2833 .respond_with(ResponseTemplate::new(200).set_body_string(""))
2834 .mount(&server)
2835 .await;
2836
2837 let sdk = make_sdk(format!("{}/", server.uri()));
2838 sdk.admin
2839 .delete_rate_limit_override("ep123", "ovr-1")
2840 .await
2841 .unwrap();
2842 }
2843
2844 #[tokio::test]
2845 async fn delete_rate_limit_override_not_found() {
2846 let server = MockServer::start().await;
2847
2848 Mock::given(method("DELETE"))
2849 .and(path("/endpoints/ep123/rate-limits/bogus"))
2850 .respond_with(ResponseTemplate::new(404).set_body_string("override not found"))
2851 .mount(&server)
2852 .await;
2853
2854 let sdk = make_sdk(format!("{}/", server.uri()));
2855 let err = sdk
2856 .admin
2857 .delete_rate_limit_override("ep123", "bogus")
2858 .await
2859 .unwrap_err();
2860 match err {
2861 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 404),
2862 other => panic!("expected SdkError::Api, got {:?}", other),
2863 }
2864 }
2865
2866 #[tokio::test]
2867 async fn get_endpoint_urls_success() {
2868 let server = MockServer::start().await;
2869
2870 Mock::given(method("GET"))
2871 .and(path("/endpoints/ep123/urls"))
2872 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2873 "data": {
2874 "http_url": "https://example.quiknode.pro/abc/",
2875 "wss_url": "wss://example.quiknode.pro/abc/",
2876 "multichain_urls": null
2877 },
2878 "error": null
2879 })))
2880 .mount(&server)
2881 .await;
2882
2883 let sdk = make_sdk(format!("{}/", server.uri()));
2884 let resp = sdk.admin.get_endpoint_urls("ep123").await.unwrap();
2885 let data = resp.data.unwrap();
2886 assert_eq!(data.http_url, "https://example.quiknode.pro/abc/");
2887 assert!(data.multichain_urls.is_none());
2888 }
2889
2890 #[tokio::test]
2891 async fn get_endpoint_urls_multichain() {
2892 let server = MockServer::start().await;
2893
2894 Mock::given(method("GET"))
2895 .and(path("/endpoints/ep123/urls"))
2896 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2897 "data": {
2898 "http_url": "https://example.quiknode.pro/abc/",
2899 "wss_url": null,
2900 "multichain_urls": {
2901 "ethereum-mainnet": {
2902 "http_url": "https://example.quiknode.pro/abc/eth/",
2903 "wss_url": "wss://example.quiknode.pro/abc/eth/"
2904 }
2905 }
2906 },
2907 "error": null
2908 })))
2909 .mount(&server)
2910 .await;
2911
2912 let sdk = make_sdk(format!("{}/", server.uri()));
2913 let resp = sdk.admin.get_endpoint_urls("ep123").await.unwrap();
2914 let data = resp.data.unwrap();
2915 let mc = data.multichain_urls.unwrap();
2916 assert_eq!(mc.len(), 1);
2917 assert_eq!(
2918 mc.get("ethereum-mainnet").unwrap().http_url,
2919 "https://example.quiknode.pro/abc/eth/"
2920 );
2921 }
2922
2923 #[tokio::test]
2924 async fn get_endpoint_metrics_success() {
2925 let server = MockServer::start().await;
2926
2927 Mock::given(method("GET"))
2928 .and(path("/endpoints/ep123/metrics"))
2929 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2930 "data": [{"data": [[1700000000, 42]], "tag": ["network", "mainnet"]}],
2931 "error": null
2932 })))
2933 .mount(&server)
2934 .await;
2935
2936 let sdk = make_sdk(format!("{}/", server.uri()));
2937 let params = GetEndpointMetricsRequest {
2938 period: "day".to_string(),
2939 metric: "credits_over_time".to_string(),
2940 };
2941 let resp = sdk
2942 .admin
2943 .get_endpoint_metrics("ep123", ¶ms)
2944 .await
2945 .unwrap();
2946 assert_eq!(resp.data.len(), 1);
2947 assert_eq!(
2948 resp.data[0].tag,
2949 vec!["network".to_string(), "mainnet".to_string()]
2950 );
2951 }
2952
2953 #[tokio::test]
2954 async fn get_account_metrics_success() {
2955 let server = MockServer::start().await;
2956
2957 Mock::given(method("GET"))
2958 .and(path("/metrics"))
2959 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2960 "data": [{"data": [[1700000000, 100]], "tag": "total"}],
2961 "error": null
2962 })))
2963 .mount(&server)
2964 .await;
2965
2966 let sdk = make_sdk(format!("{}/", server.uri()));
2967 let params = GetAccountMetricsRequest {
2968 period: "week".to_string(),
2969 metric: "credits_over_time".to_string(),
2970 percentile: None,
2971 };
2972 let resp = sdk.admin.get_account_metrics(¶ms).await.unwrap();
2973 assert_eq!(resp.data.len(), 1);
2974 assert_eq!(resp.data[0].tag, vec!["total".to_string()]);
2975 }
2976
2977 #[tokio::test]
2982 async fn get_account_metrics_decodes_tuple_tag() {
2983 let server = MockServer::start().await;
2984
2985 Mock::given(method("GET"))
2986 .and(path("/metrics"))
2987 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
2988 "data": [
2989 {"tag": ["network", "arbitrum-mainnet"], "data": [[1779109200, 40]]},
2990 {"tag": ["network", "mainnet"], "data": [[1779116400, 40]]},
2991 {"tag": "p95", "data": [[1779116400, 12]]}
2992 ],
2993 "error": null
2994 })))
2995 .mount(&server)
2996 .await;
2997
2998 let sdk = make_sdk(format!("{}/", server.uri()));
2999 let params = GetAccountMetricsRequest {
3000 period: "day".to_string(),
3001 metric: "credits_over_time".to_string(),
3002 percentile: None,
3003 };
3004 let resp = sdk.admin.get_account_metrics(¶ms).await.unwrap();
3005 assert_eq!(resp.data.len(), 3);
3006 assert_eq!(
3007 resp.data[0].tag,
3008 vec!["network".to_string(), "arbitrum-mainnet".to_string()]
3009 );
3010 assert_eq!(
3011 resp.data[1].tag,
3012 vec!["network".to_string(), "mainnet".to_string()]
3013 );
3014 assert_eq!(resp.data[2].tag, vec!["p95".to_string()]);
3015 }
3016
3017 #[tokio::test]
3018 async fn list_chains_success() {
3019 let server = MockServer::start().await;
3020
3021 Mock::given(method("GET"))
3022 .and(path("/chains"))
3023 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3024 "data": [
3025 {
3026 "slug": "eth",
3027 "networks": [{"slug": "mainnet", "name": "Ethereum Mainnet", "chain_id": 1}],
3028 "is_select_chain": true
3029 }
3030 ],
3031 "error": null
3032 })))
3033 .mount(&server)
3034 .await;
3035
3036 let sdk = make_sdk(format!("{}/", server.uri()));
3037 let resp = sdk.admin.list_chains().await.unwrap();
3038 assert_eq!(resp.data.len(), 1);
3039 assert_eq!(resp.data[0].slug, "eth");
3040 assert_eq!(resp.data[0].networks[0].chain_id, Some(1));
3041 }
3042
3043 #[tokio::test]
3044 async fn account_info_success() {
3045 let server = MockServer::start().await;
3046
3047 Mock::given(method("GET"))
3048 .and(path("/account/info"))
3049 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3050 "data": {
3051 "id": 794770,
3052 "name": "MCP Test Account",
3053 "created_at": "2026-03-27T20:22:32.536Z",
3054 "billing_version": "v6",
3055 "subscription": {
3056 "plan_name": "Accelerate",
3057 "status": "active",
3058 "interval": "monthly"
3059 }
3060 },
3061 "error": null
3062 })))
3063 .mount(&server)
3064 .await;
3065
3066 let sdk = make_sdk(format!("{}/", server.uri()));
3067 let resp = sdk.admin.account_info().await.unwrap();
3068 let data = resp.data.expect("expected account data");
3069 assert_eq!(data.id, 794770);
3070 assert_eq!(data.name, "MCP Test Account");
3071 assert_eq!(data.billing_version.as_deref(), Some("v6"));
3072 let subscription = data.subscription.expect("expected subscription");
3073 assert_eq!(subscription.plan_name.as_deref(), Some("Accelerate"));
3074 assert_eq!(subscription.status.as_deref(), Some("active"));
3075 assert_eq!(subscription.interval.as_deref(), Some("monthly"));
3076 }
3077
3078 #[tokio::test]
3079 async fn account_info_api_error() {
3080 let server = MockServer::start().await;
3081
3082 Mock::given(method("GET"))
3083 .and(path("/account/info"))
3084 .respond_with(ResponseTemplate::new(401).set_body_string("Unauthorized"))
3085 .mount(&server)
3086 .await;
3087
3088 let sdk = make_sdk(format!("{}/", server.uri()));
3089 let err = sdk.admin.account_info().await.unwrap_err();
3090 let SdkError::Api { status, .. } = err else {
3091 unreachable!("expected SdkError::Api, got {err:?}");
3092 };
3093 assert_eq!(status.as_u16(), 401);
3094 }
3095
3096 #[tokio::test]
3097 async fn get_api_credits_success() {
3098 let server = MockServer::start().await;
3099
3100 Mock::given(method("GET"))
3101 .and(path("/api-credits/ethereum"))
3102 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3103 "data": [
3104 {"method": "eth_chainId", "credits": 20},
3105 {"method": "eth_sendRawTransaction", "credits": 40}
3106 ],
3107 "error": null
3108 })))
3109 .mount(&server)
3110 .await;
3111
3112 let sdk = make_sdk(format!("{}/", server.uri()));
3113 let resp = sdk.admin.get_api_credits("ethereum").await.unwrap();
3114 let data = resp.data.expect("expected credits data");
3115 assert_eq!(data.len(), 2);
3116 assert_eq!(data[0].method, "eth_chainId");
3117 assert_eq!(data[0].credits, 20);
3118 assert_eq!(data[1].method, "eth_sendRawTransaction");
3119 assert_eq!(data[1].credits, 40);
3120 }
3121
3122 #[tokio::test]
3123 async fn get_api_credits_unknown_chain() {
3124 let server = MockServer::start().await;
3125
3126 Mock::given(method("GET"))
3127 .and(path("/api-credits/not-a-chain"))
3128 .respond_with(ResponseTemplate::new(404).set_body_json(serde_json::json!({
3129 "data": null,
3130 "error": "Chain not found"
3131 })))
3132 .mount(&server)
3133 .await;
3134
3135 let sdk = make_sdk(format!("{}/", server.uri()));
3136 let err = sdk.admin.get_api_credits("not-a-chain").await.unwrap_err();
3137 let SdkError::Api { status, body } = err else {
3138 unreachable!("expected SdkError::Api, got {err:?}");
3139 };
3140 assert_eq!(status.as_u16(), 404);
3141 assert!(body.contains("Chain not found"));
3142 }
3143
3144 #[tokio::test]
3145 async fn get_api_credits_api_error() {
3146 let server = MockServer::start().await;
3147
3148 Mock::given(method("GET"))
3149 .and(path("/api-credits/ethereum"))
3150 .respond_with(ResponseTemplate::new(401).set_body_string("Unauthorized"))
3151 .mount(&server)
3152 .await;
3153
3154 let sdk = make_sdk(format!("{}/", server.uri()));
3155 let err = sdk.admin.get_api_credits("ethereum").await.unwrap_err();
3156 let SdkError::Api { status, .. } = err else {
3157 unreachable!("expected SdkError::Api, got {err:?}");
3158 };
3159 assert_eq!(status.as_u16(), 401);
3160 }
3161
3162 #[tokio::test]
3163 async fn list_invoices_success() {
3164 let server = MockServer::start().await;
3165
3166 Mock::given(method("GET"))
3167 .and(path("/billing/invoices"))
3168 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3169 "data": {
3170 "invoices": [
3171 {
3172 "id": "inv123",
3173 "status": "paid",
3174 "billing_reason": "subscription",
3175 "lines": [{"description": "Pro plan", "amount": 4900}],
3176 "amount_due": 4900,
3177 "amount_paid": 4900,
3178 "period_start": 1700000000,
3179 "period_end": 1702592000,
3180 "created": 1700000000,
3181 "subtotal": 4900
3182 }
3183 ]
3184 },
3185 "error": null
3186 })))
3187 .mount(&server)
3188 .await;
3189
3190 let sdk = make_sdk(format!("{}/", server.uri()));
3191 let resp = sdk.admin.list_invoices().await.unwrap();
3192 let data = resp.data.unwrap();
3193 assert_eq!(data.invoices.len(), 1);
3194 assert_eq!(data.invoices[0].id, "inv123");
3195 }
3196
3197 #[tokio::test]
3198 async fn list_invoices_api_error() {
3199 let server = MockServer::start().await;
3200
3201 Mock::given(method("GET"))
3202 .and(path("/billing/invoices"))
3203 .respond_with(ResponseTemplate::new(401).set_body_string("Unauthorized"))
3204 .mount(&server)
3205 .await;
3206
3207 let sdk = make_sdk(format!("{}/", server.uri()));
3208 let err = sdk.admin.list_invoices().await.unwrap_err();
3209 match err {
3210 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 401),
3211 other => panic!("expected SdkError::Api, got {:?}", other),
3212 }
3213 }
3214
3215 #[tokio::test]
3216 async fn list_payments_success() {
3217 let server = MockServer::start().await;
3218
3219 Mock::given(method("GET"))
3220 .and(path("/billing/payments"))
3221 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3222 "data": {
3223 "payments": [
3224 {
3225 "amount": "49.00",
3226 "card_last_4": "4242",
3227 "created_at": "2024-01-01T00:00:00Z",
3228 "currency": "usd",
3229 "status": "succeeded",
3230 "marketplace_amount": "9.0"
3231 }
3232 ]
3233 },
3234 "error": null
3235 })))
3236 .mount(&server)
3237 .await;
3238
3239 let sdk = make_sdk(format!("{}/", server.uri()));
3240 let resp = sdk.admin.list_payments().await.unwrap();
3241 let data = resp.data.unwrap();
3242 assert_eq!(data.payments.len(), 1);
3243 assert_eq!(data.payments[0].currency, "usd");
3244 }
3245
3246 #[tokio::test]
3247 async fn list_teams_success() {
3248 let server = MockServer::start().await;
3249
3250 Mock::given(method("GET"))
3251 .and(path("/teams"))
3252 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3253 "data": [{"id": 1, "name": "Engineering", "members_count": 5, "users": []}],
3254 "error": null
3255 })))
3256 .mount(&server)
3257 .await;
3258
3259 let sdk = make_sdk(format!("{}/", server.uri()));
3260 let resp = sdk.admin.list_teams().await.unwrap();
3261 assert_eq!(resp.data.len(), 1);
3262 assert_eq!(resp.data[0].name, "Engineering");
3263 }
3264
3265 #[tokio::test]
3266 async fn list_teams_api_error() {
3267 let server = MockServer::start().await;
3268
3269 Mock::given(method("GET"))
3270 .and(path("/teams"))
3271 .respond_with(ResponseTemplate::new(401).set_body_string("Unauthorized"))
3272 .mount(&server)
3273 .await;
3274
3275 let sdk = make_sdk(format!("{}/", server.uri()));
3276 let err = sdk.admin.list_teams().await.unwrap_err();
3277 match err {
3278 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 401),
3279 other => panic!("expected SdkError::Api, got {:?}", other),
3280 }
3281 }
3282
3283 #[tokio::test]
3284 async fn create_team_success() {
3285 let server = MockServer::start().await;
3286
3287 Mock::given(method("POST"))
3288 .and(path("/teams"))
3289 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3290 "data": {"id": 42, "name": "New Team", "default_role": null, "members_count": 0},
3291 "error": null
3292 })))
3293 .mount(&server)
3294 .await;
3295
3296 let sdk = make_sdk(format!("{}/", server.uri()));
3297 let params = CreateTeamRequest {
3298 name: "New Team".to_string(),
3299 };
3300 let resp = sdk.admin.create_team(¶ms).await.unwrap();
3301 assert_eq!(resp.data.unwrap().id, 42);
3302 }
3303
3304 #[tokio::test]
3305 async fn get_team_success() {
3306 let server = MockServer::start().await;
3307
3308 Mock::given(method("GET"))
3309 .and(path("/teams/1"))
3310 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3311 "data": {
3312 "id": 1,
3313 "name": "Engineering",
3314 "default_role": "member",
3315 "members_count": 3,
3316 "users": [],
3317 "pending_invites": []
3318 },
3319 "error": null
3320 })))
3321 .mount(&server)
3322 .await;
3323
3324 let sdk = make_sdk(format!("{}/", server.uri()));
3325 let resp = sdk.admin.get_team(1).await.unwrap();
3326 assert_eq!(resp.data.unwrap().name, "Engineering");
3327 }
3328
3329 #[tokio::test]
3330 async fn delete_team_success() {
3331 let server = MockServer::start().await;
3332
3333 Mock::given(method("DELETE"))
3334 .and(path("/teams/1"))
3335 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3336 "data": {"message": "Team deleted"},
3337 "error": null
3338 })))
3339 .mount(&server)
3340 .await;
3341
3342 let sdk = make_sdk(format!("{}/", server.uri()));
3343 let resp = sdk.admin.delete_team(1).await.unwrap();
3344 assert!(resp.data.is_some());
3345 }
3346
3347 #[tokio::test]
3348 async fn list_team_endpoints_success() {
3349 let server = MockServer::start().await;
3350
3351 Mock::given(method("GET"))
3352 .and(path("/teams/1/endpoints"))
3353 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3354 "data": [{"id": 10, "subdomain": "abc123", "chain": "ethereum", "network": "mainnet"}],
3355 "error": null
3356 })))
3357 .mount(&server)
3358 .await;
3359
3360 let sdk = make_sdk(format!("{}/", server.uri()));
3361 let resp = sdk.admin.list_team_endpoints(1).await.unwrap();
3362 assert_eq!(resp.data.len(), 1);
3363 assert_eq!(resp.data[0].subdomain, "abc123");
3364 }
3365
3366 #[tokio::test]
3367 async fn update_team_endpoints_success() {
3368 let server = MockServer::start().await;
3369
3370 Mock::given(method("PATCH"))
3371 .and(path("/teams/1/endpoints"))
3372 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3373 "data": {"success": true},
3374 "error": null
3375 })))
3376 .mount(&server)
3377 .await;
3378
3379 let sdk = make_sdk(format!("{}/", server.uri()));
3380 let params = UpdateTeamEndpointsRequest {
3381 endpoint_ids: vec!["ep1".to_string()],
3382 };
3383 let resp = sdk.admin.update_team_endpoints(1, ¶ms).await.unwrap();
3384 assert!(resp.data.unwrap().success.unwrap());
3385 }
3386
3387 #[tokio::test]
3391 async fn update_team_endpoints_empty_array_wire_body() {
3392 use wiremock::matchers::body_json;
3393 let server = MockServer::start().await;
3394 Mock::given(method("PATCH"))
3395 .and(path("/teams/1/endpoints"))
3396 .and(body_json(serde_json::json!({ "endpoint_ids": [] })))
3397 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3398 "data": {"success": true},
3399 "error": null
3400 })))
3401 .mount(&server)
3402 .await;
3403 let sdk = make_sdk(format!("{}/", server.uri()));
3404 let params = UpdateTeamEndpointsRequest {
3405 endpoint_ids: vec![],
3406 };
3407 sdk.admin.update_team_endpoints(1, ¶ms).await.unwrap();
3408 }
3409
3410 #[tokio::test]
3411 async fn invite_team_member_success() {
3412 let server = MockServer::start().await;
3413
3414 Mock::given(method("POST"))
3415 .and(path("/teams/1/members"))
3416 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3417 "data": {
3418 "id": 99,
3419 "email": "user@example.com",
3420 "full_name": null,
3421 "role": "member",
3422 "status": "pending",
3423 "created_at": null,
3424 "photo_url": null,
3425 "account_primary_user": null
3426 },
3427 "error": null
3428 })))
3429 .mount(&server)
3430 .await;
3431
3432 let sdk = make_sdk(format!("{}/", server.uri()));
3433 let params = InviteTeamMemberRequest {
3434 email: "user@example.com".to_string(),
3435 full_name: None,
3436 role: None,
3437 };
3438 let resp = sdk.admin.invite_team_member(1, ¶ms).await.unwrap();
3439 assert_eq!(resp.data.unwrap().email, "user@example.com");
3440 }
3441
3442 #[tokio::test]
3443 async fn remove_team_member_success() {
3444 let server = MockServer::start().await;
3445
3446 Mock::given(method("DELETE"))
3447 .and(path("/teams/1/members/99"))
3448 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3449 "data": {"message": "Member removed"},
3450 "error": null
3451 })))
3452 .mount(&server)
3453 .await;
3454
3455 let sdk = make_sdk(format!("{}/", server.uri()));
3456 let params = RemoveTeamMemberRequest { destroy_user: None };
3457 let resp = sdk.admin.remove_team_member(1, 99, ¶ms).await.unwrap();
3458 assert!(resp.data.is_some());
3459 }
3460
3461 #[tokio::test]
3462 async fn resend_team_invite_success() {
3463 let server = MockServer::start().await;
3464
3465 Mock::given(method("POST"))
3466 .and(path("/teams/1/members/99/resend_invite"))
3467 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3468 "data": {"message": "Invite resent"},
3469 "error": null
3470 })))
3471 .mount(&server)
3472 .await;
3473
3474 let sdk = make_sdk(format!("{}/", server.uri()));
3475 let resp = sdk.admin.resend_team_invite(1, 99).await.unwrap();
3476 assert!(resp.data.is_some());
3477 }
3478
3479 #[tokio::test]
3480 async fn bulk_update_endpoint_status_success() {
3481 let server = MockServer::start().await;
3482 Mock::given(method("POST"))
3483 .and(path("/endpoints/bulk/status"))
3484 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3485 "data": {
3486 "total": 2,
3487 "updated_count": 2,
3488 "failed_count": 0,
3489 "results": [
3490 { "id": "a", "success": true },
3491 { "id": "b", "success": true }
3492 ]
3493 }
3494 })))
3495 .mount(&server)
3496 .await;
3497
3498 let sdk = make_sdk(format!("{}/", server.uri()));
3499 let params = BulkUpdateEndpointStatusRequest {
3500 ids: vec!["a".to_string(), "b".to_string()],
3501 status: "paused".to_string(),
3502 };
3503 let resp = sdk
3504 .admin
3505 .bulk_update_endpoint_status(¶ms)
3506 .await
3507 .unwrap();
3508 let data = resp.data.expect("data present");
3509 assert_eq!(data.total, 2);
3510 assert_eq!(data.updated_count, 2);
3511 assert_eq!(data.results.len(), 2);
3512 }
3513
3514 #[tokio::test]
3515 async fn bulk_update_endpoint_status_api_error() {
3516 let server = MockServer::start().await;
3517 Mock::given(method("POST"))
3518 .and(path("/endpoints/bulk/status"))
3519 .respond_with(ResponseTemplate::new(400).set_body_string("bad request"))
3520 .mount(&server)
3521 .await;
3522
3523 let sdk = make_sdk(format!("{}/", server.uri()));
3524 let params = BulkUpdateEndpointStatusRequest {
3525 ids: vec!["a".to_string()],
3526 status: "paused".to_string(),
3527 };
3528 let err = sdk
3529 .admin
3530 .bulk_update_endpoint_status(¶ms)
3531 .await
3532 .unwrap_err();
3533 match err {
3534 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 400),
3535 other => panic!("expected Api, got {:?}", other),
3536 }
3537 }
3538
3539 #[tokio::test]
3540 async fn bulk_add_tag_success() {
3541 let server = MockServer::start().await;
3542 Mock::given(method("POST"))
3543 .and(path("/endpoints/bulk/tags"))
3544 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3545 "data": {
3546 "total": 1,
3547 "updated_count": 1,
3548 "failed_count": 0,
3549 "results": [{ "id": "a", "success": true }],
3550 "tag": { "tag_id": 7, "label": "prod" }
3551 }
3552 })))
3553 .mount(&server)
3554 .await;
3555
3556 let sdk = make_sdk(format!("{}/", server.uri()));
3557 let params = BulkAddTagRequest {
3558 ids: vec!["a".to_string()],
3559 label: "prod".to_string(),
3560 };
3561 let resp = sdk.admin.bulk_add_tag(¶ms).await.unwrap();
3562 let data = resp.data.expect("data present");
3563 assert_eq!(data.tag.tag_id, 7);
3564 assert_eq!(data.tag.label, "prod");
3565 }
3566
3567 #[tokio::test]
3568 async fn bulk_remove_tag_success() {
3569 let server = MockServer::start().await;
3570 Mock::given(method("DELETE"))
3571 .and(path("/endpoints/bulk/tags"))
3572 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3573 "data": {
3574 "total": 2,
3575 "updated_count": 2,
3576 "failed_count": 0,
3577 "results": [
3578 { "id": "a", "success": true },
3579 { "id": "b", "success": true }
3580 ]
3581 }
3582 })))
3583 .mount(&server)
3584 .await;
3585
3586 let sdk = make_sdk(format!("{}/", server.uri()));
3587 let params = BulkRemoveTagRequest {
3588 ids: vec!["a".to_string(), "b".to_string()],
3589 tag_id: 42,
3590 };
3591 let resp = sdk.admin.bulk_remove_tag(¶ms).await.unwrap();
3592 assert_eq!(resp.data.expect("data").updated_count, 2);
3593 }
3594
3595 #[test]
3596 fn endpoint_token_debug_is_redacted() {
3597 let t = EndpointToken {
3598 id: "tok_1".to_string(),
3599 token: "super-secret".to_string(),
3600 };
3601 let dbg = format!("{t:?}");
3602 assert!(dbg.contains("tok_1"));
3603 assert!(!dbg.contains("super-secret"));
3604 assert!(dbg.contains("[redacted]"));
3605 }
3606
3607 #[test]
3608 fn endpoint_jwt_debug_redacts_public_key() {
3609 let j = EndpointJwt {
3610 id: "jwt_1".to_string(),
3611 public_key: "-----BEGIN PUBLIC KEY-----\nAAAA\n-----END PUBLIC KEY-----".to_string(),
3612 kid: "kid1".to_string(),
3613 name: "myjwt".to_string(),
3614 };
3615 let dbg = format!("{j:?}");
3616 assert!(dbg.contains("jwt_1"));
3617 assert!(dbg.contains("kid1"));
3618 assert!(!dbg.contains("BEGIN PUBLIC KEY"));
3619 assert!(dbg.contains("[redacted]"));
3620 }
3621
3622 #[tokio::test]
3623 async fn bulk_methods_reject_empty_ids() {
3624 let sdk = make_sdk("http://127.0.0.1:1/".to_string());
3626
3627 let err = sdk
3628 .admin
3629 .bulk_update_endpoint_status(&BulkUpdateEndpointStatusRequest {
3630 ids: vec![],
3631 status: "paused".to_string(),
3632 })
3633 .await
3634 .unwrap_err();
3635 assert!(matches!(err, SdkError::Config(_)));
3636
3637 let err = sdk
3638 .admin
3639 .bulk_add_tag(&BulkAddTagRequest {
3640 ids: vec![],
3641 label: "x".to_string(),
3642 })
3643 .await
3644 .unwrap_err();
3645 assert!(matches!(err, SdkError::Config(_)));
3646
3647 let err = sdk
3648 .admin
3649 .bulk_remove_tag(&BulkRemoveTagRequest {
3650 ids: vec![],
3651 tag_id: 1,
3652 })
3653 .await
3654 .unwrap_err();
3655 assert!(matches!(err, SdkError::Config(_)));
3656 }
3657
3658 #[tokio::test]
3659 async fn list_tags_success() {
3660 let server = MockServer::start().await;
3661 Mock::given(method("GET"))
3662 .and(path("/endpoints/tags"))
3663 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3664 "data": {
3665 "tags": [
3666 { "id": 1, "label": "prod", "usage_count": 3 },
3667 { "id": 2, "label": "staging", "usage_count": 0 }
3668 ]
3669 },
3670 "error": null
3671 })))
3672 .mount(&server)
3673 .await;
3674
3675 let sdk = make_sdk(format!("{}/", server.uri()));
3676 let resp = sdk.admin.list_tags().await.unwrap();
3677 let data = resp.data.expect("data present");
3678 assert_eq!(data.tags.len(), 2);
3679 assert_eq!(data.tags[0].label, "prod");
3680 assert_eq!(data.tags[1].usage_count, 0);
3681 }
3682
3683 #[tokio::test]
3684 async fn list_tags_api_error() {
3685 let server = MockServer::start().await;
3686 Mock::given(method("GET"))
3687 .and(path("/endpoints/tags"))
3688 .respond_with(ResponseTemplate::new(500).set_body_string("oops"))
3689 .mount(&server)
3690 .await;
3691
3692 let sdk = make_sdk(format!("{}/", server.uri()));
3693 let err = sdk.admin.list_tags().await.unwrap_err();
3694 match err {
3695 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 500),
3696 other => panic!("expected Api, got {:?}", other),
3697 }
3698 }
3699
3700 #[tokio::test]
3701 async fn rename_tag_success() {
3702 let server = MockServer::start().await;
3703 Mock::given(method("PATCH"))
3704 .and(path("/endpoints/tags/7"))
3705 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3706 "data": { "id": 7, "label": "prod-v2", "usage_count": 3 },
3707 "error": null
3708 })))
3709 .mount(&server)
3710 .await;
3711
3712 let sdk = make_sdk(format!("{}/", server.uri()));
3713 let params = RenameTagRequest {
3714 label: "prod-v2".to_string(),
3715 };
3716 let resp = sdk.admin.rename_tag(7, ¶ms).await.unwrap();
3717 let tag = resp.data.expect("tag present");
3718 assert_eq!(tag.id, 7);
3719 assert_eq!(tag.label, "prod-v2");
3720 }
3721
3722 #[tokio::test]
3723 async fn delete_account_tag_success() {
3724 let server = MockServer::start().await;
3725 Mock::given(method("DELETE"))
3726 .and(path("/endpoints/tags/7"))
3727 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3728 "data": { "success": true },
3729 "error": null
3730 })))
3731 .mount(&server)
3732 .await;
3733
3734 let sdk = make_sdk(format!("{}/", server.uri()));
3735 let resp = sdk.admin.delete_account_tag(7).await.unwrap();
3736 assert!(resp.data.expect("data").success);
3737 }
3738
3739 #[tokio::test]
3740 async fn delete_account_tag_still_in_use() {
3741 let server = MockServer::start().await;
3742 Mock::given(method("DELETE"))
3743 .and(path("/endpoints/tags/7"))
3744 .respond_with(ResponseTemplate::new(400).set_body_string("tag still in use"))
3745 .mount(&server)
3746 .await;
3747
3748 let sdk = make_sdk(format!("{}/", server.uri()));
3749 let err = sdk.admin.delete_account_tag(7).await.unwrap_err();
3750 match err {
3751 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 400),
3752 other => panic!("expected Api, got {:?}", other),
3753 }
3754 }
3755
3756 #[tokio::test]
3757 async fn get_usage_by_tag_success() {
3758 let server = MockServer::start().await;
3759 Mock::given(method("GET"))
3760 .and(path("/usage/rpc/by-tag"))
3761 .and(query_param("start_time", "1700000000"))
3762 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3763 "data": {
3764 "tags": [
3765 { "tag_id": 1, "label": "prod", "credits_used": 1234, "requests": 10 },
3766 { "tag_id": null, "label": "untagged", "credits_used": 50, "requests": 2 }
3767 ],
3768 "start_time": 1700000000,
3769 "end_time": 1700003600
3770 },
3771 "error": null
3772 })))
3773 .mount(&server)
3774 .await;
3775
3776 let sdk = make_sdk(format!("{}/", server.uri()));
3777 let params = GetUsageRequest {
3778 start_time: Some(1_700_000_000),
3779 ..Default::default()
3780 };
3781 let resp = sdk.admin.get_usage_by_tag(¶ms).await.unwrap();
3782 let data = resp.data.expect("data present");
3783 assert_eq!(data.tags.len(), 2);
3784 assert_eq!(data.tags[0].tag_id, Some(1));
3785 assert_eq!(data.tags[1].tag_id, None);
3786 assert_eq!(data.tags[1].label, "untagged");
3787 }
3788
3789 #[tokio::test]
3790 async fn get_endpoint_security_success() {
3791 let server = MockServer::start().await;
3792 Mock::given(method("GET"))
3793 .and(path("/endpoints/abc123/security"))
3794 .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
3795 "data": {
3796 "options": { "tokens": true, "ips": false },
3797 "tokens": [{ "id": "tok_1", "token": "secret" }],
3798 "jwts": [],
3799 "referrers": [],
3800 "domain_masks": [],
3801 "ips": [],
3802 "request_filters": []
3803 },
3804 "error": null
3805 })))
3806 .mount(&server)
3807 .await;
3808
3809 let sdk = make_sdk(format!("{}/", server.uri()));
3810 let resp = sdk.admin.get_endpoint_security("abc123").await.unwrap();
3811 let data = resp.data.expect("data present");
3812 let tokens = data.tokens.expect("tokens present");
3813 assert_eq!(tokens.len(), 1);
3814 assert_eq!(tokens[0].id, "tok_1");
3815 }
3816
3817 #[tokio::test]
3818 async fn get_endpoint_security_not_found() {
3819 let server = MockServer::start().await;
3820 Mock::given(method("GET"))
3821 .and(path("/endpoints/missing/security"))
3822 .respond_with(ResponseTemplate::new(404).set_body_string("not found"))
3823 .mount(&server)
3824 .await;
3825
3826 let sdk = make_sdk(format!("{}/", server.uri()));
3827 let err = sdk
3828 .admin
3829 .get_endpoint_security("missing")
3830 .await
3831 .unwrap_err();
3832 match err {
3833 SdkError::Api { status, .. } => assert_eq!(status.as_u16(), 404),
3834 other => panic!("expected Api, got {:?}", other),
3835 }
3836 }
3837
3838 #[test]
3839 fn negative_timeout_secs_returns_error() {
3840 use crate::{HttpConfig, SdkConfig, SdkFullConfig};
3841 let result = SdkConfig::new(&SdkFullConfig {
3842 api_key: Some("test-key".to_string()),
3843 http: Some(HttpConfig {
3844 timeout_secs: Some(-1),
3845 pool_max_idle_per_host: None,
3846 headers: None,
3847 }),
3848 admin: None,
3849 streams: None,
3850 webhooks: None,
3851 kvstore: None,
3852 sql: None,
3853 rpc: None,
3854 });
3855 assert!(matches!(result, Err(crate::errors::SdkError::Config(_))));
3856 }
3857}