Skip to content

fix: make page a nullable property and specs updated#3426

Open
marslanabdulrauf wants to merge 4 commits intomainfrom
marslan/10652-page-nullable
Open

fix: make page a nullable property and specs updated#3426
marslanabdulrauf wants to merge 4 commits intomainfrom
marslan/10652-page-nullable

Conversation

@marslanabdulrauf
Copy link
Copy Markdown
Contributor

@marslanabdulrauf marslanabdulrauf commented Mar 26, 2026

What are the relevant tickets?

https://github.com/mitodl/hq/issues/10652

Description (What does it do?)

This pull request updates the course and program serializers to properly handle cases where a course or program does not have an associated CMS page. The serializers now return null for the page field instead of a default object, and the OpenAPI specs are updated to reflect this. Corresponding tests are added to ensure this behavior.

Serializer improvements:

  • Updated CourseSerializer and ProgramSerializer to allow the page field to be null if there is no associated CMS page, returning None instead of a default object. (courses/serializers/v2/courses.py [1] courses/serializers/v2/programs.py [2]
  • Cleaned up imports in programs.py by removing unused get_thumbnail_url. (courses/serializers/v2/programs.py courses/serializers/v2/programs.pyL15-R15)

OpenAPI specification updates:

  • Added nullable: true to the page fields for courses and programs in all OpenAPI spec versions (v0.yaml, v1.yaml, v2.yaml) to indicate that these fields can be null.

Testing:

  • Added tests to verify that the serializers return page: None when a course or program does not have a CMS page. (courses/serializers/v2/courses_test.py [1] courses/serializers/v2/programs_test.py [2]

How can this be tested?

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 26, 2026

OpenAPI Changes

Show/hide ## Changes for v0.yaml:
## Changes for v0.yaml:
19 changes: 19 error, 0 warning, 0 info
error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/course_certificates/{cert_uuid}/
		the response property `course_run/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/courses/
		the response property `results/items/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/courses/{id}/
		the response property `page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/enrollments/
		the response property `items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API POST /api/v2/enrollments/
		the response property `run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `201`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/pages/?fields=*&type=cms.coursepage
		the response property `items/items/course_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/pages/?fields=*&type=cms.programpage
		the response property `items/items/program_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/pages/{id}/
		the response property `oneOf[#/components/schemas/CoursePageItem]/course_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/pages/{id}/
		the response property `oneOf[#/components/schemas/ProgramPageItem]/program_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/program_certificates/{cert_uuid}/
		the response property `program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/program_enrollments/
		the response property `items/enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/program_enrollments/
		the response property `items/program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API DELETE /api/v2/program_enrollments/{id}/
		the response property `items/enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API DELETE /api/v2/program_enrollments/{id}/
		the response property `items/program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/program_enrollments/{id}/
		the response property `enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/program_enrollments/{id}/
		the response property `program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/programs/
		the response property `results/items/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API GET /api/v2/programs/{id}/
		the response property `page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v0.yaml	
	in API POST /api/v2/verified_program_enrollments/{courserun_id}/
		the response property `run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `201`



## Changes for v1.yaml:
19 changes: 19 error, 0 warning, 0 info
error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/course_certificates/{cert_uuid}/
		the response property `course_run/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/courses/
		the response property `results/items/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/courses/{id}/
		the response property `page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/enrollments/
		the response property `items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API POST /api/v2/enrollments/
		the response property `run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `201`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/pages/?fields=*&type=cms.coursepage
		the response property `items/items/course_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/pages/?fields=*&type=cms.programpage
		the response property `items/items/program_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/pages/{id}/
		the response property `oneOf[#/components/schemas/CoursePageItem]/course_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/pages/{id}/
		the response property `oneOf[#/components/schemas/ProgramPageItem]/program_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/program_certificates/{cert_uuid}/
		the response property `program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/program_enrollments/
		the response property `items/enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/program_enrollments/
		the response property `items/program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API DELETE /api/v2/program_enrollments/{id}/
		the response property `items/enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API DELETE /api/v2/program_enrollments/{id}/
		the response property `items/program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/program_enrollments/{id}/
		the response property `enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/program_enrollments/{id}/
		the response property `program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/programs/
		the response property `results/items/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API GET /api/v2/programs/{id}/
		the response property `page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v1.yaml	
	in API POST /api/v2/verified_program_enrollments/{courserun_id}/
		the response property `run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `201`



## Changes for v2.yaml:
19 changes: 19 error, 0 warning, 0 info
error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/course_certificates/{cert_uuid}/
		the response property `course_run/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/courses/
		the response property `results/items/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/courses/{id}/
		the response property `page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/enrollments/
		the response property `items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API POST /api/v2/enrollments/
		the response property `run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `201`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/pages/?fields=*&type=cms.coursepage
		the response property `items/items/course_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/pages/?fields=*&type=cms.programpage
		the response property `items/items/program_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/pages/{id}/
		the response property `oneOf[#/components/schemas/CoursePageItem]/course_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/pages/{id}/
		the response property `oneOf[#/components/schemas/ProgramPageItem]/program_details/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/program_certificates/{cert_uuid}/
		the response property `program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/program_enrollments/
		the response property `items/enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/program_enrollments/
		the response property `items/program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API DELETE /api/v2/program_enrollments/{id}/
		the response property `items/enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API DELETE /api/v2/program_enrollments/{id}/
		the response property `items/program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/program_enrollments/{id}/
		the response property `enrollments/items/run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/program_enrollments/{id}/
		the response property `program/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/programs/
		the response property `results/items/page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API GET /api/v2/programs/{id}/
		the response property `page` became nullable for the status `200`

error	[response-property-became-nullable] at head/openapi/specs/v2.yaml	
	in API POST /api/v2/verified_program_enrollments/{courserun_id}/
		the response property `run/allOf[#/components/schemas/V2CourseRunWithCourse]/course/allOf[#/components/schemas/V2Course]/page` became nullable for the status `201`



Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@marslanabdulrauf marslanabdulrauf force-pushed the marslan/10652-page-nullable branch from 7746b42 to 283396f Compare March 26, 2026 07:09
@marslanabdulrauf marslanabdulrauf force-pushed the marslan/10652-page-nullable branch 2 times, most recently from fc9abea to 1a2f449 Compare April 16, 2026 13:05
@marslanabdulrauf
Copy link
Copy Markdown
Contributor Author

@ChristopherChudzicki should we bump the version for breaking change which is causing github check failure ?

@ChristopherChudzicki
Copy link
Copy Markdown
Contributor

@ChristopherChudzicki should we bump the version for breaking change which is causing github check failure ?

Sorry I didn't have time to review this more carefully today, but IMO this isn't a breaking change—it's just making the spec accurate. The API can return null now, but the spec doesn't reflect that. We aren't changing the API, we are fixing the spec.

@marslanabdulrauf marslanabdulrauf force-pushed the marslan/10652-page-nullable branch from 1a2f449 to fd56e0e Compare May 5, 2026 09:51
@marslanabdulrauf
Copy link
Copy Markdown
Contributor Author

Oh, I checked the V2Course serializer didn't had allow_null=True which I added and generated the v2 specs.

Also had similar requirement for Programs as well:

Additionally, api/v2/programs page has an incorrect type in the spec. Sometimes it's the full page object, sometimes it's just { feature_image_src }. My preference would be to make a breaking change to the api/v2/programs api to make it nullable, rather than the { feature_image_src } fallback which is never used on MITxOnline or Learn. (The fallback for v1 is used, I believe.)

So made changes around that as well and updated the v2 specs.

@ChristopherChudzicki
Copy link
Copy Markdown
Contributor

Oh, I checked the V2Course serializer didn't had allow_null=True which I added and generated the v2 specs.

👍 That sounds good to me. Again, not a breaking change—the serializer already returned null for page, e.g., https://mitxonline.mit.edu/api/v2/courses/course-v1:UAI_NTUA_SOURCE+NTUA.UAI.VT.3/ (since DRF serializers don't validate responses, only requests).

My suggestion is:

  1. Fix tests / linting (the linting failure is probably pre-existing, but the line number changed, so it's not getting excluded by drf_lint_baseline.json anymore...may need to regenerate that.)
  2. Before we merge, let's get a PR ready for MIT Learn for the updated client. The change to nullable page is accurate—reflects reality—but will trigger some new TS warnings in MIT Learn. We can generate a API client for the branch via https://github.com/mitodl/mitxonline-api-clients#branch-clients-local-development and prepare the Learn PR

@marslanabdulrauf marslanabdulrauf force-pushed the marslan/10652-page-nullable branch 2 times, most recently from 41ac17a to 7fa7635 Compare May 7, 2026 08:26
@marslanabdulrauf marslanabdulrauf force-pushed the marslan/10652-page-nullable branch from 7fa7635 to 00f3ead Compare May 7, 2026 08:29
@marslanabdulrauf marslanabdulrauf force-pushed the marslan/10652-page-nullable branch from ee7a32c to 271506c Compare May 7, 2026 09:01
@marslanabdulrauf marslanabdulrauf marked this pull request as ready for review May 7, 2026 09:22
@marslanabdulrauf marslanabdulrauf requested a review from Copilot May 7, 2026 09:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the v2 course/program serializers and OpenAPI specs so the page field can be null when no CMS page is associated, and adds test coverage for that behavior.

Changes:

  • Allow page to serialize as None for courses/programs without an associated CMS page and add serializer tests for this case.
  • Update OpenAPI spec files (v0/v1/v2) to mark page as nullable: true.
  • Add an oasdiff ignore file and wire it into the OpenAPI diff workflow; adjust spec-check script to ignore the new .txt file.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
scripts/test/openapi_spec_check.sh Exclude .txt files from spec directory diffs (but current invocation is broken; see comment).
openapi/specs/v0.yaml Mark course/program page schemas as nullable.
openapi/specs/v1.yaml Mark course/program page schemas as nullable.
openapi/specs/v2.yaml Mark course/program page schemas as nullable.
openapi/specs/oasdiff-err-ignore.txt New ignore list for oasdiff breaking checks related to page becoming nullable.
drf_lint_baseline.json Update baseline line references due to code shifts.
courses/serializers/v2/programs.py Return None for program page when absent; avoid repeated .all() calls.
courses/serializers/v2/programs_test.py Add test asserting page is None when program has no CMS page.
courses/serializers/v2/courses.py Allow nested course page field to be null.
courses/serializers/v2/courses_test.py Add test asserting page is None when course has no CMS page.
.github/workflows/openapi-diff.yml Pass --err-ignore file to oasdiff breaking when present.
Comments suppressed due to low confidence (1)

scripts/test/openapi_spec_check.sh:15

  • diff options need to come before the directory operands. As written, --exclude="*.txt" will be treated as a third path argument on many systems and cause the script to fail. Also, with set -e, a non-zero diff (when specs are out of date) will exit immediately before the following if [ $? -eq 0 ] block runs; wrap the diff in the if diff ...; then ... form (or otherwise capture the exit status) so the custom message executes reliably.
diff $TMPDIR $SPECS_DIR --exclude="*.txt"

if [ $? -eq 0 ]; then
	echo "OpenAPI spec is up to date!"
	exit 0

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ChristopherChudzicki
Copy link
Copy Markdown
Contributor

👍 Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants