-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from open-craft/tecoholic/BB-5559-centrally-sto…
…re-lti-keys [BB-5559] Create the plugin to store LTI configurations
- Loading branch information
Showing
24 changed files
with
650 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[run] | ||
omit = | ||
*apps.py, | ||
*migrations/*.py, | ||
*settings*.py, | ||
*tests/*.py, | ||
*urls.py, | ||
*wsgi.py, | ||
*asgi.py, | ||
*test.py, | ||
manage.py, | ||
setup.py, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
*.py[cod] | ||
__pycache__ | ||
.pytest_cache | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Packages | ||
*.egg | ||
*.egg-info | ||
/dist | ||
/build | ||
/eggs | ||
/parts | ||
/bin | ||
/var | ||
/sdist | ||
/develop-eggs | ||
/.installed.cfg | ||
/lib | ||
/lib64 | ||
|
||
# Installer logs | ||
pip-log.txt | ||
|
||
# Unit test / coverage reports | ||
.cache/ | ||
.pytest_cache/ | ||
.coverage | ||
.coverage.* | ||
.tox | ||
coverage.xml | ||
htmlcov/ | ||
|
||
|
||
|
||
# The Silver Searcher | ||
.agignore | ||
|
||
# OS X artifacts | ||
*.DS_Store | ||
|
||
# Logging | ||
log/ | ||
logs/ | ||
chromedriver.log | ||
ghostdriver.log | ||
|
||
# Complexity | ||
output/*.html | ||
output/*/index.html | ||
|
||
|
||
*.sqlite |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
lint: | ||
black lti_store | ||
|
||
test: | ||
pytest --cov-report term-missing lti_store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,66 @@ | ||
# Open edX LTI Store (openedx-ltistore) | ||
# Openedx LTI Store | ||
|
||
A plugin for openedx-platform to store LTI configurations centrally. This allows course creators to add [LTI components](https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/exercises_tools/lti_component.html) without having to enter the details like secrets, keys and URLs everytime the component block is created. | ||
|
||
## Development | ||
|
||
The development instructions are written with the [Open edX Devstack](https://edx.readthedocs.io/projects/open-edx-devstack/en/latest/index.html) as reference. | ||
|
||
1. Clone the repostiory to `<devstack_root>/src/` directory | ||
```sh | ||
cd <devstack_root>/src/ | ||
git clone [email protected]:open-craft/openedx-ltistore.git | ||
``` | ||
2. Install the plugin inside the Studio container and run migrations | ||
```sh | ||
cd <devstack_root>/devstack/ | ||
make studio-shell | ||
pip install -e /edx/src/openedx-ltistore | ||
python manage.py cms migrate lti_store | ||
exit | ||
``` | ||
3. Install the plugin inside the LMS Container | ||
```sh | ||
make lms-shell | ||
pip install -e /edx/src/openedx-ltistore | ||
exit | ||
``` | ||
4. The LTI Consumer XBlock can fetch configurations to LTI Tools using `openedx-filters` mechanism. It calls the filter `org.openedx.xblock.lti_consumer.configuration.listed.v1` whenever it wants to fetch the configurations from external tools like plugins. In order for **LTI Store** to send available LTI Tools, add the following to any existing `openedx-filters` configurations for both LMS (`edx-platform/lms/envs/devstack.py` or `private.py`) and studio (`edx-platform/cms/envs/devstack.py`): | ||
```py | ||
OPEN_EDX_FILTERS_CONFIG = { | ||
"org.openedx.xblock.lti_consumer.configuration.listed.v1": { | ||
"fail_silently": False, | ||
"pipeline": [ | ||
"lti_store.pipelines.GetLtiConfigurations" | ||
] | ||
} | ||
} | ||
``` | ||
5. Restart the LMS & Studio for the latest config to take effect | ||
```sh | ||
make lms-restart | ||
make studio-restart | ||
``` | ||
|
||
Now any changes made to the source code should reflect in the application | ||
|
||
## Adding LTI Tools to the store | ||
|
||
1. Go to `https://localhost:18010/admin` | ||
2. Look for `LTI_STORE` and add **LTI Configurations** by clicking `+ Add` button | ||
|
||
## Linting | ||
|
||
The project uses [Black](https://black.readthedocs.io/en/stable/) for linting. To lint the code | ||
|
||
``` | ||
make lint | ||
``` | ||
|
||
## Testing | ||
|
||
Unit tests can be run with | ||
|
||
``` | ||
make test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "0.0.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from django.contrib import admin | ||
|
||
from .models import ExternalLtiConfiguration | ||
from .apps import LtiStoreConfig as App | ||
|
||
|
||
class LtiConfigurationAdmin(admin.ModelAdmin): | ||
list_display = ("id", "name", "version", "filter_key") | ||
list_filter = ("version",) | ||
prepopulated_fields = {"slug": ("name",)} | ||
|
||
def filter_key(self, obj): | ||
return f"{App.name}:{obj.slug}" | ||
|
||
|
||
admin.site.register(ExternalLtiConfiguration, LtiConfigurationAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class LtiStoreConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "lti_store" | ||
plugin_app = {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# Generated by Django 3.2.12 on 2022-04-08 20:30 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="ExternalLtiConfiguration", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
("name", models.CharField(max_length=80, unique=True)), | ||
("slug", models.SlugField(max_length=80, unique=True)), | ||
("description", models.TextField(blank=True, default="")), | ||
( | ||
"version", | ||
models.CharField( | ||
choices=[("lti_1p1", "LTI 1.1"),], | ||
default="lti_1p1", | ||
max_length=10, | ||
), | ||
), | ||
( | ||
"lti_1p1_launch_url", | ||
models.CharField( | ||
blank=True, | ||
help_text="The URL of the external tool that initiates the launch.", | ||
max_length=255, | ||
), | ||
), | ||
( | ||
"lti_1p1_client_key", | ||
models.CharField( | ||
blank=True, | ||
help_text="Client key provided by the LTI tool provider.", | ||
max_length=255, | ||
), | ||
), | ||
( | ||
"lti_1p1_client_secret", | ||
models.CharField( | ||
blank=True, | ||
help_text="Client secret provided by the LTI tool provider.", | ||
max_length=255, | ||
), | ||
), | ||
], | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from django.db import models | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
|
||
class LTIVersion(models.TextChoices): | ||
LTI_1P1 = "lti_1p1", _("LTI 1.1") | ||
|
||
|
||
class ExternalLtiConfiguration(models.Model): | ||
|
||
name = models.CharField(max_length=80, unique=True) | ||
slug = models.SlugField(max_length=80, unique=True) | ||
description = models.TextField(blank=True, default="") | ||
|
||
version = models.CharField( | ||
max_length=10, choices=LTIVersion.choices, default=LTIVersion.LTI_1P1 | ||
) | ||
|
||
# LTI 1.1 Related variables | ||
lti_1p1_launch_url = models.CharField( | ||
max_length=255, | ||
blank=True, | ||
help_text=_("The URL of the external tool that initiates the launch."), | ||
) | ||
lti_1p1_client_key = models.CharField( | ||
max_length=255, | ||
blank=True, | ||
help_text=_("Client key provided by the LTI tool provider."), | ||
) | ||
|
||
lti_1p1_client_secret = models.CharField( | ||
max_length=255, | ||
blank=True, | ||
help_text=_("Client secret provided by the LTI tool provider."), | ||
) | ||
|
||
def __str__(self): | ||
return f"<ExternalLtiConfiguration #{self.id}: {self.slug}>" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from typing import Dict | ||
|
||
from django.forms.models import model_to_dict | ||
|
||
from openedx_filters import PipelineStep | ||
|
||
from lti_store.models import ExternalLtiConfiguration | ||
from lti_store.apps import LtiStoreConfig | ||
|
||
|
||
class GetLtiConfigurations(PipelineStep): | ||
""" | ||
Get all available LTI configurations | ||
Example usage: | ||
Add the following configurations to your configuration file: | ||
OPEN_EDX_FILTERS_CONFIG = { | ||
"org.openedx.xblock.lti_consumer.configuration.listed.v1": { | ||
"fail_silently": false, | ||
"pipeline": [ | ||
"lti_store.pipelines.GetLtiConfigurations" | ||
] | ||
} | ||
} | ||
""" | ||
|
||
PLUGIN_PREFIX = LtiStoreConfig.name | ||
|
||
def run_filter( | ||
self, context: Dict, config_id: str, configurations: Dict, *args, **kwargs | ||
): # pylint: disable=arguments-differ, unused-argument | ||
config = {} | ||
if config_id: | ||
_slug = config_id.split(":")[1] | ||
try: | ||
config_object = ExternalLtiConfiguration.objects.get(slug=_slug) | ||
config = { | ||
f"{self.PLUGIN_PREFIX}:{config_object.slug}": model_to_dict( | ||
config_object | ||
) | ||
} | ||
except ExternalLtiConfiguration.DoesNotExist: | ||
config = {} | ||
else: | ||
config_objs = ExternalLtiConfiguration.objects.all() | ||
config = { | ||
f"{self.PLUGIN_PREFIX}:{c.slug}": model_to_dict(c) for c in config_objs | ||
} | ||
|
||
configurations.update(config) | ||
return { | ||
"configurations": configurations, | ||
"config_id": config_id, | ||
"context": context, | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from django.test import TestCase | ||
from lti_store.models import ExternalLtiConfiguration, LTIVersion | ||
|
||
|
||
class LTIConfigurationTestCase(TestCase): | ||
def test_string_representation_of_model(self): | ||
cfg = ExternalLtiConfiguration.objects.create( | ||
name="Test Config", slug="test-config" | ||
) | ||
self.assertEqual(str(cfg), "<ExternalLtiConfiguration #1: test-config>") | ||
cfg.delete() |
Oops, something went wrong.