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