-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BB-5559] Decouple LTI 1.3 from LTI Consumer XBlock functionality #254
[BB-5559] Decouple LTI 1.3 from LTI Consumer XBlock functionality #254
Conversation
Thanks for the pull request, @tecoholic! Please note that it may take us up to several weeks or months to complete a review and merge your PR. Feel free to add as much of the following information to the ticket as you can:
All technical communication about the code itself will be done via the GitHub pull request interface. As a reminder, our process documentation is here. Please let us know once your PR is ready for our review and all tests are green. |
198068d
to
0e7c50c
Compare
Codecov Report
@@ Coverage Diff @@
## master #254 +/- ##
==========================================
- Coverage 97.01% 96.55% -0.47%
==========================================
Files 71 71
Lines 5162 5252 +90
==========================================
+ Hits 5008 5071 +63
- Misses 154 181 +27
Flags with carried forward coverage won't be shown. Click here to find out more.
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
61c2444
to
ae42e75
Compare
@tecoholic Thank you for the contribution, please let me know once this is ready for our review. |
Unfortunately, we can't, this is used by external tools - and there's already a lot of them configured on edX.org. Studio should only display the new URL from now onwards. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tecoholic Woah, great progress here!
You'll also need to update the URLs on https:/openedx/xblock-lti-consumer/blob/master/lti_consumer/utils.py#L56 to point to the new endpoints and update references to those.
310a2e2
to
60a24e6
Compare
NOTE: The |
Hi @tecoholic. This is great, we also have some very similar work queued up to start doing LTI1.3 launches outside of the XBlock. (including #260) I believe @giovannicimolin is out for the better part of the month but I'd like to sync up so we aren't duplicating effort. Who is the best person to reach out to? |
d4ff48b
to
c7a8545
Compare
@Zacharis278 Hi! Looks like #260 is a very similar to this. 👀 You are right, @giovannicimolin is going to be unavailable for the month. @Agrendalath is handling the reviews in the meantime. Maybe he can help. |
lti_consumer/api.py
Outdated
# set to CONFIG_ON_XBLOCK | ||
if config_changed or conf.config_store == conf.CONFIG_ON_XBLOCK: | ||
conf.lti_config.update(block_config) | ||
conf.config_store = conf.CONFIG_ON_DB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also need to set config_type
to 'external'
? I see that _get_lti_config
is checking this, not _get_lti_config
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. I have not considered the external case. For now we can simply return the config without any changes in the get_or_update_lti_config
(Line 91). Adding
if block.config_type == "external":
return conf
2f7e77b
to
3976c2b
Compare
b85f2da
to
718f18f
Compare
Original content removed Never mind. I figured out the solution literally 5 mins after posting the comment. I think articulating the design challenge helped see the problem clearly. :) Sorry for the noise. |
ed4e4b3
to
9c2e441
Compare
c4adc1e
to
1684785
Compare
@giovannicimolin I have removed the overengineered parts, incorporated your feedback about the logging, rebased the code to master. Kindly give this another go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be building off this, so I'm reviewing to better understand your work. I left a few small thoughts.
lti_consumer/lti_xblock.py
Outdated
@@ -710,6 +700,14 @@ def editable_fields(self): | |||
if not is_external_config_filter_enabled: | |||
noneditable_fields.append('external_config') | |||
|
|||
if self.lti_version == 'lti_1p3' and is_database_config_enabled and self.config_type == 'database': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had something very similar in #260. I removed it in #266, because I believe that the Javascript should be responsible for showing or hiding fields. If this method doesn't return the LTI 1.3 fields when the config_type
is database, then the Javascript cannot show the LTI 1.3 fields when the config_type
is changed to XBlock in the modal.
Is this leftover code from #260 that should be removed? Or is it necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MichaelRoytman I believe this would be leftover code from #260. I am not sure why rebasing hasn't updated it. Maybe a conflict line. Let me update it to the latest code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tecoholic This is still showing up for me :)
lti_consumer/lti_xblock.py
Outdated
) | ||
# Asserting that the consumer can be created. This makes sure that the LtiConfiguration | ||
# object exists before calling the Django View | ||
assert self._get_lti_consumer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need exception handling similar to what you have in launch_gate_endpoint
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original code doesn't have any error handling around this line. So I assume it is safe to call. We can add one here if it is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Launches are working! @tecoholic Nice job!
I left a few comments in the PR though, we can address the remaining issues next sprint with smaller PRs.
if usage_id: | ||
lti_config = LtiConfiguration.objects.get(location=UsageKey.from_string(usage_id)) | ||
elif lti_config_id: | ||
lti_config = LtiConfiguration.objects.get(config_id=lti_config_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If none of these blocks execute, you'll have a NameError: name 'lti_config' is not defined
, which is unhandled by the try-except block below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But wouldn't the Django's URL resolver take care this path and return a 404? The two paths that lead to this function are
'lti_consumer/v1/public_keysets/<uuid:lti_config_id>'
f'lti_consumer/v1/public_keysets/{settings.USAGE_ID_PATTERN}$'
""" | ||
usage_id = request.GET.get('login_hint') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is getting too big - we'll have to change the way we give the context to the LTI consumer on a follow-up PR. Let's get these endpoints working on django with this PR first.
lti_consumer/plugin/views.py
Outdated
if lti_config_id: | ||
lti_config = LtiConfiguration.objects.get(config_id=lti_config_id) | ||
else: | ||
usage_key = UsageKey.from_string(usage_id) | ||
lti_config = LtiConfiguration.objects.get(location=usage_key) | ||
except LtiConfiguration.DoesNotExist as exc: | ||
log.warning("Error getting the LTI configuration with id %r: %s", lti_config_id, exc) | ||
raise Http404 from exc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add an extra case for the uncaught path here, and improve log output.
if lti_config_id: | |
lti_config = LtiConfiguration.objects.get(config_id=lti_config_id) | |
else: | |
usage_key = UsageKey.from_string(usage_id) | |
lti_config = LtiConfiguration.objects.get(location=usage_key) | |
except LtiConfiguration.DoesNotExist as exc: | |
log.warning("Error getting the LTI configuration with id %r: %s", lti_config_id, exc) | |
raise Http404 from exc | |
if lti_config_id: | |
lti_config = LtiConfiguration.objects.get(config_id=lti_config_id) | |
elif usage_id: | |
usage_key = UsageKey.from_string(usage_id) | |
lti_config = LtiConfiguration.objects.get(location=usage_key) | |
else: | |
raise ValueError("Either `lti_config` or `usage_id` need to be set for this function to work") | |
except (LtiConfiguration.DoesNotExist, ValueError) as exc: | |
log.warning("Error getting the LTI configuration with id %r: %s", lti_config_id, exc, exc_info=True) | |
raise Http404 from exc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question as above? The Djanog's URL resolver would reject calls without either lti_config_id
or usage_id
.
lti_consumer/lti_xblock.py
Outdated
@@ -710,6 +700,14 @@ def editable_fields(self): | |||
if not is_external_config_filter_enabled: | |||
noneditable_fields.append('external_config') | |||
|
|||
if self.lti_version == 'lti_1p3' and is_database_config_enabled and self.config_type == 'database': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tecoholic This is still showing up for me :)
Hey guys, wanted to check in on where we stand with this PR. We've got some work lined up that would require doing a 1.3 launch without an xblock context so this would be great to have in master since there's some big changes here, even if we haven't totally solved the context/login_hint solution in More than happy to collaborate on how to solve that in a future PR since it's key to our needs as well! |
The LTI 1.3 Launch callback handler is removed from the XBlock as the logic has been moved to its corresponding Django View. The associated tests are also moved from the XBlock testcase to the Django View TestCase.
@Zacharis278 I have made the changes to address the comments from @giovannicimolin. I think it is close to being merged. |
@Zacharis278 This is in the final stages of review. |
@tecoholic I'm almost done with the review - just wanted to test the grade passback functionality, but I don't have any tools with that capability set up locally. Other than that, launches and DL launches are working - just want to make sure the other services work too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
- I tested this:
I set up the following tools and checked that they still work as expected: - IMS Reference implementation (https://lti-ri.imsglobal.org/)
- H5P.com (https://opencraft.h5p.com/)
- Locally, with IMS's PHP tool reference implementation.
- I checked that launches are working as expected ✔️
- I checked that Grades are being sent back to the LMS ✔️
- I checked that Deep linking configuration is still working ✔️
- I read through the code
-
I checked for accessibility issuesNA - Includes documentation
@tecoholic Good to go, I've checked that everything keeps working with this new rework.
Before this is merged, can you add a version bump?
We should go to __version__ = '4.4.0'
with this change.
@giovannicimolin Great. Thank you for testing it so thoroughly. I have updated bumped the version to 4.4.0 |
…ndpoints from BB-5559 (#254)
…ndpoints from BB-5559 (#254)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also run through a basic reference tool launch with this. Looks great!
@tecoholic 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future. |
Description
The goal of the PR is to decouple the LTI 1.3 functionality from the XBlock and move the functionality to the Django Plugin, while keeping backward compatibility with the XBlock Handlers. The main consideration is that, this does not break any existing integrations. So all the endpoints and XBlock handlers are made backward compatible.
Changes introduced by the refactor
LtiConfiguration
ID is introduced for the Access Token endpoint and is used when a LTI Consumer is configured without alocation
allowing LTI integrations to be created without the XBlock context.LtiConfiguration
ID is introduced for the Keyset Endpoint and is used with thelocation
of the XBlock is not available in the configuration.Testing instructions
pip install git+https:/open-craft/xblock-lti-consumer.git@tecoholic/BB-5559-lti1p3-refactor
. If you are using devstack, install it in both lms and studio containers.