Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…low-ai into develop
  • Loading branch information
shuhaib-aot committed Feb 27, 2024
2 parents 0bfe191 + c5f0c15 commit 37b087f
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/forms-flow-api-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:

strategy:
matrix:
python-version: [3.12.1]
python-version: [3.12.2]

steps:
- uses: actions/checkout@v2
Expand Down
3 changes: 2 additions & 1 deletion forms-flow-api/src/formsflow_api/resources/user.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Resource to call Keycloak Service API calls and filter responses."""

from http import HTTPStatus

from flask import current_app, g, request
Expand Down Expand Up @@ -165,7 +166,7 @@ def get(): # pylint: disable=too-many-locals
kc_admin = KeycloakFactory.get_instance()
if group_name:
(users_list, users_count) = kc_admin.get_users(
page_no, limit, role, group_name, count
page_no, limit, role, group_name, count, search
)
user_service = UserService()
response = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ def update_group(self, group_id: str, data: Dict):

@abstractmethod
def get_users( # pylint: disable-msg=too-many-arguments
self, page_no: int, limit: int, role: bool, group_name: str, count: bool
self,
page_no: int,
limit: int,
role: bool,
group_name: str,
count: bool,
search: str,
):
"""Get users."""
raise NotImplementedError("Method not implemented")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Keycloak Admin implementation for client related operations."""

from typing import Dict, List

from flask import current_app
from formsflow_api_utils.exceptions import BusinessException
from formsflow_api_utils.utils.user_context import UserContext, user_context

from formsflow_api.constants import BusinessErrorCode
from formsflow_api.services import KeycloakAdminAPIService
from formsflow_api.services import KeycloakAdminAPIService, UserService

from .keycloak_admin import KeycloakAdmin

Expand All @@ -17,6 +18,7 @@ class KeycloakClientService(KeycloakAdmin):
def __init__(self):
"""Initialize client."""
self.client = KeycloakAdminAPIService()
self.user_service = UserService()

def __populate_user_roles(self, user_list: List) -> List:
"""Collects roles for a user list and populates the role attribute."""
Expand All @@ -26,13 +28,6 @@ def __populate_user_roles(self, user_list: List) -> List:
)
return user_list

# Keycloak doesn't provide count API for this one
def __get_users_count(self, client_id: str, group_name: str):
"""Returns user list count under a group."""
url_path = f"clients/{client_id}/roles/{group_name}/users"
user_list = self.client.get_request(url_path)
return len(user_list)

def get_analytics_groups(self, page_no: int, limit: int):
"""Get analytics roles."""
return self.client.get_analytics_roles(page_no, limit)
Expand All @@ -47,7 +42,13 @@ def get_group(self, group_id: str):
return response

def get_users( # pylint: disable-msg=too-many-arguments
self, page_no: int, limit: int, role: bool, group_name: str, count: bool
self,
page_no: int,
limit: int,
role: bool,
group_name: str,
count: bool,
search: str,
):
"""Get users under this client with formsflow-reviewer role."""
# group_name was hardcoded before as `formsflow-reviewer` make sure
Expand All @@ -57,10 +58,12 @@ def get_users( # pylint: disable-msg=too-many-arguments
)
client_id = self.client.get_client_id()
url = f"clients/{client_id}/roles/{group_name}/users"
if page_no and limit:
url += f"?first={(page_no - 1) * limit}&max={limit}"
users_list = self.client.get_request(url)
users_count = self.__get_users_count(client_id, group_name) if count else None
if search:
users_list = self.user_service.user_search(search, users_list)
users_count = len(users_list) if count else None
if page_no and limit:
users_list = self.user_service.paginate(users_list, page_no, limit)
if role:
users_list = self.__populate_user_roles(users_list)
return (users_list, users_count)
Expand Down Expand Up @@ -149,19 +152,8 @@ def get_tenant_users(
url = f"users?q=tenantKey:{tenant_key}"
current_app.logger.debug("Getting tenant users...")
result = self.client.get_request(url)
search_fields = ["username", "firstName", "lastName", "email"]
if search:
result = [
item
for item in result
if any(search in item[key] for key in search_fields)
]
result = self.user_service.user_search(search, result)
count = len(result) if count else None
result = self.paginate(result, page_no, limit)
result = self.user_service.paginate(result, page_no, limit)
return result, count

def paginate(self, data, page_number, page_size):
"""Paginate data."""
start_index = (page_number - 1) * page_size
end_index = start_index + page_size
return data[start_index:end_index]
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Keycloak factory implementation."""

from flask import current_app

from .keycloak_admin import KeycloakAdmin
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Keycloak implementation for keycloak group related operations."""

from typing import Dict, List

import requests
from flask import current_app
from formsflow_api_utils.exceptions import BusinessException

from formsflow_api.constants import BusinessErrorCode
from formsflow_api.services import KeycloakAdminAPIService
from formsflow_api.services import KeycloakAdminAPIService, UserService

from .keycloak_admin import KeycloakAdmin

Expand All @@ -17,6 +18,7 @@ class KeycloakGroupService(KeycloakAdmin):
def __init__(self):
"""Initialize client."""
self.client = KeycloakAdminAPIService()
self.user_service = UserService()

def __populate_user_groups(self, user_list: List) -> List:
"""Collect groups for a user list and populate the role attribute."""
Expand All @@ -26,13 +28,6 @@ def __populate_user_groups(self, user_list: List) -> List:
)
return user_list

# Keycloak doesn't provide count API for this one
def __get_users_count(self, group_id: str):
"""Returns user list count under a group."""
url_path = f"groups/{group_id}/members?briefRepresentation=true"
user_list = self.client.get_request(url_path)
return len(user_list)

def get_analytics_groups(self, page_no: int, limit: int):
"""Get analytics groups."""
return self.client.get_analytics_groups(page_no, limit)
Expand All @@ -43,7 +38,13 @@ def get_group(self, group_id: str):
return self.format_response(response)

def get_users( # pylint: disable-msg=too-many-arguments
self, page_no: int, limit: int, role: bool, group_name: str, count: bool
self,
page_no: int,
limit: int,
role: bool,
group_name: str,
count: bool,
search: str,
):
"""Get users under formsflow-reviewer group."""
user_list: List[Dict] = []
Expand All @@ -54,10 +55,12 @@ def get_users( # pylint: disable-msg=too-many-arguments
group = self.client.get_request(url_path=f"group-by-path/{group_name}")
group_id = group.get("id")
url_path = f"groups/{group_id}/members"
if page_no and limit:
url_path += f"?first={(page_no - 1) * limit}&max={limit}"
user_list = self.client.get_request(url_path)
user_count = self.__get_users_count(group_id) if count else None
if search:
user_list = self.user_service.user_search(search, user_list)
user_count = len(user_list) if count else None
if page_no and limit:
user_list = self.user_service.paginate(user_list, page_no, limit)
if role:
user_list = self.__populate_user_groups(user_list)
return (user_list, user_count)
Expand Down
4 changes: 2 additions & 2 deletions forms-flow-api/src/formsflow_api/services/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def get_user_filters(**kwargs):
filter_obj = Filter(
name="All Tasks",
variables=[
{"name": "applicationId", "label": "Application Id"},
{"name": "applicationId", "label": "Submission ID"},
{"name": "formName", "label": "Form Name"},
],
status="active",
Expand Down Expand Up @@ -105,7 +105,7 @@ def get_user_filters(**kwargs):
)
filter_data = filter_schema.dump(filters, many=True)
default_variables = [
{"name": "applicationId", "label": "Application Id"},
{"name": "applicationId", "label": "Submission ID"},
{"name": "formName", "label": "Form Name"},
]
# User who created the filter or admin have edit permission.
Expand Down
28 changes: 28 additions & 0 deletions forms-flow-api/src/formsflow_api/services/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,31 @@ def get_users(self, query_params, users_list):
for user in users_list:
response.append(UserService._as_dict(user))
return response

def user_search(self, search, user_list):
"""
Replicates Keycloak users search functionality.
Searches for a given string within usernames,
first names, last names, or email addresses.
Parameters:
- search (str): The string to search for within user attributes.
- user_list (list): A list of users to search within.
Returns:
- list: A filtered list of users matching the search criteria.
"""
search_fields = ["username", "firstName", "lastName", "email"]
result = [
item
for item in user_list
if any(search in item.get(key, "") for key in search_fields)
]
return result

def paginate(self, data, page_number, page_size):
"""Paginate the provided data."""
start_index = (page_number - 1) * page_size
end_index = start_index + page_size
return data[start_index:end_index]
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ const TaskFilterViewComponent = React.memo(

rows[rows.length - 1].push(
<Col key={i} xs={6}>
<label>{e.label}</label>
<label>{e.label === 'Application Id' ? 'Submission Id' : e.label}</label>
<input
title={t("Task variables")}
className="form-control"
Expand Down

0 comments on commit 37b087f

Please sign in to comment.