From 41739a2f5062a15a243c30533f95f85a8cf721be Mon Sep 17 00:00:00 2001 From: akarukappadath <39962314+akarukappadath@users.noreply.github.com> Date: Fri, 15 Feb 2019 10:14:50 -0500 Subject: [PATCH] Specify JupyterLab extension from single JS file (#778) * first try * yay * doc update * automation test * fixed test * ts support --- .../bucket-tests/example_lab_extension.js | 8 +++++++ .../leonardo/NotebookCustomizationSpec.scala | 16 ++++++++++++++ docker/jupyter/Dockerfile | 1 + .../jupyter_install_lab_extension.sh | 21 ++++++++++++++++++- src/main/resources/swagger/api-docs.yaml | 2 +- 5 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 automation/src/test/resources/bucket-tests/example_lab_extension.js diff --git a/automation/src/test/resources/bucket-tests/example_lab_extension.js b/automation/src/test/resources/bucket-tests/example_lab_extension.js new file mode 100644 index 00000000000..e968e6695c0 --- /dev/null +++ b/automation/src/test/resources/bucket-tests/example_lab_extension.js @@ -0,0 +1,8 @@ +module.exports = [{ + id: 'example_lab_extension', + autoStart: true, + activate: function(app) { + console.log('JupyterLab extension example_lab_extension is activated!'); + console.log(app.commands); + } +}]; \ No newline at end of file diff --git a/automation/src/test/scala/org/broadinstitute/dsde/workbench/leonardo/NotebookCustomizationSpec.scala b/automation/src/test/scala/org/broadinstitute/dsde/workbench/leonardo/NotebookCustomizationSpec.scala index af9dbe08239..13093d5d617 100644 --- a/automation/src/test/scala/org/broadinstitute/dsde/workbench/leonardo/NotebookCustomizationSpec.scala +++ b/automation/src/test/scala/org/broadinstitute/dsde/workbench/leonardo/NotebookCustomizationSpec.scala @@ -92,5 +92,21 @@ final class NotebookCustomizationSpec extends FreeSpec } } } + + "should install user specified lab extensions from a js file" in { + withProject { project => implicit token => + val exampleLabExtensionFile = ResourceFile("bucket-tests/example_lab_extension.js") + withResourceFileInBucket(project, exampleLabExtensionFile, "text/plain") { exampleLabExtensionBucketPath => + val clusterRequestWithLabExtension = ClusterRequest(userJupyterExtensionConfig = Some(UserJupyterExtensionConfig(labExtensions = Map("example_lab_extension" -> exampleLabExtensionBucketPath.toUri)))) + withNewCluster(project, request = clusterRequestWithLabExtension) { cluster => + withWebDriver { implicit driver => + withNewNotebook(cluster) { notebookPage => + notebookPage.executeCell("!jupyter labextension list").get should include("example_lab_extension") + } + } + } + } + } + } } } diff --git a/docker/jupyter/Dockerfile b/docker/jupyter/Dockerfile index 8d2f3d5d82d..044e45787a9 100644 --- a/docker/jupyter/Dockerfile +++ b/docker/jupyter/Dockerfile @@ -285,6 +285,7 @@ RUN apt-get update \ && pip3 install python-datauri \ && pip3 install jupyter_contrib_nbextensions \ && pip3 install jupyter_nbextensions_configurator \ + && pip3 install cookiecutter \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/docker/jupyter/scripts/extension/jupyter_install_lab_extension.sh b/docker/jupyter/scripts/extension/jupyter_install_lab_extension.sh index 2144178bbc5..0c3c4eaf763 100644 --- a/docker/jupyter/scripts/extension/jupyter_install_lab_extension.sh +++ b/docker/jupyter/scripts/extension/jupyter_install_lab_extension.sh @@ -5,6 +5,25 @@ set -e if [ -n "$1" ]; then JUPYTER_EXTENSION=$1 - jupyter labextension install $JUPYTER_EXTENSION + if [[ ${JUPYTER_EXTENSION} == *'.js' ]]; then + # use jupyterlab extension template to create an extension using the specified JS file + # see https://github.com/jupyterlab/extension-cookiecutter-js + JUPYTER_EXTENSION_NAME=`basename ${JUPYTER_EXTENSION%%.*}` + cookiecutter --no-input https://github.com/jupyterlab/extension-cookiecutter-js extension_name=${JUPYTER_EXTENSION_NAME} + cp -f ${JUPYTER_EXTENSION} ${JUPYTER_EXTENSION_NAME}/lib/plugin.js + cd ${JUPYTER_EXTENSION_NAME} + jlpm + jupyter labextension install . + elif [[ ${JUPYTER_EXTENSION} == *'.ts' ]]; then + # same as above but in typescript, see https://github.com/jupyterlab/extension-cookiecutter-ts + JUPYTER_EXTENSION_NAME=`basename ${JUPYTER_EXTENSION%%.*}` + cookiecutter --no-input https://github.com/jupyterlab/extension-cookiecutter-ts extension_name=${JUPYTER_EXTENSION_NAME} + cp -f ${JUPYTER_EXTENSION} ${JUPYTER_EXTENSION_NAME}/src/index.ts + cd ${JUPYTER_EXTENSION_NAME} + jlpm + jupyter labextension install . + else + jupyter labextension install $JUPYTER_EXTENSION + fi fi diff --git a/src/main/resources/swagger/api-docs.yaml b/src/main/resources/swagger/api-docs.yaml index 1603f167262..fa28d498698 100644 --- a/src/main/resources/swagger/api-docs.yaml +++ b/src/main/resources/swagger/api-docs.yaml @@ -975,7 +975,7 @@ definitions: labExtensions: type: object description: | - Optional, map of extension name and lab extension. The extension should be a verified jupyterlab extension that is uploaded to npm (list of public extensions here: https://github.com/search?utf8=%E2%9C%93&q=topic%3Ajupyterlab-extension&type=Repositories), a gzipped tarball made using 'npm pack', a folder structured by 'jlpm build', or a URL to either of the latter. + Optional, map of extension name and lab extension. The extension should be a verified jupyterlab extension that is uploaded to npm (list of public extensions here: https://github.com/search?utf8=%E2%9C%93&q=topic%3Ajupyterlab-extension&type=Repositories), a gzipped tarball made using 'npm pack', a folder structured by 'jlpm build', a JS file to be inserted into an JL extension template (see https://github.com/jupyterlab/extension-cookiecutter-js), or a URL to one of the last three options. SubsystemStatus: description: status of a subsystem Leonardo depends on