test: Enforce application layering using import linter (#36581)

* test: add import linters to promote clean dependencies

* docs: update a few comments / todos
This commit is contained in:
Braden MacDonald
2025-04-22 21:28:30 -07:00
committed by GitHub
parent 9d45f855d6
commit ce00b16be7
4 changed files with 41 additions and 1 deletions

View File

@@ -9,6 +9,8 @@ files =
cms/lib/xblock/upstream_sync.py,
cms/djangoapps/contentstore/rest_api/v2/views/downstreams.py,
openedx/core/djangoapps/content/learning_sequences,
# FIXME: need to solve type issues and add 'search' app here:
# openedx/core/djangoapps/content/search,
openedx/core/djangoapps/content_staging,
openedx/core/djangoapps/content_libraries,
openedx/core/djangoapps/programs/rest_api,

View File

@@ -131,7 +131,6 @@ class UserClipboard(models.Model):
def clean(self):
""" Check that this model is being used correctly. """
# These could probably be replaced with constraints in Django 4.1+
if self.user.id != self.content.user.id:
raise ValidationError("User ID mismatch.")
if self.content.purpose != CLIPBOARD_PURPOSE:

View File

@@ -81,6 +81,12 @@ class ClipboardEndpoint(APIView):
def post(self, request):
"""
Put some piece of content into the user's clipboard.
FIXME: This API needs to be deprecated and replaced by dedicated APIs
within each learning context (POST /course/foo/bar/copy, POST
/library/foo/bar/copy, etc.) We don't want to encode course- and
library-specific logic in content staging, and it shouldn't import
course or library modules.
"""
# Check if the content exists and the user has permission to read it.
# Parse the usage key:

View File

@@ -194,3 +194,36 @@ source_modules =
forbidden_modules =
openedx_learning.apps
allow_indirect_imports = True
[importlinter:contract:4]
name = Low-level apps should not depend on high-level apps
type = layers
layers =
# Layers from high-level to low-level. Imports should only occur from higher to lower.
cms.lib.xblock.upstream_sync | openedx.core.djangoapps.content.search | openedx.core.djangoapps.olx_rest_api
openedx.core.djangoapps.content_libraries
openedx.core.djangoapps.content_staging
openedx.core.djangoapps.xblock
openedx.core.lib.xblock_serializer
openedx.core.djangoapps.content_tagging
ignore_imports =
# Test code can break these layering rules
**.tests.** -> **
# FIXME: the exceptions below are from before we added this import linting rule. Should refactor to eliminate them.
# In particular, the contentstore.helpers module is too big and has too many imports - split it up?
# The CSV export hard-codes support for courses and libraries. Refactor to do something like learning_context.get_children()
openedx.core.djangoapps.content_tagging.helpers.objecttag_export_helpers -> openedx.core.djangoapps.content_libraries.api
# The permissions checking code for tagging requires libraries model data to get all the orgs that a user is using:
openedx.core.djangoapps.content_tagging.utils -> openedx.core.djangoapps.content_libraries.api
# Content staging POST to clipboard API uses libraries APIs. We're working on moving this code to content_libraries
openedx.core.djangoapps.content_staging.views -> openedx.core.djangoapps.content_libraries.api
# content_staging.serializers imports contentstore.helpers which imports contentstore.utils which imports the libraries API.
openedx.core.djangoapps.content_staging.serializers -> cms.djangoapps.contentstore.helpers
# content_libraries.rest_api.libraries imports cms.djangoapps.contentstore.views.course which imports
# contentstore.toggles which imports djangoapps.content.search.api
openedx.core.djangoapps.content_libraries.rest_api.libraries -> cms.djangoapps.contentstore.views.course
# Content libraries imports contentstore.helpers which imports upstream_sync
openedx.core.djangoapps.content_libraries.api.blocks -> cms.djangoapps.contentstore.helpers
openedx.core.djangoapps.content_libraries.api.libraries -> cms.djangoapps.contentstore.helpers