@@ -38,6 +38,17 @@ pub struct FilterParams {
38
38
/// Retrieve tokens that are (or are not) revoked
39
39
#[ serde( rename = "filter[revoked]" ) ]
40
40
revoked : Option < bool > ,
41
+
42
+ /// Retrieve tokens that are (or are not) expired
43
+ #[ serde( rename = "filter[expired]" ) ]
44
+ expired : Option < bool > ,
45
+
46
+ /// Retrieve tokens that are (or are not) valid
47
+ ///
48
+ /// Valid means that the token has not expired, is not revoked, and has not
49
+ /// reached its usage limit.
50
+ #[ serde( rename = "filter[valid]" ) ]
51
+ valid : Option < bool > ,
41
52
}
42
53
43
54
impl std:: fmt:: Display for FilterParams {
@@ -52,6 +63,14 @@ impl std::fmt::Display for FilterParams {
52
63
write ! ( f, "{sep}filter[revoked]={revoked}" ) ?;
53
64
sep = '&' ;
54
65
}
66
+ if let Some ( expired) = self . expired {
67
+ write ! ( f, "{sep}filter[expired]={expired}" ) ?;
68
+ sep = '&' ;
69
+ }
70
+ if let Some ( valid) = self . valid {
71
+ write ! ( f, "{sep}filter[valid]={valid}" ) ?;
72
+ sep = '&' ;
73
+ }
55
74
56
75
let _ = sep;
57
76
Ok ( ( ) )
@@ -109,12 +128,14 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
109
128
110
129
#[ tracing:: instrument( name = "handler.admin.v1.registration_tokens.list" , skip_all) ]
111
130
pub async fn handler (
112
- CallContext { mut repo, .. } : CallContext ,
131
+ CallContext {
132
+ mut repo, clock, ..
133
+ } : CallContext ,
113
134
Pagination ( pagination) : Pagination ,
114
135
params : FilterParams ,
115
136
) -> Result < Json < PaginatedResponse < UserRegistrationToken > > , RouteError > {
116
137
let base = format ! ( "{path}{params}" , path = UserRegistrationToken :: PATH ) ;
117
- let mut filter = UserRegistrationTokenFilter :: new ( ) ;
138
+ let mut filter = UserRegistrationTokenFilter :: new ( clock . now ( ) ) ;
118
139
119
140
if let Some ( used) = params. used {
120
141
filter = filter. with_been_used ( used) ;
@@ -124,6 +145,14 @@ pub async fn handler(
124
145
filter = filter. with_revoked ( revoked) ;
125
146
}
126
147
148
+ if let Some ( expired) = params. expired {
149
+ filter = filter. with_expired ( expired) ;
150
+ }
151
+
152
+ if let Some ( valid) = params. valid {
153
+ filter = filter. with_valid ( valid) ;
154
+ }
155
+
127
156
let page = repo
128
157
. user_registration_token ( )
129
158
. list ( filter, pagination)
@@ -612,6 +641,274 @@ mod tests {
612
641
"# ) ;
613
642
}
614
643
644
+ #[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
645
+ async fn test_filter_by_expired ( pool : PgPool ) {
646
+ setup ( ) ;
647
+ let mut state = TestState :: from_pool ( pool) . await . unwrap ( ) ;
648
+ let admin_token = state. token_with_scope ( "urn:mas:admin" ) . await ;
649
+ create_test_tokens ( & mut state) . await ;
650
+
651
+ // Filter for expired tokens
652
+ let request = Request :: get ( "/api/admin/v1/user-registration-tokens?filter[expired]=true" )
653
+ . bearer ( & admin_token)
654
+ . empty ( ) ;
655
+ let response = state. request ( request) . await ;
656
+ response. assert_status ( StatusCode :: OK ) ;
657
+
658
+ let body: serde_json:: Value = response. json ( ) ;
659
+ insta:: assert_json_snapshot!( body, @r#"
660
+ {
661
+ "meta": {
662
+ "count": 1
663
+ },
664
+ "data": [
665
+ {
666
+ "type": "user-registration_token",
667
+ "id": "01FSHN9AG064K8BYZXSY5G511Z",
668
+ "attributes": {
669
+ "token": "token_expired",
670
+ "usage_limit": 5,
671
+ "times_used": 0,
672
+ "created_at": "2022-01-16T14:40:00Z",
673
+ "last_used_at": null,
674
+ "expires_at": "2022-01-15T14:40:00Z",
675
+ "revoked_at": null
676
+ },
677
+ "links": {
678
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
679
+ }
680
+ }
681
+ ],
682
+ "links": {
683
+ "self": "/api/admin/v1/user-registration-tokens?filter[expired]=true&page[first]=10",
684
+ "first": "/api/admin/v1/user-registration-tokens?filter[expired]=true&page[first]=10",
685
+ "last": "/api/admin/v1/user-registration-tokens?filter[expired]=true&page[last]=10"
686
+ }
687
+ }
688
+ "# ) ;
689
+
690
+ // Filter for non-expired tokens
691
+ let request = Request :: get ( "/api/admin/v1/user-registration-tokens?filter[expired]=false" )
692
+ . bearer ( & admin_token)
693
+ . empty ( ) ;
694
+ let response = state. request ( request) . await ;
695
+ response. assert_status ( StatusCode :: OK ) ;
696
+
697
+ let body: serde_json:: Value = response. json ( ) ;
698
+ insta:: assert_json_snapshot!( body, @r#"
699
+ {
700
+ "meta": {
701
+ "count": 4
702
+ },
703
+ "data": [
704
+ {
705
+ "type": "user-registration_token",
706
+ "id": "01FSHN9AG07HNEZXNQM2KNBNF6",
707
+ "attributes": {
708
+ "token": "token_used",
709
+ "usage_limit": 10,
710
+ "times_used": 1,
711
+ "created_at": "2022-01-16T14:40:00Z",
712
+ "last_used_at": "2022-01-16T14:40:00Z",
713
+ "expires_at": null,
714
+ "revoked_at": null
715
+ },
716
+ "links": {
717
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
718
+ }
719
+ },
720
+ {
721
+ "type": "user-registration_token",
722
+ "id": "01FSHN9AG09AVTNSQFMSR34AJC",
723
+ "attributes": {
724
+ "token": "token_revoked",
725
+ "usage_limit": 10,
726
+ "times_used": 0,
727
+ "created_at": "2022-01-16T14:40:00Z",
728
+ "last_used_at": null,
729
+ "expires_at": null,
730
+ "revoked_at": "2022-01-16T14:40:00Z"
731
+ },
732
+ "links": {
733
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
734
+ }
735
+ },
736
+ {
737
+ "type": "user-registration_token",
738
+ "id": "01FSHN9AG0MZAA6S4AF7CTV32E",
739
+ "attributes": {
740
+ "token": "token_unused",
741
+ "usage_limit": 10,
742
+ "times_used": 0,
743
+ "created_at": "2022-01-16T14:40:00Z",
744
+ "last_used_at": null,
745
+ "expires_at": null,
746
+ "revoked_at": null
747
+ },
748
+ "links": {
749
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
750
+ }
751
+ },
752
+ {
753
+ "type": "user-registration_token",
754
+ "id": "01FSHN9AG0S3ZJD8CXQ7F11KXN",
755
+ "attributes": {
756
+ "token": "token_used_revoked",
757
+ "usage_limit": 10,
758
+ "times_used": 1,
759
+ "created_at": "2022-01-16T14:40:00Z",
760
+ "last_used_at": "2022-01-16T14:40:00Z",
761
+ "expires_at": null,
762
+ "revoked_at": "2022-01-16T14:40:00Z"
763
+ },
764
+ "links": {
765
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
766
+ }
767
+ }
768
+ ],
769
+ "links": {
770
+ "self": "/api/admin/v1/user-registration-tokens?filter[expired]=false&page[first]=10",
771
+ "first": "/api/admin/v1/user-registration-tokens?filter[expired]=false&page[first]=10",
772
+ "last": "/api/admin/v1/user-registration-tokens?filter[expired]=false&page[last]=10"
773
+ }
774
+ }
775
+ "# ) ;
776
+ }
777
+
778
+ #[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
779
+ async fn test_filter_by_valid ( pool : PgPool ) {
780
+ setup ( ) ;
781
+ let mut state = TestState :: from_pool ( pool) . await . unwrap ( ) ;
782
+ let admin_token = state. token_with_scope ( "urn:mas:admin" ) . await ;
783
+ create_test_tokens ( & mut state) . await ;
784
+
785
+ // Filter for valid tokens
786
+ let request = Request :: get ( "/api/admin/v1/user-registration-tokens?filter[valid]=true" )
787
+ . bearer ( & admin_token)
788
+ . empty ( ) ;
789
+ let response = state. request ( request) . await ;
790
+ response. assert_status ( StatusCode :: OK ) ;
791
+
792
+ let body: serde_json:: Value = response. json ( ) ;
793
+ insta:: assert_json_snapshot!( body, @r#"
794
+ {
795
+ "meta": {
796
+ "count": 2
797
+ },
798
+ "data": [
799
+ {
800
+ "type": "user-registration_token",
801
+ "id": "01FSHN9AG07HNEZXNQM2KNBNF6",
802
+ "attributes": {
803
+ "token": "token_used",
804
+ "usage_limit": 10,
805
+ "times_used": 1,
806
+ "created_at": "2022-01-16T14:40:00Z",
807
+ "last_used_at": "2022-01-16T14:40:00Z",
808
+ "expires_at": null,
809
+ "revoked_at": null
810
+ },
811
+ "links": {
812
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG07HNEZXNQM2KNBNF6"
813
+ }
814
+ },
815
+ {
816
+ "type": "user-registration_token",
817
+ "id": "01FSHN9AG0MZAA6S4AF7CTV32E",
818
+ "attributes": {
819
+ "token": "token_unused",
820
+ "usage_limit": 10,
821
+ "times_used": 0,
822
+ "created_at": "2022-01-16T14:40:00Z",
823
+ "last_used_at": null,
824
+ "expires_at": null,
825
+ "revoked_at": null
826
+ },
827
+ "links": {
828
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
829
+ }
830
+ }
831
+ ],
832
+ "links": {
833
+ "self": "/api/admin/v1/user-registration-tokens?filter[valid]=true&page[first]=10",
834
+ "first": "/api/admin/v1/user-registration-tokens?filter[valid]=true&page[first]=10",
835
+ "last": "/api/admin/v1/user-registration-tokens?filter[valid]=true&page[last]=10"
836
+ }
837
+ }
838
+ "# ) ;
839
+
840
+ // Filter for invalid tokens
841
+ let request = Request :: get ( "/api/admin/v1/user-registration-tokens?filter[valid]=false" )
842
+ . bearer ( & admin_token)
843
+ . empty ( ) ;
844
+ let response = state. request ( request) . await ;
845
+ response. assert_status ( StatusCode :: OK ) ;
846
+
847
+ let body: serde_json:: Value = response. json ( ) ;
848
+ insta:: assert_json_snapshot!( body, @r#"
849
+ {
850
+ "meta": {
851
+ "count": 3
852
+ },
853
+ "data": [
854
+ {
855
+ "type": "user-registration_token",
856
+ "id": "01FSHN9AG064K8BYZXSY5G511Z",
857
+ "attributes": {
858
+ "token": "token_expired",
859
+ "usage_limit": 5,
860
+ "times_used": 0,
861
+ "created_at": "2022-01-16T14:40:00Z",
862
+ "last_used_at": null,
863
+ "expires_at": "2022-01-15T14:40:00Z",
864
+ "revoked_at": null
865
+ },
866
+ "links": {
867
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG064K8BYZXSY5G511Z"
868
+ }
869
+ },
870
+ {
871
+ "type": "user-registration_token",
872
+ "id": "01FSHN9AG09AVTNSQFMSR34AJC",
873
+ "attributes": {
874
+ "token": "token_revoked",
875
+ "usage_limit": 10,
876
+ "times_used": 0,
877
+ "created_at": "2022-01-16T14:40:00Z",
878
+ "last_used_at": null,
879
+ "expires_at": null,
880
+ "revoked_at": "2022-01-16T14:40:00Z"
881
+ },
882
+ "links": {
883
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG09AVTNSQFMSR34AJC"
884
+ }
885
+ },
886
+ {
887
+ "type": "user-registration_token",
888
+ "id": "01FSHN9AG0S3ZJD8CXQ7F11KXN",
889
+ "attributes": {
890
+ "token": "token_used_revoked",
891
+ "usage_limit": 10,
892
+ "times_used": 1,
893
+ "created_at": "2022-01-16T14:40:00Z",
894
+ "last_used_at": "2022-01-16T14:40:00Z",
895
+ "expires_at": null,
896
+ "revoked_at": "2022-01-16T14:40:00Z"
897
+ },
898
+ "links": {
899
+ "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0S3ZJD8CXQ7F11KXN"
900
+ }
901
+ }
902
+ ],
903
+ "links": {
904
+ "self": "/api/admin/v1/user-registration-tokens?filter[valid]=false&page[first]=10",
905
+ "first": "/api/admin/v1/user-registration-tokens?filter[valid]=false&page[first]=10",
906
+ "last": "/api/admin/v1/user-registration-tokens?filter[valid]=false&page[last]=10"
907
+ }
908
+ }
909
+ "# ) ;
910
+ }
911
+
615
912
#[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
616
913
async fn test_combined_filters ( pool : PgPool ) {
617
914
setup ( ) ;
0 commit comments