Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions .github/workflows/generate_openapi_schemas.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# generate_openapi_schemas.yml
#
# Purpose: Generate OpenAPI schemas for the LMS and CMS using drf-spectacular,
# then open a pull request if either schema changed. The generated schema files
# are consumed by the openedx-platform-sdk repo's regen_sdk.sh script to keep
# the SDK in sync with the platform's tagged API views.

name: Generate OpenAPI Schemas

permissions:
contents: write
pull-requests: write

on:
workflow_dispatch:

schedule:
# Regenerate and commit the full OpenAPI schemas every Monday at 09:00 UTC.
- cron: "0 9 * * 1"

jobs:
generate-schemas:
runs-on: ubuntu-latest
Comment thread
Faraz32123 marked this conversation as resolved.

# Schema generation needs a migrated database: drf-spectacular evaluates a
# queryset while building a warning for edxval's VideoList, so the tables
# have to exist, not just the server. Mongo is needed for the migration
# itself rather than for generation -- split_modulestore_django's
# 0002_data_migration reads the modulestore. Credentials match the defaults
# in openedx/envs/common.py.
services:
mongo:
image: mongo:7
ports:
- 27017:27017
# Note: Calling mongo here only works with mongo 4, in newer versions of mongo
# we'll have to use `mongosh`, hence the 'which mongosh mongo'.
options: >-
--health-cmd "$(which mongosh mongo) --quiet --eval 'db.runCommand(\"ping\")'"
--health-interval 10s
--health-timeout 5s
--health-retries 3
mysql:
image: mysql:8
ports:
- 3306:3306
env:
MYSQL_DATABASE: "edxapp"
MYSQL_USER: "edxapp001"
MYSQL_PASSWORD: "password"
MYSQL_RANDOM_ROOT_PASSWORD: true
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 3

steps:
- name: Setup mongodb user
run: |
docker exec ${{ job.services.mongo.id }} mongosh edxapp --eval '
db.createUser(
{
user: "edxapp",
pwd: "password",
roles: [
{ role: "readWrite", db: "edxapp" },
]
}
);
'

- name: Check out repository
Comment thread
feanil marked this conversation as resolved.
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

# mysqlclient ships no wheels, so pip builds it from source and needs the
# MySQL and xmlsec headers. migrations-check.yml and unit-tests.yml do the
# same before installing Python dependencies.
- name: Install system packages
run: |
sudo apt-get update
make ubuntu-requirements

- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
enable-cache: true

# `default-groups = []` in pyproject.toml means a bare `uv sync` installs
# no groups at all, which leaves out ora2 and makes both schema steps fail
# with ModuleNotFoundError. `docs` includes `bundled`, which carries the
# bundled XBlocks — the same set .readthedocs.yaml and the other workflows
# install.
- name: Install dependencies
run: uv sync --no-default-groups --group docs --frozen

# Each migrate runs under the same settings as the generate step below
# it, so the tables land in the database that step reads. Without the
# settings module manage.py falls back to devstack, whose DATABASES is
# empty without an LMS_CFG, and the step fails before reaching the
# service.
- name: Run migrations
run: |
DJANGO_SETTINGS_MODULE=docs.docs_settings uv run ./manage.py lms migrate
DJANGO_SETTINGS_MODULE=cms.envs.development uv run ./manage.py cms migrate

# `docs.docs_settings` is what `make swagger` uses: it applies the
Comment thread
Faraz32123 marked this conversation as resolved.
# unfiltered /api-docs configuration instead of the narrow, SDK-facing
# schema that SPECTACULAR_SETTINGS carries by default. Without it this
# step would overwrite the published LMS reference with the
# enrollment-only subset.
#
# NOTE: docs_settings only carries that unfiltered configuration as of
# https://github.com/openedx/openedx-platform/pull/39108, which must merge
# before this workflow runs.
- name: Generate LMS OpenAPI schema
run: |
DJANGO_SETTINGS_MODULE=docs.docs_settings \
uv run python manage.py lms spectacular --file docs/lms-openapi.yaml

# Studio has no docs_settings equivalent. `manage.py cms` defaults to
# cms.envs.devstack, which imports production.py and so requires a
# CMS_CFG file that does not exist in CI — settings raise
# ImproperlyConfigured before the command is even registered.
# cms.envs.development needs no such file, and inherits the Authoring
# API's SPECTACULAR_SETTINGS from cms/envs/common.py, so this emits the
# same contentstore-scoped surface Studio publishes at
# /authoring-api/schema/.
- name: Generate CMS OpenAPI schema
run: |
DJANGO_SETTINGS_MODULE=cms.envs.development \
uv run python manage.py cms spectacular --file docs/cms-openapi.yaml

Comment thread
feanil marked this conversation as resolved.
- name: Open pull request if schemas changed
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8
with:
branch: chore/update-openapi-schemas
# Without a suffix, a run that happens while the previous PR is
# still open pushes onto that same branch. Matches
# compile-python-requirements.yml and upgrade-one-python-dependency.yml.
branch-suffix: short-commit-hash
commit-message: |
chore: regenerate OpenAPI schemas

Commit generated by workflow `${{ github.workflow_ref }}`
title: 'chore: update OpenAPI schemas for SDK generation'
team-reviewers: wg-maintenance-openedx-platform-oncall
body: |
## Auto-generated OpenAPI schema update

PR generated by workflow `${{ github.workflow_ref }}`.
It contains regenerated `docs/lms-openapi.yaml` and/or `docs/cms-openapi.yaml`
files reflecting the latest state of the platform's API views.

`docs/lms-openapi.yaml` is the full LMS API surface — the same schema
`make swagger` produces, which the docs build publishes.
`docs/cms-openapi.yaml` is Studio's tagged schema.

Both are consumed by the
[openedx-platform-sdk](https://github.com/edly-io/openedx-platform-sdk)
repository's `regen_sdk.sh`, which filters them down to the tagged
operations it generates a client for.

**Do not edit these files by hand** — they will be overwritten on the next run.
add-paths: |
docs/lms-openapi.yaml
docs/cms-openapi.yaml
23 changes: 23 additions & 0 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,29 @@ def _should_send_xblock_events(settings):
# This affects the Authoring API swagger docs but not the legacy swagger docs under /api-docs/.
REST_FRAMEWORK['DEFAULT_SCHEMA_CLASS'] = 'drf_spectacular.openapi.AutoSchema' # noqa: F405

# The Authoring API schema, for any settings module that does not define its
# own SPECTACULAR_SETTINGS -- notably cms.envs.development, which generates the
# committed docs/cms-openapi.yaml in CI. Without these the document has no
# title, version 0.0.0, and every endpoint in the service rather than the
# Authoring API's own surface.
#
# devstack.py and production.py replace this wholesale, adding SERVERS and a
# longer DESCRIPTION. Those are the only parts that depend on CMS_BASE and
# AUTHORING_API_URL, which are empty here; the filtering and the path prefix do
# not, so they belong at this level.
SPECTACULAR_SETTINGS = {
'TITLE': 'Authoring API',
'DESCRIPTION': 'Experimental API to edit xblocks and course content.',
'VERSION': '0.1.0',
Comment thread
Faraz32123 marked this conversation as resolved.
'SERVE_INCLUDE_SCHEMA': False,
# Restrict the schema to the Authoring API's endpoints (cms/lib/spectacular.py).
'PREPROCESSING_HOOKS': ['cms.lib.spectacular.cms_api_filter'],
# Trim the shared prefix from the published paths; consumers supply it
# through their base URL.
'SCHEMA_PATH_PREFIX': '/api/contentstore',
'SCHEMA_PATH_PREFIX_TRIM': '/api/contentstore',
}

################### Studio Search (beta), using Meilisearch ###################

# Enable Studio search features (powered by Meilisearch) (beta, off by default)
Expand Down
Loading