feat: added another 'Date' expose-headers for outline api clients (#27221)
Exposed the Date header on the outline api so clients can accurately compute times relative to the dates returned by the API; this was previously done with the course API (#26979) Browser time is notoriously unreliable for this, especially for a Learner-facing countdown call-to-action based on the access expiration date. (REV-2126) Using the Date header for this allows the client to make use of information that is already sent, does not require additional calls nor modifying the API, and could be generalized to more or all our APIs without modifying them.
This commit is contained in:
@@ -213,3 +213,13 @@ def reset_course_deadlines(request):
|
||||
|
||||
referrer = request.META.get('HTTP_REFERER')
|
||||
return redirect(referrer) if referrer else HttpResponse()
|
||||
|
||||
|
||||
def expose_header(header, response):
|
||||
"""
|
||||
Add a header name to Access-Control-Expose-Headers to allow client code to access that header's value
|
||||
"""
|
||||
exposedHeaders = response.get('Access-Control-Expose-Headers', '')
|
||||
exposedHeaders += f', {header}' if exposedHeaders else header
|
||||
response['Access-Control-Expose-Headers'] = exposedHeaders
|
||||
return response
|
||||
|
||||
@@ -19,6 +19,7 @@ from rest_framework.response import Response
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.util.views import expose_header
|
||||
from lms.djangoapps.course_goals.api import (
|
||||
add_course_goal,
|
||||
get_course_goal,
|
||||
@@ -295,6 +296,20 @@ class OutlineTabView(RetrieveAPIView):
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
def finalize_response(self, request, response, *args, **kwargs):
|
||||
"""
|
||||
Return the final response, exposing the 'Date' header for computing relative time to the dates in the data.
|
||||
|
||||
Important dates such as 'access_expiration' are enforced server-side based on correct time; client-side clocks
|
||||
are frequently substantially far off which could lead to inaccurate messaging and incorrect expectations.
|
||||
Therefore, any messaging about those dates should be based on the server time and preferably in relative terms
|
||||
(time remaining); the 'Date' header is a straightforward and generalizable way for client-side code to get this
|
||||
reference.
|
||||
"""
|
||||
response = super().finalize_response(request, response, *args, **kwargs)
|
||||
# Adding this header should be moved to global middleware, not just this endpoint
|
||||
return expose_header('Date', response)
|
||||
|
||||
|
||||
@api_view(['POST'])
|
||||
@authentication_classes((JwtAuthentication,))
|
||||
|
||||
@@ -19,6 +19,7 @@ from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.util.views import expose_header
|
||||
from lms.djangoapps.edxnotes.helpers import is_feature_enabled
|
||||
from lms.djangoapps.certificates.api import get_certificate_url
|
||||
from lms.djangoapps.certificates.models import GeneratedCertificate
|
||||
@@ -444,13 +445,16 @@ class CoursewareInformation(RetrieveAPIView):
|
||||
def finalize_response(self, request, response, *args, **kwargs):
|
||||
"""
|
||||
Return the final response, exposing the 'Date' header for computing relative time to the dates in the data.
|
||||
|
||||
Important dates such as 'access_expiration' are enforced server-side based on correct time; client-side clocks
|
||||
are frequently substantially far off which could lead to inaccurate messaging and incorrect expectations.
|
||||
Therefore, any messaging about those dates should be based on the server time and preferably in relative terms
|
||||
(time remaining); the 'Date' header is a straightforward and generalizable way for client-side code to get this
|
||||
reference.
|
||||
"""
|
||||
response = super().finalize_response(request, response, *args, **kwargs)
|
||||
# Adding this header should be moved somewhere global, not just this endpoint
|
||||
exposedHeaders = response.get('Access-Control-Expose-Headers', '')
|
||||
exposedHeaders += ', Date' if exposedHeaders else 'Date'
|
||||
response['Access-Control-Expose-Headers'] = exposedHeaders
|
||||
return response
|
||||
# Adding this header should be moved to global middleware, not just this endpoint
|
||||
return expose_header('Date', response)
|
||||
|
||||
|
||||
class SequenceMetadata(DeveloperErrorViewMixin, APIView):
|
||||
|
||||
Reference in New Issue
Block a user