206 lines
5.8 KiB
Python
206 lines
5.8 KiB
Python
# myapp/middleware.py
|
|
import json
|
|
import re
|
|
|
|
import structlog
|
|
from django.core.cache import cache
|
|
|
|
logger = structlog.get_logger(__name__)
|
|
|
|
# TODO: refactor so that the query from the client is loaded
|
|
COURSE_QUERY = """
|
|
query courseQuery($slug: String!, $user: String) {
|
|
course(slug: $slug) {
|
|
id
|
|
title
|
|
slug
|
|
category_name
|
|
profiles
|
|
course_session_users(id: $user) {
|
|
id
|
|
__typename
|
|
chosen_profile
|
|
course_session {
|
|
id
|
|
__typename
|
|
}
|
|
}
|
|
configuration {
|
|
id
|
|
enable_circle_documents
|
|
enable_learning_mentor
|
|
enable_competence_certificates
|
|
is_uk
|
|
is_vv
|
|
__typename
|
|
}
|
|
action_competences {
|
|
competence_id
|
|
...CoursePageFields
|
|
performance_criteria {
|
|
competence_id
|
|
learning_unit {
|
|
id
|
|
slug
|
|
evaluate_url
|
|
__typename
|
|
}
|
|
...CoursePageFields
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
learning_path {
|
|
...CoursePageFields
|
|
topics {
|
|
is_visible
|
|
...CoursePageFields
|
|
circles {
|
|
description
|
|
goals
|
|
profiles
|
|
is_base_circle
|
|
...CoursePageFields
|
|
learning_sequences {
|
|
icon
|
|
...CoursePageFields
|
|
learning_units {
|
|
evaluate_url
|
|
...CoursePageFields
|
|
performance_criteria {
|
|
...CoursePageFields
|
|
__typename
|
|
}
|
|
learning_contents {
|
|
can_user_self_toggle_course_completion
|
|
content_url
|
|
minutes
|
|
description
|
|
...CoursePageFields
|
|
... on LearningContentAssignmentObjectType {
|
|
assignment_type
|
|
content_assignment {
|
|
id
|
|
assignment_type
|
|
__typename
|
|
}
|
|
competence_certificate {
|
|
...CoursePageFields
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
... on LearningContentEdoniqTestObjectType {
|
|
checkbox_text
|
|
has_extended_time_test
|
|
content_assignment {
|
|
id
|
|
assignment_type
|
|
__typename
|
|
}
|
|
competence_certificate {
|
|
...CoursePageFields
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
... on LearningContentRichTextObjectType {
|
|
text
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
__typename
|
|
}
|
|
}
|
|
fragment CoursePageFields on CoursePageInterface {
|
|
title
|
|
id
|
|
slug
|
|
content_type
|
|
frontend_url
|
|
__typename
|
|
}
|
|
"""
|
|
|
|
COURSE_SESSION_USER_QUERY = """
|
|
query courseQuery($slug: String!, $user: String) {
|
|
course(slug: $slug) {
|
|
course_session_users(id: $user) {
|
|
id
|
|
__typename
|
|
chosen_profile
|
|
course_session {
|
|
id
|
|
__typename
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
|
|
class GraphQLQueryFilterMiddleware:
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
# Check if the request is a GraphQL request
|
|
if request.content_type == "application/json" and "/graphql" in request.path:
|
|
try:
|
|
# Parse the GraphQL query from the request body
|
|
body = json.loads(request.body)
|
|
query = body.get("query", "")
|
|
|
|
if self.normalize_string(query) == self.normalize_string(COURSE_QUERY):
|
|
logger.debug("CourseQuery detected")
|
|
slug = body.get("variables", {}).get("slug", "")
|
|
|
|
if slug:
|
|
key = f"course_query_{slug}"
|
|
cache_data = cache.get(key)
|
|
|
|
if cache_data:
|
|
# Cache hit: only make course session user query and add to cache data
|
|
logger.debug("cache hit", key=key)
|
|
body["query"] = COURSE_SESSION_USER_QUERY
|
|
request._body = json.dumps(body).encode("utf-8")
|
|
response = self.get_response(request)
|
|
content = json.loads(response.content)
|
|
cache_data["data"]["course"]["course_session_users"] = (
|
|
content["data"]["course"]["course_session_users"]
|
|
)
|
|
response.content = json.dumps(cache_data)
|
|
return response
|
|
|
|
else:
|
|
# Cache miss: make the original query and cache the result
|
|
logger.debug("cache miss", key=key)
|
|
response = self.get_response(request)
|
|
content = json.loads(response.content)
|
|
del content["data"]["course"]["course_session_users"]
|
|
cache.set(key, content, 60 * 10)
|
|
|
|
return response
|
|
except Exception as e:
|
|
# Handle any exceptions in parsing or filtering
|
|
logger.error("Error in GraphQLQueryFilterMiddleware", exc_info=e)
|
|
|
|
# Continue processing the request if not blocked
|
|
response = self.get_response(request)
|
|
return response
|
|
|
|
def normalize_string(self, s):
|
|
# Remove all whitespace characters (space, tabs, newlines)
|
|
return re.sub(r"\s+", "", s)
|