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