@@ -3,14 +3,16 @@ import {
3
3
ERROR_WHILE_CREATING_REQUEST ,
4
4
IMPERSONATION_NOT_COMPLETED ,
5
5
REQUEST_ALREADY_PENDING ,
6
- REQUEST_STATE
6
+ REQUEST_STATE ,
7
+ ERROR_WHILE_FETCHING_REQUEST
7
8
} from "../constants/requests" ;
8
9
import { Timestamp } from "firebase-admin/firestore" ;
9
- import { CreateImpersonationRequestModelDto , ImpersonationRequest } from "../types/impersonationRequest" ;
10
+ import { Query , CollectionReference } from '@google-cloud/firestore' ;
11
+ import { CreateImpersonationRequestModelDto , ImpersonationRequest , PaginatedImpersonationRequests , ImpersonationRequestQuery } from "../types/impersonationRequest" ;
10
12
import { Forbidden } from "http-errors" ;
11
13
const logger = require ( "../utils/logger" ) ;
12
-
13
14
const impersonationRequestModel = firestore . collection ( "impersonationRequests" ) ;
15
+ const DEFAULT_PAGE_SIZE = 5 ;
14
16
15
17
/**
16
18
* Creates a new impersonation request in Firestore.
@@ -55,4 +57,115 @@ export const createImpersonationRequest = async (
55
57
logger . error ( ERROR_WHILE_CREATING_REQUEST , { error, requestData : body } ) ;
56
58
throw error ;
57
59
}
58
- } ;
60
+ } ;
61
+
62
+ /**
63
+ * Retrieves an impersonation request by its ID.
64
+ * @param {string } id - The ID of the impersonation request to retrieve.
65
+ * @returns {Promise<ImpersonationRequest|null> } The found impersonation request or null if not found.
66
+ * @throws {Error } Logs and rethrows any error encountered during fetch.
67
+ */
68
+ export const getImpersonationRequestById = async (
69
+ id : string
70
+ ) : Promise < ImpersonationRequest | null > => {
71
+ try {
72
+ const requestDoc = await impersonationRequestModel . doc ( id ) . get ( ) ;
73
+ if ( ! requestDoc . exists ) {
74
+ return null ;
75
+ }
76
+ const data = requestDoc . data ( ) as ImpersonationRequest ;
77
+ return {
78
+ id : requestDoc . id ,
79
+ ...data ,
80
+ } ;
81
+ } catch ( error ) {
82
+ logger . error ( `${ ERROR_WHILE_FETCHING_REQUEST } for ID: ${ id } ` , error ) ;
83
+ throw error ;
84
+ }
85
+ } ;
86
+
87
+ /**
88
+ * Retrieves a paginated list of impersonation requests based on query filters.
89
+ * @param {object } query - The query filters.
90
+ * @param {string } [query.createdBy] - Filter by the username of the request creator.
91
+ * @param {string } [query.createdFor] - Filter by the username of the user the request is created for.
92
+ * @param {string } [query.status] - Filter by request status (e.g., "APPROVED", "PENDING", "REJECTED").
93
+ * @param {string } [query.prev] - Document ID to use as the ending point for backward pagination.
94
+ * @param {string } [query.next] - Document ID to use as the starting point for forward pagination.
95
+ * @param {string } [query.size] - Number of results per page.
96
+ * @returns {Promise<PaginatedImpersonationRequests|null> } The paginated impersonation requests or null if none found.
97
+ * @throws Logs and rethrows any error encountered during fetch.
98
+ */
99
+ export const getImpersonationRequests = async (
100
+ query
101
+ ) : Promise < PaginatedImpersonationRequests | null > => {
102
+
103
+ let { createdBy, createdFor, status, prev, next, size = DEFAULT_PAGE_SIZE } = query ;
104
+
105
+ size = size ? Number . parseInt ( size ) : DEFAULT_PAGE_SIZE ;
106
+
107
+
108
+ try {
109
+ let requestQuery : Query < ImpersonationRequest > = impersonationRequestModel as CollectionReference < ImpersonationRequest > ;
110
+
111
+ if ( createdBy ) {
112
+ requestQuery = requestQuery . where ( "createdBy" , "==" , createdBy ) ;
113
+ }
114
+ if ( status ) {
115
+ requestQuery = requestQuery . where ( "status" , "==" , status ) ;
116
+ }
117
+ if ( createdFor ) {
118
+ requestQuery = requestQuery . where ( "createdFor" , "==" , createdFor ) ;
119
+ }
120
+
121
+ requestQuery = requestQuery . orderBy ( "createdAt" , "desc" ) ;
122
+ let requestQueryDoc = requestQuery ;
123
+
124
+ if ( prev ) {
125
+ requestQueryDoc = requestQueryDoc . limitToLast ( size ) ;
126
+ } else {
127
+ requestQueryDoc = requestQueryDoc . limit ( size ) ;
128
+ }
129
+
130
+ if ( next ) {
131
+ const doc = await impersonationRequestModel . doc ( next ) . get ( ) ;
132
+ requestQueryDoc = requestQueryDoc . startAt ( doc ) ;
133
+ } else if ( prev ) {
134
+ const doc = await impersonationRequestModel . doc ( prev ) . get ( ) ;
135
+ requestQueryDoc = requestQueryDoc . endAt ( doc ) ;
136
+ }
137
+
138
+ const snapshot = await requestQueryDoc . get ( ) ;
139
+ let nextDoc ;
140
+ let prevDoc ;
141
+
142
+ if ( ! snapshot . empty ) {
143
+ const first = snapshot . docs [ 0 ] ;
144
+ prevDoc = await requestQuery . endBefore ( first ) . limitToLast ( 1 ) . get ( ) ;
145
+ const last = snapshot . docs [ snapshot . docs . length - 1 ] ;
146
+ nextDoc = await requestQuery . startAfter ( last ) . limit ( 1 ) . get ( ) ;
147
+ }
148
+
149
+ const allRequests = snapshot . empty
150
+ ? [ ]
151
+ : snapshot . docs . map ( doc => ( {
152
+ id : doc . id ,
153
+ ...doc . data ( )
154
+ } ) ) ;
155
+
156
+ if ( allRequests . length === 0 ) {
157
+ return null ;
158
+ }
159
+
160
+ const count = allRequests . length ;
161
+ return {
162
+ allRequests,
163
+ prev : prevDoc && ! prevDoc . empty ? prevDoc . docs [ 0 ] . id : null ,
164
+ next : nextDoc && ! nextDoc . empty ? nextDoc . docs [ 0 ] . id : null ,
165
+ count,
166
+ } ;
167
+ } catch ( error ) {
168
+ logger . error ( ERROR_WHILE_FETCHING_REQUEST , error ) ;
169
+ throw error ;
170
+ }
171
+ }
0 commit comments