REST API for modulestore_migrator

REST API for modulestore_migrator

The modulestore_migrator REST API will power migration (a.k.a. import) of content from courses and legacy libraries (ModuleStore) into new libraries (Learning Core).

Implementation tickets:

This is not final documentation, it’s an implementation guide. The documentation should be built into the platform using the api_doc_tools package, which will generate interactive Swagger docs.

Data Concepts

Each ModulestoreSource object points to a unique course or legacy library, indicating that the content may be migrated to a new library. Each ModulestoreSource can be migrated zero or more times, with each migration represented by a ModulestoreMigration object.

Furthermore, each ModulestoreSource may specficy at most one forwarded ModulestoreMigration; this forwarded migration is the “authoritative” migration of that content for the purposes of following legacy references through to new references. Practically speaking, we expect that:

  • Course-to-Library migrations will never be forwarded

  • The chronologically first Legacy-Library-to-Library migration for any given legacy library will be forwarded.

Finally, each ModulestoreSource will be associated with many ModulestoreBlockSources (one per block), each ModulestoreMigration will be associated with many ModulestoreBlockMigrations (one per block), and each ModulestoreBlockSource will point at its ModulestoreBlockMigrations. At this time, we do not plan to represent either of the Block-level models in the API.

Endpoints

  • POST /api/modulestore_migrator/v1/migrations

    • Starts the process of importing a legacy learning context into a new library

    • JSON request body:

      • source (str) : Source course or legacy library key

      • target (str): Target library key

      • forward_source_to_target (bool): If true, then set the forwarded field on the ModulestoreSource and the ModulestoreBlockSources to their respective target package and entities. This tells the system to forward any references the the source content to the legacy content. Concrete example: the first time a legacy library is migrated to a V2 library collection, forward_source_to_target=true should be specified, ensuring that any course references to that legacy library will now point to the migrated content in a V2 library.

      • preserve_url_slugs (bool): If true, for each migrated item, try to use the URL slug from the source library. If false, then generate a new slug based on its. In either case, the system will append digits as necessary (_1, _2, etc.) in order to find a unique slug for each item.

      • target_collection_slug (str): Slug of the collection to add all migrated items. Will be created new if it doesn’t already exist.

      • composition_level (str) : Level up to which content should be aggregated. Options:

        • component (default)

        • unit

        • subsection

        • section

      • repeat_handling_strategy (str): If a source course or library is imported into the same target library more than once, how should the system handle conflicts? Options:

        • update : Overwrite each existing item in the target library based on the corresponding source item. For a target component, this means updating its title and content from the source component. For a target container, this means updating its title and adding, removing, or reordering children to match the source container. Please note, if an update causes a child to be removed from a container, the child will not be deleted from the target library.

        • skip: Do not re-migrate source items which have already been migrated to the target library. Novel source items will still be migrated; however, if they are children of containers which already exist in the target library, then those containers will not be updated to reflect the new child.

        • fork : Migrate a new, updated copy of the item in the target library while preserving the existing target library item.

        • Note: All of the above strategies involve a mix of creating and updating items not deleting them. This API will never delete items in the target library, regardless of whether they exist in the source.

    • Responses

      • 200: Migration successfully started. JSON Response body:

        { "uuid": ".....", "name": migrate_from_modulestore", "state": "Succeeded", "state_text": "Succeeded", # Translation into the current language of the current state "completed_steps": 11, "total_steps": 11, "attempts": 1, "created": "2025-05-14T22:24:37.048539Z", "modified": "2025-05-14T22:24:59.128068Z", "artifacts": [], "parameters": [ ... # Same as parameters above! ] }
      • 400: Parameter is missing or invalid. JSON Response body:

        { // This is a standard DRF validatione error response schema. // <https://www.django-rest-framework.org/api-guide/exceptions/#exception-handling-in-rest-framework-views> "source_library": "<details of why source is missing/invalid>", "target_library": "<details of why target is missing/invalid>", "composition": "<details of why level is invalid>", ... }
      • 401: User is not logged in

      • 404: User lacks read access on the source or write access on the target library.

      • 405: Method is anything other than POST or GET

  • GET /api/modulestore_migrator/v1/migrations/<uuid>

    • 200: Get a particular migration, in the schema described above.

    • 401: User is not logged in

    • 404: Migration not found OR user lacks read access on migration’s source.

    • 405: Method is anything other than GET.

  • GET /api/modulestore_migrator/v1/migrations?sources=<source_keys,>

    • 200: List of existing migrations, filtered by sources, in the schema described above. Paginated. sources is required for non-global-staff.

      • NOTE: This schema assumes that the client has already hit an API to get a list of valid sources (e.g., legacy libraries that they have access to) and that they would pass some or all of that list in to this API.

    • 401: User is not logged in

    • 403: Sources is not provided but user is not global staff.

    • 404: A source is not found OR user lacks read access to one of the sources

    • 405: Method is anything other than GET