const permissionsAndTransformStartTime = Date.now(); this.logger.debug('[getGlobalSearchActionList] Running permission checks and transformations in parallel'); const permissionBranchPromise = (async () => { const permissionBranchStartTime = Date.now(); const permissionResult = await this.checkPermissions( finalActionList.map((action: { id: string }) => action.id), userId, ACTION_PERMISSION.map(({ permission }) => permission) ); const permissionBranchDuration = Date.now() - permissionBranchStartTime; this.logger.debug( `[getGlobalSearchActionList] checkPermissions branch completed in ${permissionBranchDuration}ms` ); return permissionResult; })(); const transformBranchPromise = (async () => { const transformBranchStartTime = Date.now(); const transformed = await Promise.all( finalActionList.map(action => this.actionMappingService.transformActionToResponseDto( action, true // isListCall = true to skip external service calls ) ) ); const transformBranchDuration = Date.now() - transformBranchStartTime; this.logger.debug( `[getGlobalSearchActionList] transformActionToResponseDto branch completed in ${transformBranchDuration}ms` ); return transformed; })(); // Run permission checks and transformations in parallel const [checkPermissionResult, transformedActions] = await Promise.all([ permissionBranchPromise, transformBranchPromise, ]); const permissionsAndTransformDuration = Date.now() - permissionsAndTransformStartTime; this.logger.debug( `[getGlobalSearchActionList] Permission checks and transformations completed in ${permissionsAndTransformDuration}ms` ); async checkPermissions( actionIds: string[], userId: string, permissions: ActionsResourceFGAPermissions[] ): Promise> { const functionStartTime = Date.now(); const fgaRelationBuilderStartTime = Date.now(); const fgaRelationChecks = []; this.logger.debug( `[checkPermissions] Checking permissions: ${permissions.join(', ')} for user: ${userId}` ); for (const actionId of actionIds) { for (const permission of permissions) { fgaRelationChecks.push( this.ActionFGAHelper.generateUserFGARelationForPermissionCheck( actionId, userId, permission ) ); } } const fgaRelationBuilderDuration = Date.now() - fgaRelationBuilderStartTime; this.logger.debug( `[checkPermissions] Prepared ${fgaRelationChecks.length} relation checks for ${actionIds.length} actions (user: ${userId}) in ${fgaRelationBuilderDuration}ms` ); this.logger.debug( `[checkPermissions] FGA Relations: ${JSON.stringify(fgaRelationChecks.map(rel => ({ resource: rel.resource, relation: rel.relation, target: rel.target })), null, 2)}` ); const descopeCallStartTime = Date.now(); this.logger.debug('[checkPermissions] Calling Descope checkRelationFGA API'); const fgaRelationChecksResponse = await this.descopeService.checkRelationFGA(fgaRelationChecks); const descopeCallDuration = Date.now() - descopeCallStartTime; this.logger.debug( `[checkPermissions] Descope checkRelationFGA API completed in ${descopeCallDuration}ms (response size: ${fgaRelationChecksResponse.length} checks)` ); const mappingStartTime = Date.now(); const resultMap = fgaRelationChecksResponse.reduce((map, fgaRelationCheck) => { const actionId = fgaRelationCheck.tuple.resource; const relationship_key = fgaRelationCheck.tuple.relation; const allowed = fgaRelationCheck.allowed; if (!map.has(actionId)) { map.set(actionId, []); } map.get(actionId).push({ relationship_key, allowed, }); return map; }, new Map()); const mappingDuration = Date.now() - mappingStartTime; const functionEndTime = Date.now(); const totalDuration = functionEndTime - functionStartTime; this.logger.debug( `[checkPermissions] Function completed. Total duration: ${totalDuration}ms\n` + ` - FGA relation builder: ${fgaRelationBuilderDuration}ms\n` + ` - Descope API call: ${descopeCallDuration}ms\n` + ` - Result mapping: ${mappingDuration}ms\n` + ` - Results mapped to ${resultMap.size} actions` ); return resultMap; }