mirror of https://gitlab.com/tildes/tildes.git
3 changed files with 334 additions and 0 deletions
@ -0,0 +1,215 @@ |
|||
# Copyright (c) 2018 Tildes contributors <code@tildes.net> |
|||
# SPDX-License-Identifier: AGPL-3.0-or-later |
|||
|
|||
"""JSON API endpoints related to users.""" |
|||
|
|||
from pyramid.request import Request |
|||
from pyramid.view import view_config |
|||
from tildes.models.user.user import User |
|||
from tildes.models.comment import Comment |
|||
from tildes.models.pagination import MixedPaginatedResults |
|||
from tildes.models.topic import Topic |
|||
from tildes.views.api.beta.api_utils import ( |
|||
build_error_response, |
|||
get_next_and_prev_link, |
|||
query_apply_pagination, |
|||
) |
|||
from tildes.views.api.beta.comment import comment_to_dict |
|||
from tildes.views.api.beta.topic import topic_to_dict |
|||
|
|||
|
|||
def _user_to_dict(user: User) -> dict: |
|||
"""Convert a User object to a dictionary for JSON serialization.""" |
|||
return { |
|||
"username": user.username, |
|||
"joined_at": user.created_time.isoformat(), |
|||
"bio_rendered_html": user.bio_rendered_html, |
|||
} |
|||
|
|||
|
|||
@view_config(route_name="apibeta.user", openapi=True, renderer="json") |
|||
def get_user(request: Request) -> dict: # noqa |
|||
"""Get a single user with their comment and post history.""" |
|||
username = request.openapi_validated.parameters.path.get("username") |
|||
limit = request.openapi_validated.parameters.query.get("limit", 20) |
|||
before = request.openapi_validated.parameters.query.get("before", None) |
|||
after = request.openapi_validated.parameters.query.get("after", None) |
|||
|
|||
# Maximum number of items to return without history permission |
|||
max_items_no_permission = 20 |
|||
|
|||
try: |
|||
query = request.query(User).include_deleted().filter(User.username == username) |
|||
user = query.one_or_none() |
|||
if not user: |
|||
raise ValueError(f"User with name {username} not found") |
|||
except ValueError as exc: |
|||
return build_error_response(str(exc), field="username") |
|||
|
|||
if not request.has_permission("view_history", user) and ( |
|||
limit > max_items_no_permission or before or after |
|||
): |
|||
return build_error_response( |
|||
f"You do not have permission to view this user's history after " |
|||
f"the first {max_items_no_permission} items. " |
|||
f"Please resubmit your request without pagination parameters. " |
|||
f"If you submit a limit, it must be less " |
|||
f"than or equal to {max_items_no_permission}.", |
|||
status=403, |
|||
field="limit/before/after", |
|||
error_type="AuthorizationError", |
|||
) |
|||
|
|||
result_sets = [] |
|||
# For the main user API endpoint, combine topics and comments |
|||
for type_to_query in [Topic, Comment]: |
|||
query = request.query(type_to_query).filter(type_to_query.user == user) |
|||
try: |
|||
query = query_apply_pagination( |
|||
query, before, after, error_if_no_anchor=True |
|||
) |
|||
except ValueError as exc: |
|||
return build_error_response(str(exc), field="pagination") |
|||
# include removed posts if the viewer has permission |
|||
if request.has_permission("view_removed_posts", user): |
|||
query = query.include_removed() |
|||
query = query.join_all_relationships() |
|||
|
|||
result_sets.append(query.get_page(limit)) |
|||
|
|||
combined_results = MixedPaginatedResults(result_sets) |
|||
|
|||
# Build the JSON history data |
|||
processed_results = [] |
|||
for item in combined_results.results: |
|||
if isinstance(item, Topic): |
|||
processed_results.append(topic_to_dict(item)) |
|||
elif isinstance(item, Comment): |
|||
processed_results.append(comment_to_dict(request, item)) |
|||
|
|||
# Construct the paging next and previous link if there are more topics |
|||
(next_link, prev_link) = get_next_and_prev_link(request, combined_results) |
|||
|
|||
# Construct the final response JSON object |
|||
response = { |
|||
"user": _user_to_dict(user), |
|||
"history": processed_results, |
|||
"pagination": { |
|||
"num_items": len(processed_results), |
|||
"next_link": next_link, |
|||
"prev_link": prev_link, |
|||
}, |
|||
} |
|||
return response |
|||
|
|||
|
|||
@view_config(route_name="apibeta.user_comments", openapi=True, renderer="json") |
|||
def get_user_comments(request: Request) -> dict: |
|||
"""Get comments made by a user.""" |
|||
username = request.openapi_validated.parameters.path.get("username") |
|||
limit = request.openapi_validated.parameters.query.get("limit", 50) |
|||
before = request.openapi_validated.parameters.query.get("before", None) |
|||
after = request.openapi_validated.parameters.query.get("after", None) |
|||
|
|||
try: |
|||
query = request.query(User).include_deleted().filter(User.username == username) |
|||
user = query.one_or_none() |
|||
if not user: |
|||
raise ValueError(f"User with name {username} not found") |
|||
except ValueError as exc: |
|||
return build_error_response(str(exc), field="username") |
|||
|
|||
if not request.has_permission("view_history", user): |
|||
return build_error_response( |
|||
"You do not have permission to view this user's comments.", |
|||
status=403, |
|||
field="N/A", |
|||
error_type="AuthorizationError", |
|||
) |
|||
|
|||
query = request.query(Comment).filter(Comment.user == user) |
|||
try: |
|||
query = query_apply_pagination(query, before, after, error_if_no_anchor=False) |
|||
except ValueError as exc: |
|||
return build_error_response(str(exc), field="pagination") |
|||
# include removed posts if the viewer has permission |
|||
if request.has_permission("view_removed_posts", user): |
|||
query = query.include_removed() |
|||
query = query.join_all_relationships() |
|||
|
|||
query_result = query.get_page(limit) |
|||
|
|||
# Build the JSON history data |
|||
processed_comments = [] |
|||
for comment in query_result.results: |
|||
processed_comments.append(comment_to_dict(request, comment)) |
|||
|
|||
# Construct the paging next and previous link if there are more comments |
|||
(next_link, prev_link) = get_next_and_prev_link(request, query_result) |
|||
|
|||
# Construct the final response JSON object |
|||
response = { |
|||
"comments": processed_comments, |
|||
"pagination": { |
|||
"num_items": len(processed_comments), |
|||
"next_link": next_link, |
|||
"prev_link": prev_link, |
|||
}, |
|||
} |
|||
return response |
|||
|
|||
|
|||
@view_config(route_name="apibeta.user_topics", openapi=True, renderer="json") |
|||
def get_user_topics(request: Request) -> dict: |
|||
"""Get topics made by a user.""" |
|||
username = request.openapi_validated.parameters.path.get("username") |
|||
limit = request.openapi_validated.parameters.query.get("limit", 50) |
|||
before = request.openapi_validated.parameters.query.get("before", None) |
|||
after = request.openapi_validated.parameters.query.get("after", None) |
|||
|
|||
try: |
|||
query = request.query(User).include_deleted().filter(User.username == username) |
|||
user = query.one_or_none() |
|||
if not user: |
|||
raise ValueError(f"User with name {username} not found") |
|||
except ValueError as exc: |
|||
return build_error_response(str(exc), field="username") |
|||
|
|||
if not request.has_permission("view_history", user): |
|||
return build_error_response( |
|||
"You do not have permission to view this user's topics.", |
|||
status=403, |
|||
field="N/A", |
|||
error_type="AuthorizationError", |
|||
) |
|||
|
|||
query = request.query(Topic).filter(Topic.user == user) |
|||
try: |
|||
query = query_apply_pagination(query, before, after, error_if_no_anchor=False) |
|||
except ValueError as exc: |
|||
return build_error_response(str(exc), field="pagination") |
|||
# include removed posts if the viewer has permission |
|||
if request.has_permission("view_removed_posts", user): |
|||
query = query.include_removed() |
|||
query = query.join_all_relationships() |
|||
|
|||
query_result = query.get_page(limit) |
|||
|
|||
# Build the JSON history data |
|||
processed_topics = [] |
|||
for topic in query_result.results: |
|||
processed_topics.append(topic_to_dict(topic)) |
|||
|
|||
# Construct the paging next and previous link if there are more topics |
|||
(next_link, prev_link) = get_next_and_prev_link(request, query_result) |
|||
|
|||
# Construct the final response JSON object |
|||
response = { |
|||
"topics": processed_topics, |
|||
"pagination": { |
|||
"num_items": len(processed_topics), |
|||
"next_link": next_link, |
|||
"prev_link": prev_link, |
|||
}, |
|||
} |
|||
return response |
Write
Preview
Loading…
Cancel
Save
Reference in new issue