Skip to content

feat: Handle nullable page field from MITx Online client update#3349

Open
zamanafzal wants to merge 4 commits into
mainfrom
zafzal/update-mitxonline-client-nullable-page
Open

feat: Handle nullable page field from MITx Online client update#3349
zamanafzal wants to merge 4 commits into
mainfrom
zafzal/update-mitxonline-client-nullable-page

Conversation

@zamanafzal
Copy link
Copy Markdown
Contributor

@zamanafzal zamanafzal commented May 18, 2026

What are the relevant tickets?

https://github.com/mitodl/hq/issues/10652
mitodl/mitxonline#3426 (comment)

Description (What does it do?)

Bumps @mitodl/mitxonline-api-axios to a pre-release build from
mitxonline#3426, which
corrects the OpenAPI spec so that the page field on V2Program,
V2ProgramDetail, V2Course, and CourseWithCourseRunsSerializerV2 is
now typed as T | null.

Once the changes are validated in this PR, we will merge the mitodl/mitxonline#3426 and use the latest mitxonline-api-clients version instead of the pre-release.

Why the type change? The API was already returning null for page
on courses/programs without a Wagtail CMS page — the spec just wasn't
reflecting it. The v2 programs serializer was also returning an
inconsistent fallback object ({"feature_image_src": url}) instead of
null; that is removed in the upstream PR.

This PR guards every page field access with optional chaining so
resources without a linked CMS page render gracefully instead of crashing.

Changes

File Guards added
ContractContent.tsx program.page?.description
CoursePage.tsx page.course_details.page?.feature_image_src, ?.description
ProgramPage.tsx page.program_details.page?.feature_image_src, ?.description
ProgramAsCoursePage.tsx page.program_details.page?.feature_image_src, ?.description
ProductSummary.tsx page?.length, page?.effort, page?.financial_assistance_form_url

Screenshots (if appropriate):

How can this be tested?

Page loads without crash, falls back to default placeholder image, description is empty:

Screenshot 2026-05-18 at 5 05 05 PM

Product page with CMS page: Hero image, description, and info box all render correctly.
Screenshot 2026-05-18 at 4 13 37 PM

Start Services

Terminal 1 — MITx Online

cd /path/to/mitxonline
docker compose up

Terminal 2 — MIT Learn

cd /path/to/mit-learn
docker compose up

Backpopulate MITx Online Data

From mit-learn/:

docker compose run --rm web python manage.py backpopulate_mitxonline_data

Select a Test Program

Pick any published program from the backpopulate output, or find one manually:

docker compose run --rm web python manage.py shell -c "
from learning_resources.models import LearningResource

p = LearningResource.objects.filter(
    resource_type='program',
    published=True,
    platform__code='mitxonline'
).first()

print(p.readable_id, '|', p.title)
"

Test A — Unit Tests

Covers the page === null scenario end-to-end.

docker exec mit-learn-watch-1 yarn test \
  frontends/main/src/app-pages/ProductPages/ \
  frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx \
  --watchAll=false

Expected Result

  • All tests pass

Test B — Baseline Program With Full CMS Page

Visit:

http://open.odl.local:8062/programs/<readable_id>

Expected Result

  • Hero image renders
  • Description renders
  • Info box renders
  • No JavaScript errors in browser console

Test C — Program With feature_image_src: null

This test removes the hero image from a program CMS page so
feature_image_src returns null, exercising the image fallback guard.


3a. In MITx Online — Remove the Program Image

From mitxonline/:

docker compose run --rm web python manage.py shell
from cms.models import ProgramPage

# Use any program that has a page
page = ProgramPage.objects.filter(program__isnull=False).first()

print(f"Using: {page.program.readable_id} | image={page.image}")

# Save the image so you can restore it later
original_image = page.image

# Remove image
page.image = None

revision = page.save_revision()
revision.publish()

print("Done — feature_image_src is now null")

3b. In MIT Learn — Sync the Change

docker compose run --rm web python manage.py backpopulate_mitxonline_data

3c. Visit the Program URL

http://open.odl.local:8062/programs/<same readable_id>

Expected Result

  • Page loads without crashing
  • No TypeError in browser console
  • Hero image falls back to default placeholder
  • Title renders
  • Description renders
  • Enrollment button renders
  • Course list renders

3d. Restore the Image

Back in the MITx Online shell from step 3a:

page.image = original_image

revision = page.save_revision()
revision.publish()

print("Restored")

Then re-sync MIT Learn:

docker compose run --rm web python manage.py backpopulate_mitxonline_data

Test D — Dashboard With Program That Has No CMS Page

This tests ContractContent where program.page is genuinely null
at runtime. This reads the MITx Online Programs API directly rather than
MIT Learn's database.


4a. In MITx Online — Create a Program With No Page

docker compose run --rm web python manage.py shell
from courses.models import Program, ProgramEnrollment
from django.contrib.auth import get_user_model

User = get_user_model()

# Find a program that has no Wagtail CMS page
p = Program.objects.filter(page__isnull=True, live=True).first()

if not p:
    # Create one
    p = Program.objects.create(
        readable_id="program-v1:test+no-page",
        title="Test Program No Page",
        live=True,
    )

    print(f"Created: {p.readable_id}")
else:
    print(f"Using existing: {p.readable_id} | {p.title}")

# Enroll your test user
user = User.objects.get(email="<your-test-user@email.com>")

enrollment, created = ProgramEnrollment.objects.get_or_create(
    user=user,
    program=p,
    defaults={
        "enrollment_mode": "honor",
        "status": "enrolled",
    },
)

print(f"Enrollment {'created' if created else 'already existed'}")

4b. Verify Dashboard Behavior

Log into MIT Learn as the enrolled test user and visit:

http://open.odl.local:8062/dashboard

Find the no-page program in the enrolled programs list.

Expected Result

  • Dashboard loads without crashing
  • No TypeError: Cannot read properties of null in console
  • Program card renders successfully
  • Description is empty instead of causing a crash

Copilot AI review requested due to automatic review settings May 18, 2026 12:26
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 18, 2026

OpenAPI Changes

No changes detected

View full changelog

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

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

This PR updates the MITx Online API client to a build where product page fields are nullable, and adjusts MITx Online product/dashboard UI code to avoid crashing when those fields are null.

Changes:

  • Replaces direct page property reads with optional chaining/fallbacks in course, program, program-as-course, summary, and dashboard components.
  • Adds regression tests for nullable page data in affected product/dashboard views.
  • Updates the MITx Online API client dependency and lockfile resolution to the pre-release tarball.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
frontends/api/package.json Points API workspace to updated MITx Online API client tarball.
frontends/main/package.json Points main workspace to updated MITx Online API client tarball.
yarn.lock Locks the updated MITx Online API client tarball/checksum.
frontends/main/src/app-pages/ProductPages/CoursePage.tsx Guards nullable course page image/description access.
frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx Adds nullable course page regression test.
frontends/main/src/app-pages/ProductPages/ProgramPage.tsx Guards nullable program page image/description access.
frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx Adds nullable program page regression test.
frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx Guards nullable program-as-course page image/description access.
frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.test.tsx Adds nullable program-as-course page regression test.
frontends/main/src/app-pages/ProductPages/ProductSummary.tsx Guards nullable program page summary and financial assistance fields.
frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx Adds nullable program page summary regression test.
frontends/main/src/app-pages/DashboardPage/ContractContent.tsx Guards nullable dashboard program description access.
frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx Adds nullable dashboard program page regression test.

Comment thread frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx Outdated
Comment thread frontends/api/package.json Outdated
},
"dependencies": {
"@mitodl/mitxonline-api-axios": "2026.5.1",
"@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey, quick question on the @mitodl/mitxonline-api-axios dependency. Is this GitHub tarball URL temporary while we wait for the updated package to be published to npm? Just want to make sure we swap it back to a version string before this merges so CI stays relia

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's right. This is temporary utl, just to test the changes. I'll update the version first and then merge this PR.

Copy link
Copy Markdown
Contributor

@daniellefrappier18 daniellefrappier18 left a comment

Choose a reason for hiding this comment

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

This looks good I was able to verify all the test cases, but please see my comment on the @mitodl/mitxonline-api-axios dependency before merging.

@zamanafzal zamanafzal force-pushed the zafzal/update-mitxonline-client-nullable-page branch from 87121de to f540d89 Compare May 21, 2026 07:15
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