Browse Source

add topics endpoint

merge-requests/160/head
pollev 2 months ago
parent
commit
9c4a8de86c
  1. 1
      tildes/tildes/models/topic/topic_query.py
  2. 6
      tildes/tildes/routes.py
  3. 1
      tildes/tildes/views/api/beta/__init__.py
  4. 141
      tildes/tildes/views/api/beta/topic.py

1
tildes/tildes/models/topic/topic_query.py

@ -142,6 +142,7 @@ class TopicQuery(PaginatedQuery):
topic.bookmark_created_time = None
topic.last_visit_time = None
topic.comments_since_last_visit = None
topic.user_bookmarked = None
topic.user_ignored = False
else:
topic = result.Topic

6
tildes/tildes/routes.py

@ -130,8 +130,10 @@ def includeme(config: Configurator) -> None:
# Routes for the JSON API
# We also provide a path for the full spec and the built-in swagger UI explorer
config.pyramid_openapi3_spec('openapi_beta.yaml', route='/api/beta/openapi.yaml')
config.pyramid_openapi3_add_explorer(route='/api/beta/ui')
config.pyramid_openapi3_spec("openapi_beta.yaml", route="/api/beta/openapi.yaml")
config.pyramid_openapi3_add_explorer(route="/api/beta/ui")
config.add_route("apibeta.topics", "/api/beta/topics")
def add_intercooler_routes(config: Configurator) -> None:

1
tildes/tildes/views/api/beta/__init__.py

@ -0,0 +1 @@
"""Contains views for the JSON web API"""

141
tildes/tildes/views/api/beta/topic.py

@ -0,0 +1,141 @@
# Copyright (c) 2018 Tildes contributors <code@tildes.net>
# SPDX-License-Identifier: AGPL-3.0-or-later
"""JSON API endpoints related to topics."""
from marshmallow.exceptions import ValidationError
from pyramid.exceptions import HTTPBadRequest
from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config
from tildes.enums import TopicSortOption
from tildes.models.topic import Topic
from tildes.schemas.fields import ShortTimePeriod
@view_config(route_name="apibeta.topics", openapi=True, renderer="json")
def get_topics(request: Request) -> dict:
"""Get topics"""
limit = request.openapi_validated.parameters.query.get("limit")
period_raw = request.openapi_validated.parameters.query.get("period")
tag = request.openapi_validated.parameters.query.get("tag", None)
order_raw = request.openapi_validated.parameters.query.get("order", None)
before = request.openapi_validated.parameters.query.get("before", None)
after = request.openapi_validated.parameters.query.get("after", None)
# Parse parameters where necessary
try:
period = ShortTimePeriod(allow_none=True)
period = period.deserialize(period_raw)
except ValidationError as exc:
return Response(
status=400,
content_type="application/json",
json=[
{"message": str(exc), "field": "period", "exception": "ValidationError"}
],
)
try:
if order_raw:
order = TopicSortOption[order_raw.upper()]
else:
order = TopicSortOption.ACTIVITY
except KeyError:
return Response(
status=400,
content_type="application/json",
json=[
{
"message": f"Invalid order value: {order_raw}",
"field": "order",
"exception": "ValidationError",
}
],
)
query = request.query(Topic).join_all_relationships().apply_sort_option(order)
# restrict the time period, if not set to "all time"
if period:
query = query.inside_time_period(period)
# restrict to a specific tag if provided
if tag:
query = query.has_tag(tag)
# apply before/after pagination restrictions if relevant
try:
if before and after:
raise ValueError("Cannot specify both before and after parameters")
if before:
query = query.before_id36(before)
if after:
query = query.after_id36(after)
except ValueError as exc:
return Response(
status=400,
content_type="application/json",
json=[
{
"message": str(exc),
"field": "before/after",
"exception": "ValidationError",
}
],
)
# Execute the query
topics = query.get_page(limit)
processed_topics = []
# Build the JSON topic data
for topic in topics:
processed_topic = {
"id": topic.topic_id,
"title": topic.title,
"text_html": topic.rendered_html,
"url": topic.link,
"comments_url": topic.permalink,
"group": str(topic.group.path),
"content_metadata": topic.content_metadata_for_display,
"created_at": topic.created_time.isoformat(),
"posted_by_user": topic.user.username,
"vote_count": topic.num_votes,
"comment_count": topic.num_comments,
"new_comment_count": topic.comments_since_last_visit,
"voted": topic.user_voted,
"bookmarked": topic.user_bookmarked,
"ignored": topic.user_ignored,
"official": topic.is_official,
"tags": topic.tags,
}
processed_topics.append(processed_topic)
# Construct the paging next and previous link if there are more topics
if topics.has_next_page:
query_vars = request.GET.copy()
query_vars.pop("before", None)
query_vars.update({"after": topics.next_page_after_id36})
next_link = request.current_route_url(_query=query_vars)
else:
next_link = None
if topics.has_prev_page:
query_vars = request.GET.copy()
query_vars.pop("after", None)
query_vars.update({"before": topics.prev_page_before_id36})
prev_link = request.current_route_url(_query=query_vars)
else:
prev_link = None
# 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
Loading…
Cancel
Save