From 62468ea8c19a81a052034bf403dc5b13597401a4 Mon Sep 17 00:00:00 2001 From: Brian Smith <112954497+brian-smith-tcril@users.noreply.github.com> Date: Wed, 11 Jan 2023 18:12:32 -0500 Subject: [PATCH] feat: support running as a tutor plugin in production --- .../README.rst | 54 +++++--- .../patches/openedx-cms-development-settings | 1 + .../patches/openedx-cms-production-settings | 1 + .../patches/openedx-common-settings | 1 + .../patches/openedx-lms-production-settings | 2 + .../tutor_library_authoring_mfe/plugin.py | 119 ++++++++++-------- .../library_authoring_mfe/jobs/init/cms.sh | 6 + .../library_authoring_mfe/tasks/cms/init | 9 -- webpack.prod.config.js | 19 +++ 9 files changed, 136 insertions(+), 76 deletions(-) create mode 100644 tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-development-settings create mode 100644 tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-production-settings create mode 100644 tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-common-settings create mode 100644 tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-lms-production-settings create mode 100644 tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/jobs/init/cms.sh delete mode 100644 tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/tasks/cms/init create mode 100644 webpack.prod.config.js diff --git a/tutor-contrib-library-authoring-mfe/README.rst b/tutor-contrib-library-authoring-mfe/README.rst index 3668f769..fc4442d3 100644 --- a/tutor-contrib-library-authoring-mfe/README.rst +++ b/tutor-contrib-library-authoring-mfe/README.rst @@ -1,31 +1,49 @@ -library_authoring_mfe plugin for `Tutor `__ +library_authoring_mfe plugin for `Tutor `_ =================================================================================== -This plugin will setup Blockstore and the Library Authoring MFE, so you can -start using it to create v2 Libraries using Blockstore. +Installation +------------ -Installation and usage ----------------------- +Follow these instructions to enable this microfrontend: -**IMPORTANT**: First, you must be using `Tutor Nightly `_, a -recent (July 2022 or newer) version of edx-platform (`master` branch), and the -`Tutor MFE plugin `_. +* Install `tutor nightly `_: ``pip install -e 'git+https://github.com/overhangio/tutor.git@nightly#egg=tutor'`` +* Install `tutor-mfe nightly `_: ``pip install -e 'git+https://github.com/overhangio/tutor-mfe.git@nightly#egg=tutor-mfe'`` +* To use blockstore with `minio `_ + + * Install `tutor-minio `_ nightly ``pip install -e 'git+https://github.com/overhangio/tutor-minio.git@nightly#egg=tutor-minio'`` + * Enable minio plugin: ``tutor plugins enable minio`` + * Install `tutor-contrib-blockstore-minio `_: ``pip install -e 'git+https://github.com/brian-smith-tcril/tutor-contrib-blockstore-minio#egg=tutor-contrib-blockstore-minio'`` + * Enable the blockstore_config_minio plugin: ``tutor plugins enable blockstore_config_minio`` -Then, follow these instructions to enable this microfrontend: +* To use blockstore with django :code:`FileSystemStorage` -1. Install this plugin: ``pip install -e 'git+https://github.com/openedx/frontend-app-library-authoring.git#egg=tutor-contrib-library-authoring-mfe&subdirectory=tutor-contrib-library-authoring-mfe'`` -2. Enable the plugin: ``tutor plugins enable library_authoring_mfe`` -3. Run ``tutor config save``. -4. Run ``tutor dev init -l library_authoring_mfe`` to set the required waffle flags in the CMS. -5. Restart your Tutor Nightly Dev environment and also start this with ``tutor dev start library-authoring`` -6. Go to http://studio.local.overhang.io:8001/home/ , click "Libraries", and create a new library with - "Library Type: Complex (beta)" + * Install `tutor-contrib-blockstore-filesystem `_: ``pip install -e 'git+https://github.com/brian-smith-tcril/tutor-contrib-blockstore-filesystem#egg=tutor-contrib-blockstore-filesystem'`` + * Enable the blockstore_config_filesystem plugin: ``tutor plugins enable blockstore_config_filesystem`` +* Install this plugin: ``pip install -e 'git+https://github.com/brian-smith-tcril/frontend-app-library-authoring.git@tutor-prod#egg=tutor-contrib-library-authoring-mfe&subdirectory=tutor-contrib-library-authoring-mfe'`` +* Enable this plugin: ``tutor plugins enable library_authoring_mfe`` +* Save the tutor config: ``tutor config save`` +* Build mfe image: ``tutor images build mfe`` (if you have trouble here you may need to run it with ``--no-cache``) +* Launch tutor: ``tutor local launch`` If you want to run this MFE in -`development mode ` -(to make changes to the code), instead of step 5 above, do this:: +`development mode `_ +(to make changes to the code), instead of step 9 above, do this:: cd /path/to/frontend-app-library-authoring tutor dev run --mount=. library-authoring npm install # Ensure NPM requirements are installed into your fork. tutor dev start --mount=. library-authoring + +Setup +----- +* Ensure you have created a user: https://docs.tutor.overhang.io/local.html#creating-a-new-user-with-staff-and-admin-rights +* Ensure you have created an organization: http://studio.local.overhang.io/admin/organizations/organization/ +* If you're using minio + + * Log in to the `minio Web UI `_ (`instructions to find credentials `_) + * Create a **public** bucket for blockstore (the default configuration expects the bucket to be named :code:`blockstore`) + +Usage +----- +* Log in to studio: http://studio.local.overhang.io/home/ +* Click on the libraries tab diff --git a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-development-settings b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-development-settings new file mode 100644 index 00000000..3ea334ca --- /dev/null +++ b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-development-settings @@ -0,0 +1 @@ +LIBRARY_AUTHORING_MICROFRONTEND_URL = "http://{{ MFE_HOST }}:{{ LIBRARY_AUTHORING_MFE_APP["port"] }}/{{ LIBRARY_AUTHORING_MFE_APP["name"] }}" diff --git a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-production-settings b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-production-settings new file mode 100644 index 00000000..8ce0ce40 --- /dev/null +++ b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-cms-production-settings @@ -0,0 +1 @@ +LIBRARY_AUTHORING_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https://{% else %}http://{% endif %}{{ MFE_HOST }}/{{ LIBRARY_AUTHORING_MFE_APP["name"] }}" diff --git a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-common-settings b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-common-settings new file mode 100644 index 00000000..cdfdda65 --- /dev/null +++ b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-common-settings @@ -0,0 +1 @@ +FEATURES["ENABLE_LIBRARY_AUTHORING_MICROFRONTEND"] = True diff --git a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-lms-production-settings b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-lms-production-settings new file mode 100644 index 00000000..1cc1f1b7 --- /dev/null +++ b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/patches/openedx-lms-production-settings @@ -0,0 +1,2 @@ +MFE_CONFIG_OVERRIDES.setdefault("library-authoring", {})["BLOCKSTORE_COLLECTION_UUID"] = "{{BLOCKSTORE_COLLECTION_UUID}}" +MFE_CONFIG_OVERRIDES["library-authoring"]["SECURE_ORIGIN_XBLOCK_BOOTSTRAP_HTML_URL"] = "/library-authoring/xblock-bootstrap.html" diff --git a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/plugin.py b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/plugin.py index 878068d5..dad7b678 100644 --- a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/plugin.py +++ b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/plugin.py @@ -1,52 +1,84 @@ from glob import glob +import os import pkg_resources +import uuid -from tutor import hooks +from tutor import hooks as tutor_hooks from .__about__ import __version__ -# Enable the MFE in the CMS FEATURES configuration -hooks.Filters.ENV_PATCHES.add_item(("cms-env-features", - "ENABLE_LIBRARY_AUTHORING_MICROFRONTEND: true" -)) +######################################## +# CONFIGURATION +######################################## + +tutor_hooks.Filters.CONFIG_DEFAULTS.add_items( + [ + ("LIBRARY_AUTHORING_MICROFRONTEND_VERSION", __version__), + ("LIBRARY_AUTHORING_MFE_APP", { + "name": "library-authoring", + "repository": "https://github.com/brian-smith-tcril/frontend-app-library-authoring", + "port": 3001, + "version": "tutor-prod", # optional + }) + ] +) + +tutor_hooks.Filters.CONFIG_UNIQUE.add_items( + [ + ("BLOCKSTORE_COLLECTION_UUID", str(uuid.uuid4())) + ] +) -# Set the URL of the MFE: -hooks.Filters.ENV_PATCHES.add_item(("openedx-cms-development-settings", - 'LIBRARY_AUTHORING_MICROFRONTEND_URL = "http://{{ MFE_HOST }}:3001/library-authoring"' -)) -# Currently Tutor does not set CMS_BASE correctly - it still defaults to 'localhost:18010' not 'localhost:8001' like we need -hooks.Filters.ENV_PATCHES.add_item(("openedx-cms-development-settings", - 'CMS_BASE = "studio.local.overhang.io:8001"' -)) -# Fix blockstore's file storage; by default it initializes -# BUNDLE_ASSET_STORAGE_SETTINGS using MEDIA_ROOT but before Tutor gets a chance -# to change MEDIA_ROOT so it defaults to /edx/var/edxapp/media/ which will give -# a permissions error. -hooks.Filters.ENV_PATCHES.add_item(("openedx-development-settings", - 'BUNDLE_ASSET_STORAGE_SETTINGS = dict(STORAGE_CLASS="django.core.files.storage.FileSystemStorage", STORAGE_KWARGS=dict(location="/openedx/media/blockstore/", base_url="http://localhost:8000/media/blockstore/"))' -)) +######################################## +# INITIALIZATION TASKS +######################################## -# Tell the tutor-mfe plugin about this MFE so we can build, run, and use it: -hooks.Filters.CONFIG_DEFAULTS.add_item(("LIBRARY_AUTHORING_MFE_APP", { - "name": "library-authoring", - "repository": "https://github.com/openedx/frontend-app-library-authoring", - "port": 3001, - "env": { - "development": { - # Tutor requires the name of the MFE in the URL so this file won't be found at /xblock-bootstrap.html, - # which is the default location for development. - "SECURE_ORIGIN_XBLOCK_BOOTSTRAP_HTML_URL": "/library-authoring/xblock-bootstrap.html", - "LMS_BASE_URL": "http://local.overhang.io:8000", - "STUDIO_BASE_URL": "http://studio.local.overhang.io:8001", - }, - } -})) +MY_INIT_TASKS: list[tuple[str, tuple[str, ...]]] = [ + ("cms", ("library_authoring_mfe", "jobs", "init", "cms.sh")), +] -# Tutor overwrites webpack.dev.config.js, but this MFE depends on some code in that file to work correctly in -# development, so we have to restore it here manually. +# For each task added to MY_INIT_TASKS, we load the task template +# and add it to the CLI_DO_INIT_TASKS filter, which tells Tutor to +# run it as part of the `init` job. +for service, template_path in MY_INIT_TASKS: + full_path: str = pkg_resources.resource_filename( + "tutor_library_authoring_mfe", os.path.join("templates", *template_path) + ) + with open(full_path, encoding="utf-8") as init_task_file: + init_task: str = init_task_file.read() + tutor_hooks.Filters.CLI_DO_INIT_TASKS.add_item((service, init_task)) + +######################################## +# TEMPLATE RENDERING +######################################## + +tutor_hooks.Filters.ENV_TEMPLATE_ROOTS.add_items( + # Root paths for template files, relative to the project root. + [ + pkg_resources.resource_filename("tutor_library_authoring_mfe", "templates"), + ] +) + +######################################## +# PATCH LOADING +######################################## + +# For each file in tutor_library_authoring_mfe/patches, +# apply a patch based on the file's name and contents. +for path in glob( + os.path.join( + pkg_resources.resource_filename("tutor_library_authoring_mfe", "patches"), + "*", + ) +): + with open(path, encoding="utf-8") as patch_file: + tutor_hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read())) + +# Tutor overwrites webpack.dev.config.js, but this MFE depends on some code +# in that file to work correctly so we have to restore it here manually. # https://github.com/openedx/frontend-app-library-authoring/blob/b95c198b/webpack.dev.config.js -hooks.Filters.ENV_PATCHES.add_item(("mfe-webpack-dev-config",""" +tutor_hooks.Filters.ENV_PATCHES.add_item(("mfe-webpack-dev-config",""" const fs = require('fs'); // If this is the Library Authoring MFE, apply this fix: @@ -65,14 +97,3 @@ }); } """)) - -# CMS initialization scripts to set required waffle flags: -hooks.Filters.COMMANDS_INIT.add_item(( - "cms", - ("library_authoring_mfe", "tasks", "cms", "init"), -)) - -# Configure plugin templates -hooks.Filters.ENV_TEMPLATE_ROOTS.add_item( - pkg_resources.resource_filename("tutor_library_authoring_mfe", "templates") -) diff --git a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/jobs/init/cms.sh b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/jobs/init/cms.sh new file mode 100644 index 00000000..228d9aaa --- /dev/null +++ b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/jobs/init/cms.sh @@ -0,0 +1,6 @@ +# Enable the waffle flag to use this MFE +(./manage.py cms waffle_flag --list | grep studio.library_authoring_mfe) || ./manage.py lms waffle_flag studio.library_authoring_mfe --create --everyone + +# Make sure a Blockstore "Collection" exists to hold the library content. +# the UUID is created in CONFIG_UNIQUE in plugin.py +echo "from blockstore.apps.bundles.models import Collection; coll, _ = Collection.objects.get_or_create(title='Libraries Content Collection', uuid='{{BLOCKSTORE_COLLECTION_UUID}}')" | ./manage.py cms shell diff --git a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/tasks/cms/init b/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/tasks/cms/init deleted file mode 100644 index 760c4b79..00000000 --- a/tutor-contrib-library-authoring-mfe/tutor_library_authoring_mfe/templates/library_authoring_mfe/tasks/cms/init +++ /dev/null @@ -1,9 +0,0 @@ -# Create waffle switches to enable the Blockstore app that's now built into -# edx-platform, rather than requiring it to be installed and enabled separately. -(./manage.py cms waffle_switch --list | grep blockstore.use_blockstore_app_api) || ./manage.py lms waffle_switch --create blockstore.use_blockstore_app_api on -# Enable the waffle flag to use this MFE -(./manage.py cms waffle_flag --list | grep studio.library_authoring_mfe) || ./manage.py lms waffle_flag studio.library_authoring_mfe --create --everyone -# Make sure a Blockstore "Collection" exists to hold the library content. -# This matches the development environment default of the MFE's BLOCKSTORE_COLLECTION_UUID setting: -# https://github.com/openedx/frontend-app-library-authoring/blob/b95c198b/.env.development#L20 -echo "from blockstore.apps.bundles.models import Collection; coll, _ = Collection.objects.get_or_create(title='Dev Libraries Content Collection', uuid='11111111-2111-4111-8111-111111111111')" | ./manage.py cms shell diff --git a/webpack.prod.config.js b/webpack.prod.config.js new file mode 100644 index 00000000..c45921a9 --- /dev/null +++ b/webpack.prod.config.js @@ -0,0 +1,19 @@ +const path = require('path'); +const { getBaseConfig } = require('@edx/frontend-build'); +const { merge } = require('webpack-merge'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); + +/** + * Allow serving xblock-bootstrap.html from the MFE itself. + */ +const baseConfig = getBaseConfig('webpack-prod'); +module.exports = merge(baseConfig, { + plugins: [ + new CopyWebpackPlugin({ + patterns: [{ + context: path.resolve(__dirname, 'src/library-authoring/edit-block/LibraryBlock'), + from: 'xblock-bootstrap.html', + }], + }), + ], +});