-
Notifications
You must be signed in to change notification settings - Fork 93
Description
Context:
In #260, support for database configuration for LTI 1.3 launches was added; these changes were put behind a CourseWaffleFlag. The CourseWaffleFlag is referenced in the LtiConsumerXBlock and in the LtiConfiguration classes. In the LtiConfiguration's clean method, the waffle flag is used to validate or clean incoming data to prevent the creation of an LtiConfiguration with a config_store value of CONFIG_ON_DB if the waffle flag is not enabled.
The problem is that the CourseWaffleFlag.is_enabled(<course_key>) method can only be called with a course_key. For LtiConfigurations with a None value for the location field, this raises an exception, because the location is used to load in the XBlock from the modulestore. The course_key is then retrieved from the XBlock. Without a location, there is no course_key, and CourseWaffleFlag.is_enabled(<course_key>) fails. This is only observed in the Django admin, because Model.full_clean() and Model.clean() are not called after Model.save().
I tried to use a WaffleFlag instead, thinking that I would sacrifice the added customizability of the CourseWaffleFlag in exchange for the ability of the flag to work outside the XBlock context. However, WaffleFlag requires there to be an available request object, and there isn't one when executing a model's clean method.
Problem:
Regardless, I believe that this kind of validation does not belong in the model clean method. This kind of business logic validation should occur before the database layer (e.g. in a Form class or in a DRF serializer). But we are not making use of Form classes or DRF serializers here. I would prefer that this check be in a centralized place within the library; however, I cannot easily determine where this should be because of the various ways LtiConfigurations can be created. The two questions I would like to answer are.
-
How can we use feature flags throughout the codebase in a way that does not assume an XBlock runtime context?
-
How can we ensure that the same kind of validation is applied in the three different ways
LtiConfigurationscan be created?
Requirements:
We want to ensure we do not save invalid data to LtiConfiguration in three places:
1. in the XBlock edit menu in Studio
Currently, this is handled by the xblock_studio_view.js Javascript. There is no backend enforcement.
We could add this waffle flag to validate_field_data.
2. in the Django admin
Currently, whatever is in the LtiConfiguration.clean() method will be run.
3. in any other places where we create LtiConfiguration instances, like the Python API
Currently, there is no validation of the data used to create LtiConfiguration instances when the Python API is used. This is because calling Model.clean() does not call Model.full_clean() and Model.clean().
It may happen that we decide to release "CONFIG_ON_DB" platform wide and remove the feature flag before edx-exams even needs it, in which case the only remaining issue is #2 under "Problems".
Other Notes:
-
In the Python API, there is no clear place to validate the incoming data. Because of the way that data is synced from the XBlock to the
LtiConfigurationin_get_lti_configand_get_or_create_local_lti_config, any time a caller intends to fetch anLtiConfiguration, it may be updated as part of the fetch to sync it with the modulestore. It's unexpected for a function likeget_lti_1p3_launch_infoto have to handle aValidationErrorif we were to add validation to_get_or_create_local_lti_config. -
Currently,
LtiConfigurationinstances are created via the_get_or_create_local_lti_configAPI method, which is used throughout the codebase. Note thatModel.full_clean()andModel.clean()are not actually called whenModel.save()is called, so the modelcleanmethod only runs when making changes through the Django admin. With support forCONFIG_ON_DB, we need to support creating and editingLtiConfigurationsfrom the Django admin while also being able to validate incoming data. It's possible to customize the model admin form to store the request and use a WaffleFlag, but it's hacky.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
