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