Skip to content

Commit

Permalink
add 'detail' query to obtain them from GET /users directly (fix #202)…
Browse files Browse the repository at this point in the history
… + use query in UI views + add missing 'status' in API schema
  • Loading branch information
fmigneault committed Apr 29, 2021
1 parent 543ba90 commit 88eb364
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 17 deletions.
20 changes: 11 additions & 9 deletions magpie/api/management/user/user_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,23 @@
@view_config(route_name=s.UsersAPI.name, request_method="GET")
def get_users_view(request):
"""
List all registered user names.
List all registered user names or details.
"""
user_status_filter = request.params.get("status")
user_status_valid = list(str(status) for status in s.UserStatuses.values())
user_status_valid.append(None) # allow unspecified as 'all'
ax.verify_param(user_status_filter, is_in=True, param_compare=user_status_valid, param_name="status",
http_error=HTTPBadRequest, msg_on_fail=s.Users_GET_BadRequestSchema.description)
user_status_filter = user_status_filter if user_status_filter is None else int(user_status_filter)
user_name_list = ax.evaluate_call(lambda: [user.user_name for user in
models.UserSearchService.search(status=user_status_filter,
db_session=request.db)],
fallback=lambda: request.db.rollback(), http_error=HTTPForbidden,
msg_on_fail=s.Users_GET_ForbiddenResponseSchema.description)
return ax.valid_http(http_success=HTTPOk, content={"user_names": sorted(user_name_list)},
detail=s.Users_GET_OkResponseSchema.description)
status = user_status_filter if user_status_filter is None else int(user_status_filter)
detail = asbool(request.params.get("detail", False))
user_list = ax.evaluate_call(lambda: models.UserSearchService.search(status=status, db_session=request.db),
fallback=lambda: request.db.rollback(), http_error=HTTPForbidden,
msg_on_fail=s.Users_GET_ForbiddenResponseSchema.description)
if detail:
data = {"users": [uf.format_user(user, basic_info=True) for user in user_list]}
else:
data = {"user_names": sorted(user_list)}
return ax.valid_http(http_success=HTTPOk, content=data, detail=s.Users_GET_OkResponseSchema.description)


@s.UsersAPI.post(schema=s.Users_POST_RequestSchema, tags=[s.UsersTag], response_schemas=s.Users_POST_responses)
Expand Down
30 changes: 26 additions & 4 deletions magpie/api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,17 +760,31 @@ class PermissionNameListSchema(colander.SequenceSchema):
)


class UserBodySchema(colander.MappingSchema):
class UserInfoSchema(colander.MappingSchema):
user_name = UserNameParameter
email = colander.SchemaNode(
colander.String(),
description="Email of the user.",
example="toto@mail.com")
status = colander.SchemaNode(
colander.Integer(),
missing=colander.drop,
description="Current status of the user account.",
example=UserStatuses.OK.value,
validator=colander.OneOf(UserStatuses.values())
)


class UserBodySchema(UserInfoSchema):
group_names = GroupNamesListSchema(
example=["administrators", "users"]
)


class UserDetailListSchema(colander.SequenceSchema):
user = UserInfoSchema()


class GroupBaseBodySchema(colander.MappingSchema):
group_name = colander.SchemaNode(
colander.String(),
Expand Down Expand Up @@ -1576,21 +1590,29 @@ class ServiceTypeResourceTypes_GET_NotFoundResponseSchema(BaseResponseSchemaAPI)
body = ErrorResponseBodySchema(code=HTTPNotFound.code, description=description)


class UsersStatusQuery(QueryRequestSchemaAPI):
class UsersQuery(QueryRequestSchemaAPI):
status = colander.SchemaNode(
colander.Integer(),
missing=colander.drop,
description="Obtain the user name list filtered by their account status. Returns all regardless otherwise.",
validator=colander.OneOf(UserStatuses.values())
)
detail = colander.SchemaNode(
colander.Boolean(),
default=False,
desiption="Request that more user details be returned in the response. "
"Each item in the list will be a user detail JSON object instead of the list of string names. "
"Only user names are returned by default for backward compatibility."
)


class Users_GET_RequestSchema(BaseRequestSchemaAPI):
querystring = UsersStatusQuery()
querystring = UsersQuery()


class Users_GET_ResponseBodySchema(BaseResponseBodySchema):
user_names = UserNamesListSchema()
user_names = UserNamesListSchema(missing=colander.drop, description="User names when no detail is requested.")
users = UserDetailListSchema(missing=colander.drop, description="List of user details when it is requested.")


class Users_GET_OkResponseSchema(BaseResponseSchemaAPI):
Expand Down
29 changes: 25 additions & 4 deletions magpie/ui/management/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
from magpie.utils import CONTENT_TYPE_JSON, get_json, get_logger

if TYPE_CHECKING:
# pylint: disable=W0611,unused-import
from typing import Dict, List, Optional, Tuple
from typing import Dict, List, Optional, Tuple, Union

from sqlalchemy.orm.session import Session

Expand Down Expand Up @@ -97,6 +96,7 @@ def get_user_names(self):

@handle_errors
def get_user_statuses(self, status=0):
# type: (Union[str, int]) -> List[str]
"""
Obtains all user names that have the corresponding status value.
"""
Expand All @@ -116,6 +116,26 @@ def get_user_emails(self):
emails.append(user_email)
return emails

@handle_errors
def get_user_details(self, status=None):
# type: (Optional[Union[str, int]]) -> List[JSON]
"""
Obtains all user details, optionally filtered to by corresponding status value.
Employ this method to avoid multiple requests fetching individual information.
.. seealso::
- :meth:`get_user_emails`
- :meth:`get_user_names`
- :meth:`get_user_statuses`
"""
query = "?detail=true"
if status is not None:
query += "&status={}".format(status)
resp = request_api(self.request, schemas.UsersAPI.path + query, "GET")
check_response(resp)
return get_json(resp)["users"]

def get_resource_types(self):
"""
:return: dictionary of all resources as {id: 'resource_type'}
Expand Down Expand Up @@ -243,15 +263,16 @@ def add_user(self):
if resp.status_code == HTTPConflict.code:
return_data["invalid_group_name"] = True
return_data["reason_group_name"] = "Conflict"
if user_email in self.get_user_emails():
user_details = self.get_user_details()
if user_email in [usr["email"] for usr in user_details]:
return_data["invalid_user_email"] = True
return_data["reason_user_email"] = "Conflict"
if user_email == "":
return_data["invalid_user_email"] = True
if len(user_name) > get_constant("MAGPIE_USER_NAME_MAX_LENGTH", self.request):
return_data["invalid_user_name"] = True
return_data["reason_user_name"] = "Too Long"
if user_name in self.get_user_names():
if user_name in [usr["user_name"] for usr in user_details]:
return_data["invalid_user_name"] = True
return_data["reason_user_name"] = "Conflict"
if user_name == "":
Expand Down

0 comments on commit 88eb364

Please sign in to comment.