This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-5258-dc33251a9622ee68aaa958a154471174c67a05f0 in repository https://gitbox.apache.org/repos/asf/texera.git
commit 839c08644ae040da1183f48f0596f5179b1b8441 Author: Ryan Zhang <[email protected]> AuthorDate: Wed Jun 24 14:10:57 2026 -0700 feat(python-notebook-migration): add notebook-migration-service microservice in backend (#5258) ### What changes were proposed in this PR? Introduces the microservice that mediates between Texera and the JupyterLab docker stack landed in `migration-tool-jupyter-docker`. Adds a new SBT subproject `notebook-migration-service` plus shared config and a frontend dev-proxy route. **New SBT subproject `notebook-migration-service/`:** - **`build.sbt`** and **`project/build.properties`** — module SBT setup; module depends on the existing `Auth`, `Config`, and `DAO` projects - **`src/main/scala/.../NotebookMigrationService.scala`** — Dropwizard `Application` entry point; sets Jersey URL pattern to `/api/*`, registers the resource class, initializes the shared SQL connection via `SqlServer.initConnection(StorageConfig.jdbcUrl, …)`, and wires in `RequestLoggingFilter`. - **`src/main/scala/.../NotebookMigrationServiceConfiguration.scala`** — Dropwizard `Configuration` subclass. - **`src/main/scala/.../resource/NotebookMigrationResource.scala`** — five REST endpoints under `/notebook-migration`: - `GET /get-jupyter-url` — health-checks the Jupyter container and returns its base URL. - `GET /get-jupyter-iframe-url` — returns the iframe-ready URL for `notebook.ipynb`. - `POST /set-notebook` — receives a notebook JSON, PUTs it into JupyterLab via its `/api/contents/work/{name}` API. - `POST /store-notebook-and-mapping` — persists a notebook + workflow-notebook mapping into Postgres in a single transaction (writes to the `notebook` and `workflow_notebook_mapping` tables added by `migration-tool-database-tables`). - `POST /fetch-notebook-and-mapping` — returns the most recent notebook + mapping for a given (wid, vid). - **`src/main/resources/logback.xml`** — logging config. - **`src/main/resources/notebook-migration-service-web-config.yaml`** — Dropwizard server config (HTTP port `9098`, DB connection refs). **Root build wiring:** - **`build.sbt`** — declares the new `NotebookMigrationService` SBT subproject and adds it to the `TexeraProject` aggregation. **Shared config:** - **`common/config/src/main/resources/storage.conf`** — new `jupyter { url = "http://localhost:9100" }` block, overridable via `STORAGE_JUPYTER_URL`. - **`common/config/src/main/scala/.../StorageConfig.scala`** — adds the `jupyterURL` accessor. **Frontend dev proxy:** - **`frontend/proxy.config.json`** — routes `/api/notebook-migration/*` to `http://localhost:9098`. ### Any related issues, documentation, discussions? Closes #5257 Parent issue #4301 - Hard dependency: **must be merged after `migration-tool-database-tables`** #5055 — the resource imports jOOQ-generated `Notebook` / `WorkflowNotebookMapping` classes that only exist once the schema PR is merged. - Soft dependency: the JupyterLab container from `migration-tool-jupyter-docker` is what `StorageConfig.jupyterURL` points to. Without it running, the Jupyter-related endpoints return a 500 with `"Cannot connect to Jupyter server"`. Service still starts and the DB-persistence endpoints work in isolation. ### How was this PR tested? ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Claude Opus 4.7) --------- Signed-off-by: Ryan Zhang <[email protected]> Co-authored-by: Copilot Autofix powered by AI <[email protected]> Co-authored-by: Meng Wang <[email protected]> --- .github/workflows/build.yml | 2 + bin/licensing/check_binary_deps.py | 1 + build.sbt | 13 +- common/config/src/main/resources/storage.conf | 10 + .../texera/common/config/StorageConfig.scala | 4 + frontend/proxy.config.json | 5 + notebook-migration-service/LICENSE-binary | 400 ++++++ notebook-migration-service/NOTICE-binary | 1419 ++++++++++++++++++++ notebook-migration-service/build.sbt | 87 ++ .../project/build.properties | 18 + .../notebook-migration-service-web-config.yaml | 36 + .../texera/service/NotebookMigrationService.scala | 107 ++ .../NotebookMigrationServiceConfiguration.scala | 22 + .../service/resource/HealthCheckResource.scala | 32 + .../resource/NotebookMigrationResource.scala | 419 ++++++ .../service/resource/WorkflowAccessResource.scala | 46 + .../service/NotebookMigrationServiceRunSpec.scala | 57 + .../resource/NotebookMigrationResourceSpec.scala | 453 +++++++ 18 files changed, 3130 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 81373be422..addb2d3236 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -637,6 +637,8 @@ jobs: sbt_project: ComputingUnitManagingService - service: workflow-compiling-service sbt_project: WorkflowCompilingService + - service: notebook-migration-service + sbt_project: NotebookMigrationService env: JAVA_OPTS: -Xms2048M -Xmx2048M -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 JVM_OPTS: -Xms2048M -Xmx2048M -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8 diff --git a/bin/licensing/check_binary_deps.py b/bin/licensing/check_binary_deps.py index 0e9574203b..f5c86ec93d 100755 --- a/bin/licensing/check_binary_deps.py +++ b/bin/licensing/check_binary_deps.py @@ -51,6 +51,7 @@ PER_MODULE_LICENSE_BINARIES: list[str] = [ "file-service/LICENSE-binary", "workflow-compiling-service/LICENSE-binary", "computing-unit-managing-service/LICENSE-binary", + "notebook-migration-service/LICENSE-binary", "amber/LICENSE-binary-java", "amber/LICENSE-binary-python", "frontend/LICENSE-binary", diff --git a/build.sbt b/build.sbt index 3fac5bd097..77525f9e47 100644 --- a/build.sbt +++ b/build.sbt @@ -169,6 +169,16 @@ lazy val WorkflowExecutionService = (project in file("amber")) ) .configs(Test) .dependsOn(DAO % "test->test", Auth % "test->test") // test scope dependency +lazy val NotebookMigrationService = (project in file("notebook-migration-service")) + .dependsOn(Auth, Config, DAO) + .settings(asfLicensingSettings) + .settings( + dependencyOverrides ++= Seq( + // override it as io.dropwizard 4 require 2.16.1 or higher + "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion + ) + ) + .dependsOn(DAO % "test->test") // test scope dependency // root project definition lazy val TexeraProject = (project in file(".")) @@ -186,7 +196,8 @@ lazy val TexeraProject = (project in file(".")) ConfigService, FileService, WorkflowCompilingService, - WorkflowExecutionService + WorkflowExecutionService, + NotebookMigrationService ) .settings( name := "texera", diff --git a/common/config/src/main/resources/storage.conf b/common/config/src/main/resources/storage.conf index 12a9919e04..81e13a777f 100644 --- a/common/config/src/main/resources/storage.conf +++ b/common/config/src/main/resources/storage.conf @@ -150,4 +150,14 @@ storage { password = "postgres" password = ${?STORAGE_JDBC_PASSWORD} } + + # Configurations of the JupyterLab service + jupyter { + url = "http://localhost:9100" + url = ${?STORAGE_JUPYTER_URL} + # Read from the same JUPYTER_TOKEN env var as the Jupyter container + # (notebook-migration-service/src/main/resources/docker-compose.yml) + token = "texera" + token = ${?JUPYTER_TOKEN} + } } diff --git a/common/config/src/main/scala/org/apache/texera/common/config/StorageConfig.scala b/common/config/src/main/scala/org/apache/texera/common/config/StorageConfig.scala index 2a31e4e116..2ff39093c8 100644 --- a/common/config/src/main/scala/org/apache/texera/common/config/StorageConfig.scala +++ b/common/config/src/main/scala/org/apache/texera/common/config/StorageConfig.scala @@ -133,4 +133,8 @@ object StorageConfig { val ENV_S3_REGION = "STORAGE_S3_REGION" val ENV_S3_AUTH_USERNAME = "STORAGE_S3_AUTH_USERNAME" val ENV_S3_AUTH_PASSWORD = "STORAGE_S3_AUTH_PASSWORD" + + // Jupyter + val jupyterURL: String = conf.getString("storage.jupyter.url") + val jupyterToken: String = conf.getString("storage.jupyter.token") } diff --git a/frontend/proxy.config.json b/frontend/proxy.config.json index f68602e071..7d68961d9b 100755 --- a/frontend/proxy.config.json +++ b/frontend/proxy.config.json @@ -10,6 +10,11 @@ "secure": false, "changeOrigin": true }, + "/api/notebook-migration": { + "target": "http://localhost:9098", + "secure": false, + "changeOrigin": true + }, "/api/models": { "target": "http://localhost:9096", "secure": false, diff --git a/notebook-migration-service/LICENSE-binary b/notebook-migration-service/LICENSE-binary new file mode 100644 index 0000000000..3abc86ea44 --- /dev/null +++ b/notebook-migration-service/LICENSE-binary @@ -0,0 +1,400 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for describing the origin of the Work and + reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Support. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or support. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +================================================================================ +THIRD-PARTY COMPONENTS +================================================================================ + +Apache Texera's binary distribution of this service includes the following third-party components, grouped by license. Each section references licenses/ for the full text of the applicable license. Components under the Apache License, Version 2.0 are governed by the same license terms as Apache Texera itself and are listed for completeness. + +Locations within the distribution: + + - Scala/Java jars listed below ship under the lib/ directory of + this service's Universal zip. + + - Source files derived from third-party projects are compiled into + the bundled jars under lib/ and live at the listed paths in the + Apache Texera source tree. + +-------------------------------------------------------------------------------- +Dependencies under the Apache License, Version 2.0 +-------------------------------------------------------------------------------- + +Scala/Java jars: + - com.fasterxml.classmate-1.7.0.jar + - com.fasterxml.jackson.core.jackson-annotations-2.18.6.jar + - com.fasterxml.jackson.core.jackson-core-2.18.6.jar + - com.fasterxml.jackson.core.jackson-databind-2.18.6.jar + - com.fasterxml.jackson.dataformat.jackson-dataformat-yaml-2.16.1.jar + - com.fasterxml.jackson.datatype.jackson-datatype-guava-2.16.1.jar + - com.fasterxml.jackson.datatype.jackson-datatype-jdk8-2.16.1.jar + - com.fasterxml.jackson.datatype.jackson-datatype-jsr310-2.16.1.jar + - com.fasterxml.jackson.jakarta.rs.jackson-jakarta-rs-base-2.16.1.jar + - com.fasterxml.jackson.jakarta.rs.jackson-jakarta-rs-json-provider-2.16.1.jar + - com.fasterxml.jackson.module.jackson-module-blackbird-2.16.1.jar + - com.fasterxml.jackson.module.jackson-module-jakarta-xmlbind-annotations-2.16.1.jar + - com.fasterxml.jackson.module.jackson-module-parameter-names-2.16.1.jar + - com.fasterxml.jackson.module.jackson-module-scala_2.13-2.18.6.jar + - com.github.ben-manes.caffeine.caffeine-3.1.8.jar + - com.google.code.findbugs.jsr305-3.0.2.jar + - com.google.errorprone.error_prone_annotations-2.25.0.jar + - com.google.guava.failureaccess-1.0.2.jar + - com.google.guava.guava-33.0.0-jre.jar + - com.google.guava.listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar + - com.google.j2objc.j2objc-annotations-2.8.jar + - com.helger.profiler-1.1.1.jar + - com.thesamet.scalapb.lenses_2.13-0.11.20.jar + - com.thesamet.scalapb.scalapb-json4s_2.13-0.12.0.jar + - com.thesamet.scalapb.scalapb-runtime_2.13-0.11.20.jar + - com.typesafe.config-1.4.6.jar + - com.typesafe.scala-logging.scala-logging_2.13-3.9.5.jar + - com.zaxxer.HikariCP-5.1.0.jar + - io.dropwizard.dropwizard-auth-4.0.7.jar + - io.dropwizard.dropwizard-configuration-4.0.7.jar + - io.dropwizard.dropwizard-core-4.0.7.jar + - io.dropwizard.dropwizard-health-4.0.7.jar + - io.dropwizard.dropwizard-jackson-4.0.7.jar + - io.dropwizard.dropwizard-jersey-4.0.7.jar + - io.dropwizard.dropwizard-jetty-4.0.7.jar + - io.dropwizard.dropwizard-lifecycle-4.0.7.jar + - io.dropwizard.dropwizard-logging-4.0.7.jar + - io.dropwizard.dropwizard-metrics-4.0.7.jar + - io.dropwizard.dropwizard-request-logging-4.0.7.jar + - io.dropwizard.dropwizard-servlets-4.0.7.jar + - io.dropwizard.dropwizard-util-4.0.7.jar + - io.dropwizard.dropwizard-validation-4.0.7.jar + - io.dropwizard.logback.logback-throttling-appender-1.4.2.jar + - io.dropwizard.metrics.metrics-annotation-4.2.25.jar + - io.dropwizard.metrics.metrics-caffeine-4.2.25.jar + - io.dropwizard.metrics.metrics-core-4.2.25.jar + - io.dropwizard.metrics.metrics-healthchecks-4.2.25.jar + - io.dropwizard.metrics.metrics-jakarta-servlets-4.2.25.jar + - io.dropwizard.metrics.metrics-jersey3-4.2.25.jar + - io.dropwizard.metrics.metrics-jetty11-4.2.25.jar + - io.dropwizard.metrics.metrics-jmx-4.2.25.jar + - io.dropwizard.metrics.metrics-json-4.2.25.jar + - io.dropwizard.metrics.metrics-jvm-4.2.25.jar + - io.dropwizard.metrics.metrics-logback-4.2.25.jar + - io.r2dbc.r2dbc-spi-0.9.0.RELEASE.jar + - jakarta.inject.jakarta.inject-api-2.0.1.jar + - jakarta.validation.jakarta.validation-api-3.0.2.jar + - org.apache.commons.commons-lang3-3.13.0.jar + - org.apache.commons.commons-text-1.11.0.jar + - org.bitbucket.b_c.jose4j-0.9.6.jar + - org.eclipse.jetty.jetty-http-11.0.20.jar + - org.eclipse.jetty.jetty-io-11.0.20.jar + - org.eclipse.jetty.jetty-security-11.0.20.jar + - org.eclipse.jetty.jetty-server-11.0.20.jar + - org.eclipse.jetty.jetty-servlet-11.0.20.jar + - org.eclipse.jetty.jetty-servlets-11.0.20.jar + - org.eclipse.jetty.jetty-util-11.0.20.jar + - org.eclipse.jetty.toolchain.jetty-jakarta-servlet-api-5.0.2.jar + - org.eclipse.jetty.toolchain.setuid.jetty-setuid-java-1.0.4.jar + - org.hibernate.validator.hibernate-validator-7.0.5.Final.jar + - org.javassist.javassist-3.30.2-GA.jar + - org.jboss.logging.jboss-logging-3.5.3.Final.jar + - org.jooq.jooq-3.16.23.jar + - org.json4s.json4s-ast_2.13-4.0.1.jar + - org.json4s.json4s-jackson-core_2.13-4.0.1.jar + - org.scala-lang.modules.scala-collection-compat_2.13-2.13.0.jar + - org.scala-lang.scala-library-2.13.18.jar + - org.scala-lang.scala-reflect-2.13.18.jar + - org.slf4j.jcl-over-slf4j-2.0.12.jar + - org.slf4j.log4j-over-slf4j-2.0.12.jar + - org.yaml.snakeyaml-2.2.jar + +-------------------------------------------------------------------------------- +Dependencies under the MIT License +-------------------------------------------------------------------------------- + +Source files derived from third-party MIT-licensed projects: + - mbknor-jackson-jsonschema + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/JsonSchemaDraft.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/JsonSchemaGenerator.scala + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaArrayWithUniqueItems.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaBool.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaDefault.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaDescription.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaExamples.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaFormat.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaInject.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaInt.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaOptions.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaString.java + common/workflow-operator/src/main/scala/com/kjetland/jackson/jsonSchema/annotations/JsonSchemaTitle.java + https://github.com/mbknor/mbknor-jackson-jsonschema + +Scala/Java jars: + - net.sourceforge.argparse4j.argparse4j-0.9.0.jar + - org.checkerframework.checker-qual-3.52.0.jar + - org.slf4j.jul-to-slf4j-2.0.12.jar + - org.slf4j.slf4j-api-2.0.12.jar + +-------------------------------------------------------------------------------- +Dependencies under the BSD 3-Clause License +-------------------------------------------------------------------------------- + +Scala/Java jars: + - com.google.protobuf.protobuf-java-3.25.8.jar + - com.thoughtworks.paranamer.paranamer-2.8.jar + +-------------------------------------------------------------------------------- +Dependencies under the BSD 2-Clause License +-------------------------------------------------------------------------------- + +Scala/Java jars: + - org.postgresql.postgresql-42.7.10.jar + +-------------------------------------------------------------------------------- +Dependencies under the Eclipse Public License, Version 2.0 (some are dual +licensed with GPL-2.0 with Classpath Exception) +-------------------------------------------------------------------------------- + +Scala/Java jars: + - jakarta.annotation.jakarta.annotation-api-2.1.1.jar + - jakarta.el.jakarta.el-api-4.0.0.jar + - jakarta.servlet.jakarta.servlet-api-5.0.0.jar + - jakarta.ws.rs.jakarta.ws.rs-api-3.0.0.jar + - org.glassfish.hk2.external.aopalliance-repackaged-3.0.6.jar + - org.glassfish.hk2.hk2-api-3.0.6.jar + - org.glassfish.hk2.hk2-locator-3.0.3.jar + - org.glassfish.hk2.hk2-utils-3.0.6.jar + - org.glassfish.hk2.osgi-resource-locator-1.0.3.jar + - org.glassfish.jakarta.el-4.0.2.jar + - org.glassfish.jersey.containers.jersey-container-servlet-3.0.12.jar + - org.glassfish.jersey.containers.jersey-container-servlet-core-3.0.12.jar + - org.glassfish.jersey.core.jersey-client-3.0.12.jar + - org.glassfish.jersey.core.jersey-common-3.0.12.jar + - org.glassfish.jersey.core.jersey-server-3.0.12.jar + - org.glassfish.jersey.ext.jersey-bean-validation-3.0.12.jar + - org.glassfish.jersey.ext.jersey-metainf-services-3.0.12.jar + - org.glassfish.jersey.inject.jersey-hk2-3.0.12.jar + +-------------------------------------------------------------------------------- +Dependencies under the Eclipse Public License, Version 1.0 (Logback is dual +licensed with LGPL-2.1) +-------------------------------------------------------------------------------- + +Scala/Java jars: + - ch.qos.logback.logback-access-1.4.14.jar + - ch.qos.logback.logback-classic-1.4.14.jar + - ch.qos.logback.logback-core-1.4.14.jar + +-------------------------------------------------------------------------------- +Dependencies under the Eclipse Distribution License, Version 1.0 +-------------------------------------------------------------------------------- + +Scala/Java jars: + - com.sun.activation.jakarta.activation-2.0.1.jar + - jakarta.activation.jakarta.activation-api-2.1.0.jar + - jakarta.xml.bind.jakarta.xml.bind-api-3.0.1.jar + +-------------------------------------------------------------------------------- +Dependencies in the Public Domain (CC0) +-------------------------------------------------------------------------------- + +Scala/Java jars: + - org.reactivestreams.reactive-streams-1.0.3.jar + +Individual jars may contain their own META-INF/LICENSE and META-INF/NOTICE +files that apply to their specific contents; those files continue to govern +the use of those components. diff --git a/notebook-migration-service/NOTICE-binary b/notebook-migration-service/NOTICE-binary new file mode 100644 index 0000000000..f5b737219f --- /dev/null +++ b/notebook-migration-service/NOTICE-binary @@ -0,0 +1,1419 @@ +Apache Texera (Incubating) +Copyright 2025 and onwards The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +-------------------------------------------------------------------------------- +org.eclipse.jetty +-------------------------------------------------------------------------------- + +Notices for Eclipse Jetty +========================= +This content is produced and maintained by the Eclipse Jetty project. + +Project home: https://eclipse.dev/jetty/ + +Trademarks +---------- +Eclipse Jetty, and Jetty are trademarks of the Eclipse Foundation. + +Copyright +--------- +All contributions are the property of the respective authors or of +entities to which copyright has been assigned by the authors (eg. employer). + +Declared Project Licenses +------------------------- +This artifacts of this project are made available under the terms of: + + * the Eclipse Public License v2.0 + https://www.eclipse.org/legal/epl-2.0 + SPDX-License-Identifier: EPL-2.0 + + or + + * the Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0 + SPDX-License-Identifier: Apache-2.0 + +The following dependencies are EPL. + * org.eclipse.jetty.orbit:org.eclipse.jdt.core + +The following dependencies are EPL and ASL2. + * org.eclipse.jetty.orbit:javax.security.auth.message + +The following dependencies are EPL and CDDL 1.0. + * org.eclipse.jetty.orbit:javax.mail.glassfish + +The following dependencies are CDDL + GPLv2 with classpath exception. +https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html + + * jakarta.servlet:jakarta.servlet-api + * javax.annotation:javax.annotation-api + * javax.transaction:javax.transaction-api + * javax.websocket:javax.websocket-api + +The following dependencies are licensed by the OW2 Foundation according to the +terms of http://asm.ow2.org/license.html + + * org.ow2.asm:asm-commons + * org.ow2.asm:asm + +The following dependencies are ASL2 licensed. + + * org.apache.taglibs:taglibs-standard-spec + * org.apache.taglibs:taglibs-standard-impl + +The following dependencies are ASL2 licensed. Based on selected classes from +following Apache Tomcat jars, all ASL2 licensed. + + * org.mortbay.jasper:apache-jsp + * org.apache.tomcat:tomcat-jasper + * org.apache.tomcat:tomcat-juli + * org.apache.tomcat:tomcat-jsp-api + * org.apache.tomcat:tomcat-el-api + * org.apache.tomcat:tomcat-jasper-el + * org.apache.tomcat:tomcat-api + * org.apache.tomcat:tomcat-util-scan + * org.apache.tomcat:tomcat-util + * org.mortbay.jasper:apache-el + * org.apache.tomcat:tomcat-jasper-el + * org.apache.tomcat:tomcat-el-api + +The following artifacts are CDDL + GPLv2 with classpath exception. +https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html + + * org.eclipse.jetty.toolchain:jetty-schemas + +Cryptography +------------ +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. + +The UnixCrypt.java code implements the one way cryptography used by +Unix systems for simple password protection. Copyright 1996 Aki Yoshida, +modified April 2001 by Iris Van den Broeke, Daniel Deville. +Permission to use, copy, modify and distribute UnixCrypt +for non-commercial or commercial purposes and without fee is +granted provided that the copyright notice appears in all copies. + +-------------------------------------------------------------------------------- +org.glassfish.jersey +-------------------------------------------------------------------------------- + +# Notice for Jersey +This content is produced and maintained by the Eclipse Jersey project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jersey + +## Trademarks +Eclipse Jersey is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jersey + +## Third-party Content + +Angular JS, v1.6.6 +* License MIT (http://www.opensource.org/licenses/mit-license.php) +* Project: http://angularjs.org +* Coyright: (c) 2010-2017 Google, Inc. + +aopalliance Version 1 +* License: all the source code provided by AOP Alliance is Public Domain. +* Project: http://aopalliance.sourceforge.net +* Copyright: Material in the public domain is not protected by copyright + +Bean Validation API 3.0.2 +* License: Apache License, 2.0 +* Project: http://beanvalidation.org/1.1/ +* Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors +* by the @authors tag. + +Hibernate Validator CDI, 7.0.5.Final +* License: Apache License, 2.0 +* Project: https://beanvalidation.org/ +* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate + +Bootstrap v3.3.7 +* License: MIT license (https://github.com/twbs/bootstrap/blob/master/LICENSE) +* Project: http://getbootstrap.com +* Copyright: 2011-2016 Twitter, Inc + +Google Guava Version 18.0 +* License: Apache License, 2.0 +* Copyright (C) 2009 The Guava Authors + +jakarta.inject Version: 1 +* License: Apache License, 2.0 +* Copyright (C) 2009 The JSR-330 Expert Group + +Javassist Version 3.29.2-GA +* License: Apache License, 2.0 +* Project: http://www.javassist.org/ +* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + +Jackson JAX-RS Providers Version 2.15.3 +* License: Apache License, 2.0 +* Project: https://github.com/FasterXML/jackson-jaxrs-providers +* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated. + +jQuery v1.12.4 +* License: jquery.org/license +* Project: jquery.org +* Copyright: (c) jQuery Foundation + +jQuery Barcode plugin 0.3 +* License: MIT & GPL (http://www.opensource.org/licenses/mit-license.php & http://www.gnu.org/licenses/gpl.html) +* Project: http://www.pasella.it/projects/jQuery/barcode +* Copyright: (c) 2009 Antonello Pasella [email protected] + +JSR-166 Extension - JEP 266 +* License: CC0 +* No copyright +* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ + +KineticJS, v4.7.1 +* License: MIT license (http://www.opensource.org/licenses/mit-license.php) +* Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS +* Copyright: Eric Rowell + +org.objectweb.asm Version 9.6 +* License: Modified BSD (https://asm.ow2.io/license.html) +* Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved. + +org.osgi.core version 6.0.0 +* License: Apache License, 2.0 +* Copyright (c) OSGi Alliance (2005, 2008). All Rights Reserved. + +org.glassfish.jersey.server.internal.monitoring.core +* License: Apache License, 2.0 +* Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved. +* Copyright 2010-2013 Coda Hale and Yammer, Inc. + +W3.org documents +* License: W3C License +* Copyright: Copyright (c) 1994-2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ + +-------------------------------------------------------------------------------- +org.glassfish.hk2 +-------------------------------------------------------------------------------- + +# Notices for Eclipse GlassFish + +This content is produced and maintained by the Eclipse GlassFish project. + +* Project home: https://projects.eclipse.org/projects/ee4j.glassfish + +## Trademarks + +Eclipse GlassFish, and GlassFish are trademarks of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/glassfish-ha-api +* https://github.com/eclipse-ee4j/glassfish-logging-annotation-processor +* https://github.com/eclipse-ee4j/glassfish-shoal +* https://github.com/eclipse-ee4j/glassfish-cdi-porting-tck +* https://github.com/eclipse-ee4j/glassfish-jsftemplating +* https://github.com/eclipse-ee4j/glassfish-hk2-extra +* https://github.com/eclipse-ee4j/glassfish-hk2 +* https://github.com/eclipse-ee4j/glassfish-fighterfish + +## Third-party Content + +This project leverages the following third party content. + +None + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. + +-------------------------------------------------------------------------------- +com.fasterxml.jackson +-------------------------------------------------------------------------------- + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta ([email protected]), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Licensing + +Jackson components are licensed under Apache (Software) License, version 2.0, +as per accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +-------------------------------------------------------------------------------- +jakarta.el.jakarta.el-api +-------------------------------------------------------------------------------- + +# Notices for Jakarta Expression Language + +This content is produced and maintained by the Jakarta Expression Language project. + +* Project home: https://projects.eclipse.org/projects/ee4j.el + +## Trademarks + +Jakarta Expression Language is a trademark of the Eclipse +Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/el-ri + +## Third-party Content + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. + +-------------------------------------------------------------------------------- +com.sun.activation.jakarta.activation +-------------------------------------------------------------------------------- + +# Notices for Jakarta Activation + +This content is produced and maintained by Jakarta Activation project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jaf + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Distribution License v. 1.0, +which is available at http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: BSD-3-Clause + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jaf + +-------------------------------------------------------------------------------- +com.fasterxml.jackson.core +-------------------------------------------------------------------------------- + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta ([email protected]), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Copyright + +Copyright 2007-, Tatu Saloranta ([email protected]) + +## Licensing + +Jackson 2.x core and extension components are licensed under Apache License 2.0 +To find the details that apply to this artifact see the accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS(-2.x) file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +-------------------------------------------------------------------------------- +org.scala-lang +-------------------------------------------------------------------------------- + +Scala +Copyright (c) 2002-2025 EPFL +Copyright (c) 2011-2025 Lightbend, Inc. dba Akka + +Scala includes software developed at +LAMP/EPFL (https://lamp.epfl.ch/) and +Akka (https://akka.io/). + +Licensed under the Apache License, Version 2.0 (the "License"). +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This software includes projects with other licenses -- see `doc/LICENSE.md`. + +-------------------------------------------------------------------------------- +com.fasterxml.jackson.core.jackson-core +-------------------------------------------------------------------------------- + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta ([email protected]), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Copyright + +Copyright 2007-, Tatu Saloranta ([email protected]) + +## Licensing + +Jackson 2.x core and extension components are licensed under Apache License 2.0 +To find the details that apply to this artifact see the accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS(-2.x) file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +## FastDoubleParser + +jackson-core bundles a shaded copy of FastDoubleParser <https://github.com/wrandelshofer/FastDoubleParser>. +That code is available under an MIT license <https://github.com/wrandelshofer/FastDoubleParser/blob/main/LICENSE> +under the following copyright. + +Copyright © 2023 Werner Randelshofer, Switzerland. MIT License. + +See FastDoubleParser-NOTICE for details of other source code included in FastDoubleParser +and the licenses and copyrights that apply to that code. + +# FastDoubleParser + +This is a Java port of Daniel Lemire's fast_float project. +This project provides parsers for double, float, BigDecimal and BigInteger values. + +## Copyright + +Copyright © 2024 Werner Randelshofer, Switzerland. + +## Licensing + +This code is licensed under MIT License. +https://github.com/wrandelshofer/FastDoubleParser/blob/522be16e145f43308c43b23094e31d5efcaa580e/LICENSE +(The file 'LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +Some portions of the code have been derived from other projects. +All these projects require that we include a copyright notice, and some require that we also include some text of their +license file. + +fast_double_parser, Copyright (c) 2022 Daniel Lemire. BSL License. +https://github.com/lemire/fast_double_parser +https://github.com/lemire/fast_double_parser/blob/07d9189a8fb815fe800cb15ca022e7a07093236e/LICENSE.BSL +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +fast_float, Copyright (c) 2021 The fast_float authors. MIT License. +https://github.com/fastfloat/fast_float +https://github.com/fastfloat/fast_float/blob/cc1e01e9eee74128e48d51488a6b1df4a767a810/LICENSE-MIT +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +bigint, Copyright 2020 Tim Buktu. 2-clause BSD License. +https://github.com/tbuktu/bigint/tree/floatfft +https://github.com/tbuktu/bigint/blob/617c8cd8a7c5e4fb4d919c6a4d11e2586107f029/LICENSE +https://github.com/wrandelshofer/FastDoubleParser/blob/39e123b15b71f29a38a087d16a0bc620fc879aa6/bigint-LICENSE +(We only use those portions of the bigint project that can be licensed under 2-clause BSD License.) +(The file 'thirdparty-LICENSE' is included in the sources and classes Jar files that are released by this project +- as is required by that license.) + +-------------------------------------------------------------------------------- +org.apache.commons.commons-lang3 +-------------------------------------------------------------------------------- + +Apache Commons Lang +Copyright 2001-2023 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). + +-------------------------------------------------------------------------------- +org.eclipse.jetty.toolchain.jetty-jakarta-servlet-api +-------------------------------------------------------------------------------- + +# Notices for Eclipse Project for Servlet + +This content is produced and maintained by the Eclipse Project for Servlet +project. + +* Project home: https://projects.eclipse.org/projects/ee4j.servlet + + +## Trademarks + +Eclipse Project for Servlet is a trademark of the Eclipse Foundation. + + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + + +## Source Code + +The project maintains the following source code repositories: + + * https://github.com/eclipse-ee4j/servlet-api + * https://github.com/eclipse/jetty.toolchain + + +## Third-party Content + +## Jakarta + +The following artifacts are EPL 2.0 + GPLv2 with classpath exception. +https://projects.eclipse.org/projects/ee4j.servlet + + * jakarta.servlet:jakarta.servlet-api + + +## GlassFish + +The following artifacts are CDDL + GPLv2 with classpath exception. +https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html + + * org.eclipse.jetty.toolchain:jetty-schemas + +-------------------------------------------------------------------------------- +org.glassfish.jersey.core.jersey-server +-------------------------------------------------------------------------------- + +# Notice for Jersey Core Server module +This content is produced and maintained by the Eclipse Jersey project. + +* https://projects.eclipse.org/projects/ee4j.jersey + +## Trademarks +Eclipse Jersey is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jersey + +## Third-party Content + +org.glassfish.jersey.server.internal.monitoring.core +* License: Apache License, 2.0 +* Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved. +* Copyright 2010-2013 Coda Hale and Yammer, Inc. + +org.objectweb.asm Version 9.6 +* License: Modified BSD (https://asm.ow2.io/license.html) +* Copyright: (c) 2000-2011 INRIA, France Telecom. All rights reserved. + +W3.org documents +* License: W3C License +* Copyright: Copyright (c) 1994-2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ + +# Notice for Jersey +This content is produced and maintained by the Eclipse Jersey project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jersey + +## Trademarks +Eclipse Jersey is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jersey + +## Third-party Content + +Angular JS, v1.6.6 +* License MIT (http://www.opensource.org/licenses/mit-license.php) +* Project: http://angularjs.org +* Coyright: (c) 2010-2017 Google, Inc. + +aopalliance Version 1 +* License: all the source code provided by AOP Alliance is Public Domain. +* Project: http://aopalliance.sourceforge.net +* Copyright: Material in the public domain is not protected by copyright + +Bean Validation API 3.0.2 +* License: Apache License, 2.0 +* Project: http://beanvalidation.org/1.1/ +* Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors +* by the @authors tag. + +Hibernate Validator CDI, 7.0.5.Final +* License: Apache License, 2.0 +* Project: https://beanvalidation.org/ +* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate + +Bootstrap v3.3.7 +* License: MIT license (https://github.com/twbs/bootstrap/blob/master/LICENSE) +* Project: http://getbootstrap.com +* Copyright: 2011-2016 Twitter, Inc + +Google Guava Version 18.0 +* License: Apache License, 2.0 +* Copyright (C) 2009 The Guava Authors + +jakarta.inject Version: 1 +* License: Apache License, 2.0 +* Copyright (C) 2009 The JSR-330 Expert Group + +Javassist Version 3.29.2-GA +* License: Apache License, 2.0 +* Project: http://www.javassist.org/ +* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + +Jackson JAX-RS Providers Version 2.15.3 +* License: Apache License, 2.0 +* Project: https://github.com/FasterXML/jackson-jaxrs-providers +* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated. + +jQuery v1.12.4 +* License: jquery.org/license +* Project: jquery.org +* Copyright: (c) jQuery Foundation + +jQuery Barcode plugin 0.3 +* License: MIT & GPL (http://www.opensource.org/licenses/mit-license.php & http://www.gnu.org/licenses/gpl.html) +* Project: http://www.pasella.it/projects/jQuery/barcode +* Copyright: (c) 2009 Antonello Pasella [email protected] + +JSR-166 Extension - JEP 266 +* License: CC0 +* No copyright +* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ + +KineticJS, v4.7.1 +* License: MIT license (http://www.opensource.org/licenses/mit-license.php) +* Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS +* Copyright: Eric Rowell + +org.objectweb.asm Version 9.6 +* License: Modified BSD (https://asm.ow2.io/license.html) +* Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved. + +org.osgi.core version 6.0.0 +* License: Apache License, 2.0 +* Copyright (c) OSGi Alliance (2005, 2008). All Rights Reserved. + +org.glassfish.jersey.server.internal.monitoring.core +* License: Apache License, 2.0 +* Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved. +* Copyright 2010-2013 Coda Hale and Yammer, Inc. + +W3.org documents +* License: W3C License +* Copyright: Copyright (c) 1994-2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ + +-------------------------------------------------------------------------------- +org.glassfish.jersey.ext.jersey-bean-validation +-------------------------------------------------------------------------------- + +# Notice for Jersey Bean Validation module +This content is produced and maintained by the Eclipse Jersey project. + +* https://projects.eclipse.org/projects/ee4j.jersey + +## Trademarks +Eclipse Jersey is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jersey + +## Third-party Content + +Hibernate Validator CDI, 7.0.5.Final +* License: Apache License, 2.0 +* Project: https://beanvalidation.org/ +* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate + +# Notice for Jersey +This content is produced and maintained by the Eclipse Jersey project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jersey + +## Trademarks +Eclipse Jersey is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jersey + +## Third-party Content + +Angular JS, v1.6.6 +* License MIT (http://www.opensource.org/licenses/mit-license.php) +* Project: http://angularjs.org +* Coyright: (c) 2010-2017 Google, Inc. + +aopalliance Version 1 +* License: all the source code provided by AOP Alliance is Public Domain. +* Project: http://aopalliance.sourceforge.net +* Copyright: Material in the public domain is not protected by copyright + +Bean Validation API 3.0.2 +* License: Apache License, 2.0 +* Project: http://beanvalidation.org/1.1/ +* Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors +* by the @authors tag. + +Hibernate Validator CDI, 7.0.5.Final +* License: Apache License, 2.0 +* Project: https://beanvalidation.org/ +* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate + +Bootstrap v3.3.7 +* License: MIT license (https://github.com/twbs/bootstrap/blob/master/LICENSE) +* Project: http://getbootstrap.com +* Copyright: 2011-2016 Twitter, Inc + +Google Guava Version 18.0 +* License: Apache License, 2.0 +* Copyright (C) 2009 The Guava Authors + +jakarta.inject Version: 1 +* License: Apache License, 2.0 +* Copyright (C) 2009 The JSR-330 Expert Group + +Javassist Version 3.29.2-GA +* License: Apache License, 2.0 +* Project: http://www.javassist.org/ +* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + +Jackson JAX-RS Providers Version 2.15.3 +* License: Apache License, 2.0 +* Project: https://github.com/FasterXML/jackson-jaxrs-providers +* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated. + +jQuery v1.12.4 +* License: jquery.org/license +* Project: jquery.org +* Copyright: (c) jQuery Foundation + +jQuery Barcode plugin 0.3 +* License: MIT & GPL (http://www.opensource.org/licenses/mit-license.php & http://www.gnu.org/licenses/gpl.html) +* Project: http://www.pasella.it/projects/jQuery/barcode +* Copyright: (c) 2009 Antonello Pasella [email protected] + +JSR-166 Extension - JEP 266 +* License: CC0 +* No copyright +* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ + +KineticJS, v4.7.1 +* License: MIT license (http://www.opensource.org/licenses/mit-license.php) +* Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS +* Copyright: Eric Rowell + +org.objectweb.asm Version 9.6 +* License: Modified BSD (https://asm.ow2.io/license.html) +* Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved. + +org.osgi.core version 6.0.0 +* License: Apache License, 2.0 +* Copyright (c) OSGi Alliance (2005, 2008). All Rights Reserved. + +org.glassfish.jersey.server.internal.monitoring.core +* License: Apache License, 2.0 +* Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved. +* Copyright 2010-2013 Coda Hale and Yammer, Inc. + +W3.org documents +* License: W3C License +* Copyright: Copyright (c) 1994-2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ + +-------------------------------------------------------------------------------- +com.fasterxml.jackson.dataformat.jackson-dataformat-yaml +-------------------------------------------------------------------------------- + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta ([email protected]), and has +been in development since 2007. +It is currently developed by a community of developers. + +## Copyright + +Copyright 2007-, Tatu Saloranta ([email protected]) + +## Licensing + +Jackson components are licensed under Apache (Software) License, version 2.0, +as per accompanying LICENSE file. + +## Credits + +A list of contributors may be found from CREDITS file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +-------------------------------------------------------------------------------- +jakarta.inject.jakarta.inject-api +-------------------------------------------------------------------------------- + +# Notices for Eclipse Jakarta Dependency Injection + +This content is produced and maintained by the Eclipse Jakarta Dependency Injection project. + +* Project home: https://projects.eclipse.org/projects/cdi.batch + +## Trademarks + +Jakarta Dependency Injection is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Apache License, Version 2.0 which is available at +https://www.apache.org/licenses/LICENSE-2.0. + +SPDX-License-Identifier: Apache-2.0 + +## Source Code + +The project maintains the following source code repositories: + +https://github.com/eclipse-ee4j/injection-api +https://github.com/eclipse-ee4j/injection-spec +https://github.com/eclipse-ee4j/injection-tck + +## Third-party Content + +This project leverages the following third party content. + +None + +## Cryptography + +None + +-------------------------------------------------------------------------------- +jakarta.xml.bind.jakarta.xml.bind-api +-------------------------------------------------------------------------------- + +[//]: # " Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved. " +[//]: # " " +[//]: # " This program and the accompanying materials are made available under the " +[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at " +[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. " +[//]: # " " +[//]: # " SPDX-License-Identifier: BSD-3-Clause " + +# Notices for Jakarta XML Binding + +This content is produced and maintained by the Jakarta XML Binding +project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jaxb + +## Trademarks + +Jakarta XML Binding is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Distribution License v. 1.0 which is available at +http://www.eclipse.org/org/documents/edl-v10.php. + +SPDX-License-Identifier: BSD-3-Clause + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jaxb-api +* https://github.com/eclipse-ee4j/jaxb-tck + +## Third-party Content + +This project leverages the following third party content. + +Apache River (3.0.0) + +* License: Apache-2.0 AND BSD-3-Clause + +ASM 7 (n/a) + +* License: BSD-3-Clause +* Project: https://asm.ow2.io/ +* Source: + https://repository.ow2.org/nexus/#nexus-search;gav~org.ow2.asm~asm-commons~~~~kw,versionexpand + +JTHarness (5.0) + +* License: (GPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0) +* Project: https://wiki.openjdk.java.net/display/CodeTools/JT+Harness +* Source: http://hg.openjdk.java.net/code-tools/jtharness/ + +normalize.css (3.0.2) + +* License: MIT + +SigTest (n/a) + +* License: GPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. + +-------------------------------------------------------------------------------- +jakarta.ws.rs.jakarta.ws.rs-api +-------------------------------------------------------------------------------- + +# Notices for Jakarta RESTful Web Services + +This content is produced and maintained by the **Jakarta RESTful Web Services** +project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jaxrs + +## Trademarks + +**Jakarta RESTful Web Services** is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jaxrs-api + +## Third-party Content + +This project leverages the following third party content. + +javaee-api (7.0) + +* License: Apache-2.0 AND W3C + +JUnit (4.11) + +* License: Common Public License 1.0 + +Mockito (2.16.0) + +* Project: http://site.mockito.org +* Source: https://github.com/mockito/mockito/releases/tag/v2.16.0 + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. + +-------------------------------------------------------------------------------- +io.r2dbc.r2dbc-spi +-------------------------------------------------------------------------------- + +Reactive Relational Database Connectivity + +Copyright 2017-2021 the original author or authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +-------------------------------------------------------------------------------- +com.fasterxml.jackson.jakarta.rs.jackson-jakarta-rs-json-provider +-------------------------------------------------------------------------------- + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta ([email protected]), and has +been in development since 2007. +It is currently developed by a community of developers, as well as supported +commercially by FasterXML.com. + +## Licensing + +Jackson core and extension components may be licensed under different licenses. +To find the details that apply to this artifact see the accompanying LICENSE file. +For more information, including possible other licensing options, contact +FasterXML.com (http://fasterxml.com). + +## Credits + +A list of contributors may be found from CREDITS file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +-------------------------------------------------------------------------------- +com.fasterxml.jackson.module.jackson-module-blackbird +-------------------------------------------------------------------------------- + +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta ([email protected]), and has +been in development since 2007. +It is currently developed by a community of developers, as well as supported +commercially by FasterXML.com. + +## Licensing + +Jackson core and extension components (as well their dependencies) may be licensed under +different licenses. +To find the details that apply to this artifact see the accompanying LICENSE file. +For more information, including possible other licensing options, contact +FasterXML.com (http://fasterxml.com). + +## Credits + +A list of contributors may be found from CREDITS file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. + +-------------------------------------------------------------------------------- +com.fasterxml.classmate +-------------------------------------------------------------------------------- + +Java ClassMate library was originally written by Tatu Saloranta ([email protected]) + +Other developers who have contributed code are: + +* Brian Langel + +## Copyright + +Copyright 2007-, Tatu Saloranta ([email protected]) + +-------------------------------------------------------------------------------- +org.glassfish.jersey.core.jersey-common +-------------------------------------------------------------------------------- + +# Notice for Jersey Core Common module +This content is produced and maintained by the Eclipse Jersey project. + + +* https://projects.eclipse.org/projects/ee4j.jersey + +## Trademarks +Eclipse Jersey is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jersey + +## Third-party Content + +Google Guava Version 18.0 +* License: Apache License, 2.0 +* Copyright: (C) 2009 The Guava Authors + +JSR-166 Extension - JEP 266 +* License: Creative Commons 1.0 (CC0) +* No copyright +* Written by Doug Lea with assistance from members of JCP JSR-166 +* Expert Group and released to the public domain, as explained at +* http://creativecommons.org/publicdomain/zero/1.0/ + +# Notice for Jersey +This content is produced and maintained by the Eclipse Jersey project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jersey + +## Trademarks +Eclipse Jersey is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jersey + +## Third-party Content + +Angular JS, v1.6.6 +* License MIT (http://www.opensource.org/licenses/mit-license.php) +* Project: http://angularjs.org +* Coyright: (c) 2010-2017 Google, Inc. + +aopalliance Version 1 +* License: all the source code provided by AOP Alliance is Public Domain. +* Project: http://aopalliance.sourceforge.net +* Copyright: Material in the public domain is not protected by copyright + +Bean Validation API 3.0.2 +* License: Apache License, 2.0 +* Project: http://beanvalidation.org/1.1/ +* Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors +* by the @authors tag. + +Hibernate Validator CDI, 7.0.5.Final +* License: Apache License, 2.0 +* Project: https://beanvalidation.org/ +* Repackaged in org.glassfish.jersey.server.validation.internal.hibernate + +Bootstrap v3.3.7 +* License: MIT license (https://github.com/twbs/bootstrap/blob/master/LICENSE) +* Project: http://getbootstrap.com +* Copyright: 2011-2016 Twitter, Inc + +Google Guava Version 18.0 +* License: Apache License, 2.0 +* Copyright (C) 2009 The Guava Authors + +jakarta.inject Version: 1 +* License: Apache License, 2.0 +* Copyright (C) 2009 The JSR-330 Expert Group + +Javassist Version 3.29.2-GA +* License: Apache License, 2.0 +* Project: http://www.javassist.org/ +* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + +Jackson JAX-RS Providers Version 2.15.3 +* License: Apache License, 2.0 +* Project: https://github.com/FasterXML/jackson-jaxrs-providers +* Copyright: (c) 2009-2023 FasterXML, LLC. All rights reserved unless otherwise indicated. + +jQuery v1.12.4 +* License: jquery.org/license +* Project: jquery.org +* Copyright: (c) jQuery Foundation + +jQuery Barcode plugin 0.3 +* License: MIT & GPL (http://www.opensource.org/licenses/mit-license.php & http://www.gnu.org/licenses/gpl.html) +* Project: http://www.pasella.it/projects/jQuery/barcode +* Copyright: (c) 2009 Antonello Pasella [email protected] + +JSR-166 Extension - JEP 266 +* License: CC0 +* No copyright +* Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ + +KineticJS, v4.7.1 +* License: MIT license (http://www.opensource.org/licenses/mit-license.php) +* Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS +* Copyright: Eric Rowell + +org.objectweb.asm Version 9.6 +* License: Modified BSD (https://asm.ow2.io/license.html) +* Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved. + +org.osgi.core version 6.0.0 +* License: Apache License, 2.0 +* Copyright (c) OSGi Alliance (2005, 2008). All Rights Reserved. + +org.glassfish.jersey.server.internal.monitoring.core +* License: Apache License, 2.0 +* Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved. +* Copyright 2010-2013 Coda Hale and Yammer, Inc. + +W3.org documents +* License: W3C License +* Copyright: Copyright (c) 1994-2001 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ + +-------------------------------------------------------------------------------- +jakarta.annotation.jakarta.annotation-api +-------------------------------------------------------------------------------- + +# Notices for Jakarta Annotations + +This content is produced and maintained by the Jakarta Annotations project. + + * Project home: https://projects.eclipse.org/projects/ee4j.ca + +## Trademarks + +Jakarta Annotations is a trademark of the Eclipse Foundation. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code + +The project maintains the following source code repositories: + + * https://github.com/eclipse-ee4j/common-annotations-api + +## Third-party Content + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. + +-------------------------------------------------------------------------------- +org.apache.commons.commons-text +-------------------------------------------------------------------------------- + +Apache Commons Text +Copyright 2014-2023 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). diff --git a/notebook-migration-service/build.sbt b/notebook-migration-service/build.sbt new file mode 100644 index 0000000000..899280ed28 --- /dev/null +++ b/notebook-migration-service/build.sbt @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import scala.collection.Seq + +name := "notebook-migration-service" + + +enablePlugins(JavaAppPackaging) + +// Ship LICENSE-binary, NOTICE-binary, DISCLAIMER, and the licenses/ +// directory at the top of the Universal dist zip. +// See project/AddMetaInfLicenseFiles.scala. +Universal / mappings := AddMetaInfLicenseFiles.distMappings( + (Universal / mappings).value, + (ThisBuild / baseDirectory).value, + baseDirectory.value / "LICENSE-binary", + baseDirectory.value / "NOTICE-binary" +) + +// Enable semanticdb for Scalafix +ThisBuild / semanticdbEnabled := true +ThisBuild / semanticdbVersion := scalafixSemanticdb.revision + +// Manage dependency conflicts by always using the latest revision +ThisBuild / conflictManager := ConflictManager.latestRevision + +// Restrict parallel execution of tests to avoid conflicts +Global / concurrentRestrictions += Tags.limit(Tags.Test, 1) + +///////////////////////////////////////////////////////////////////////////// +// Compiler Options +///////////////////////////////////////////////////////////////////////////// + +// Scala compiler options +Compile / scalacOptions ++= Seq( + "-Xelide-below", "WARNING", // Turn on optimizations with "WARNING" as the threshold + "-feature", // Check feature warnings + "-deprecation", // Check deprecation warnings + "-Ywarn-unused:imports" // Check for unused imports +) + +///////////////////////////////////////////////////////////////////////////// +// Version Variables +///////////////////////////////////////////////////////////////////////////// + +val dropwizardVersion = "4.0.7" +val mockitoVersion = "5.4.0" +val assertjVersion = "3.24.2" + +///////////////////////////////////////////////////////////////////////////// +// Test-related Dependencies +///////////////////////////////////////////////////////////////////////////// + +libraryDependencies ++= Seq( + "org.scalamock" %% "scalamock" % "5.2.0" % Test, // ScalaMock + "org.scalatest" %% "scalatest" % "3.2.17" % Test, // ScalaTest + "io.dropwizard" % "dropwizard-testing" % dropwizardVersion % Test, // Dropwizard Testing + "org.mockito" % "mockito-core" % mockitoVersion % Test, // Mockito for mocking + "org.assertj" % "assertj-core" % assertjVersion % Test, // AssertJ for assertions + "com.novocode" % "junit-interface" % "0.11" % Test // SBT interface for JUnit +) + +///////////////////////////////////////////////////////////////////////////// +// Dependencies +///////////////////////////////////////////////////////////////////////////// + +// Core Dependencies +libraryDependencies ++= Seq( + "io.dropwizard" % "dropwizard-core" % dropwizardVersion, + "io.dropwizard" % "dropwizard-auth" % dropwizardVersion, // Dropwizard Authentication module + "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.18.6" +) \ No newline at end of file diff --git a/notebook-migration-service/project/build.properties b/notebook-migration-service/project/build.properties new file mode 100644 index 0000000000..d0ba9f1421 --- /dev/null +++ b/notebook-migration-service/project/build.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +sbt.version = 1.12.9 \ No newline at end of file diff --git a/notebook-migration-service/src/main/resources/notebook-migration-service-web-config.yaml b/notebook-migration-service/src/main/resources/notebook-migration-service-web-config.yaml new file mode 100644 index 0000000000..947dd59c4f --- /dev/null +++ b/notebook-migration-service/src/main/resources/notebook-migration-service-web-config.yaml @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +server: + applicationConnectors: + - type: http + port: 9098 + adminConnectors: [] + requestLog: + type: classic + appenders: [] + +logging: + level: ${TEXERA_SERVICE_LOG_LEVEL:-INFO} + appenders: + - type: console + threshold: ${TEXERA_SERVICE_LOG_LEVEL:-INFO} + - type: file + currentLogFilename: logs/notebook-migration-service.log + archive: true + archivedLogFilenamePattern: logs/notebook-migration-service-%d.log.gz + archivedFileCount: 5 \ No newline at end of file diff --git a/notebook-migration-service/src/main/scala/org/apache/texera/service/NotebookMigrationService.scala b/notebook-migration-service/src/main/scala/org/apache/texera/service/NotebookMigrationService.scala new file mode 100644 index 0000000000..a94e728b00 --- /dev/null +++ b/notebook-migration-service/src/main/scala/org/apache/texera/service/NotebookMigrationService.scala @@ -0,0 +1,107 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.texera.service + +import com.fasterxml.jackson.module.scala.DefaultScalaModule +import com.typesafe.scalalogging.LazyLogging +import io.dropwizard.auth.AuthDynamicFeature +import io.dropwizard.configuration.{EnvironmentVariableSubstitutor, SubstitutingSourceProvider} +import io.dropwizard.core.Application +import io.dropwizard.core.setup.{Bootstrap, Environment} +import org.apache.texera.common.config.StorageConfig +import org.apache.texera.auth.{ + JwtAuthFilter, + RequestLoggingFilter, + SessionUser, + UnauthorizedExceptionMapper +} +import org.apache.texera.dao.SqlServer +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature +import java.nio.file.Path +import org.apache.texera.service.resource.{HealthCheckResource, NotebookMigrationResource} + +class NotebookMigrationService + extends Application[NotebookMigrationServiceConfiguration] + with LazyLogging { + override def initialize(bootstrap: Bootstrap[NotebookMigrationServiceConfiguration]): Unit = { + // enable environment variable substitution in YAML config + bootstrap.setConfigurationSourceProvider( + new SubstitutingSourceProvider( + bootstrap.getConfigurationSourceProvider, + new EnvironmentVariableSubstitutor(false) + ) + ) + // Register Scala module to Dropwizard default object mapper + bootstrap.getObjectMapper.registerModule(DefaultScalaModule) + + SqlServer.initConnection( + StorageConfig.jdbcUrl, + StorageConfig.jdbcUsername, + StorageConfig.jdbcPassword + ) + } + + override def run( + configuration: NotebookMigrationServiceConfiguration, + environment: Environment + ): Unit = { + // Serve backend at /api + environment.jersey.setUrlPattern("/api/*") + + environment.jersey.register(classOf[HealthCheckResource]) + + NotebookMigrationService.registerAuthFeatures(environment) + + environment.jersey.register(classOf[NotebookMigrationResource]) + + // Route request logs through SLF4J, controlled by TEXERA_SERVICE_LOG_LEVEL + RequestLoggingFilter.register(environment.getApplicationContext) + } +} +object NotebookMigrationService { + // Registers JWT auth, @Auth injection, and @RolesAllowed enforcement. + // Mirrors the other Dropwizard services' registerAuthFeatures so they don't drift apart. + def registerAuthFeatures(environment: Environment): Unit = { + // Register JWT authentication filter + environment.jersey.register(new AuthDynamicFeature(classOf[JwtAuthFilter])) + environment.jersey.register(classOf[UnauthorizedExceptionMapper]) + + // Enable @Auth annotation for injecting SessionUser + environment.jersey.register( + new io.dropwizard.auth.AuthValueFactoryProvider.Binder(classOf[SessionUser]) + ) + + // Enforce @RolesAllowed annotations on resource methods + environment.jersey.register(classOf[RolesAllowedDynamicFeature]) + } + + def main(args: Array[String]): Unit = { + val notebookMigrationPath = Path + .of(sys.env.getOrElse("TEXERA_HOME", ".")) + .resolve("notebook-migration-service") + .resolve("src") + .resolve("main") + .resolve("resources") + .resolve("notebook-migration-service-web-config.yaml") + .toAbsolutePath + .toString + + // Start the Dropwizard application + new NotebookMigrationService().run("server", notebookMigrationPath) + } +} diff --git a/notebook-migration-service/src/main/scala/org/apache/texera/service/NotebookMigrationServiceConfiguration.scala b/notebook-migration-service/src/main/scala/org/apache/texera/service/NotebookMigrationServiceConfiguration.scala new file mode 100644 index 0000000000..dc908e7565 --- /dev/null +++ b/notebook-migration-service/src/main/scala/org/apache/texera/service/NotebookMigrationServiceConfiguration.scala @@ -0,0 +1,22 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.texera.service + +import io.dropwizard.core.Configuration + +class NotebookMigrationServiceConfiguration extends Configuration {} diff --git a/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/HealthCheckResource.scala b/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/HealthCheckResource.scala new file mode 100644 index 0000000000..ef928b4dd1 --- /dev/null +++ b/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/HealthCheckResource.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.service.resource + +import jakarta.annotation.security.PermitAll +import jakarta.ws.rs.core.MediaType +import jakarta.ws.rs.{GET, Path, Produces} + +@Path("/healthcheck") +@PermitAll +@Produces(Array(MediaType.APPLICATION_JSON)) +class HealthCheckResource { + @GET + def healthCheck: Map[String, String] = Map("status" -> "ok") +} diff --git a/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/NotebookMigrationResource.scala b/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/NotebookMigrationResource.scala new file mode 100644 index 0000000000..b8ebbb9f19 --- /dev/null +++ b/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/NotebookMigrationResource.scala @@ -0,0 +1,419 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.texera.service.resource + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.scala.DefaultScalaModule +import com.typesafe.scalalogging.LazyLogging +import io.dropwizard.auth.Auth +import jakarta.annotation.security.RolesAllowed +import jakarta.ws.rs._ +import jakarta.ws.rs.core._ +import org.apache.texera.auth.SessionUser +import org.apache.texera.dao.SqlServer +import org.jooq.JSONB +import org.jooq.exception.DataAccessException +import org.apache.texera.dao.jooq.generated.tables.Notebook +import org.apache.texera.dao.jooq.generated.tables.WorkflowNotebookMapping +import java.net.{HttpURLConnection, URL} +import java.nio.charset.StandardCharsets +import scala.util.control.NonFatal +import org.apache.texera.common.config.StorageConfig + +object NotebookMigrationResource extends LazyLogging { + + private val mapper: ObjectMapper = new ObjectMapper().registerModule(DefaultScalaModule) + + // Build an error response body via the mapper so the message is JSON-escaped; interpolating + // e.getMessage directly produces malformed JSON when it contains quotes, backslashes, or newlines. + private def errorJson(message: String): String = + mapper.writeValueAsString(mapper.createObjectNode().put("error", message)) + + // Build a {"success": true, "url": ...} body via the mapper so the URL is JSON-escaped + // rather than raw-interpolated. + private def successUrlJson(url: String): String = + mapper.writeValueAsString(mapper.createObjectNode().put("success", true).put("url", url)) + + private val jupyterUrl = StorageConfig.jupyterURL + private val jupyterToken = StorageConfig.jupyterToken + // The token is passed as a URL param so the browser iframe can authenticate when loading the notebook. + // jupyterIframeURL is process-global state. This is safe ONLY because each user runs their own pod + // (own notebook-migration-service JVM + own Jupyter) in the k8s deployment, so this singleton is + // effectively per-user. Do NOT deploy this service as a shared multi-user instance without adding + // per-user keying here, or one user's upload would overwrite another's iframe URL. + @volatile private var jupyterIframeURL = + s"$jupyterUrl/notebooks/work/notebook.ipynb?token=$jupyterToken" + + private def isJupyterAvailable(jupyterUrl: String): Boolean = { + var conn: java.net.HttpURLConnection = null + try { + conn = new java.net.URL(s"$jupyterUrl/api") + .openConnection() + .asInstanceOf[java.net.HttpURLConnection] + + conn.setRequestMethod("GET") + conn.setConnectTimeout(2000) + conn.setReadTimeout(2000) + + val status = conn.getResponseCode + + status == 200 || status == 403 + } catch { + case _: Exception => false + } finally { + if (conn != null) conn.disconnect() + } + } + + // Returns the Jupyter iframe reference URL + def getJupyterIframeURL(): Response = { + if (!isJupyterAvailable(jupyterUrl)) { + return Response + .status(500) + .entity( + """ + { + "success": false, + "message": "Cannot connect to Jupyter server" + } + """ + ) + .build() + } + + Response.ok(successUrlJson(jupyterIframeURL)).build() + } + + // Returns the URL of Jupyter + def getJupyterURL(): Response = { + if (!isJupyterAvailable(jupyterUrl)) { + return Response + .status(500) + .entity( + """ + { + "success": false, + "message": "Cannot connect to Jupyter server" + } + """ + ) + .build() + } + + Response.ok(successUrlJson(jupyterUrl)).build() + } + + // Set the notebook in Jupyter + def setNotebook(body: String): Response = { + var conn: HttpURLConnection = null + try { + val json = mapper.readTree(body) + + val notebookName = json.get("notebookName").asText() + val notebookData = json.get("notebookData") + + // Allow only a plain ".ipynb" filename. Validated before any network call so a + // bad name is rejected with a 400 up front. This blocks path traversal in the + // Jupyter contents URL (e.g. "../../etc/x.ipynb") and keeps notebookName out of + // the raw-interpolated jupyterIframeURL JSON (no quotes/control chars). + if (!notebookName.matches("[A-Za-z0-9._-]+\\.ipynb")) { + return Response + .status(Response.Status.BAD_REQUEST) + .entity(errorJson(s"Invalid notebook name: $notebookName")) + .build() + } + + if (!isJupyterAvailable(jupyterUrl)) { + return Response + .status(500) + .entity( + """ + { + "success": false, + "message": "Cannot connect to Jupyter server" + } + """ + ) + .build() + } + + // Construct Jupyter API URL + val apiUrl = s"$jupyterUrl/api/contents/work/$notebookName" + + val url = new URL(apiUrl) + conn = url.openConnection().asInstanceOf[HttpURLConnection] + + conn.setRequestMethod("PUT") + conn.setDoOutput(true) + conn.setRequestProperty("Content-Type", "application/json") + // The Jupyter Contents API requires authentication; send the configured token. + conn.setRequestProperty("Authorization", s"token $jupyterToken") + + val requestBody = + s""" + { + "type": "notebook", + "content": $notebookData + } + """ + + val os = conn.getOutputStream + os.write(requestBody.getBytes(StandardCharsets.UTF_8)) + os.flush() + os.close() + + val status = conn.getResponseCode + + if (status != 200 && status != 201) { + return Response + .status(500) + .entity( + s""" + { + "success": false, + "message": "Failed to upload notebook to Jupyter (status $status)" + } + """ + ) + .build() + } + + jupyterIframeURL = s"$jupyterUrl/notebooks/work/$notebookName?token=$jupyterToken" + + Response + .ok( + s""" + { + "success": true, + "message": "Notebook successfully sent to Jupyter." + } + """ + ) + .build() + + } catch { + case NonFatal(e) => + logger.error("Error sending notebook to Jupyter", e) + Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(errorJson(e.getMessage)) + .build() + } finally { + if (conn != null) conn.disconnect() + } + } + + // Store notebook + mapping in database + def storeNotebookAndMapping(body: String, uid: java.lang.Integer): Response = { + try { + val json = mapper.readTree(body) + + val wid: java.lang.Integer = json.get("wid").asInt() + val vid: java.lang.Integer = json.get("vid").asInt() + val mappingNode = json.get("mapping") + val notebookNode = json.get("notebook") + + // Only a user with write access to the workflow may store its notebook. + if (!WorkflowAccessResource.hasWriteAccess(wid, uid)) { + return Response + .status(Response.Status.FORBIDDEN) + .entity(errorJson(s"No write access to workflow $wid")) + .build() + } + + val dsl = SqlServer.getInstance().createDSLContext() + + // notebook.wid is UNIQUE: a workflow has at most one notebook. If one already + // exists, reject the re-store with a 409 rather than letting the INSERT trip the + // constraint and surface as a 500. + val alreadyStored = dsl.fetchExists( + dsl.selectFrom(Notebook.NOTEBOOK).where(Notebook.NOTEBOOK.WID.eq(wid)) + ) + if (alreadyStored) { + return Response + .status(Response.Status.CONFLICT) + .entity(errorJson(s"A notebook is already stored for workflow $wid")) + .build() + } + + val nid: java.lang.Integer = SqlServer.withTransaction(dsl) { ctx => + // Insert notebook + val notebookRecord = ctx + .insertInto(Notebook.NOTEBOOK) + .set(Notebook.NOTEBOOK.WID, wid) + .set(Notebook.NOTEBOOK.NOTEBOOK_, JSONB.valueOf(notebookNode.toString)) + .returning(Notebook.NOTEBOOK.NID) + .fetchOne() + + val nidInside: java.lang.Integer = notebookRecord.getValue(Notebook.NOTEBOOK.NID) + + // Insert workflow-notebook mapping + ctx + .insertInto(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING) + .set(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.WID, wid) + .set(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.VID, vid) + .set(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.NID, nidInside) + .set( + WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.MAPPING, + JSONB.valueOf(mappingNode.toString) + ) + .execute() + + nidInside + } + + Response + .ok( + s""" + { + "success": true, + "message": "Notebook and mapping successfully stored. wid: $wid, vid: $vid, nid: $nid" + } + """ + ) + .build() + + } catch { + // Backstop for the pre-check TOCTOU race: two writers on a shared workflow can both + // pass the existence check, then one INSERT trips the UNIQUE(wid) constraint. Translate + // that (Postgres SQLState 23505) to a 409 rather than a generic 500. + case e: DataAccessException if e.sqlState == "23505" => + Response + .status(Response.Status.CONFLICT) + .entity(errorJson("A notebook is already stored for this workflow")) + .build() + case NonFatal(e) => + logger.error("Error storing mapping and workflow", e) + Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(errorJson(e.getMessage)) + .build() + } + } + + // Fetch notebook + mapping + def fetchNotebookAndMapping(body: String, uid: java.lang.Integer): Response = { + try { + val json = mapper.readTree(body) + + val wid: java.lang.Integer = json.get("wid").asInt() + val vid: java.lang.Integer = json.get("vid").asInt() + + // Only a user with write access to the workflow may fetch its notebook. + if (!WorkflowAccessResource.hasWriteAccess(wid, uid)) { + return Response + .status(Response.Status.FORBIDDEN) + .entity(errorJson(s"No write access to workflow $wid")) + .build() + } + + val dsl = SqlServer.getInstance().createDSLContext() + + // Fetch the most recent notebook (highest nid) for this workflow version + val result = dsl + .select( + Notebook.NOTEBOOK.NID, + Notebook.NOTEBOOK.NOTEBOOK_, + WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.MAPPING + ) + .from(Notebook.NOTEBOOK) + .join(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING) + .on(Notebook.NOTEBOOK.WID.eq(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.WID)) + .and(Notebook.NOTEBOOK.NID.eq(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.NID)) + .where(Notebook.NOTEBOOK.WID.eq(wid)) + .and(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.VID.eq(vid)) + .orderBy(Notebook.NOTEBOOK.NID.desc()) // most recent nid first + .limit(1) // only take the latest + .fetchOne() + + if (result == null) { + Response.ok("""{"exists": false}""").build() + } else { + val nid: Int = result.getValue(Notebook.NOTEBOOK.NID) + val notebookJson: String = + result.get(Notebook.NOTEBOOK.NOTEBOOK_).asInstanceOf[JSONB].data() + val mappingJson: String = result + .get(WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING.MAPPING) + .asInstanceOf[JSONB] + .data() + + Response + .ok( + s""" + { + "exists": true, + "notebook": $notebookJson, + "mapping": $mappingJson + } + """ + ) + .build() + } + + } catch { + case NonFatal(e) => + logger.error("Database error retrieving mapping", e) + Response + .status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(errorJson(e.getMessage)) + .build() + } + } +} + +@Path("/notebook-migration") +@RolesAllowed(Array("REGULAR", "ADMIN")) +@Produces(Array(MediaType.APPLICATION_JSON)) +@Consumes(Array(MediaType.APPLICATION_JSON)) +class NotebookMigrationResource extends LazyLogging { + + @GET + @Path("/get-jupyter-iframe-url") + def getJupyterIframeURL(@Auth user: SessionUser): Response = { + logger.info("Getting Jupyter iframe URL") + NotebookMigrationResource.getJupyterIframeURL() + } + + @GET + @Path("/get-jupyter-url") + def getJupyterURL(@Auth user: SessionUser): Response = { + logger.info("Getting Jupyter API URL") + NotebookMigrationResource.getJupyterURL() + } + + @POST + @Path("/set-notebook") + def setNotebook(body: String, @Auth user: SessionUser): Response = { + logger.info("Setting notebook") + NotebookMigrationResource.setNotebook(body) + } + + @POST + @Path("/store-notebook-and-mapping") + def storeNotebookAndMapping(body: String, @Auth user: SessionUser): Response = { + logger.info("Storing notebook and mapping") + NotebookMigrationResource.storeNotebookAndMapping(body, user.getUid) + } + + @POST + @Path("/fetch-notebook-and-mapping") + def fetchNotebookAndMapping(body: String, @Auth user: SessionUser): Response = { + logger.info("Fetching notebook and mapping") + NotebookMigrationResource.fetchNotebookAndMapping(body, user.getUid) + } +} diff --git a/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/WorkflowAccessResource.scala b/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/WorkflowAccessResource.scala new file mode 100644 index 0000000000..bef9c38cc2 --- /dev/null +++ b/notebook-migration-service/src/main/scala/org/apache/texera/service/resource/WorkflowAccessResource.scala @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.texera.service.resource + +import org.apache.texera.dao.SqlServer +import org.apache.texera.dao.jooq.generated.Tables.WORKFLOW_USER_ACCESS +import org.apache.texera.dao.jooq.generated.enums.PrivilegeEnum +import org.jooq.DSLContext + +object WorkflowAccessResource { + private def context: DSLContext = + SqlServer.getInstance().createDSLContext() + + /** + * Whether the given user holds a direct WRITE grant on the given workflow. + * + * Only the direct WORKFLOW_USER_ACCESS grant is consulted — no project or + * public-visibility fallback — so the notebook endpoints stay self-contained. + * + * @param wid workflow id + * @param uid user id + */ + def hasWriteAccess(wid: Integer, uid: Integer): Boolean = { + val privilege = context + .select(WORKFLOW_USER_ACCESS.PRIVILEGE) + .from(WORKFLOW_USER_ACCESS) + .where(WORKFLOW_USER_ACCESS.WID.eq(wid).and(WORKFLOW_USER_ACCESS.UID.eq(uid))) + .fetchOne(WORKFLOW_USER_ACCESS.PRIVILEGE) + privilege == PrivilegeEnum.WRITE + } +} diff --git a/notebook-migration-service/src/test/scala/org/apache/texera/service/NotebookMigrationServiceRunSpec.scala b/notebook-migration-service/src/test/scala/org/apache/texera/service/NotebookMigrationServiceRunSpec.scala new file mode 100644 index 0000000000..e71529a367 --- /dev/null +++ b/notebook-migration-service/src/test/scala/org/apache/texera/service/NotebookMigrationServiceRunSpec.scala @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.service + +import io.dropwizard.auth.AuthDynamicFeature +import io.dropwizard.core.setup.Environment +import io.dropwizard.jersey.setup.JerseyEnvironment +import io.dropwizard.jetty.MutableServletContextHandler +import io.dropwizard.jetty.setup.ServletEnvironment +import org.apache.texera.auth.UnauthorizedExceptionMapper +import org.apache.texera.service.resource.{HealthCheckResource, NotebookMigrationResource} +import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature +import org.mockito.ArgumentMatchers.isA +import org.mockito.Mockito.{mock, verify, when} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class NotebookMigrationServiceRunSpec extends AnyFlatSpec with Matchers { + + "NotebookMigrationService.run" should "register the resources and the JWT auth stack on the Jersey environment" in { + val jersey = mock(classOf[JerseyEnvironment]) + val servlets = mock(classOf[ServletEnvironment]) + val context = mock(classOf[MutableServletContextHandler]) + val env = mock(classOf[Environment]) + when(env.jersey).thenReturn(jersey) + when(env.servlets).thenReturn(servlets) + when(env.getApplicationContext).thenReturn(context) + + val service = new NotebookMigrationService + service.run(mock(classOf[NotebookMigrationServiceConfiguration]), env) + + verify(jersey).setUrlPattern("/api/*") + verify(jersey).register(classOf[HealthCheckResource]) + verify(jersey).register(classOf[NotebookMigrationResource]) + // Auth stack from registerAuthFeatures — without these, @RolesAllowed / @Auth are ignored. + verify(jersey).register(isA(classOf[AuthDynamicFeature])) + verify(jersey).register(classOf[UnauthorizedExceptionMapper]) + verify(jersey).register(classOf[RolesAllowedDynamicFeature]) + } +} diff --git a/notebook-migration-service/src/test/scala/org/apache/texera/service/resource/NotebookMigrationResourceSpec.scala b/notebook-migration-service/src/test/scala/org/apache/texera/service/resource/NotebookMigrationResourceSpec.scala new file mode 100644 index 0000000000..a36ecdd590 --- /dev/null +++ b/notebook-migration-service/src/test/scala/org/apache/texera/service/resource/NotebookMigrationResourceSpec.scala @@ -0,0 +1,453 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.service.resource + +import jakarta.ws.rs.core.Response +import org.apache.texera.auth.SessionUser +import org.apache.texera.dao.MockTexeraDB +import org.apache.texera.dao.jooq.generated.enums.{PrivilegeEnum, UserRoleEnum} +import org.apache.texera.dao.jooq.generated.tables.Notebook.NOTEBOOK +import org.apache.texera.dao.jooq.generated.tables.User.USER +import org.apache.texera.dao.jooq.generated.tables.Workflow.WORKFLOW +import org.apache.texera.dao.jooq.generated.tables.WorkflowNotebookMapping.WORKFLOW_NOTEBOOK_MAPPING +import org.apache.texera.dao.jooq.generated.tables.WorkflowUserAccess.WORKFLOW_USER_ACCESS +import org.apache.texera.dao.jooq.generated.tables.WorkflowVersion.WORKFLOW_VERSION +import org.apache.texera.dao.jooq.generated.tables.daos.{ + UserDao, + WorkflowDao, + WorkflowUserAccessDao, + WorkflowVersionDao +} +import org.apache.texera.dao.jooq.generated.tables.pojos.{ + User, + Workflow, + WorkflowUserAccess, + WorkflowVersion +} +import org.jooq.JSONB +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach} + +import com.sun.net.httpserver.HttpServer + +import java.net.InetSocketAddress +import java.sql.Timestamp +import java.util.UUID + +class NotebookMigrationResourceSpec + extends AnyFlatSpec + with Matchers + with BeforeAndAfterAll + with BeforeAndAfterEach + with MockTexeraDB { + + // Randomise the seeded wid so a parallel run of unrelated specs that happen + // to seed the same id wouldn't collide on the embedded postgres. + private val testWid = 9000 + scala.util.Random.nextInt(1000) + private val writerEmail = s"[email protected]" + private val readerEmail = s"[email protected]" + + private var workflowDao: WorkflowDao = _ + private var workflowVersionDao: WorkflowVersionDao = _ + private var userDao: UserDao = _ + private var workflowUserAccessDao: WorkflowUserAccessDao = _ + private var seededVid: Integer = _ + private var writerUid: Integer = _ // holds WRITE access to testWid + private var readerUid: Integer = _ // holds READ access to testWid + + private val sampleNotebook = + """{"cells":[{"cell_type":"code","metadata":{},"source":"print(1)"}]}""" + private val sampleMapping = + """{"operator_to_cell":{},"cell_to_operator":{}}""" + + override protected def beforeAll(): Unit = initializeDBAndReplaceDSLContext() + override protected def afterAll(): Unit = shutdownDB() + + override protected def beforeEach(): Unit = { + val cfg = getDSLContext.configuration() + workflowDao = new WorkflowDao(cfg) + workflowVersionDao = new WorkflowVersionDao(cfg) + userDao = new UserDao(cfg) + workflowUserAccessDao = new WorkflowUserAccessDao(cfg) + cleanup() + + val workflow = new Workflow + workflow.setWid(testWid) + workflow.setName(s"wf_${UUID.randomUUID().toString.substring(0, 8)}") + workflow.setContent("{}") + workflow.setDescription("") + workflow.setCreationTime(new Timestamp(System.currentTimeMillis())) + workflow.setLastModifiedTime(new Timestamp(System.currentTimeMillis())) + workflowDao.insert(workflow) + + val version = new WorkflowVersion + version.setWid(testWid) + version.setContent("{}") + version.setCreationTime(new Timestamp(System.currentTimeMillis())) + workflowVersionDao.insert(version) + seededVid = version.getVid + + // One user with WRITE access (the happy path) and one with only READ + // access, so the write-access gate can be exercised both ways. + writerUid = insertUser("nbms_writer", writerEmail) + readerUid = insertUser("nbms_reader", readerEmail) + insertAccess(writerUid, PrivilegeEnum.WRITE) + insertAccess(readerUid, PrivilegeEnum.READ) + } + + override protected def afterEach(): Unit = cleanup() + + private def insertUser(name: String, email: String): Integer = { + val user = new User + user.setName(name) + user.setEmail(email) + user.setRole(UserRoleEnum.REGULAR) + user.setPassword("password") + userDao.insert(user) + user.getUid + } + + private def insertAccess(uid: Integer, privilege: PrivilegeEnum): Unit = { + val access = new WorkflowUserAccess + access.setWid(testWid) + access.setUid(uid) + access.setPrivilege(privilege) + workflowUserAccessDao.insert(access) + } + + private def cleanup(): Unit = { + // Delete children before parents. workflow_user_access and notebook cascade + // on workflow delete, but explicit deletes keep state observable across tests + // and avoid depending on cascade ordering. + getDSLContext.deleteFrom(WORKFLOW_NOTEBOOK_MAPPING).execute() + getDSLContext.deleteFrom(NOTEBOOK).execute() + getDSLContext + .deleteFrom(WORKFLOW_USER_ACCESS) + .where(WORKFLOW_USER_ACCESS.WID.eq(testWid)) + .execute() + getDSLContext + .deleteFrom(WORKFLOW_VERSION) + .where(WORKFLOW_VERSION.WID.eq(testWid)) + .execute() + getDSLContext.deleteFrom(WORKFLOW).where(WORKFLOW.WID.eq(testWid)).execute() + getDSLContext.deleteFrom(USER).where(USER.EMAIL.in(writerEmail, readerEmail)).execute() + } + + private def storePayload( + notebook: String = sampleNotebook, + mapping: String = sampleMapping, + vid: Integer = seededVid + ): String = + s"""{"wid": $testWid, "vid": $vid, "notebook": $notebook, "mapping": $mapping}""" + + private def fetchPayload(vid: Integer = seededVid): String = + s"""{"wid": $testWid, "vid": $vid}""" + + private val resource = new NotebookMigrationResource() + + private def sessionUser(uid: Integer): SessionUser = { + val u = new User + u.setUid(uid) + new SessionUser(u) + } + + // Runs `test` with a stub Jupyter server on localhost:9100 (the configured + // jupyter URL): GET /api returns 200 so isJupyterAvailable() passes, and PUT + // /api/contents/<name> returns `contentsStatus`. Lets the HTTP success/failure + // paths run without a real Jupyter. Sequential test execution (Tags.limit) keeps + // this from colliding with the "unreachable" test, which needs the port free. + private def withFakeJupyter(contentsStatus: Int, apiStatus: Int = 200)(test: => Unit): Unit = { + val server = HttpServer.create(new InetSocketAddress("localhost", 9100), 0) + server.createContext( + "/api", + (exchange: com.sun.net.httpserver.HttpExchange) => { + exchange.getRequestBody.readAllBytes() + val body = """{"version":"2.7.0"}""".getBytes("UTF-8") + exchange.sendResponseHeaders(apiStatus, body.length) + val os = exchange.getResponseBody + os.write(body) + os.close() + } + ) + // Longest-prefix match means /api/contents/... routes here, not to /api. + server.createContext( + "/api/contents", + (exchange: com.sun.net.httpserver.HttpExchange) => { + exchange.getRequestBody.readAllBytes() + val body = "{}".getBytes("UTF-8") + exchange.sendResponseHeaders(contentsStatus, body.length) + val os = exchange.getResponseBody + os.write(body) + os.close() + } + ) + server.start() + try test + finally server.stop(0) + } + + // -- storeNotebookAndMapping ------------------------------------------------ + + "storeNotebookAndMapping" should "insert one notebook and one mapping tied to the workflow version" in { + val response = NotebookMigrationResource.storeNotebookAndMapping(storePayload(), writerUid) + response.getStatus shouldBe Response.Status.OK.getStatusCode + + getDSLContext.fetchCount(NOTEBOOK) shouldBe 1 + getDSLContext.fetchCount(WORKFLOW_NOTEBOOK_MAPPING) shouldBe 1 + + val notebookRow = getDSLContext.selectFrom(NOTEBOOK).fetchOne() + notebookRow.get(NOTEBOOK.WID) shouldBe testWid + + val mappingRow = getDSLContext.selectFrom(WORKFLOW_NOTEBOOK_MAPPING).fetchOne() + mappingRow.get(WORKFLOW_NOTEBOOK_MAPPING.WID) shouldBe testWid + mappingRow.get(WORKFLOW_NOTEBOOK_MAPPING.VID) shouldBe seededVid + // The mapping row must reference the just-inserted notebook by its returned nid. + mappingRow.get(WORKFLOW_NOTEBOOK_MAPPING.NID) shouldBe notebookRow.get(NOTEBOOK.NID) + } + + it should "round-trip notebook and mapping JSON content through the JSONB columns" in { + val notebook = + """{"cells":[{"cell_type":"code","metadata":{"uuid":"abc-123"},"source":"x = 1"}]}""" + val mapping = + """{"operator_to_cell":{"op1":["cell1"]},"cell_to_operator":{"cell1":["op1"]}}""" + + NotebookMigrationResource.storeNotebookAndMapping(storePayload(notebook, mapping), writerUid) + + val storedNotebookJson = + getDSLContext + .selectFrom(NOTEBOOK) + .fetchOne() + .get(NOTEBOOK.NOTEBOOK_) + .asInstanceOf[JSONB] + .data() + val storedMappingJson = + getDSLContext + .selectFrom(WORKFLOW_NOTEBOOK_MAPPING) + .fetchOne() + .get(WORKFLOW_NOTEBOOK_MAPPING.MAPPING) + .asInstanceOf[JSONB] + .data() + + // Use whitespace-agnostic substring checks — postgres canonicalises JSONB + // text on the way out, so exact-string compare against the input would be + // fragile across postgres versions. + storedNotebookJson should include("\"abc-123\"") + storedNotebookJson should include("x = 1") + storedMappingJson should include("\"op1\"") + storedMappingJson should include("\"cell1\"") + } + + it should "roll back the notebook insert when the mapping insert fails its FK constraint" in { + // workflow_notebook_mapping.vid has FK -> workflow_version(vid). Passing an + // unknown vid trips the mapping insert; because both inserts share a single + // SqlServer.withTransaction block, the notebook insert must roll back too. + // Without this guarantee, orphaned notebook rows would accumulate on every + // failed store. + val unknownVid: Integer = -1 + val response = NotebookMigrationResource.storeNotebookAndMapping( + storePayload(vid = unknownVid), + writerUid + ) + response.getStatus shouldBe Response.Status.INTERNAL_SERVER_ERROR.getStatusCode + getDSLContext.fetchCount(NOTEBOOK) shouldBe 0 + getDSLContext.fetchCount(WORKFLOW_NOTEBOOK_MAPPING) shouldBe 0 + } + + it should "return 409 Conflict on a second store for a workflow that already has a notebook" in { + // notebook.wid is UNIQUE — one notebook per workflow. The second store must be + // rejected with an explicit 409 (not a 500 from the constraint violation), and + // must not add a second notebook or mapping row. + val first = NotebookMigrationResource.storeNotebookAndMapping(storePayload(), writerUid) + first.getStatus shouldBe Response.Status.OK.getStatusCode + + val second = NotebookMigrationResource.storeNotebookAndMapping(storePayload(), writerUid) + second.getStatus shouldBe Response.Status.CONFLICT.getStatusCode + + getDSLContext.fetchCount(NOTEBOOK) shouldBe 1 + getDSLContext.fetchCount(WORKFLOW_NOTEBOOK_MAPPING) shouldBe 1 + } + + // -- fetchNotebookAndMapping ------------------------------------------------ + + "fetchNotebookAndMapping" should "return exists=false when no notebook is stored for the (wid, vid)" in { + val response = NotebookMigrationResource.fetchNotebookAndMapping(fetchPayload(), writerUid) + response.getStatus shouldBe Response.Status.OK.getStatusCode + response.getEntity.toString should include("\"exists\": false") + } + + it should "return exists=true with the stored notebook and mapping when a row exists" in { + NotebookMigrationResource.storeNotebookAndMapping(storePayload(), writerUid) + + val entity = + NotebookMigrationResource + .fetchNotebookAndMapping(fetchPayload(), writerUid) + .getEntity + .toString + entity should include("\"exists\": true") + entity should include("\"notebook\":") + entity should include("\"mapping\":") + } + + it should "return the stored notebook content for a (wid, vid) on fetch" in { + // notebook.wid is UNIQUE — one notebook per workflow — so the endpoint's + // orderBy(NID.desc).limit(1) resolves to that single row. This pins the + // workflow-reopen path: after a store, fetch must return that notebook's content. + val notebook = + """{"cells":[{"cell_type":"code","metadata":{},"source":"v1"}]}""" + + NotebookMigrationResource.storeNotebookAndMapping( + storePayload(notebook, sampleMapping), + writerUid + ) + + val entity = + NotebookMigrationResource + .fetchNotebookAndMapping(fetchPayload(), writerUid) + .getEntity + .toString + entity should include("\"v1\"") + } + + // -- workflow write-access enforcement -------------------------------------- + + "store/fetch" should "return 403 Forbidden when the user lacks write access to the workflow" in { + // readerUid holds only READ access; the endpoints require WRITE, so both must + // be rejected with a 403 and no notebook may be written. + NotebookMigrationResource + .storeNotebookAndMapping(storePayload(), readerUid) + .getStatus shouldBe Response.Status.FORBIDDEN.getStatusCode + + NotebookMigrationResource + .fetchNotebookAndMapping(fetchPayload(), readerUid) + .getStatus shouldBe Response.Status.FORBIDDEN.getStatusCode + + getDSLContext.fetchCount(NOTEBOOK) shouldBe 0 + } + + // -- JAX-RS resource class (@Auth wrappers + Jupyter reachability) ---------- + + "the resource class endpoints" should "store and fetch via the authenticated class methods for a write-access user" in { + resource + .storeNotebookAndMapping(storePayload(), sessionUser(writerUid)) + .getStatus shouldBe Response.Status.OK.getStatusCode + + resource + .fetchNotebookAndMapping(fetchPayload(), sessionUser(writerUid)) + .getEntity + .toString should include("\"exists\": true") + } + + it should "reject the class methods for a read-only user with 403" in { + resource + .storeNotebookAndMapping(storePayload(), sessionUser(readerUid)) + .getStatus shouldBe Response.Status.FORBIDDEN.getStatusCode + resource + .fetchNotebookAndMapping(fetchPayload(), sessionUser(readerUid)) + .getStatus shouldBe Response.Status.FORBIDDEN.getStatusCode + } + + it should "return 500 from the Jupyter endpoints when the Jupyter server is unreachable" in { + // No Jupyter server runs in the unit-test environment, so isJupyterAvailable + // fails the connection and these endpoints surface a 500 rather than crashing. + val user = sessionUser(writerUid) + val validNotebook = """{"notebookName": "notebook.ipynb", "notebookData": {"cells": []}}""" + + resource.setNotebook(validNotebook, user).getStatus shouldBe 500 + resource.getJupyterURL(user).getStatus shouldBe 500 + resource.getJupyterIframeURL(user).getStatus shouldBe 500 + } + + it should "return 500 when the request body is malformed JSON" in { + // Exercises the NonFatal catch paths in setNotebook and fetchNotebookAndMapping. + val user = sessionUser(writerUid) + resource.setNotebook("not json", user).getStatus shouldBe 500 + resource.fetchNotebookAndMapping("not json", user).getStatus shouldBe 500 + } + + it should "upload the notebook and return success when Jupyter accepts it" in { + withFakeJupyter(contentsStatus = 201) { + val body = """{"notebookName": "notebook.ipynb", "notebookData": {"cells": []}}""" + val resp = resource.setNotebook(body, sessionUser(writerUid)) + resp.getStatus shouldBe Response.Status.OK.getStatusCode + resp.getEntity.toString should include("success") + } + } + + it should "return 500 when Jupyter rejects the notebook upload" in { + withFakeJupyter(contentsStatus = 500) { + val body = """{"notebookName": "notebook.ipynb", "notebookData": {"cells": []}}""" + resource.setNotebook(body, sessionUser(writerUid)).getStatus shouldBe 500 + } + } + + it should "return the Jupyter URL and iframe URL when the server is reachable" in { + withFakeJupyter(contentsStatus = 201) { + val urlResp = resource.getJupyterURL(sessionUser(writerUid)) + urlResp.getStatus shouldBe Response.Status.OK.getStatusCode + urlResp.getEntity.toString should include("localhost:9100") + + val iframeResp = resource.getJupyterIframeURL(sessionUser(writerUid)) + iframeResp.getStatus shouldBe Response.Status.OK.getStatusCode + iframeResp.getEntity.toString should include("/notebooks/work/") + } + } + + it should "treat a 403 from Jupyter's /api as reachable" in { + // isJupyterAvailable accepts 200 OR 403 (403 = server up but auth-gated). + withFakeJupyter(contentsStatus = 201, apiStatus = 403) { + resource + .getJupyterURL(sessionUser(writerUid)) + .getStatus shouldBe Response.Status.OK.getStatusCode + } + } + + it should "treat an unexpected /api status (neither 200 nor 403) as unavailable" in { + withFakeJupyter(contentsStatus = 201, apiStatus = 500) { + resource.getJupyterURL(sessionUser(writerUid)).getStatus shouldBe 500 + } + } + + it should "treat a 200 from the contents API as a successful upload" in { + // Jupyter returns 200 when overwriting an existing notebook, 201 when creating. + withFakeJupyter(contentsStatus = 200) { + val body = """{"notebookName": "notebook.ipynb", "notebookData": {"cells": []}}""" + resource + .setNotebook(body, sessionUser(writerUid)) + .getStatus shouldBe Response.Status.OK.getStatusCode + } + } + + // -- setNotebook ------------------------------------------------------------ + + "setNotebook" should "reject a notebook name that is not a plain .ipynb filename with 400" in { + // The name is validated before any Jupyter call, so these are rejected with a + // 400 without a running Jupyter server. Covers path traversal, a wrong + // extension, and an embedded subpath. + Seq("../../etc/evil.ipynb", "notebook.txt", "work/notebook.ipynb").foreach { name => + val body = s"""{"notebookName": "$name", "notebookData": {"cells": []}}""" + withClue(s"name=$name: ") { + NotebookMigrationResource + .setNotebook(body) + .getStatus shouldBe Response.Status.BAD_REQUEST.getStatusCode + } + } + } +}
