diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f1a86ef5..c3f96bfa 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,7 +3,7 @@ Fixes https://xenitsupport.jira.com/browse/ALFREDAPI-<**YOUR TICKET ID**> - [ ] Is [CHANGELOG.md](https://github.com/xenit-eu/alfred-api/blob/master/CHANGELOG.md) extended? - [ ] Does this PR avoid breaking the API? Breaking changes include adding, changing or removing endpoints and/or JSON objects used in requests and responses. -- [ ] Does the PR comply to REST HTTP result codes policy outlined in the [user guide](https://docs.xenit.eu/alfred-api/stable-user/rest-api/index.html#rest-http-result-codes)? +- [ ] Does the PR comply to REST HTTP result codes policy outlined in the [user guide](https://docs.xenit.eu/alfred-api/user/rest-api/index.html#rest-http-result-codes)? - [ ] Is error handling done through a method annotated with `@ExceptionHandler` in the webscript classes? - [ ] Does the PR follow our [coding styleguide and other active procedures](https://xenitsupport.jira.com/wiki/spaces/XEN/pages/624558081/XeniT+Enhancement+Proposals+XEP)? - [ ] Is usage of `this.` prefix avoided? diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1492fb2c..2bb5e748 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,21 +1,16 @@ name: 'Continuous Integration' on: push: - branches: - - 'master*' - - 'release*' - pull_request: workflow_dispatch: env: ALFRESCO_NEXUS_USERNAME: ${{ secrets.ALFRESCO_NEXUS_USERNAME }} ALFRESCO_NEXUS_PASSWORD: ${{ secrets.ALFRESCO_NEXUS_PASSWORD }} - CLOUDSMITH_USER: ${{ secrets.CLOUDSMITH_USER }} - CLOUDSMITH_APIKEY: ${{ secrets.CLOUDSMITH_APIKEY }} jobs: BuildAndPublish: strategy: matrix: - alfresco_version: [ 62, 70, 71, 72, 73 ] + alfresco_version: [ 70, 71, 72, 73, 74 ] + fail-fast: false runs-on: ubuntu-latest steps: - name: Check out @@ -31,28 +26,23 @@ jobs: registry: private.docker.xenit.eu username: ${{ secrets.CLOUDSMITH_USER }} password: ${{ secrets.CLOUDSMITH_APIKEY }} + - name: Build interface # Execute before integration testing to catch errors early uses: gradle/gradle-build-action@v2.3.0 with: arguments: :apix-interface:build :apix-interface:javadoc + - name: Unit test REST API - uses: gradle/gradle-build-action@v2.3.0 + uses: gradle/gradle-build-action@v2.4.2 with: arguments: --info :apix-rest-v1:test - name: Build and test - uses: gradle/gradle-build-action@v2.3.0 + uses: gradle/gradle-build-action@v2.4.2 with: arguments: >- --info :apix-impl:apix-impl-${{ matrix.alfresco_version }}:test - :apix-integrationtests:test-${{ matrix.alfresco_version }}:integrationTest - - name: Upload test reports - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-result - path: /home/runner/work/**/build/reports - retention-days: 2 + :apix-integrationtests:alfresco:${{ matrix.alfresco_version }}:integrationTest - name: Publish Test Report uses: mikepenz/action-junit-report@v3.7.6 if: success() || failure() @@ -63,8 +53,8 @@ jobs: detailed_summary: true - name: Publish - if: ${{ startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/release') }} - uses: gradle/gradle-build-action@v2.3.0 + if: ${{ startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/release') }}" + uses: gradle/gradle-build-action@v2.4.2 env: ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MAVEN_CENTRAL_GPG_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MAVEN_CENTRAL_GPG_PASSWORD }} @@ -75,4 +65,4 @@ jobs: arguments: >- --info -PsigningKeyId=DF8285F0 :apix-interface:publish - :apix-impl:apix-impl-${{ matrix.alfresco_version }}:publish + :alfresco:${{ matrix.alfresco_version }}:publish \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a9b22ff1..f88dae43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Alfred API - Changelog +## 5.0.0 (2023-12-12) +From this version onward Dynamic Extensions is replaced by [Alfresco MVC](https://github.com/dgradecak/alfresco-mvc) +as framework to reduce maintenance efforts. + +To make this change clearer the Alfred API Maven group ID has been updated from `eu.xenit.apix` +to `eu.xenit.alfred.api`. + +This release also drops support for Alfresco 6.2 and adds support for 7.4. + +### Added +* [ALFREDAPI-519](https://xenitsupport.jira.com/browse/ALFREDAPI-519): Add support for Alfresco 7.4 + +### Changed +* [ALFREDAPI-527](https://xenitsupport.jira.com/browse/ALFREDAPI-527): +Alfresco containers use port 8080 now instead of ephemeral ports +* [ALFREDAPI-536](https://xenitsupport.jira.com/browse/ALFREDAPI-536): Reabsorb alfred-api-docs repo into this + +### Fixed +* [ALFREDAPI-520](https://xenitsupport.jira.com/browse/ALFREDAPI-520): Enforce encoding on bulk json responses to guarantee clean text +* [ALFREDAPI-531](https://xenitsupport.jira.com/browse/ALFREDAPI-531): Fix facet qname splitting for dates +* [ALFREDAPI-532](https://xenitsupport.jira.com/browse/ALFREDAPI-532): Fix :apix-interface:javadoc + +### Removed +* [ALFREDAPI-504](https://xenitsupport.jira.com/browse/ALFREDAPI-504): Drop Dynamic Extensions in favor of Alfresco MVC +* [ALFREDAPI-519](https://xenitsupport.jira.com/browse/ALFREDAPI-519): Remove support for Alfresco 6.2 + + ## 4.0.1 (2023-06-13) This release removes swaggerui_5x from alfred-api artifact and changes generation of Snapshot qualifier to comform to maven format. @@ -9,6 +36,7 @@ This release removes swaggerui_5x from alfred-api artifact and changes generatio * [ALFREDAPI-513](https://xenitsupport.jira.com/browse/ALFREDAPI-513): Remove swaggerui_5x from alfred-api artifact * [ALFREDAPI-514](https://xenitsupport.jira.com/browse/ALFREDAPI-514): Change generation of Snapshot qualifier to comform to maven format. * [ALFREDAPI-522](https://xenitsupport.jira.com/browse/ALFREDAPI-522): Change gradle repositories from artifactory.xenit to cloudsmith && artifactory.alfresco. +* [ALFREDAPI-516](https://xenitsupport.jira.com/browse/ALFREDAPI-516): Classpath cleanup ## 4.0.0 (2023-01-17) diff --git a/README.md b/README.md index 2baa44e6..82a1c748 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Alfred API [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) -[![Jenkins Build Status](https://jenkins-2.xenit.eu/buildStatus/icon?job=Xenit+Github%2Falfred-api%2Fmaster&subject=Jenkins)](https://jenkins-2.xenit.eu/job/Xenit%20Github/job/alfred-api/job/master/) -[![Maven Central](https://img.shields.io/maven-central/v/eu.xenit.apix/apix-interface.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22eu.xenit.apix%22%20AND%20a%3A%22apix-interface%22) +![CI status](https://github.com/xenit-eu/alfred-api/actions/workflows/ci.yml/badge.svg) +[![Maven Central](https://img.shields.io/maven-central/v/eu.xenit.alfred.api/apix-interface.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22eu.xenit.alfred.api%22%20AND%20a%3A%22apix-interface%22) Alfred API abstracts away past and future changes to the Alfresco, across major and minor versions, providing a stable interface to Alfresco on which client-side applications can be built. @@ -19,14 +19,7 @@ are not supported by the Alfresco Public API. ## Usage -Full documentation can be found at the [project's documentation](https://docs.xenit.eu/alfred-api/stable-user/index.html). - -## Installation - -### Pre-requisites -Alfred API requires **_Dynamic Extensions For Alfresco_**, version 2.0.1 or later. This module should be installed first. -Acquisition and installation instructions can be found [here](https://github.com/xenit-eu/dynamic-extensions-for-alfresco). - +Full documentation can be found at the [project's documentation](https://docs.xenit.eu/alfred-api/). ## Contributing @@ -40,7 +33,7 @@ Acquisition and installation instructions can be found [here](https://github.com * Add a note to the changelog with upgrade instructions * Notify all customers at the next release * When working in REST code, please comply to **REST HTTP result codes** policy outlined in the - [user guide](https://docs.xenit.eu/alfred-api/stable-user/rest-api/index.html#rest-http-result-codes). + [user guide](https://docs.xenit.eu/alfred-api/user/rest-api/index.html#rest-http-result-codes). * Prefer unit tests over integration tests to keep builds fast * Avoid `this.` prefix for consistency (unless the scope is ambiguous). * Follow our [coding styleguide and other active procedures](https://xenitsupport.jira.com/wiki/spaces/XEN/pages/624558081/XeniT+Enhancement+Proposals+XEP). @@ -49,12 +42,10 @@ Acquisition and installation instructions can be found [here](https://github.com * *apix-interface* builds the interface of Alfred API. This part is agnostic of the Alfresco version used. * *apix-rest-v1* builds the REST API of Alfred API. -* *apix-impl* builds the AMP which is the main deliverable for Alfred API. The AMP contains the JARs of -*apix-interface* and *apix-rest-v1*. - * The top directory also contains code shared over different Alfresco versions. - * *apix-impl/xx* contains all code per Alfresco version. It has a *src/java* folder - for code specific to that Alfresco version and a *src/java-shared code* for the code shared between - versions. This code is automatically symlinked from the *apix-impl* directory. +* *apix-impl* builds the Java code for each version of Alfresco. +* *alfresco* builds the AMP for each Alfresco version that is the main deliverable for Alfred API. The AMP contains + the JARs of *apix-interface* and *apix-rest-v1*. + * *alfresco/xx* contains the correct properties for each Alfresco version. * *apix-integrationtests* contains the integration tests for each Alfresco version. ### How to @@ -70,7 +61,7 @@ Where `VERSION` is e.g. `70`. #### Run integration tests ```bash -./gradlew :apix-integrationtests:test-${VERSION}:integrationTest +./gradlew :apix-integrationtests:alfresco:${VERSION}:integrationTest ``` Again, where `VERSION` is e.g. `70`. @@ -78,53 +69,20 @@ However, this starts (and afterwards stops) docker containers. This includes sta adding a startup time of several minutes. To circumvent this you also run the test on already running containers with for example: ```bash -./gradlew -x composeUp -x composeDown :apix-integrationtests:test-61:integrationTest -Pprotocol=http -Phost=localhost -Pport=8061 +./gradlew -x composeUp -x composeDown :apix-integrationtests:alfresco:74:integrationTest -Pprotocol=http -Phost=localhost -Pport=8074 ``` +If you only want to run specific tests, you can specify this on the Gradle invocation with a pattern. For example: + ```bash +./gradlew :apix-integrationtests:alfresco:74:integrationTest -x composeDown --tests C*ServiceTest + ``` #### Run integration tests under debugger 1. Debugging settings are already added by `apix-docker/${VERSION}/debug-extension.docker-compose.yml`, including a -portmapping `8000:8000`. This file does not get loaded when running in Jenkins. +portmapping `8000:8000`. This file does not get loaded when running in CI. 2. Prepare your remote debugger in IntelliJ and set breakpoints where you want in your tests (or Alfred API code). 3. Run the integration tests (see section above). 4. Wait until the container is started and healthy, then attach the debugger. Again, where `VERSION` is e.g. `70`. - -#### Deploy code changes for development -In a development scenario, it is possible to upload code changes to a running alfresco through dynamic extensions. -This requires the running alfresco to already have an older or equal version of alfred-api installed, and -the use of the jar artifact instead of the amp to do the new install. -The JAR has the format `apix-impl-{ALFRESCO-VERSION}-{APIX-VERSION}.jar` and can be found under -`apix-impl/{ALFRESCO-VERSION}/build/libs/`, where `ALFRESCO-VERSION` is one of *(62|70|71|72|73)*. -The new installation can be done either through the DE web interface, or with the following gradle task. -```bash -./gradlew :apix-impl:apix-impl-{ALFRESCO-VERSION}:installBundle -Phost={ALFRESCO-HOST} -Pport={ALFRESCO-PORT} -``` -Where `VERSION` is e.g. `70` and here `PORT` is the port mapping of the *alfresco-core* container e.g. `32774`. - -*Protip:* If you get tired of changing the port after every `docker-compose up`, you can temporarily put a -fixed port in the *docker-compose.yml* of the version you are working with. (The rationale behind using -variable ephemeral ports is that during parallel builds on Jenkins port clashes must be avoided.) - -For example for version 7.0, change in *apix-docker/70/docker-compose.yml* -the ports line from: -```yaml -services: - alfresco-core: - ports: - - ${DOCKER_IP}:8080 -``` -to: -```yaml -services: - alfresco-core: - ports: - - ${DOCKER_IP}:9070:8080 -``` -and then restart the containers with: - -```bash -./gradlew :apix-docker:docker-70:composeUp --info -``` diff --git a/_idea_runConfigurations/Integration_Tests_50.xml b/_idea_runConfigurations/Integration_Tests_50.xml deleted file mode 100644 index cd6b8a11..00000000 --- a/_idea_runConfigurations/Integration_Tests_50.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/_idea_runConfigurations/Integration_tests_51__JUnit_.xml b/_idea_runConfigurations/Integration_tests_51__JUnit_.xml deleted file mode 100644 index 9ea37389..00000000 --- a/_idea_runConfigurations/Integration_tests_51__JUnit_.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/_idea_runConfigurations/Integration_tests_51__JUnit___1_.xml b/_idea_runConfigurations/Integration_tests_51__JUnit___1_.xml deleted file mode 100644 index 42e55020..00000000 --- a/_idea_runConfigurations/Integration_tests_51__JUnit___1_.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/alfresco/70/overrides.gradle b/alfresco/70/overrides.gradle new file mode 100644 index 00000000..137528ec --- /dev/null +++ b/alfresco/70/overrides.gradle @@ -0,0 +1,5 @@ +ext { + alfresco_version = "7.0.0" + alfresco_min_version = alfresco_version.substring(0, 3) + ".0" + alfresco_max_version = alfresco_version.substring(0, 3) + ".99" +} \ No newline at end of file diff --git a/alfresco/71/overrides.gradle b/alfresco/71/overrides.gradle new file mode 100644 index 00000000..0fd91973 --- /dev/null +++ b/alfresco/71/overrides.gradle @@ -0,0 +1,5 @@ +ext { + alfresco_version = "7.1.1" + alfresco_min_version = alfresco_version.substring(0, 3) + ".0" + alfresco_max_version = alfresco_version.substring(0, 3) + ".99" +} \ No newline at end of file diff --git a/alfresco/72/overrides.gradle b/alfresco/72/overrides.gradle new file mode 100644 index 00000000..7624de02 --- /dev/null +++ b/alfresco/72/overrides.gradle @@ -0,0 +1,5 @@ +ext { + alfresco_version = "7.2.1" + alfresco_min_version = alfresco_version.substring(0, 3) + ".0" + alfresco_max_version = alfresco_version.substring(0, 3) + ".99" +} diff --git a/alfresco/73/overrides.gradle b/alfresco/73/overrides.gradle new file mode 100644 index 00000000..e48c594e --- /dev/null +++ b/alfresco/73/overrides.gradle @@ -0,0 +1,7 @@ +description = "Xenit Alfred API implementation Alfresco 7.3" + +ext { + alfresco_version = '7.3.1' + alfresco_min_version = alfresco_version.substring(0, 3) + ".0" + alfresco_max_version = alfresco_version.substring(0, 3) + ".99" +} \ No newline at end of file diff --git a/alfresco/74/overrides.gradle b/alfresco/74/overrides.gradle new file mode 100644 index 00000000..725d90ca --- /dev/null +++ b/alfresco/74/overrides.gradle @@ -0,0 +1,8 @@ +description = "Xenit Alfred API implementation Alfresco 7.4" + +ext { + alfresco_version = '7.4.0' + alfresco_min_version = alfresco_version.substring(0, 3) + ".0" + //Not setting alfresco_max_version here to make it easier to test on next Alfresco version (only for latest version) + alfresco_max_version = null // explicit null to overwrite properties from other projects +} \ No newline at end of file diff --git a/alfresco/build.gradle b/alfresco/build.gradle new file mode 100644 index 00000000..44ed8457 --- /dev/null +++ b/alfresco/build.gradle @@ -0,0 +1,69 @@ +subprojects { + apply from: "${project.projectDir}/overrides.gradle" + apply from: "${rootProject.projectDir}/publish.gradle" + apply plugin: 'eu.xenit.amp' + + configurations { + ampArtifact + } + + artifacts { + ampArtifact amp + } + + sourceSets { + main { + amp { + module { + it.put("module.id", "alfred-api-${project.name}") + it.put("module.title", "alfred-api-${project.name}") + it.put("module.description", + "Xenit Alfred API implementation Alfresco ${alfresco_version.substring(0, 3)}") + it.put("module.version", project.version) + it.put("module.repo.version.min", project.alfresco_min_version) + if(project.hasProperty("alfresco_max_version") + && project.alfresco_max_version != null) { + it.put("module.repo.version.max", project.alfresco_max_version) + } + } + } + } + } + + dependencies { + implementation(project(":apix-impl")) + implementation(project(":apix-impl:apix-impl-${project.name}")) + implementation(project(":apix-rest-v1")) + } + + publishing { + publications { + maven(MavenPublication) { + artifactId "alfred-api-${project.name}" + artifact tasks.amp + } + } + } + + project.tasks.jar.enabled = false + + // Extend amp plugin task: + // We want to add 'alfresco-global.properties' etc to the module-specific folder in the AMP + // (e.g. 'config/module/alfresco/module/apix-impl-61/') without hard-coding project name for each Alfresco version. + amp { + archiveBaseName = "alfred-api-${project.name}" + into("config/alfresco/module/${project.name}") { + from("${rootProject.projectDir}/src/config/alfresco-global.properties") + from("${rootProject.projectDir}/src/config/log4j.properties") + from("${rootProject.projectDir}/src/config/module-context.xml") + expand(moduleId: project.name) + } + into("config/alfresco/module/${project.name}/messages") { + from("${rootProject.projectDir}/src/config/messages/") + } + } + + afterEvaluate { + signMavenPublication.dependsOn amp + } +} diff --git a/apix-docker/62/build.gradle b/apix-docker/62/build.gradle deleted file mode 100644 index bdc41d08..00000000 --- a/apix-docker/62/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -dependencies { - baseAlfrescoWar platform("org.alfresco:acs-packaging:6.2.2.19") - baseAlfrescoWar 'org.alfresco:content-services:6.2.2.19@war' - // Fix for https://github.com/xenit-eu/dynamic-extensions-for-alfresco#alfresco-61---wrong-version-of-commons-annotations-used - alfrescoAmp(group: 'eu.xenit.alfresco', name: 'alfresco-hotfix-MNT-20557', version: '1.0.2', ext: 'amp') -} - -dockerAlfresco { - baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:6.2.2.19' -} \ No newline at end of file diff --git a/apix-docker/62/docker-compose.yml b/apix-docker/62/docker-compose.yml deleted file mode 100644 index dcc44fa3..00000000 --- a/apix-docker/62/docker-compose.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: '2' - -services: - alfresco-core: - image: ${DOCKER_IMAGE} - ports: - - ${DOCKER_IP}::8080 - volumes: - - alfresco:/opt/alfresco/alf_data - restart: unless-stopped - environment: - - SOLR_HOST=solr - - TERM=xterm - - solr: - image: private.docker.xenit.eu/alfresco-enterprise/alfresco-solr6:2.0.3 - restart: unless-stopped - environment: - - ALFRESCO_HOST=alfresco-core - - postgresql: - image: docker.io/xenit/postgres - volumes: - - postgres:/var/lib/postgresql/data - environment: - - POSTGRES_USER=alfresco - - POSTGRES_PASSWORD=admin - - POSTGRES_DB=alfresco - restart: unless-stopped - - jodconverter: - image: hub.xenit.eu/public/jodconverter-ws - restart: unless-stopped - -volumes: - alfresco: - solr: - postgres: \ No newline at end of file diff --git a/apix-docker/70/build.gradle b/apix-docker/70/build.gradle index a185d327..2a95edd4 100644 --- a/apix-docker/70/build.gradle +++ b/apix-docker/70/build.gradle @@ -1,8 +1,8 @@ dependencies { - baseAlfrescoWar platform("org.alfresco:acs-packaging:7.0.1.3") + baseAlfrescoWar platform("org.alfresco:acs-packaging:7.0.1.9") baseAlfrescoWar 'org.alfresco:content-services@war' } dockerAlfresco { - baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.0.1.3' + baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.0.1.9' } diff --git a/apix-docker/70/docker-compose.yml b/apix-docker/70/docker-compose.yml index 1c7a4894..af551880 100644 --- a/apix-docker/70/docker-compose.yml +++ b/apix-docker/70/docker-compose.yml @@ -4,12 +4,11 @@ services: alfresco-core: image: ${DOCKER_IMAGE} ports: - - "${DOCKER_IP}::8080" + - "${DOCKER_IP}:8080:8080" volumes: - alfresco:/opt/alfresco/alf_data restart: unless-stopped environment: - - SOLR_HOST=solr - TERM=xterm - GLOBAL_messaging.broker.url=failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true - GLOBAL_localTransform.core-aio.url=http://transform-core-aio:8090/ diff --git a/apix-docker/71/build.gradle b/apix-docker/71/build.gradle index 10529b9f..706ff2a4 100644 --- a/apix-docker/71/build.gradle +++ b/apix-docker/71/build.gradle @@ -1,8 +1,8 @@ dependencies { - baseAlfrescoWar platform("org.alfresco:acs-packaging:7.1.0") + baseAlfrescoWar platform("org.alfresco:acs-packaging:7.1.0.6") baseAlfrescoWar 'org.alfresco:content-services@war' } dockerAlfresco { - baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.1' + baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.1.0.6' } diff --git a/apix-docker/71/docker-compose.yml b/apix-docker/71/docker-compose.yml index 1c7a4894..af551880 100644 --- a/apix-docker/71/docker-compose.yml +++ b/apix-docker/71/docker-compose.yml @@ -4,12 +4,11 @@ services: alfresco-core: image: ${DOCKER_IMAGE} ports: - - "${DOCKER_IP}::8080" + - "${DOCKER_IP}:8080:8080" volumes: - alfresco:/opt/alfresco/alf_data restart: unless-stopped environment: - - SOLR_HOST=solr - TERM=xterm - GLOBAL_messaging.broker.url=failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true - GLOBAL_localTransform.core-aio.url=http://transform-core-aio:8090/ diff --git a/apix-docker/72/build.gradle b/apix-docker/72/build.gradle index a84be615..aa29f799 100644 --- a/apix-docker/72/build.gradle +++ b/apix-docker/72/build.gradle @@ -1,8 +1,8 @@ dependencies { - baseAlfrescoWar platform("org.alfresco:acs-packaging:7.2.0") + baseAlfrescoWar platform("org.alfresco:acs-packaging:7.2.1.13") baseAlfrescoWar 'org.alfresco:content-services@war' } dockerAlfresco { - baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.2' + baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.2.1.13' } diff --git a/apix-docker/72/docker-compose.yml b/apix-docker/72/docker-compose.yml index 1c7a4894..af551880 100644 --- a/apix-docker/72/docker-compose.yml +++ b/apix-docker/72/docker-compose.yml @@ -4,12 +4,11 @@ services: alfresco-core: image: ${DOCKER_IMAGE} ports: - - "${DOCKER_IP}::8080" + - "${DOCKER_IP}:8080:8080" volumes: - alfresco:/opt/alfresco/alf_data restart: unless-stopped environment: - - SOLR_HOST=solr - TERM=xterm - GLOBAL_messaging.broker.url=failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true - GLOBAL_localTransform.core-aio.url=http://transform-core-aio:8090/ diff --git a/apix-docker/73/build.gradle b/apix-docker/73/build.gradle index 78de5f43..6cd5164d 100644 --- a/apix-docker/73/build.gradle +++ b/apix-docker/73/build.gradle @@ -1,8 +1,8 @@ dependencies { - baseAlfrescoWar platform("org.alfresco:acs-packaging:7.3.0.1") + baseAlfrescoWar platform("org.alfresco:acs-packaging:7.3.1.2") baseAlfrescoWar 'org.alfresco:content-services@war' } dockerAlfresco { - baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.3.0.1' + baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.3.1.2' } diff --git a/apix-docker/73/docker-compose.yml b/apix-docker/73/docker-compose.yml index 1c7a4894..1260a213 100644 --- a/apix-docker/73/docker-compose.yml +++ b/apix-docker/73/docker-compose.yml @@ -4,7 +4,7 @@ services: alfresco-core: image: ${DOCKER_IMAGE} ports: - - "${DOCKER_IP}::8080" + - "${DOCKER_IP}:8080:8080" volumes: - alfresco:/opt/alfresco/alf_data restart: unless-stopped diff --git a/apix-docker/74/build.gradle b/apix-docker/74/build.gradle new file mode 100644 index 00000000..c8b8ddcb --- /dev/null +++ b/apix-docker/74/build.gradle @@ -0,0 +1,8 @@ +dependencies { + baseAlfrescoWar platform("org.alfresco:acs-packaging:7.4.1.3") + baseAlfrescoWar 'org.alfresco:content-services@war' +} + +dockerAlfresco { + baseImage = 'private.docker.xenit.eu/alfresco-enterprise/alfresco-repository-enterprise:7.4.1.3' +} diff --git a/apix-docker/62/debug-extension.docker-compose.yml b/apix-docker/74/debug-extension.docker-compose.yml similarity index 71% rename from apix-docker/62/debug-extension.docker-compose.yml rename to apix-docker/74/debug-extension.docker-compose.yml index d9b4673a..825ba2ab 100644 --- a/apix-docker/62/debug-extension.docker-compose.yml +++ b/apix-docker/74/debug-extension.docker-compose.yml @@ -7,10 +7,10 @@ services: - DEBUG=true - SHARE_HOST=alfresco-share alfresco-share: - image: hub.xenit.eu/public/alfresco-share-community:6.1 + image: docker.io/xenit/alfresco-share-community:7.4 ports: - 8090:8080 environment: - DEBUG=true - ALFRESCO_HOST=alfresco-core - - ALFRESCO_INTERNAL_HOST=alfresco-core + - ALFRESCO_INTERNAL_HOST=alfresco-core \ No newline at end of file diff --git a/apix-docker/74/docker-compose.yml b/apix-docker/74/docker-compose.yml new file mode 100644 index 00000000..8e88c690 --- /dev/null +++ b/apix-docker/74/docker-compose.yml @@ -0,0 +1,48 @@ +version: '2' + +services: + alfresco-core: + image: ${DOCKER_IMAGE} + ports: + - "${DOCKER_IP}:8080:8080" + volumes: + - alfresco:/opt/alfresco/alf_data + restart: unless-stopped + environment: + - SOLR_HOST=solr + - TERM=xterm + - GLOBAL_messaging.broker.url=failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true + - GLOBAL_localTransform.core-aio.url=http://transform-core-aio:8090/ + + solr: + image: private.docker.xenit.eu/alfresco-enterprise/alfresco-solr6:2.0.8.2 + volumes: + - solr:/opt/alfresco/alf_data + restart: unless-stopped + environment: + - ALFRESCO_HOST=alfresco-core + + postgresql: + image: docker.io/xenit/postgres + volumes: + - postgres:/var/lib/postgresql/data + environment: + - POSTGRES_USER=alfresco + - POSTGRES_PASSWORD=admin + - POSTGRES_DB=alfresco + restart: unless-stopped + + activemq: + image: alfresco/alfresco-activemq:5.16.1 + mem_limit: 1g + + transform-core-aio: + image: alfresco/alfresco-transform-core-aio:2.5.0 + environment: + JAVA_OPTS: " -Xms256m -Xmx512m" + ACTIVEMQ_URL: "nio://activemq:61616" + +volumes: + alfresco: + solr: + postgres: diff --git a/apix-docker/build.gradle b/apix-docker/build.gradle index bcf85b0f..79c91f88 100644 --- a/apix-docker/build.gradle +++ b/apix-docker/build.gradle @@ -1,9 +1,3 @@ -plugins { - id 'eu.xenit.docker-alfresco' version '5.3.1' apply false - id 'eu.xenit.docker-compose' version '5.3.1' apply false - id 'be.vbgn.ci-detect' version '0.5.0' apply false -} - subprojects { apply plugin: 'eu.xenit.docker-alfresco' apply plugin: 'eu.xenit.docker-compose' @@ -18,14 +12,17 @@ subprojects { def subproject_alfresco_version = project.name.substring(project.name.length() - 2) dependencies { - alfrescoAmp project(path: ":apix-impl:apix-impl-${subproject_alfresco_version}", configuration: 'ampArtifact') - alfrescoAmp project(path: ':apix-integrationtests-model-amp', configuration: 'ampArchives') + alfrescoAmp project(path: ":alfresco:${subproject_alfresco_version}", configuration: 'ampArtifact') + alfrescoAmp project(path: ':apix-integrationtests:model-amp', configuration: 'ampArchives') + alfrescoSM "com.gradecak.alfresco-mvc:alfresco-mvc-rest:${mvc}" + alfrescoSM "com.gradecak.alfresco-mvc:alfresco-mvc-aop:${mvc}" + alfrescoSM files(jar) alfrescoAmp "eu.xenit:alfresco-dynamic-extensions-repo-${subproject_alfresco_version}:${de_version}@amp" } dockerCompose { useComposeFiles = ['docker-compose.yml'] - // Don't use the dev compose file on Jenkins + // Don't use the dev compose file during CI builds if (!ci.isCi()) { // Allow the dev compose file to be deleted def extraComposeFile = 'debug-extension.docker-compose.yml' @@ -34,5 +31,4 @@ subprojects { } } } - } diff --git a/apix-impl/62/overrides.gradle b/apix-impl/62/overrides.gradle deleted file mode 100644 index 0042acd8..00000000 --- a/apix-impl/62/overrides.gradle +++ /dev/null @@ -1,9 +0,0 @@ -description = "Xenit API-X implementation Alfresco 6.2" - -ext { - alfresco_version = alfresco_62_version - alfresco_repo_version = '7.199.0' - alfresco_dm_version = alfresco_62_dm_version - alfresco_min_version = "6.2" - alfresco_max_version = "6.2.99" -} diff --git a/apix-impl/62/src/main/config/module.properties b/apix-impl/62/src/main/config/module.properties deleted file mode 100644 index c38166f0..00000000 --- a/apix-impl/62/src/main/config/module.properties +++ /dev/null @@ -1,7 +0,0 @@ -module.id=${project.name} -module.title=${project.name} -module.description=${project.description} -module.version=${project.version} - -module.repo.version.min=${project.alfresco_min_version} -module.repo.version.max=${project.alfresco_max_version} diff --git a/apix-impl/70/.gitkeep b/apix-impl/70/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-impl/70/overrides.gradle b/apix-impl/70/overrides.gradle deleted file mode 100644 index 762229b5..00000000 --- a/apix-impl/70/overrides.gradle +++ /dev/null @@ -1,9 +0,0 @@ -description = "Xenit API-X implementation Alfresco 7.0" - -ext { - alfresco_version = alfresco_70_version - alfresco_repo_version = '8.424' - alfresco_dm_version = alfresco_70_dm_version - alfresco_min_version = "7.0" - alfresco_max_version = "7.0.99" -} \ No newline at end of file diff --git a/apix-impl/70/src/main/config/module.properties b/apix-impl/70/src/main/config/module.properties deleted file mode 100644 index c38166f0..00000000 --- a/apix-impl/70/src/main/config/module.properties +++ /dev/null @@ -1,7 +0,0 @@ -module.id=${project.name} -module.title=${project.name} -module.description=${project.description} -module.version=${project.version} - -module.repo.version.min=${project.alfresco_min_version} -module.repo.version.max=${project.alfresco_max_version} diff --git a/apix-impl/71/.gitkeep b/apix-impl/71/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-impl/71/overrides.gradle b/apix-impl/71/overrides.gradle deleted file mode 100644 index 6da46b6a..00000000 --- a/apix-impl/71/overrides.gradle +++ /dev/null @@ -1,9 +0,0 @@ -description = "Xenit API-X implementation Alfresco 7.1" - -ext { - alfresco_version = alfresco_71_version - alfresco_repo_version = '12.23' - alfresco_dm_version = alfresco_71_dm_version - alfresco_min_version = "7.1" - alfresco_max_version = "7.1.99" -} \ No newline at end of file diff --git a/apix-impl/71/src/main/config/module.properties b/apix-impl/71/src/main/config/module.properties deleted file mode 100644 index c38166f0..00000000 --- a/apix-impl/71/src/main/config/module.properties +++ /dev/null @@ -1,7 +0,0 @@ -module.id=${project.name} -module.title=${project.name} -module.description=${project.description} -module.version=${project.version} - -module.repo.version.min=${project.alfresco_min_version} -module.repo.version.max=${project.alfresco_max_version} diff --git a/apix-impl/72/.gitkeep b/apix-impl/72/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-impl/72/overrides.gradle b/apix-impl/72/overrides.gradle deleted file mode 100644 index d4fce3cf..00000000 --- a/apix-impl/72/overrides.gradle +++ /dev/null @@ -1,9 +0,0 @@ -description = "Xenit API-X implementation Alfresco 7.2" - -ext { - alfresco_version = alfresco_72_version - alfresco_repo_version = '14.142' - alfresco_dm_version = alfresco_72_dm_version - alfresco_min_version = "7.2" - alfresco_max_version = "7.2.99" -} \ No newline at end of file diff --git a/apix-impl/72/src/main/config/module.properties b/apix-impl/72/src/main/config/module.properties deleted file mode 100644 index c38166f0..00000000 --- a/apix-impl/72/src/main/config/module.properties +++ /dev/null @@ -1,7 +0,0 @@ -module.id=${project.name} -module.title=${project.name} -module.description=${project.description} -module.version=${project.version} - -module.repo.version.min=${project.alfresco_min_version} -module.repo.version.max=${project.alfresco_max_version} diff --git a/apix-impl/73/.gitkeep b/apix-impl/73/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-impl/73/overrides.gradle b/apix-impl/73/overrides.gradle deleted file mode 100644 index c0d84b19..00000000 --- a/apix-impl/73/overrides.gradle +++ /dev/null @@ -1,9 +0,0 @@ -description = "Xenit API-X implementation Alfresco 7.3" - -ext { - alfresco_version = alfresco_73_version - alfresco_repo_version = '17.175' - alfresco_dm_version = alfresco_73_dm_version - alfresco_min_version = "7.3" - //Not setting alfresco_max_version here to make it easier to test on next Alfresco version (only for latest version) -} \ No newline at end of file diff --git a/apix-impl/73/src/main/config/module.properties b/apix-impl/73/src/main/config/module.properties deleted file mode 100644 index 18c2abc5..00000000 --- a/apix-impl/73/src/main/config/module.properties +++ /dev/null @@ -1,6 +0,0 @@ -module.id=${project.name} -module.title=${project.name} -module.description=${project.description} -module.version=${project.version} - -module.repo.version.min=${project.alfresco_min_version} diff --git a/apix-impl/74/.gitkeep b/apix-impl/74/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-impl/build.gradle b/apix-impl/build.gradle index c9e839b0..565b7abe 100644 --- a/apix-impl/build.gradle +++ b/apix-impl/build.gradle @@ -1,109 +1,55 @@ -plugins { - id 'eu.xenit.de' version '2.1.3' apply false - id 'eu.xenit.amp' version '1.1.0' apply false - id 'eu.xenit.alfresco' version '1.1.0' apply false -} - -// Give IntelliJ a default source of non-shared classes -ext { - alfresco_version = alfresco_62_version - alfresco_repo_version = '7.199.0' - alfresco_dm_version = alfresco_62_dm_version - alfresco_min_version = '6.2' -} - sourceSets.main.java.srcDirs += 'src/main/java' - subprojects { - apply from: "${project.projectDir}/overrides.gradle" - apply from: "${rootProject.projectDir}/publish.gradle" - apply plugin: 'eu.xenit.amp' - - configurations { - ampArtifact - dynamicExtensionsBundles - } + def shortAlfrescoVersion = project.name.split("-")[2] + apply from: "${rootProject.projectDir}/alfresco/${shortAlfrescoVersion}/overrides.gradle" + apply plugin: 'java-library' dependencies { - compile(project(":apix-impl")) - } - - publishing { - publications { - maven(MavenPublication) { - artifactId "${project.name}-amp" - artifact tasks.amp - } - } - } - - artifacts { - ampArtifact amp + api(project(":apix-impl")) } sourceSets { main { java { - srcDirs = ['src/main/java', '../src/main/java'] - } - amp { - dynamicExtension() - module("src/main/config/module.properties") + srcDirs = ["${project.parent.projectDir}/src/main/java"] } } test { java { - srcDirs = ['../src/test/java'] + srcDirs = ["${project.parent.projectDir}/src/test/java"] } - testResultsDirName = "${buildDir}/test-results/test" + testResultsDirName = "${project.parent.buildDir}/test-results/test" } } - - // Extend amp plugin task: - // We want to add 'alfresco-global.properties' etc to the module-specific folder in the AMP - // (e.g. 'config/module/alfresco/module/apix-impl-51/') without hard-coding project name for each Alfresco version. - amp { - deBundles = configurations.dynamicExtensionsBundles + files(jar) - into("config/alfresco/module/${project.name}") { - from("${project.parent.projectDir}/config/alfresco-global.properties") - from("${project.parent.projectDir}/config/log4j.properties") - from("${project.parent.projectDir}/config/module-context.xml") - expand(moduleId: project.name) - } - into("config/alfresco/module/${project.name}/messages") { - from("${project.parent.projectDir}/config/messages/") - } - } - - afterEvaluate { - signMavenPublication.dependsOn amp - } } allprojects { + // Subproject of supported Alfresco versions + def subproject_alfresco_version = project.projectDir.name == "apix-impl" + ? "70" : project.projectDir.name + apply from: "$rootProject.projectDir/alfresco/${subproject_alfresco_version}/overrides.gradle" // allprojects also applies to shared code (under apix-impl/src) that needs to work in IntelliJ - apply plugin: 'eu.xenit.de' apply plugin: 'eu.xenit.alfresco' + apply plugin: 'java-library' dependencies { - compile(project(":apix-interface")) - compile(project(":apix-rest-v1")) { - exclude group: 'org.alfresco' + api(project(":apix-interface")) { + exclude group: 'com.fasterxml.jackson.core', module: 'jackson-annotations' + exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind' } - alfrescoProvided group: 'org.alfresco', name: 'alfresco-repository', version: alfresco_version - alfrescoProvided group: 'org.alfresco', name: 'alfresco-remote-api', version: alfresco_version - - compile group: 'org.yaml', name: 'snakeyaml', version: '1.15' - compile group: 'javax.validation', name: 'validation-api', version: '1.1.0.Final' - - testCompile group: 'eu.xenit.de', name: 'annotations', version: de_version - testCompile group: 'org.alfresco', name: 'alfresco-repository', version: alfresco_repo_version - testCompile group: 'org.alfresco', name: 'alfresco-data-model', version: alfresco_dm_version - testCompile group: 'org.alfresco', name: 'alfresco-remote-api', version: alfresco_version - - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.25.1' - testCompile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.2' - testCompile group: 'eu.xenit.testing', name: 'integration-testing', version: '1.1.0' + implementation group: 'commons-lang', name: 'commons-lang', version: '1.0' + implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final' + implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.16.0' + + alfrescoProvided platform("org.alfresco:acs-community-packaging:${alfresco_version}") + alfrescoProvided "org.alfresco:alfresco-repository" + alfrescoProvided 'org.alfresco:alfresco-remote-api' + + testImplementation platform("org.alfresco:acs-community-packaging:${alfresco_version}") + testImplementation 'org.alfresco:alfresco-repository' + testImplementation 'org.alfresco:alfresco-remote-api' + testImplementation 'org.alfresco:alfresco-data-model' + testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.25.1' } task generateVersionFile(type: Task) { @@ -114,7 +60,7 @@ allprojects { /* * WARNING: THIS FILE IS AUTO-GENERATED BY GRADLE DURING BUILD. * ANY CHANGES TO THIS FILE WILL BE LOST AFTER NEW BUILD. - * SEE task generateVersionFile IN api-x_dynamicextensions/build.gradle + * SEE Gradle task generateVersionFile */ public class Version { public static final String Number = "${project.versionWithoutQualifier}"; @@ -131,40 +77,4 @@ public class Version { // Show test results when running from the CLI testLogging { events "PASSED", "FAILED", "SKIPPED" } } - - jar { - // This BundleSymbolicName is also used in - // apix-impl/src/integration-test/java/eu/xenit/apix/tests/ApixImplBundleFilter.java - // to find which integration tests need to run, so when changing it, change it there too. - bnd( - 'Export-Package': 'eu.xenit.apix.*; -split-package:=merge-first', - 'Include-Resource': includeResource(configurations.compile), - 'Bundle-ClassPath': bundleClassPath(configurations.compile), - 'Bundle-SymbolicName': "${project.group}.${project.name}", - 'Bundle-Description': 'Alfred API: Java API and REST API', - 'Import-Package': 'org.springframework.beans.factory,' - + 'org.springframework.cglib.core,' - + 'org.springframework.cglib.proxy,' - // Unintuitively, the notation 'version="1.0"' means any version >= 1.0 - + 'org.json;version="1.0",' - // Alfresco >=7.3 uses a SLF4J version >2.0, so we need to override the default upper boundary of 2.0. - + 'org.slf4j.*;version="1.7",' - + '!com.google.appengine.api,' - + '!com.google.apphosting.api,' - + '!org.joda.convert,' - + '*' - ) - } - - alfrescoDynamicExtensions { - versions.dynamicExtensions = de_version - // Configure endpoint for installBundle task - repository { - endpoint { - protocol = project.hasProperty('protocol') ? project.protocol : 'http' - host = project.hasProperty('host') ? project.host : 'localhost' - port = project.hasProperty('port') ? project.port : 8080 - } - } - } } diff --git a/apix-impl/src/main/config/module.properties b/apix-impl/src/main/config/module.properties deleted file mode 100644 index 7539abd7..00000000 --- a/apix-impl/src/main/config/module.properties +++ /dev/null @@ -1 +0,0 @@ -# Unused. Only exists to simplify apix-impl/build.gradle \ No newline at end of file diff --git a/apix-impl/src/main/java/eu/xenit/apix/SpringConfiguration.java b/apix-impl/src/main/java/eu/xenit/apix/SpringConfiguration.java new file mode 100644 index 00000000..2cdac337 --- /dev/null +++ b/apix-impl/src/main/java/eu/xenit/apix/SpringConfiguration.java @@ -0,0 +1,12 @@ +package eu.xenit.apix; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan(basePackageClasses = { + Version.class, +}) +public class SpringConfiguration { + +} diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/ApixToAlfrescoConversion.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/ApixToAlfrescoConversion.java index 68623640..4c89eb28 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/ApixToAlfrescoConversion.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/ApixToAlfrescoConversion.java @@ -1,7 +1,6 @@ package eu.xenit.apix.alfresco; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -21,7 +20,6 @@ @Component("eu.xenit.apix.alfresco.ApixToAlfrescoConversion") -@OsgiService public class ApixToAlfrescoConversion { private static final Logger logger = LoggerFactory.getLogger(ApixToAlfrescoConversion.class); diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/WIP/WIPServiceImpl.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/WIP/WIPServiceImpl.java index 3bc5a820..40ac8835 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/WIP/WIPServiceImpl.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/WIP/WIPServiceImpl.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.WIP; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.WIP.IWIPService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import org.alfresco.service.ServiceRegistry; @@ -13,7 +12,6 @@ /** * Created by jasper on 29/09/17. */ -@OsgiService @Service("eu.xenit.apix.WIP.WIPService") public class WIPServiceImpl implements IWIPService { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/categories/CategoryService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/categories/CategoryService.java index f1a78259..5dca7f62 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/categories/CategoryService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/categories/CategoryService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.categories; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import com.google.common.collect.Iterables; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.categories.Category; @@ -19,10 +18,9 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@OsgiService -@Component("eu.xenit.apix.categories.ICategoryService") +@Service("eu.xenit.apix.categories.ICategoryService") public class CategoryService implements ICategoryService { @Autowired diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/comments/CommentService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/comments/CommentService.java index 2f5947b5..67fe70b9 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/comments/CommentService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/comments/CommentService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.comments; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.comments.Comment; import eu.xenit.apix.comments.Conversation; @@ -26,7 +25,6 @@ import org.springframework.stereotype.Service; @Service("eu.xenit.apix.comments.CommentService") -@OsgiService public class CommentService implements ICommentService { private final Logger logger = LoggerFactory.getLogger(CommentService.class); @@ -114,23 +112,23 @@ protected Comment toComment(org.alfresco.service.cmr.repository.NodeRef document Comment response = new Comment(); response.setId(apixCommentNodeRef); response.setContent(content); - List property = commentMetadata.properties.get(new QName(ContentModel.PROP_TITLE.toString())); + List property = commentMetadata.getProperties().get(new QName(ContentModel.PROP_TITLE.toString())); if (property != null && !property.isEmpty()) { response.setTitle(property.get(0)); } - property = commentMetadata.properties.get(new QName(ContentModel.PROP_CREATED.toString())); + property = commentMetadata.getProperties().get(new QName(ContentModel.PROP_CREATED.toString())); if (property != null && !property.isEmpty()) { response.setCreatedAt(property.get(0)); } - property = commentMetadata.properties.get(new QName(ContentModel.PROP_CREATOR.toString())); + property = commentMetadata.getProperties().get(new QName(ContentModel.PROP_CREATOR.toString())); if (property != null && !property.isEmpty()) { response.setCreatedBy(property.get(0)); } - property = commentMetadata.properties.get(new QName(ContentModel.PROP_MODIFIED.toString())); + property = commentMetadata.getProperties().get(new QName(ContentModel.PROP_MODIFIED.toString())); if (property != null && !property.isEmpty()) { response.setModifiedAt(property.get(0)); } - property = commentMetadata.properties.get(new QName(ContentModel.PROP_MODIFIER.toString())); + property = commentMetadata.getProperties().get(new QName(ContentModel.PROP_MODIFIER.toString())); if (property != null && !property.isEmpty()) { response.setModifiedBy(property.get(0)); } diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/configuration/ConfigurationServiceImpl.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/configuration/ConfigurationServiceImpl.java index 736a90cb..1f668d05 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/configuration/ConfigurationServiceImpl.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/configuration/ConfigurationServiceImpl.java @@ -1,10 +1,11 @@ package eu.xenit.apix.alfresco.configuration; +import static org.alfresco.model.ContentModel.PROP_NAME; +import static org.alfresco.model.ContentModel.TYPE_FOLDER; + import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import eu.xenit.apix.configuration.ConfigurationFile; import eu.xenit.apix.configuration.ConfigurationFileFlags; import eu.xenit.apix.configuration.ConfigurationService; @@ -17,34 +18,25 @@ import eu.xenit.apix.node.ChildParentAssociation; import eu.xenit.apix.node.INodeService; import eu.xenit.apix.node.NodeMetadata; -import eu.xenit.apix.rest.v1.configuration.ConfigurationWebscript1; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.SafeConstructor; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; -import static org.alfresco.model.ContentModel.PROP_NAME; -import static org.alfresco.model.ContentModel.TYPE_FOLDER; - -@OsgiService -@Component("eu.xenit.apix.configuration.ConfigurationService") +@Service("eu.xenit.apix.configuration.ConfigurationService") public class ConfigurationServiceImpl implements ConfigurationService { private static final String QNAME_FOLDER = TYPE_FOLDER.toString(); private static final QName QNAME_NAME = new QName(PROP_NAME.toString()); - Logger logger = LoggerFactory.getLogger(ConfigurationWebscript1.class); + private static final Logger logger = LoggerFactory.getLogger(ConfigurationServiceImpl.class); @Autowired IFileFolderService fileFolderService; @@ -90,7 +82,6 @@ public Configurations getConfigurationFiles(String searchDirectory, String nameF logger.debug("Found {} configuration files: {}", configurationFiles.size(), configurationFiles); - Yaml yamlMapper = new Yaml(new SafeConstructor()); ObjectMapper jsonMapper = new ObjectMapper(new JsonFactory()); for (ConfigurationFile configurationFile : configurationFiles) { @@ -117,11 +108,13 @@ public Configurations getConfigurationFiles(String searchDirectory, String nameF ContentInputStream configStream = contentService.getContent(configurationFile.getNodeRef()); if (configStream != null) { String mimetype = configStream.getMimetype(); - String name = configurationFile.getMetadata().properties.get(QNAME_NAME).get(0); + String name = configurationFile.getMetadata().getProperties().get(QNAME_NAME).get(0); logger.debug("Mimetype is {}; filename is {}", mimetype, name); Object parsedContent = null; if (mimetype.equals("text/x-yaml") || name.endsWith(".yaml") || name.endsWith(".yml")) { - parsedContent = yamlMapper.loadAs(configStream.getInputStream(), Object.class); + ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + yamlMapper.findAndRegisterModules(); + parsedContent = yamlMapper.readValue(configStream.getInputStream(), Object.class); } else if (mimetype.equals("application/json") || name.endsWith(".json")) { parsedContent = jsonMapper.readValue(configStream.getInputStream(), Object.class); } else { @@ -150,7 +143,7 @@ private List getChildrenRecursive(NodeRef parent, Configurati for (ChildParentAssociation childParentAssociation : childParentAssociations) { NodeRef child = childParentAssociation.getTarget(); NodeMetadata childMetadata = nodeService.getMetadata(child); - if (childMetadata.type.getValue().equals(QNAME_FOLDER)) { + if (childMetadata.getType().getValue().equals(QNAME_FOLDER)) { files.addAll(getChildrenRecursive(child, filter)); } else if (filter.isAccepted(childMetadata)) { files.add(new ConfigurationFile(child, childMetadata)); @@ -177,7 +170,7 @@ class NameFilter implements ConfigurationServiceImpl.Filter { @Override public boolean isAccepted(NodeMetadata metadata) { - String name = metadata.properties.get(QNAME_NAME).get(0); + String name = metadata.getProperties().get(QNAME_NAME).get(0); logger.debug("Checking if {} matches {}", name, filter); return filter.matcher(name).find(); } diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/content/ContentService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/content/ContentService.java index cd35fe4c..01653725 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/content/ContentService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/content/ContentService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.content; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.content.IContentService; import eu.xenit.apix.node.INodeService; import java.io.InputStream; @@ -9,7 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -@OsgiService @Component("eu.xenit.apix.content.IContentService") public class ContentService implements IContentService { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/AspectService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/AspectService.java index 45674599..0278908c 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/AspectService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/AspectService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.dictionary; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.QName; import eu.xenit.apix.dictionary.aspects.AspectDefinition; @@ -16,7 +15,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -@OsgiService @Component("eu.xenit.apix.dictionary.aspects.IAspectService") public class AspectService implements IAspectService { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/DictionaryService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/DictionaryService.java index 0fed7439..9c097de6 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/DictionaryService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/DictionaryService.java @@ -1,7 +1,6 @@ package eu.xenit.apix.alfresco.dictionary; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.dictionary.IDictionaryService; import eu.xenit.apix.dictionary.aspects.AspectDefinition; @@ -35,11 +34,10 @@ * Created by Michiel Huygen on 24/11/2015. */ -@OsgiService @Component("eu.xenit.apix.dictionary.IDictionaryService") public class DictionaryService implements IDictionaryService { - private final static Logger logger = LoggerFactory.getLogger(DictionaryService.class); + private static final Logger logger = LoggerFactory.getLogger(DictionaryService.class); @Autowired private ApixToAlfrescoConversion c; @Autowired @@ -57,8 +55,6 @@ public class DictionaryService implements IDictionaryService { @Autowired private NamespaceService namespaceService; - public DictionaryService() { - } public Namespaces getNamespaces() { Map ret = new HashMap<>(); @@ -67,7 +63,7 @@ public Namespaces getNamespaces() { if (s == null || s.length() == 0) { continue; } - ret.put(s, new Namespace(s, new ArrayList(this.namespaceService.getPrefixes(s)))); + ret.put(s, new Namespace(s, new ArrayList<>(this.namespaceService.getPrefixes(s)))); } return new Namespaces(ret); } diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/TypeService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/TypeService.java index 50951411..f4bb673e 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/TypeService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/dictionary/TypeService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.dictionary; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.QName; import eu.xenit.apix.dictionary.types.ITypeService; @@ -18,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -@OsgiService @Component("eu.xenit.apix.dictionary.types.ITypeService") public class TypeService implements ITypeService { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/filefolder/FileFolderService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/filefolder/FileFolderService.java index c116bbb5..1f1295aa 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/filefolder/FileFolderService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/filefolder/FileFolderService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.filefolder; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.StoreRef; @@ -28,7 +27,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -@OsgiService @Component("eu.xenit.apix.filefolder.IFileFolderService") public class FileFolderService implements IFileFolderService { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/metadata/NodeService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/metadata/NodeService.java index 3645c264..3dd204c5 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/metadata/NodeService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/metadata/NodeService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.metadata; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.node.ChildParentAssociation; import eu.xenit.apix.node.INodeService; @@ -59,14 +58,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; /** * Created by mhgam on 23/11/2015. */ -@OsgiService -@Component("eu.xenit.apix.alfresco.metadata.NodeService") +@Service("eu.xenit.apix.alfresco.metadata.NodeService") public class NodeService implements INodeService { private final static String NAMESPACE_BEGIN = "" + '{'; diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/people/PeopleService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/people/PeopleService.java index 02e8b0ab..73e07ca4 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/people/PeopleService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/people/PeopleService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.people; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.groups.Group; @@ -27,7 +26,6 @@ import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; -@OsgiService @Service("eu.xenit.apix.people.IPeopleService") @Primary public class PeopleService implements IPeopleService { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/permissions/PermissionService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/permissions/PermissionService.java index 49156ba3..6709c33a 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/permissions/PermissionService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/permissions/PermissionService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.permissions; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.QName; @@ -27,13 +26,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; /** * Created by kenneth on 10.03.16. */ -@Component("eu.xenit.apix.permissions.IPermissionService") -@OsgiService +@Service("eu.xenit.apix.permissions.IPermissionService") public class PermissionService implements IPermissionService { private final static Logger logger = LoggerFactory.getLogger(PermissionService.class); diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/properties/PropertyServiceImpl.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/properties/PropertyServiceImpl.java index bd2ef91e..d49e3950 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/properties/PropertyServiceImpl.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/properties/PropertyServiceImpl.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.properties; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.dictionary.properties.IPropertyService; import eu.xenit.apix.properties.Properties; @@ -21,11 +20,10 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@OsgiService -@Component("eu.xenit.apix.properties.IPropertyService") +@Service("eu.xenit.apix.properties.IPropertyService") @Primary public class PropertyServiceImpl implements IPropertyService { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchFacetsServiceImpl.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchFacetsServiceImpl.java index 6201ee3a..cc505c9e 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchFacetsServiceImpl.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchFacetsServiceImpl.java @@ -248,8 +248,8 @@ private Map> getFacetResults(SearchParameters sp // facetTokenName => @{http://www.alfresco.org/model/content/1.0}created // qName => {http://www.alfresco.org/model/content/1.0}created // 7 => {!afts} - key = key.substring(7); - String facetTokenName = key.substring(0, key.lastIndexOf(':')); + key = key.replace("{!afts}",""); + String facetTokenName = key.substring(0, key.indexOf(":[")); String qName = facetTokenToQname(facetTokenName); // Retrieve the previous facet queries @@ -260,7 +260,7 @@ private Map> getFacetResults(SearchParameters sp // Get the handler for this qName FacetLabelDisplayHandler handler = facetLabelDisplayHandlerRegistry.getDisplayHandler(facetTokenName); - String val = key.substring(key.indexOf('}') + key.substring(key.indexOf('}')).indexOf(':') + 1); + String val = key.substring( key.indexOf(":[") + 1); FacetLabel facetLabel = (handler == null) ? new FacetLabel(val, val, -1) : handler.getDisplayLabel(key); // See if we have a nice textual version of this label diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchService.java index 982c1e4b..fcc7e517 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/search/SearchService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.search; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.alfresco.dictionary.PropertyService; import eu.xenit.apix.data.QName; @@ -12,10 +11,10 @@ import eu.xenit.apix.search.SearchQuery.HighlightOptions; import eu.xenit.apix.search.SearchQueryConsistency; import eu.xenit.apix.search.SearchQueryResult; -import eu.xenit.apix.utils.java8.Optional; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.FieldHighlightParameters; @@ -32,7 +31,6 @@ import org.springframework.stereotype.Service; @Service("eu.xenit.apix.search.SearchService") -@OsgiService public class SearchService implements ISearchService { public static final int MAX_ITEMS_DEFAULT = 1000; diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/sites/SiteService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/sites/SiteService.java index e9cdbd8c..d81a3648 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/sites/SiteService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/sites/SiteService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.sites; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.sites.ISite; @@ -15,10 +14,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@OsgiService -@Component("eu.xenit.apix.alfresco.sites.SiteService") +@Service("eu.xenit.apix.alfresco.sites.SiteService") public class SiteService implements ISiteService { private final static Logger logger = LoggerFactory.getLogger(SiteService.class); diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/transaction/TransactionService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/transaction/TransactionService.java index 822a29e9..ddb6b132 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/transaction/TransactionService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/transaction/TransactionService.java @@ -1,15 +1,13 @@ package eu.xenit.apix.alfresco.transaction; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.transaction.ITransactionService; import java.util.concurrent.Callable; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@OsgiService -@Component("eu.xenit.apix.alfresco.transaction.TransactionService") +@Service("eu.xenit.apix.alfresco.transaction.TransactionService") public class TransactionService implements ITransactionService { @Autowired diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/translation/TranslationService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/translation/TranslationService.java index a2f309d6..ecf377e7 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/translation/TranslationService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/translation/TranslationService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.translation; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.dictionary.IDictionaryService; import eu.xenit.apix.translation.ITranslationService; @@ -31,11 +30,10 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.extensions.surf.util.I18NUtil; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@Component("eu.xenit.apix.translation.ITranslationService") -@OsgiService +@Service("eu.xenit.apix.translation.ITranslationService") public class TranslationService implements ITranslationService { private static final Logger logger = LoggerFactory.getLogger(TranslationService.class); diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/version/VersionService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/version/VersionService.java index 17f80264..02b02a68 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/version/VersionService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/version/VersionService.java @@ -1,20 +1,18 @@ package eu.xenit.apix.alfresco.version; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.Version; import eu.xenit.apix.version.IVersionService; import eu.xenit.apix.version.VersionDescription; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; -@OsgiService -@Component("eu.xenit.apix.version.IVersionService") +@Service("eu.xenit.apix.version.IVersionService") public class VersionService implements IVersionService { @Override public VersionDescription getVersionDescription() { VersionDescription ret = VersionDescription.createFromVersionString( Version.Number, - "XeniT Api-X java alfresco wrapper and REST interface"); + "Xenit Alfred API Alfresco wrapper and REST interface"); return ret; } } diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/versionhistory/VersionHistoryService.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/versionhistory/VersionHistoryService.java index 43ee72d7..06d23589 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/versionhistory/VersionHistoryService.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/versionhistory/VersionHistoryService.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.versionhistory; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.QName; @@ -10,7 +9,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import java.util.Collection.*; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -20,9 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -@OsgiService @Service("eu.xenit.apix.versionhistory.VersionHistoryService") - public class VersionHistoryService implements IVersionHistoryService { private ApixToAlfrescoConversion c; diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/web/WebUtils.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/web/WebUtils.java index e8f74be1..cf6d4bdb 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/web/WebUtils.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/web/WebUtils.java @@ -1,16 +1,15 @@ package eu.xenit.apix.alfresco.web; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.web.IWebUtils; import org.alfresco.repo.admin.SysAdminParams; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; /** * Provides utility functions to access information about the current web context */ -@Component("eu.xenit.apix.web.WebUtils") -@OsgiService + +@Service("eu.xenit.apix.web.WebUtils") public class WebUtils implements IWebUtils { @Autowired diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/AbstractApixAlfrescoWorkflowConvertor.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/AbstractApixAlfrescoWorkflowConvertor.java index 5fca349a..22c121da 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/AbstractApixAlfrescoWorkflowConvertor.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/AbstractApixAlfrescoWorkflowConvertor.java @@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; public abstract class AbstractApixAlfrescoWorkflowConvertor extends AbstractApixWorkflowConvertor { @@ -81,10 +82,13 @@ public abstract class AbstractApixAlfrescoWorkflowConvertor extends AbstractApix @Autowired protected ApixToAlfrescoConversion c; @Autowired + @Qualifier("WorkflowService") protected WorkflowService workflowService; @Autowired + @Qualifier("MessageService") private MessageService messageService; @Autowired + @Qualifier("NamespaceService") private NamespaceService namespaceService; protected static void setOwner(final WorkflowService ws, final String taskID, final String userName) { diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceActivitiImpl.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceActivitiImpl.java index b70143f4..824d4ba9 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceActivitiImpl.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceActivitiImpl.java @@ -1,6 +1,5 @@ package eu.xenit.apix.alfresco.workflow; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.alfresco.workflow.activiti.query.ApixHistoricInstanceQuery; import eu.xenit.apix.alfresco.workflow.utils.DebugHelper; @@ -32,8 +31,9 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; -@OsgiService +@Service public class WorkflowServiceActivitiImpl implements IWorkflowService { private final Logger logger = LoggerFactory.getLogger(WorkflowServiceActivitiImpl.class); @@ -56,6 +56,7 @@ public class WorkflowServiceActivitiImpl implements IWorkflowService { private AbstractApixQueryConverter apixWfProcQueryConverter; @Autowired + @Qualifier("WorkflowService") private WorkflowService workflowService; @PostConstruct diff --git a/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceApsImpl.java b/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceApsImpl.java index 8eacb4d2..60da6616 100644 --- a/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceApsImpl.java +++ b/apix-impl/src/main/java/eu/xenit/apix/alfresco/workflow/WorkflowServiceApsImpl.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.github.dynamicextensionsalfresco.osgi.OsgiService; import eu.xenit.apix.alfresco.ApixSpringConfiguration; import eu.xenit.apix.alfresco.workflow.aps.ApsFormDefinition; import eu.xenit.apix.alfresco.workflow.aps.ApsFormField; @@ -34,14 +33,16 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; -@OsgiService +@Service public class WorkflowServiceApsImpl implements IWorkflowService { @Autowired @@ -250,7 +251,7 @@ public Workflow startWorkflow(String definitionKeyOrId, Map()); + Map facetQueries = new HashMap<>(); + facetQueries.put("{!afts}@{http://www.alfresco.org/model/content/1.0}content.size:[0 TO 10240]", 1); + facetQueries.put("{!afts}@{http://www.alfresco.org/model/content/1.0}modified:[NOW/DAY-1YEAR TO NOW/DAY+1DAY]", 2); + facetQueries.put("{!afts}@{http://www.alfresco.org/model/content/1.0}created:[2020-08-31T07:00:00.000Z TO 2023-09-02T10:01:00.000Z]", 1); + when(resultSetMock.getFacetQueries()).thenReturn(facetQueries); searchParametersMock = mock(SearchParameters.class); List fieldFacets = new ArrayList<>(); @@ -145,6 +150,33 @@ public List initExpectedResult_for_assertThat_getFacetResults documentStatusValues.add(draftFacetValue); documentStatusResult.setValues(documentStatusValues); expectedResult.add(documentStatusResult); + FacetSearchResult contentResult = new FacetSearchResult(); + contentResult.setName("{http://www.alfresco.org/model/content/1.0}content.size"); + List contentValues = new ArrayList<>(); + FacetValue contentFacetValue = new FacetValue(); + contentFacetValue.setValue("[0 TO 10240]"); + contentFacetValue.setCount(1); + contentValues.add(contentFacetValue); + contentResult.setValues(contentValues); + expectedResult.add(contentResult); + FacetSearchResult modifiedResult = new FacetSearchResult(); + modifiedResult.setName("{http://www.alfresco.org/model/content/1.0}modified"); + List modifiedValues = new ArrayList<>(); + FacetValue modifiedFacetValue = new FacetValue(); + modifiedFacetValue.setValue("[NOW/DAY-1YEAR TO NOW/DAY+1DAY]"); + modifiedFacetValue.setCount(2); + modifiedValues.add(modifiedFacetValue); + modifiedResult.setValues(modifiedValues); + expectedResult.add(modifiedResult); + FacetSearchResult createdResult = new FacetSearchResult(); + createdResult.setName("{http://www.alfresco.org/model/content/1.0}created"); + List createdValues = new ArrayList<>(); + FacetValue createdFacetValue = new FacetValue(); + createdFacetValue.setValue("[2020-08-31T07:00:00.000Z TO 2023-09-02T10:01:00.000Z]"); + createdFacetValue.setCount(1); + createdValues.add(createdFacetValue); + createdResult.setValues(createdValues); + expectedResult.add(createdResult); return expectedResult; } diff --git a/apix-impl/src/test/java/eu/xenit/apix/tests/search/SearchServiceUnitTest.java b/apix-impl/src/test/java/eu/xenit/apix/tests/search/SearchServiceUnitTest.java index 106b5ec4..3c0dfc91 100644 --- a/apix-impl/src/test/java/eu/xenit/apix/tests/search/SearchServiceUnitTest.java +++ b/apix-impl/src/test/java/eu/xenit/apix/tests/search/SearchServiceUnitTest.java @@ -1,6 +1,6 @@ package eu.xenit.apix.tests.search; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/apix-integrationtests/62/overrides.gradle b/apix-integrationtests/62/overrides.gradle deleted file mode 100644 index 2f5c71ca..00000000 --- a/apix-integrationtests/62/overrides.gradle +++ /dev/null @@ -1,14 +0,0 @@ -ext { - subproject_alfresco_version = "62" - alfresco_version = alfresco_62_version - alfresco_dm_version = alfresco_62_dm_version - alfresco_repo_version = '7.134.1' - alfresco_remote_api_version = '7.107.1' -} - -dependencies { - integrationTestImplementationRemote(group: 'org.apache.httpcomponents', name: 'fluent-hc', version: http_version) { - exclude group: 'commons-logging' - exclude group: 'org.apache.httpcomponents', module: 'httpcore' - } -} \ No newline at end of file diff --git a/apix-integrationtests/62/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java b/apix-integrationtests/62/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java deleted file mode 100644 index b39f9643..00000000 --- a/apix-integrationtests/62/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java +++ /dev/null @@ -1,22 +0,0 @@ -package eu.xenit.apix.util; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import javax.sql.DataSource; -import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class should only be used by the integration tests. - */ -public class SolrTestHelperImpl extends SolrTestHelperBaseImpl { - private Logger logger = LoggerFactory.getLogger(SolrTestHelperImpl.class); - - public SolrTestHelperImpl(String baseUrl, DataSource dataSource, SwitchableApplicationContextFactory searchSubSystem) { - super(baseUrl, dataSource, searchSubSystem); - } -} diff --git a/apix-integrationtests/70/overrides.gradle b/apix-integrationtests/70/overrides.gradle deleted file mode 100644 index 0a41d118..00000000 --- a/apix-integrationtests/70/overrides.gradle +++ /dev/null @@ -1,14 +0,0 @@ -ext { - subproject_alfresco_version = "70" - alfresco_version = alfresco_70_version - alfresco_dm_version = alfresco_70_dm_version - alfresco_repo_version = '8.424' - alfresco_remote_api_version = '8.424' -} - -dependencies { - integrationTestImplementationRemote(group: 'org.apache.httpcomponents', name: 'fluent-hc', version: http_version) { - exclude group: 'commons-logging' - exclude group: 'org.apache.httpcomponents', module: 'httpcore' - } -} \ No newline at end of file diff --git a/apix-integrationtests/71/overrides.gradle b/apix-integrationtests/71/overrides.gradle deleted file mode 100644 index 9e6eeb47..00000000 --- a/apix-integrationtests/71/overrides.gradle +++ /dev/null @@ -1,14 +0,0 @@ -ext { - subproject_alfresco_version = "71" - alfresco_version = alfresco_71_version - alfresco_dm_version = alfresco_71_dm_version - alfresco_repo_version = '12.23' - alfresco_remote_api_version = '12.23' -} - -dependencies { - integrationTestImplementationRemote(group: 'org.apache.httpcomponents', name: 'fluent-hc', version: http_version) { - exclude group: 'commons-logging' - exclude group: 'org.apache.httpcomponents', module: 'httpcore' - } -} \ No newline at end of file diff --git a/apix-integrationtests/71/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java b/apix-integrationtests/71/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java deleted file mode 100644 index f622da0d..00000000 --- a/apix-integrationtests/71/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.xenit.apix.util; - -import javax.sql.DataSource; -import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class should only be used by the integration tests. - */ -public class SolrTestHelperImpl extends SolrTestHelperBaseImpl { - private Logger logger = LoggerFactory.getLogger(SolrTestHelperImpl.class); - - public SolrTestHelperImpl(String baseUrl, DataSource dataSource, SwitchableApplicationContextFactory searchSubSystem) { - super(baseUrl, dataSource, searchSubSystem); - } -} diff --git a/apix-integrationtests/72/overrides.gradle b/apix-integrationtests/72/overrides.gradle deleted file mode 100644 index c454125d..00000000 --- a/apix-integrationtests/72/overrides.gradle +++ /dev/null @@ -1,14 +0,0 @@ -ext { - subproject_alfresco_version = "72" - alfresco_version = alfresco_72_version - alfresco_dm_version = alfresco_72_dm_version - alfresco_repo_version = '14.142' - alfresco_remote_api_version = '14.142' -} - -dependencies { - integrationTestImplementationRemote(group: 'org.apache.httpcomponents', name: 'fluent-hc', version: http_version) { - exclude group: 'commons-logging' - exclude group: 'org.apache.httpcomponents', module: 'httpcore' - } -} \ No newline at end of file diff --git a/apix-integrationtests/72/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java b/apix-integrationtests/72/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java deleted file mode 100644 index f622da0d..00000000 --- a/apix-integrationtests/72/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.xenit.apix.util; - -import javax.sql.DataSource; -import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class should only be used by the integration tests. - */ -public class SolrTestHelperImpl extends SolrTestHelperBaseImpl { - private Logger logger = LoggerFactory.getLogger(SolrTestHelperImpl.class); - - public SolrTestHelperImpl(String baseUrl, DataSource dataSource, SwitchableApplicationContextFactory searchSubSystem) { - super(baseUrl, dataSource, searchSubSystem); - } -} diff --git a/apix-integrationtests/73/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java b/apix-integrationtests/73/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java deleted file mode 100644 index f622da0d..00000000 --- a/apix-integrationtests/73/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.xenit.apix.util; - -import javax.sql.DataSource; -import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class should only be used by the integration tests. - */ -public class SolrTestHelperImpl extends SolrTestHelperBaseImpl { - private Logger logger = LoggerFactory.getLogger(SolrTestHelperImpl.class); - - public SolrTestHelperImpl(String baseUrl, DataSource dataSource, SwitchableApplicationContextFactory searchSubSystem) { - super(baseUrl, dataSource, searchSubSystem); - } -} diff --git a/apix-integrationtests/alfresco/70/.gitkeep b/apix-integrationtests/alfresco/70/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-integrationtests/alfresco/71/.gitkeep b/apix-integrationtests/alfresco/71/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-integrationtests/alfresco/72/.gitkeep b/apix-integrationtests/alfresco/72/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-integrationtests/73/overrides.gradle b/apix-integrationtests/alfresco/73/overrides.gradle similarity index 100% rename from apix-integrationtests/73/overrides.gradle rename to apix-integrationtests/alfresco/73/overrides.gradle diff --git a/apix-integrationtests/70/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java b/apix-integrationtests/alfresco/73/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java similarity index 100% rename from apix-integrationtests/70/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java rename to apix-integrationtests/alfresco/73/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java diff --git a/apix-integrationtests/alfresco/74/.gitkeep b/apix-integrationtests/alfresco/74/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apix-integrationtests/alfresco/build.gradle b/apix-integrationtests/alfresco/build.gradle new file mode 100644 index 00000000..6d6f183b --- /dev/null +++ b/apix-integrationtests/alfresco/build.gradle @@ -0,0 +1,131 @@ +def integrationTestsProjectDir = project.projectDir +allprojects { + // Subproject of supported Alfresco versions + def subproject_alfresco_version = project.projectDir.name == "alfresco" + ? "70" // minimum supported version + : project.projectDir.name + + // Following needs to apply to shared (./src) project and version-specific (e.g. ./70) projects. + apply plugin: 'idea' + apply plugin: 'java-library' + apply plugin: 'eu.xenit.de' + apply plugin: 'eu.xenit.alfresco-remote-testrunner' + apply plugin: 'eu.xenit.alfresco' + apply from: "${rootProject.projectDir}/alfresco/${subproject_alfresco_version}/overrides.gradle" + + sourceSets { + main.java.srcDirs = [] + main.resources.srcDirs = [] + integrationTest.java.srcDirs = ["${integrationTestsProjectDir}/src/main/java"] + integrationTest.resources.srcDirs = ["${integrationTestsProjectDir}/src/main/resources"] + } + + configurations { + implementation { + // Fixes IDE source highlighting + extendsFrom integrationTestImplementation + } + testImplementation { + extendsFrom integrationTestImplementation + } + integrationTestImplementationLocal { + // Removes weird dependency lodged inside the remote testing tools + // https://github.com/xenit-eu/alfresco-remote-testrunner/blob/564df30f25dab78f6a7aad02f3ba01fcf9a38b13/alfresco-remote-testrunner-gradle-plugin/src/main/java/eu/xenit/testing/integrationtesting/gradle/internal/IntegrationTestConfigurations.java#L84 + exclude(group: 'commons-lang', module: 'commons-lang') + } + } + + dependencies { + alfrescoProvided(project(":apix-rest-v1")) { transitive = false } + alfrescoProvided(project(":apix-interface")) { transitive = false } + + // Add services used to the integration test fatjar, since we can't access the ones deployed in Alfresco + integrationTestImplementationRemote (project(":apix-impl:apix-impl-${subproject_alfresco_version}")) { + // Already includes apix-interface, we need to exclude it to avoid Linkage errors from + // having 2 of the same classes on the classpath + // (1 in Alfresco from apix-impl AMP + 1 in DE from the integration tests fat jar) + exclude(group: "eu.xenit.alfred.api", module: "apix-interface") + } + + integrationTestImplementationRemote("org.apache.httpcomponents:fluent-hc:${http_version}") { + exclude group: 'commons-logging' + exclude group: 'org.apache.httpcomponents', module: 'httpcore' + } + + // Below is a temporary fix to circumvent using commons-lang 1.0.0 JAR. This jar + // - is injected by alfresco-remote-testrunner + // - is not on Maven Central (only 1.0) is + // - nobody what it used for exactly + // - nobody knows where it came from + // Ergo, kill it with fire. + // The proper way is a fix of alfresco-remote-testrunner, but until then we use this workaround. + integrationTestImplementationLocal "commons-lang:commons-lang:1.0" + + alfrescoProvided platform("org.alfresco:acs-community-packaging:${alfresco_version}") + alfrescoProvided("org.alfresco:alfresco-repository") + alfrescoProvided('org.alfresco:alfresco-remote-api') + alfrescoProvided('org.alfresco:alfresco-data-model') + alfrescoProvided group: 'org.osgi', name: 'org.osgi.core', version: '4.3.1' + + testImplementation project.sourceSets.integrationTest.output + testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.25.1' + } +} + +subprojects { + // Subproject of supported Alfresco versions + def projectApixImpl = project(":apix-impl:apix-impl-${project.projectDir.name}") + def projectDocker = project(":apix-docker:docker-${project.projectDir.name}") + evaluationDependsOn(projectApixImpl.path) + evaluationDependsOn(projectDocker.path) + def assembleTask = projectApixImpl.tasks.getByName("assemble") + def composeUpTask = projectDocker.tasks.getByName("composeUp") + def composeDownTask = projectDocker.tasks.getByName("composeDown") + + alfrescoIntegrationTest { + integrationTestPackages = ["eu.xenit.apix"] + } + + alfrescoDynamicExtensions { + repository { + endpoint { + protocol = project.hasProperty('protocol') ? project.protocol : 'http' + host = project.hasProperty('host') ? project.host : 'localhost' + port = project.hasProperty('port') ? project.port : 8080 + } + } + } + + jar.enabled = false // empty jar + installBundle.enabled = false // no need to install an empty jar + + installIntegrationTestingBundle { + dependsOn assembleTask + dependsOn composeUpTask + doFirst { + def serviceInfo = composeUpTask.servicesInfos.get("alfresco-core") + if (serviceInfo != null) { + alfrescoDynamicExtensions { + repository { + endpoint { + host = serviceInfo.getHost() + port = serviceInfo.ports[8080] + } + } + } + } + } + } + + integrationTest { + // After the tests, the docker setup should be stopped + finalizedBy composeDownTask + } + + // Fix to let BND deal with multi-releases JARs + // So far this only applies to snakeyaml + integrationTestFatJar { + bnd('-fixupmessages': '^Classes found in the wrong directory: \\\\{META-INF/versions/.*') + } +} + diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/staging/tests/StagingBaseTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/staging/tests/StagingBaseTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/staging/tests/StagingBaseTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/staging/tests/StagingBaseTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/staging/tests/WorkflowTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/staging/tests/WorkflowTest.java similarity index 97% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/staging/tests/WorkflowTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/staging/tests/WorkflowTest.java index 7ea85af3..361590a5 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/staging/tests/WorkflowTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/staging/tests/WorkflowTest.java @@ -25,6 +25,7 @@ import org.alfresco.service.namespace.QName; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -124,7 +125,7 @@ public void testClaimWorkflow() throws IOException { httppost.setEntity(new StringEntity(json(String.format("{\n" + "\t\"id\" : \"%s\",\n" + "\t\"userName\" : \"admin\"\n" + - "}", wfTask.getId())))); + "}", wfTask.getId())), ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { String jsonString = EntityUtils.toString(response.getEntity()); @@ -165,7 +166,7 @@ public Object execute() throws Throwable { httppost.setEntity(new StringEntity(json(String.format("{\n" + "\t\"id\" : \"%s\"\n" + - "}", wfTask.getId())))); + "}", wfTask.getId())), ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { String jsonString = EntityUtils.toString(response.getEntity()); diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/AllNodeInfoTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/AllNodeInfoTest.java similarity index 93% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/AllNodeInfoTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/AllNodeInfoTest.java index 90b95f3c..bee5382e 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/AllNodeInfoTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/AllNodeInfoTest.java @@ -12,6 +12,7 @@ import org.apache.http.client.fluent.Request; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -68,7 +69,7 @@ public void testGetAllNodeInfoOfMultipleNodes() throws IOException, JSONExceptio final String url = makeAlfrescoBaseurl("admin", "admin") + "/apix/v1/nodes/nodeInfo"; logger.debug("url: " + url); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(200, response.getStatusLine().getStatusCode()); @@ -80,13 +81,13 @@ public void testGetAllNodeInfoOfMultipleNodes() throws IOException, JSONExceptio @Test public void testGetAllNodeInfoWithNoNodesListed() throws IOException { - String jsonString = json("{}"); + String jsonString = json(""); final CloseableHttpClient httpclient = HttpClients.createDefault(); final String url = makeAlfrescoBaseurl("admin", "admin") + "/apix/v1/nodes/nodeInfo"; logger.debug("url: " + url); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(400, response.getStatusLine().getStatusCode()); @@ -105,7 +106,7 @@ public void testGetAllNodeInfoWithoutNodeWithoutPermissions() throws IOException final String url = makeAlfrescoBaseurl(RestV1BaseTest.USERWITHOUTRIGHTS, RestV1BaseTest.USERWITHOUTRIGHTS) + "/apix/v1/nodes/nodeInfo"; logger.debug("url: " + url); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(200, response.getStatusLine().getStatusCode()); @@ -129,7 +130,7 @@ public void testGetAllNodeInfoForNodeWithoutPermissions() throws IOException { "]}"); final CloseableHttpClient httpclient = HttpClients.createDefault(); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(200, response.getStatusLine().getStatusCode()); diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/AssociationsTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/AssociationsTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/AssociationsTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/AssociationsTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/BulkTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/BulkTest.java similarity index 97% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/BulkTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/BulkTest.java index baf5f9dd..8748936b 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/BulkTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/BulkTest.java @@ -21,6 +21,7 @@ import org.alfresco.service.transaction.TransactionService; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -168,7 +169,7 @@ public void testPostBulk() throws IOException { String.format("[{'url':'%s','method':'%s','body':%s},{'url':'%s','method':'%s','body':%s}]", firstPostUrl, "post", firstJsonBody, secondPostUrl, "post", secondJsonBody)); - httpPost.setEntity(new StringEntity(jsonString)); + httpPost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); transactionService.getRetryingTransactionHelper() .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @@ -199,7 +200,7 @@ public Object execute() throws Throwable { jsonString = json(String.format("[{'url':'%s','method':'%s','body':%s}]", firstPostUrl, "post", firstJsonBody)); final HttpPost httpPost2 = new HttpPost(url); - httpPost2.setEntity(new StringEntity(jsonString)); + httpPost2.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); transactionService.getRetryingTransactionHelper() .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override @@ -221,7 +222,7 @@ public Object execute() throws Throwable { jsonString = json(String.format("[{'url':'%s','method':'%s','body':%s}]", secondPostUrl, "post", secondJsonBody)); final HttpPost httpPost3 = new HttpPost(url); - httpPost3.setEntity(new StringEntity(jsonString)); + httpPost3.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); transactionService.getRetryingTransactionHelper() .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override @@ -301,7 +302,7 @@ public void testPutBulk() throws IOException { String firstJsonBody = String.format("{'parent':'%s'}", mainFolderRef); String jsonString = json( String.format("[{'url':'%s', 'method':'%s', 'body':%s}]", firstPutUrl, "put", firstJsonBody)); - httpPost.setEntity(new StringEntity(jsonString)); + httpPost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); transactionService.getRetryingTransactionHelper() .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @@ -337,7 +338,7 @@ public void testDelete() throws IOException { String firstDeleteUrl = removePrefixAndAuthenticate( makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME), "admin", "admin")); String jsonString = json(String.format("[{'url':'%s', 'method':'%s'}]", firstDeleteUrl, "delete")); - httpPost.setEntity(new StringEntity(jsonString)); + httpPost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); transactionService.getRetryingTransactionHelper() .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @@ -356,7 +357,7 @@ public Object execute() throws Throwable { "admin")); jsonString = json(String.format("[{'url':'%s', 'method':'%s', 'body':{}}]", secondDeleteUrl, "delete")); final HttpPost httpPost2 = new HttpPost(url); - httpPost2.setEntity(new StringEntity(jsonString)); + httpPost2.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); transactionService.getRetryingTransactionHelper() .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CheckoutCheckinTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CheckoutCheckinTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CheckoutCheckinTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CheckoutCheckinTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CommentsTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CommentsTest.java similarity index 93% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CommentsTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CommentsTest.java index e59a5559..a5eb9b50 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CommentsTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CommentsTest.java @@ -1,6 +1,7 @@ package eu.xenit.apix.rest.v1.tests; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -23,6 +24,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -139,7 +141,7 @@ public void testPutContent() throws IOException { HttpPut req = new HttpPut(url); final CloseableHttpClient checkoutHttpclient = HttpClients.createDefault(); String checkoutJsonString = "{ \"content\" : \"new content\" }"; - req.setEntity(new StringEntity(checkoutJsonString)); + req.setEntity(new StringEntity(checkoutJsonString, ContentType.APPLICATION_JSON)); String result = ""; try (CloseableHttpResponse response = checkoutHttpclient.execute(req)) { @@ -160,7 +162,7 @@ public void testPutContentOnContent() throws IOException { HttpPut req = new HttpPut(url); final CloseableHttpClient checkoutHttpclient = HttpClients.createDefault(); String checkoutJsonString = "{ \"content\" : \"new content\" }"; - req.setEntity(new StringEntity(checkoutJsonString)); + req.setEntity(new StringEntity(checkoutJsonString, ContentType.APPLICATION_JSON)); String result = ""; try (CloseableHttpResponse response = checkoutHttpclient.execute(req)) { @@ -170,20 +172,25 @@ public void testPutContentOnContent() throws IOException { } @Test - public void testDeleteComment() throws IOException { + public void testDeleteComment() throws IOException, InterruptedException { HashMap initializedRefs = init(); String url = makeCommentsUrl(initializedRefs.get(COMMENTNODE1), "admin", "admin"); HttpDelete req = new HttpDelete(url); final CloseableHttpClient checkoutHttpclient = HttpClients.createDefault(); - String result = ""; try (CloseableHttpResponse response = checkoutHttpclient.execute(req)) { - result = EntityUtils.toString(response.getEntity()); + EntityUtils.toString(response.getEntity()); assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); } - assertEquals(false, nodeService.exists(initializedRefs.get(COMMENTNODE1))); + // Alfresco Cache is lagging behind... + Thread.sleep(2000); + + boolean exists = this.transactionHelper.doInTransaction( + () -> nodeService.exists(initializedRefs.get(COMMENTNODE1)), true, true + ); + assertFalse(exists); } @Test @@ -216,7 +223,7 @@ public void testAppendComments() throws IOException { HttpPost req = new HttpPost(url); String checkoutJsonString = "{ \"content\" : \"new content\" }"; - req.setEntity(new StringEntity(checkoutJsonString)); + req.setEntity(new StringEntity(checkoutJsonString, ContentType.APPLICATION_JSON)); final CloseableHttpClient checkoutHttpclient = HttpClients.createDefault(); String result = ""; try (CloseableHttpResponse response = checkoutHttpclient.execute(req)) { diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/ConfigurationTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/ConfigurationTest.java similarity index 63% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/ConfigurationTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/ConfigurationTest.java index c441ad34..c6f3ef02 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/ConfigurationTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/ConfigurationTest.java @@ -1,17 +1,10 @@ package eu.xenit.apix.rest.v1.tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import eu.xenit.apix.content.IContentService; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.QName; import eu.xenit.apix.filefolder.IFileFolderService; import eu.xenit.apix.node.INodeService; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.URLEncoder; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.archive.NodeArchiveService; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -21,6 +14,7 @@ import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.transaction.TransactionService; +import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.client.fluent.Request; import org.apache.http.util.EntityUtils; @@ -34,13 +28,23 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.MediaType; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URLEncoder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.http.HttpHeaders.CONTENT_TYPE; /** * Created by kenneth on 14.03.16. */ public class ConfigurationTest extends RestV1BaseTest { - private final static Logger logger = LoggerFactory.getLogger(ConfigurationTest.class); + private static final Logger logger = LoggerFactory.getLogger(ConfigurationTest.class); @Autowired @Qualifier("FileFolderService") @@ -83,35 +87,33 @@ public void setup() { TransactionService transactionService = serviceRegistry.getTransactionService(); - RetryingTransactionHelper.RetryingTransactionCallback txnWork = new RetryingTransactionHelper.RetryingTransactionCallback() { - public Object execute() throws Exception { - eu.xenit.apix.data.NodeRef dataDictionary = apixFileFolderService.getDataDictionary(); - eu.xenit.apix.data.NodeRef testFolder = apixFileFolderService - .createFolder(dataDictionary, "ConfigurationTests"); - - jsonNodeRef = nodeService - .createNode(testFolder, "xyz.json", new QName(ContentModel.TYPE_CONTENT.toString())); - ByteArrayInputStream jsonContent = new ByteArrayInputStream("{\"contents\": \"abc\"}".getBytes()); - contentService.setContent(jsonNodeRef, jsonContent, "abc.json"); - - yamlNodeRef = nodeService - .createNode(testFolder, "xyz.yaml", new QName(ContentModel.TYPE_CONTENT.toString())); - ByteArrayInputStream yamlContent = new ByteArrayInputStream("contents: abc".getBytes()); - contentService.setContent(yamlNodeRef, yamlContent, "abc.yaml"); - - otherNodeRef = nodeService - .createNode(testFolder, "xyz.json.disabled", new QName(ContentModel.TYPE_CONTENT.toString())); - ByteArrayInputStream otherContent = new ByteArrayInputStream("{\"contents\": \"other\"}".getBytes()); - contentService.setContent(otherNodeRef, otherContent, "abc.json"); - - NodeRef subFolder = apixFileFolderService.createFolder(testFolder, "subFolder"); - - yamlsubNodeRef = nodeService - .createNode(subFolder, "sub.yaml", new QName(ContentModel.TYPE_CONTENT.toString())); - ByteArrayInputStream yamlSubContent = new ByteArrayInputStream("contents: sub".getBytes()); - contentService.setContent(yamlsubNodeRef, yamlSubContent, "abc.yaml"); - return null; - } + RetryingTransactionHelper.RetryingTransactionCallback txnWork = () -> { + NodeRef dataDictionary = apixFileFolderService.getDataDictionary(); + NodeRef testFolder = apixFileFolderService + .createFolder(dataDictionary, "ConfigurationTests"); + + jsonNodeRef = nodeService + .createNode(testFolder, "xyz.json", new QName(ContentModel.TYPE_CONTENT.toString())); + ByteArrayInputStream jsonContent = new ByteArrayInputStream("{\"contents\": \"abc\"}".getBytes()); + contentService.setContent(jsonNodeRef, jsonContent, "abc.json"); + + yamlNodeRef = nodeService + .createNode(testFolder, "xyz.yaml", new QName(ContentModel.TYPE_CONTENT.toString())); + ByteArrayInputStream yamlContent = new ByteArrayInputStream("contents: abc".getBytes()); + contentService.setContent(yamlNodeRef, yamlContent, "abc.yaml"); + + otherNodeRef = nodeService + .createNode(testFolder, "xyz.json.disabled", new QName(ContentModel.TYPE_CONTENT.toString())); + ByteArrayInputStream otherContent = new ByteArrayInputStream("{\"contents\": \"other\"}".getBytes()); + contentService.setContent(otherNodeRef, otherContent, "abc.json"); + + NodeRef subFolder = apixFileFolderService.createFolder(testFolder, "subFolder"); + + yamlsubNodeRef = nodeService + .createNode(subFolder, "sub.yaml", new QName(ContentModel.TYPE_CONTENT.toString())); + ByteArrayInputStream yamlSubContent = new ByteArrayInputStream("contents: sub".getBytes()); + contentService.setContent(yamlsubNodeRef, yamlSubContent, "abc.yaml"); + return null; }; transactionService.getRetryingTransactionHelper().doInTransaction(txnWork, false, true); @@ -125,16 +127,7 @@ private String makeBasePath() { @Test public void testConfigurationGet() throws IOException, JSONException { - String requestUrl = makeBasePath(); - - HttpResponse response = Request.Get(requestUrl).execute().returnResponse(); - - assertEquals(200, response.getStatusLine().getStatusCode()); - - JSONObject jsonObject = new JSONObject(EntityUtils.toString(response.getEntity())); - - JSONArray jsonFiles = jsonObject.getJSONArray("files"); - + JSONArray jsonFiles = callConfiguration(makeBasePath()); assertEquals(4, jsonFiles.length()); for (int i = 0; i < jsonFiles.length(); i++) { @@ -147,28 +140,31 @@ public void testConfigurationGet() throws IOException, JSONException { assertEquals("{\"contents\": \"abc\"}", jsonFile.optString("content")); } - assertEquals(2, jsonFile.length()); + assertEquals(jsonFile.toString(), 2, jsonFile.length()); } } @Test - public void testConfigurationGetFields() throws IOException, JSONException { - String requestUrl = makeBasePath() + "&fields=nodeRef,path,metadata,parsedContent"; - - HttpResponse response = Request.Get(requestUrl).execute().returnResponse(); - + public void testConfigurationGetJS() throws IOException, JSONException { + HttpResponse response = Request + .Get(makeBasePath()) + .addHeader(HttpHeaders.ACCEPT, "application/js") + .execute() + .returnResponse(); assertEquals(200, response.getStatusLine().getStatusCode()); + assertEquals("application/js", response.getFirstHeader(CONTENT_TYPE).getValue()); + } - JSONObject jsonObject = new JSONObject(EntityUtils.toString(response.getEntity())); - - JSONArray jsonFiles = jsonObject.getJSONArray("files"); - + @Test + public void testConfigurationGetFields() throws IOException, JSONException { + String requestUrl = makeBasePath() + "&fields=nodeRef,path,metadata,parsedContent"; + JSONArray jsonFiles = callConfiguration(requestUrl); assertEquals(4, jsonFiles.length()); for (int i = 0; i < jsonFiles.length(); i++) { JSONObject jsonFile = jsonFiles.getJSONObject(i); - assertEquals(4, jsonFile.length()); + assertEquals(jsonFile.toString(), 4, jsonFile.length()); String nodeRef = jsonFile.optString("nodeRef"); assertNotNull(nodeRef); @@ -199,18 +195,27 @@ public void testConfigurationGetFields() throws IOException, JSONException { } - @Test - public void testConfigurationFilterFields() throws IOException, JSONException { - String requestUrl = makeBasePath() + "&filter.name=" + URLEncoder.encode("\\.yaml$", "UTF-8"); - - HttpResponse response = Request.Get(requestUrl).execute().returnResponse(); + private JSONArray callConfiguration(String requestUrl) throws IOException { + HttpResponse response = Request + .Get(requestUrl) + .addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .execute() + .returnResponse(); assertEquals(200, response.getStatusLine().getStatusCode()); + assertEquals("application/json", response.getFirstHeader(CONTENT_TYPE).getValue()); JSONObject jsonObject = new JSONObject(EntityUtils.toString(response.getEntity())); JSONArray jsonFiles = jsonObject.getJSONArray("files"); + return jsonFiles; + } + + @Test + public void testConfigurationFilterFields() throws IOException, JSONException { + String requestUrl = makeBasePath() + "&filter.name=" + URLEncoder.encode("\\.yaml$", "UTF-8"); + JSONArray jsonFiles = callConfiguration(requestUrl); assertEquals(2, jsonFiles.length()); for (int i = 0; i < jsonFiles.length(); i++) { JSONObject jsonFile = jsonFiles.getJSONObject(i); @@ -223,15 +228,7 @@ public void testConfigurationFilterFields() throws IOException, JSONException { @Test public void testConfigurationSubdirectory() throws IOException, JSONException { String requestUrl = makeBasePath() + "/subFolder"; - - HttpResponse response = Request.Get(requestUrl).execute().returnResponse(); - - assertEquals(200, response.getStatusLine().getStatusCode()); - - JSONObject jsonObject = new JSONObject(EntityUtils.toString(response.getEntity())); - - JSONArray jsonFiles = jsonObject.getJSONArray("files"); - + JSONArray jsonFiles = callConfiguration(requestUrl); assertEquals(1, jsonFiles.length()); for (int i = 0; i < jsonFiles.length(); i++) { JSONObject jsonFile = jsonFiles.getJSONObject(i); @@ -243,19 +240,16 @@ public void testConfigurationSubdirectory() throws IOException, JSONException { @After public void cleanUp() { - RetryingTransactionHelper.RetryingTransactionCallback txnWork = new RetryingTransactionHelper.RetryingTransactionCallback() { - public Object execute() throws Exception { - try { - eu.xenit.apix.data.NodeRef dataDictionary = apixFileFolderService.getDataDictionary(); - eu.xenit.apix.data.NodeRef testFolder = apixFileFolderService - .getChildNodeRef(dataDictionary, "ConfigurationTests"); - removeTestNode(new org.alfresco.service.cmr.repository.NodeRef(testFolder.toString())); - } catch (RuntimeException ex) { - logger.debug("Did not need to remove mainTestFolder because it did not exist"); - //ex.printStackTrace(); - } - return null; + RetryingTransactionHelper.RetryingTransactionCallback txnWork = () -> { + try { + NodeRef dataDictionary = apixFileFolderService.getDataDictionary(); + NodeRef testFolder = apixFileFolderService + .getChildNodeRef(dataDictionary, "ConfigurationTests"); + removeTestNode(new org.alfresco.service.cmr.repository.NodeRef(testFolder.toString())); + } catch (RuntimeException ex) { + logger.debug("Did not need to remove mainTestFolder because it did not exist"); } + return null; }; TransactionService transactionService = serviceRegistry.getTransactionService(); transactionService.getRetryingTransactionHelper().doInTransaction(txnWork, false, true); diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CopyNodeTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CopyNodeTest.java similarity index 96% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CopyNodeTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CopyNodeTest.java index 3a7c059b..7e0b8a81 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CopyNodeTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CopyNodeTest.java @@ -5,6 +5,7 @@ import eu.xenit.apix.data.QName; import eu.xenit.apix.node.INodeService; import eu.xenit.apix.rest.v1.nodes.CreateNodeOptions; + import java.util.HashMap; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -13,16 +14,12 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; public class CopyNodeTest extends NodesBaseTest { - private final static Logger logger = LoggerFactory.getLogger(CopyNodeTest.class); private NodeRef mainTestFolder; - private NodeRef parentTestFolder; private NodeRef copyFromFile; private NodeRef copyFromFolder; @@ -41,7 +38,6 @@ public void setup() { AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); final HashMap initializedNodeRefs = init(); mainTestFolder = c.apix(getMainTestFolder()); - parentTestFolder = initializedNodeRefs.get(RestV1BaseTest.TESTFOLDER_NAME); copyFromFile = initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME); copyFromFolder = initializedNodeRefs.get(RestV1BaseTest.TESTFOLDER_NAME); } @@ -136,7 +132,7 @@ public void testCopyFileDuplicateName() { @Test public void testCopyFolderDuplicateName() { final NodeRef childRef = nodeService.getChildAssociations(mainTestFolder).get(0).getTarget(); - final String newName = nodeService.getMetadata(childRef).properties.get(c.apix(ContentModel.PROP_NAME)).get(0); + final String newName = nodeService.getMetadata(childRef).getProperties().get(c.apix(ContentModel.PROP_NAME)).get(0); CreateNodeOptions createNodeOptions = getCreateNodeOptions(mainTestFolder, newName, null, null, copyFromFolder); NodeRef newRef = transactionService.getRetryingTransactionHelper() diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CreateNodeTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CreateNodeTest.java similarity index 99% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CreateNodeTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CreateNodeTest.java index 3f7a7001..e7b7dd74 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/CreateNodeTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/CreateNodeTest.java @@ -3,6 +3,7 @@ import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.QName; + import java.util.HashMap; import eu.xenit.apix.rest.v1.nodes.CreateNodeOptions; import org.alfresco.model.ContentModel; diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/DictionaryTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/DictionaryTest.java similarity index 90% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/DictionaryTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/DictionaryTest.java index cfedcb1b..962d3980 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/DictionaryTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/DictionaryTest.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.net.URLEncoder; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import org.alfresco.repo.dictionary.DictionaryDAO; @@ -25,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.util.UriComponentsBuilder; public class DictionaryTest extends RestV1BaseTest { @@ -59,9 +61,12 @@ private void executeDictionaryTypeTest(String dictionaryType, String shortName, String baseUrl = makeAlfrescoBaseurlAdmin() + "/apix/v1/dictionary/" + dictionaryType + "/"; // Short qname lookup - HttpResponse httpResponse = Request.Get(baseUrl + shortName).execute().returnResponse(); + String uri = baseUrl + URLEncoder.encode(shortName, + String.valueOf(Charset.defaultCharset()) + ); + HttpResponse httpResponse = Request.Get(uri).execute().returnResponse(); logger.debug(EntityUtils.toString(httpResponse.getEntity())); - assertEquals(200, httpResponse.getStatusLine().getStatusCode()); + assertEquals(uri, 200, httpResponse.getStatusLine().getStatusCode()); JSONObject jsonObject = new JSONObject(EntityUtils.toString(httpResponse.getEntity())); assertEquals(longName, jsonObject.getString("name")); if (mandatoryAspect != null) { @@ -69,10 +74,12 @@ private void executeDictionaryTypeTest(String dictionaryType, String shortName, } // full qname lookup - httpResponse = Request.Get(baseUrl + URLEncoder.encode(longName, "utf-8").replaceAll("%2F", "/")).execute() - .returnResponse(); + String sanitizedLongQName = URLEncoder.encode(longName, + String.valueOf(Charset.defaultCharset())) + .replaceAll("%2F", "/"); + httpResponse = Request.Get(baseUrl + sanitizedLongQName).execute().returnResponse(); logger.debug(EntityUtils.toString(httpResponse.getEntity())); - assertEquals(200, httpResponse.getStatusLine().getStatusCode()); + assertEquals(sanitizedLongQName, 200, httpResponse.getStatusLine().getStatusCode()); jsonObject = new JSONObject(EntityUtils.toString(httpResponse.getEntity())); assertEquals(longName, jsonObject.getString("name")); @@ -89,7 +96,6 @@ public void testTypeDefinitionGet() throws IOException, JSONException { "{http://www.alfresco.org/model/content/1.0}auditable"); } - @Test public void testTypesGet() throws IOException, JSONException { String baseUrl = makeAlfrescoBaseurlAdmin() + "/apix/v1/dictionary/types"; diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/ExampleTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/ExampleTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/ExampleTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/ExampleTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/MetadataTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/MetadataTest.java similarity index 76% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/MetadataTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/MetadataTest.java index 0a7b6f6e..e251a7e6 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/MetadataTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/MetadataTest.java @@ -15,6 +15,7 @@ import org.apache.http.client.fluent.Request; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -109,7 +110,8 @@ public void testMetadataPost() throws IOException, JSONException { //Adding the cm:versionable aspect as a test httppost.setEntity(new StringEntity(json(String - .format("{'aspectsToAdd':['%s'], %s}", ContentModel.ASPECT_VERSIONABLE.toString(), propertiesToSet)))); + .format("{'aspectsToAdd':['%s'], %s}", ContentModel.ASPECT_VERSIONABLE.toString(), propertiesToSet)), + ContentType.APPLICATION_JSON)); final NodeRef testNodeRef; try (CloseableHttpResponse response = httpclient.execute(httppost)) { @@ -123,16 +125,13 @@ public void testMetadataPost() throws IOException, JSONException { } transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - org.alfresco.service.cmr.repository.NodeRef alfTestRef = new org.alfresco.service.cmr.repository.NodeRef( - testNodeRef.toString()); - assertEquals("newTitle", nodeService.getProperty(alfTestRef, ContentModel.PROP_TITLE)); - assertEquals(RestV1BaseTest.TESTFILE_NAME, nodeService.getProperty(alfTestRef, ContentModel.PROP_NAME)); - assertTrue(nodeService.hasAspect(alfTestRef, ContentModel.ASPECT_VERSIONABLE)); - return null; - } + .doInTransaction(() -> { + org.alfresco.service.cmr.repository.NodeRef alfTestRef = new org.alfresco.service.cmr.repository.NodeRef( + testNodeRef.toString()); + assertEquals("newTitle", nodeService.getProperty(alfTestRef, ContentModel.PROP_TITLE)); + assertEquals(RestV1BaseTest.TESTFILE_NAME, nodeService.getProperty(alfTestRef, ContentModel.PROP_NAME)); + assertTrue(nodeService.hasAspect(alfTestRef, ContentModel.ASPECT_VERSIONABLE)); + return null; }, false, true); } @@ -151,7 +150,8 @@ public void testMetadataPostReturnsAccesDenied() throws IOException, JSONExcepti //Adding the cm:versionable aspect as a test httppost.setEntity(new StringEntity(json(String - .format("{'aspectsToAdd':['%s'], %s}", ContentModel.ASPECT_VERSIONABLE.toString(), propertiesToSet)))); + .format("{'aspectsToAdd':['%s'], %s}", ContentModel.ASPECT_VERSIONABLE.toString(), propertiesToSet)), + ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { String jsonString = EntityUtils.toString(response.getEntity()); @@ -178,39 +178,38 @@ private boolean checkExists(NodeRef ref) { } @Test - public void testDeletePermanently() throws IOException { + public void testDeletePermanently() throws IOException, InterruptedException { final HashMap initializedNodeRefs = CreateAdminNode(); final String url = getUrl(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME)) + "?permanently=true"; + Request.Delete(url).execute().returnResponse(); + // Alfresco Cache is lagging behind... + Thread.sleep(2000); transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - Request.Delete(url).execute().returnResponse(); - org.alfresco.service.cmr.repository.NodeRef archivedRef = nodeArchiveService.getArchivedNode( - new org.alfresco.service.cmr.repository.NodeRef(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue())); - assertFalse(checkExists(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME))); - logger.debug(" deleted node: " + archivedRef.toString()); - assertNotNull(archivedRef); - return null; - } + .doInTransaction(() -> { + assertFalse(checkExists(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME))); + org.alfresco.service.cmr.repository.NodeRef archivedRef = nodeArchiveService.getArchivedNode( + new org.alfresco.service.cmr.repository.NodeRef(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue())); + logger.debug(" deleted node: {}", archivedRef); + assertNotNull(archivedRef); + return null; }, true, true); } @Test - public void testDeleteToArchive() throws IOException { + public void testDeleteToArchive() throws IOException, InterruptedException { final HashMap initializedNodeRefs = CreateAdminNode(); final String url = getUrl(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME)); + Request.Delete(url).execute().returnResponse(); + // Alfresco Cache is lagging behind... + Thread.sleep(2000); transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - Request.Delete(url).execute().returnResponse(); - assertFalse(checkExists(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME))); - org.alfresco.service.cmr.repository.NodeRef archivedRef = nodeArchiveService.getArchivedNode( - new org.alfresco.service.cmr.repository.NodeRef(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue())); - assertNotNull(archivedRef); - return null; - } + .doInTransaction(() -> { + assertFalse(checkExists(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME))); + org.alfresco.service.cmr.repository.NodeRef archivedRef = nodeArchiveService.getArchivedNode( + new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue())); + assertNotNull(archivedRef); + return null; }, true, true); } diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/MoveNodeTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/MoveNodeTest.java similarity index 97% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/MoveNodeTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/MoveNodeTest.java index a8304a81..2d332e46 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/MoveNodeTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/MoveNodeTest.java @@ -13,6 +13,7 @@ import org.alfresco.service.transaction.TransactionService; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -100,7 +101,8 @@ public void testMoveNodeReturnsAccesDenied() throws IOException { logger.debug(" URL: " + url); final CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPut httpPut = new HttpPut(url); - httpPut.setEntity(new StringEntity(String.format("{\"parent\":\"%s\"}", mainTestFolder.toString()))); + httpPut.setEntity(new StringEntity(String.format("{\"parent\":\"%s\"}", mainTestFolder.toString()), + ContentType.APPLICATION_JSON)); try(CloseableHttpResponse httpResponse = httpClient.execute(httpPut)) { assertEquals(403, httpResponse.getStatusLine().getStatusCode()); diff --git a/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/NodeContentTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/NodeContentTest.java new file mode 100644 index 00000000..849ed101 --- /dev/null +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/NodeContentTest.java @@ -0,0 +1,225 @@ +package eu.xenit.apix.rest.v1.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import eu.xenit.apix.data.ContentInputStream; +import eu.xenit.apix.data.NodeRef; +import eu.xenit.apix.node.INodeService; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.nio.charset.Charset; +import java.util.HashMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +public class NodeContentTest extends RestV1BaseTest { + @Autowired + INodeService nodeService; + + @Autowired + @Qualifier("TransactionService") + TransactionService transactionService; + + @Before + public void setup() { + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + } + + @Test + public void testSetNodeContent() { + final HashMap initializedNodeRefs = init(); + + final String url = makeNodesUrl( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), + "/content", "admin", "admin"); + final CloseableHttpClient httpclient = HttpClients.createDefault(); + + int returnedStatusCode = transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + HttpPut httpput = new HttpPut(url); + HttpEntity httpBody = MultipartEntityBuilder.create() + .addBinaryBody( + "file", createTestFile()) + .build(); + httpput.setEntity(httpBody); + + try (CloseableHttpResponse response = httpclient.execute(httpput)) { + return response.getStatusLine().getStatusCode(); + } + }, false, true); + assertEquals(200, returnedStatusCode); + + final INodeService ns = this.nodeService; + final NodeRef nodeRef = initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME); + String content = transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + ContentInputStream c = ns.getContent(nodeRef); + try(InputStream inputStream = c.getInputStream()) { + return IOUtils.toString(inputStream, Charset.defaultCharset()); + } + }, false, true); + assertEquals("This is the content", content); + } + + @Test + public void testSetNodeContentReturnsAccessDenied() { + final HashMap initializedNodeRefs = init(); + + final String url = makeNodesUrl( + initializedNodeRefs.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME), + "/content", RestV1BaseTest.USERWITHOUTRIGHTS, + RestV1BaseTest.USERWITHOUTRIGHTS); + final CloseableHttpClient httpclient = HttpClients.createDefault(); + + int receivedStatusCode = transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + HttpPut httpput = new HttpPut(url); + HttpEntity httpBody = MultipartEntityBuilder.create() + .addBinaryBody( + "file", createTestFile()) + .build(); + httpput.setEntity(httpBody); + + try (CloseableHttpResponse response = httpclient.execute(httpput)) { + return response.getStatusLine().getStatusCode(); + } + }, false, true); + assertEquals(403, receivedStatusCode); + } + + @Test + public void testDeleteNodeContent() { + final HashMap initializedNodeRefs = init(); + + final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), + "/content", "admin", "admin"); + final CloseableHttpClient httpclient = HttpClients.createDefault(); + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + nodeService.setContent(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), + new ByteArrayInputStream("test contentabc".getBytes()), + "abc.txt"); + return null; + }, false, true); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + HttpDelete httpDelete = new HttpDelete(url); + + try (CloseableHttpResponse response = httpclient.execute(httpDelete)) { + assertEquals(200, response.getStatusLine().getStatusCode()); + } + return null; + }, false, true); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + assertNull(nodeService.getContent(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME))); + return null; + }, false, true); + } + + @Test + public void testDeleteNodeContentReturnsAccesDenied() { + final HashMap initializedNodeRefs = init(); + + final String url = makeNodesUrl( + initializedNodeRefs.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME), + "/content", RestV1BaseTest.USERWITHOUTRIGHTS, + RestV1BaseTest.USERWITHOUTRIGHTS); + final CloseableHttpClient httpclient = HttpClients.createDefault(); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + try (CloseableHttpResponse response = httpclient.execute(new HttpDelete(url))) { + assertEquals(403, response.getStatusLine().getStatusCode()); + } + return null; + }, false, true); + } + + @Test + public void testGetNodeContent() { + final HashMap initializedNodeRefs = init(); + + final String url = makeNodesUrl( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), + "/content", "admin", "admin"); + final CloseableHttpClient httpclient = HttpClients.createDefault(); + final INodeService ns = this.nodeService; + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + ns.setContent(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), + new ByteArrayInputStream("test contentdef".getBytes()), + "abc.txt"); + return null; + }, false, true); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + HttpGet httpGet = new HttpGet(url); + + try (CloseableHttpResponse response = httpclient.execute(httpGet)) { + assertEquals(200, response.getStatusLine().getStatusCode()); + InputStream inputStream = response.getEntity().getContent(); + assertEquals("test contentdef", + IOUtils.toString(inputStream, Charset.defaultCharset())); + inputStream.close(); + } + return null; + }, false, true); + } + + @Test + public void testGetNodeContentReturnsAccesDenied() { + final HashMap initializedNodeRefs = init(); + + final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME), + "/content", RestV1BaseTest.USERWITHOUTRIGHTS, + RestV1BaseTest.USERWITHOUTRIGHTS); + final CloseableHttpClient httpclient = HttpClients.createDefault(); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + try (CloseableHttpResponse response = httpclient.execute(new HttpGet(url))) { + assertEquals(403, response.getStatusLine().getStatusCode()); + } + return null; + }, false, true); + } + + private File createTestFile() throws IOException { + String pathName = "test.txt"; + File result = new File(pathName); + result.createNewFile(); + PrintWriter writer = new PrintWriter(pathName, "UTF-8"); + String contentString = "This is the content"; + writer.print(contentString); + writer.close(); + return result; + } + + @After + public void cleanUp() { + this.removeMainTestFolder(); + } +} diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/NodesBaseTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/NodesBaseTest.java similarity index 81% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/NodesBaseTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/NodesBaseTest.java index 180b4e98..89f77609 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/NodesBaseTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/NodesBaseTest.java @@ -23,6 +23,7 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -64,12 +65,12 @@ public eu.xenit.apix.data.NodeRef doPostNodes(CreateNodeOptions createNodeOption if (nodeInfo != null) { //deserialization succeeded - return nodeInfo.noderef; + return nodeInfo.getNoderef(); } else { //deserialization failed HttpEntityEnclosingRequestBase req = new HttpPost(url); final CloseableHttpClient checkoutHttpclient = HttpClients.createDefault(); - req.setEntity(new StringEntity(requestBody)); + req.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = checkoutHttpclient.execute(req)) { String result = EntityUtils.toString(response.getEntity()); assertEquals(expectedResponseCode, response.getStatusLine().getStatusCode()); @@ -92,12 +93,7 @@ protected CreateNodeOptions getCreateNodeOptions(eu.xenit.apix.data.NodeRef pare String copyFromString = (copyFrom != null) ? copyFrom.toString() : null; String typeString = (type != null) ? type.toString() : null; - try { - return new CreateNodeOptions(parentRefString, name, typeString, properties, aspectsToAdd, aspectsToRemove, copyFromString); - } catch (IOException e) { - e.printStackTrace(); - } - return null; + return new CreateNodeOptions(parentRefString, name, typeString, properties, aspectsToAdd, aspectsToRemove, copyFromString); } protected CreateNodeOptions getCreateNodeOptions(eu.xenit.apix.data.NodeRef parentRef, @@ -108,25 +104,25 @@ protected CreateNodeOptions getCreateNodeOptions(eu.xenit.apix.data.NodeRef pare public void checkCreatedNode(NodeRef newRef, CreateNodeOptions createNodeOptions) { assertTrue(nodeService.exists(newRef)); - assertEquals(createNodeOptions.parent, nodeService.getParentAssociations(newRef).get(0).getTarget().toString()); + assertEquals(createNodeOptions.getParent(), nodeService.getParentAssociations(newRef).get(0).getTarget().toString()); - if (createNodeOptions.type != null) { - assertEquals(createNodeOptions.type, nodeService.getMetadata(newRef).type.toString()); + if (createNodeOptions.getType() != null) { + assertEquals(createNodeOptions.getType(), nodeService.getMetadata(newRef).getType().toString()); } - if (createNodeOptions.copyFrom != null) { - assertTrue(nodeService.exists(new NodeRef(createNodeOptions.copyFrom))); + if (createNodeOptions.getCopyFrom() != null) { + assertTrue(nodeService.exists(new NodeRef(createNodeOptions.getCopyFrom()))); } - if (createNodeOptions.properties != null) { - for (Map.Entry property : createNodeOptions.properties.entrySet()) { - assertArrayEquals(property.getValue(), nodeService.getMetadata(newRef).properties.get(property.getKey()).toArray()); + if (createNodeOptions.getProperties() != null) { + for (Map.Entry property : createNodeOptions.getProperties().entrySet()) { + assertArrayEquals(property.getValue(), nodeService.getMetadata(newRef).getProperties().get(property.getKey()).toArray()); } } - if (createNodeOptions.aspectsToAdd != null) { - for (QName aspect : createNodeOptions.aspectsToAdd) { - assertNotNull(nodeService.getMetadata(newRef).aspects + if (createNodeOptions.getAspectsToAdd() != null) { + for (QName aspect : createNodeOptions.getAspectsToAdd()) { + assertNotNull(nodeService.getMetadata(newRef).getAspects() .stream() .filter(testAspect -> testAspect.equals(aspect)) .findFirst() @@ -134,9 +130,9 @@ public void checkCreatedNode(NodeRef newRef, CreateNodeOptions createNodeOptions } } - if (createNodeOptions.aspectsToRemove != null) { - for (QName aspect : createNodeOptions.aspectsToRemove) { - assertNull(nodeService.getMetadata(newRef).aspects + if (createNodeOptions.getAspectsToRemove() != null) { + for (QName aspect : createNodeOptions.getAspectsToRemove()) { + assertNull(nodeService.getMetadata(newRef).getAspects() .stream() .filter(testAspect -> testAspect.equals(aspect)) .findFirst() diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/PathTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/PathTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/PathTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/PathTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/PeopleTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/PeopleTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/PeopleTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/PeopleTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/PermissionsTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/PermissionsTest.java similarity index 97% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/PermissionsTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/PermissionsTest.java index 0ed0b5ae..077e49a9 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/PermissionsTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/PermissionsTest.java @@ -10,6 +10,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.apache.http.HttpResponse; import org.apache.http.client.fluent.Request; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; import org.json.JSONArray; @@ -179,7 +180,8 @@ public void testSetNodeAclsReturnsAccesDenied() throws IOException { + " \"permission\": \"Collaborator\"\n" + " }\n" + "]}" - )).execute().returnResponse(); + , + ContentType.APPLICATION_JSON)).execute().returnResponse(); logger.debug(EntityUtils.toString(httpResponse.getEntity())); String result = EntityUtils.toString(httpResponse.getEntity()); assertEquals(403, httpResponse.getStatusLine().getStatusCode()); @@ -192,7 +194,8 @@ public void testSetInheritFromParentReturnsAccesDenied() throws IOException { Request.Post( makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME), "/acl/inheritFromParent", RestV1BaseTest.USERWITHOUTRIGHTS, RestV1BaseTest.USERWITHOUTRIGHTS)) - .body(new StringEntity("{\"inheritFromParent\":true}")) + .body(new StringEntity("{\"inheritFromParent\":true}", + ContentType.APPLICATION_JSON)) .execute() .returnResponse() .getStatusLine() diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/RestV1BaseTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/RestV1BaseTest.java similarity index 85% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/RestV1BaseTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/RestV1BaseTest.java index 6eebef33..645bf430 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/RestV1BaseTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/RestV1BaseTest.java @@ -36,6 +36,7 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -202,53 +203,56 @@ protected String makeCommentsUrl(eu.xenit.apix.data.NodeRef nodeRef, String user } protected HashMap init() { + return init(null); + } + + protected HashMap init(final String testName) { final HashMap initializedNodeRefs = new HashMap<>(); TransactionService transactionService = serviceRegistry.getTransactionService(); this.removeMainTestFolder(); - RetryingTransactionHelper.RetryingTransactionCallback txnWork = new RetryingTransactionHelper.RetryingTransactionCallback() { - public Object execute() throws Exception { - NodeRef companyHomeRef = repository.getCompanyHome(); - FileInfo mainTestFolder = createTestNode(companyHomeRef, MAIN_TESTFOLDER_NAME, - ContentModel.TYPE_FOLDER); - FileInfo testFolder = createTestNode(mainTestFolder.getNodeRef(), TESTFOLDER_NAME, - ContentModel.TYPE_FOLDER); - initializedNodeRefs.put(TESTFOLDER_NAME, new eu.xenit.apix.data.NodeRef(testFolder.getNodeRef().toString())); - FileInfo testNode = createTestNode(testFolder.getNodeRef(), TESTFILE_NAME, ContentModel.TYPE_CONTENT); - NodeRef testNodeRef = testNode.getNodeRef(); - eu.xenit.apix.data.NodeRef apixTestNodeRef = new eu.xenit.apix.data.NodeRef(testNodeRef.toString()); - initializedNodeRefs.put(TESTFILE_NAME, apixTestNodeRef); - - FileInfo testFolder2 = createTestNode(mainTestFolder.getNodeRef(), TESTFOLDER2_NAME, - ContentModel.TYPE_FOLDER); - FileInfo testNode2 = createTestNode(testFolder2.getNodeRef(), TESTFILE2_NAME, - ContentModel.TYPE_CONTENT); - NodeRef testNodeRef2 = testNode2.getNodeRef(); - eu.xenit.apix.data.NodeRef apixTestNodeRef2 = new eu.xenit.apix.data.NodeRef(testNodeRef2.toString()); - initializedNodeRefs.put(TESTFILE2_NAME, apixTestNodeRef2); - - FileInfo testNode3 = createTestNode(testFolder2.getNodeRef(), TESTFILE3_NAME, - ContentModel.TYPE_CONTENT); - NodeRef testNodeRef3 = testNode3.getNodeRef(); - eu.xenit.apix.data.NodeRef apixTestNodeRef3 = new eu.xenit.apix.data.NodeRef(testNodeRef3.toString()); - initializedNodeRefs.put(TESTFILE3_NAME, apixTestNodeRef3); - - FileInfo noUserRightsFolder = createTestNode(mainTestFolder.getNodeRef(), NOUSERRIGHTS_FOLDER_NAME, - ContentModel.TYPE_FOLDER); - setPermissionInheritance(noUserRightsFolder.getNodeRef(), false); - FileInfo noUserRightsNode = createTestNode(noUserRightsFolder.getNodeRef(), NOUSERRIGHTS_FILE_NAME, - ContentModel.TYPE_CONTENT); - NodeRef noUserRightsNodeRef = noUserRightsNode.getNodeRef(); - setPermissionInheritance(noUserRightsNodeRef, false); - eu.xenit.apix.data.NodeRef apixNoUserRightsNodeRef = new eu.xenit.apix.data.NodeRef( - noUserRightsNodeRef.toString()); - initializedNodeRefs.put(NOUSERRIGHTS_FILE_NAME, apixNoUserRightsNodeRef); - - createUser(USERWITHOUTRIGHTS, USERWITHOUTRIGHTS, USERWITHOUTRIGHTS, - USERWITHOUTRIGHTS_EMAIL); - return null; - } + RetryingTransactionHelper.RetryingTransactionCallback txnWork = () -> { + String mainTestFolderName = MAIN_TESTFOLDER_NAME + (testName != null ? "_" + testName : ""); + NodeRef companyHomeRef = repository.getCompanyHome(); + FileInfo mainTestFolder = createTestNode(companyHomeRef, mainTestFolderName, + ContentModel.TYPE_FOLDER); + FileInfo testFolder = createTestNode(mainTestFolder.getNodeRef(), TESTFOLDER_NAME, + ContentModel.TYPE_FOLDER); + initializedNodeRefs.put(TESTFOLDER_NAME, new eu.xenit.apix.data.NodeRef(testFolder.getNodeRef().toString())); + FileInfo testNode = createTestNode(testFolder.getNodeRef(), TESTFILE_NAME, ContentModel.TYPE_CONTENT); + NodeRef testNodeRef = testNode.getNodeRef(); + eu.xenit.apix.data.NodeRef apixTestNodeRef = new eu.xenit.apix.data.NodeRef(testNodeRef.toString()); + initializedNodeRefs.put(TESTFILE_NAME, apixTestNodeRef); + + FileInfo testFolder2 = createTestNode(mainTestFolder.getNodeRef(), TESTFOLDER2_NAME, + ContentModel.TYPE_FOLDER); + FileInfo testNode2 = createTestNode(testFolder2.getNodeRef(), TESTFILE2_NAME, + ContentModel.TYPE_CONTENT); + NodeRef testNodeRef2 = testNode2.getNodeRef(); + eu.xenit.apix.data.NodeRef apixTestNodeRef2 = new eu.xenit.apix.data.NodeRef(testNodeRef2.toString()); + initializedNodeRefs.put(TESTFILE2_NAME, apixTestNodeRef2); + + FileInfo testNode3 = createTestNode(testFolder2.getNodeRef(), TESTFILE3_NAME, + ContentModel.TYPE_CONTENT); + NodeRef testNodeRef3 = testNode3.getNodeRef(); + eu.xenit.apix.data.NodeRef apixTestNodeRef3 = new eu.xenit.apix.data.NodeRef(testNodeRef3.toString()); + initializedNodeRefs.put(TESTFILE3_NAME, apixTestNodeRef3); + + FileInfo noUserRightsFolder = createTestNode(mainTestFolder.getNodeRef(), NOUSERRIGHTS_FOLDER_NAME, + ContentModel.TYPE_FOLDER); + setPermissionInheritance(noUserRightsFolder.getNodeRef(), false); + FileInfo noUserRightsNode = createTestNode(noUserRightsFolder.getNodeRef(), NOUSERRIGHTS_FILE_NAME, + ContentModel.TYPE_CONTENT); + NodeRef noUserRightsNodeRef = noUserRightsNode.getNodeRef(); + setPermissionInheritance(noUserRightsNodeRef, false); + eu.xenit.apix.data.NodeRef apixNoUserRightsNodeRef = new eu.xenit.apix.data.NodeRef( + noUserRightsNodeRef.toString()); + initializedNodeRefs.put(NOUSERRIGHTS_FILE_NAME, apixNoUserRightsNodeRef); + + createUser(USERWITHOUTRIGHTS, USERWITHOUTRIGHTS, USERWITHOUTRIGHTS, + USERWITHOUTRIGHTS_EMAIL); + return null; }; transactionService.getRetryingTransactionHelper().doInTransaction(txnWork, false, true); @@ -400,7 +404,7 @@ private T doWithBody(HttpEntityEnclosingRequestBase req, Class returnType final CloseableHttpClient checkoutHttpclient = HttpClients.createDefault(); if (jsonBody != null) { String checkoutJsonString = json(String.format(jsonBody, args)); - req.setEntity(new StringEntity(checkoutJsonString)); + req.setEntity(new StringEntity(checkoutJsonString, ContentType.APPLICATION_JSON)); } try (CloseableHttpResponse response = checkoutHttpclient.execute(req)) { diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/SetInheritParentPermissionsTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/SetInheritParentPermissionsTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/SetInheritParentPermissionsTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/SetInheritParentPermissionsTest.java diff --git a/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/VersionHistoryTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/VersionHistoryTest.java new file mode 100644 index 00000000..f40b7d0b --- /dev/null +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/VersionHistoryTest.java @@ -0,0 +1,207 @@ +package eu.xenit.apix.rest.v1.tests; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; +import eu.xenit.apix.data.NodeRef; +import eu.xenit.apix.versionhistory.Version; +import eu.xenit.apix.versionhistory.VersionHistory; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.version.VersionBaseModel; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.transaction.TransactionService; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.fluent.Request; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +import java.io.IOException; +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +public class VersionHistoryTest extends RestV1BaseTest { + + private static final Logger logger = LoggerFactory.getLogger(VersionHistoryTest.class); + @Autowired + @Qualifier("TransactionService") + TransactionService transactionService; + @Autowired + @Qualifier("NodeService") + NodeService nodeService; + @Autowired + @Qualifier("DictionaryService") + DictionaryService dictionaryService; + @Autowired + private org.alfresco.service.cmr.version.VersionService alfrizcoVersionHistoryService; + @Autowired + private ApixToAlfrescoConversion c; + + @Before + public void setup() { + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + } + + // Holy + // + @Test + public void testGetVersionHistorySimpleNode() throws IOException { + final HashMap initializedNodeRefs = init(); + final String[] url = new String[1]; + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + NodeRef testNode = initializedNodeRefs.get(RestV1BaseTest.TESTFILE3_NAME); + url[0] = createApixUrl("/versionhistory/%s/%s/%s/versions", testNode.getStoreRefProtocol(), + testNode.getStoreRefId(), testNode.getGuid()); + HashMap versionProperties = new HashMap<>(); + versionProperties.put(VersionBaseModel.PROP_VERSION_TYPE, + org.alfresco.service.cmr.version.VersionType.MAJOR); + versionProperties.put(VersionBaseModel.PROP_DESCRIPTION, "Test123"); + org.alfresco.service.cmr.version.Version version = alfrizcoVersionHistoryService + .createVersion(c.alfresco(testNode), versionProperties); + logger.debug(" versioning 1 label: " + version.getVersionLabel()); + versionProperties.put(VersionBaseModel.PROP_DESCRIPTION, "Test456"); + org.alfresco.service.cmr.version.Version version2 = alfrizcoVersionHistoryService + .createVersion(c.alfresco(testNode), versionProperties); + logger.debug(" versioning 2 label: " + version2.getVersionLabel()); + return null; + }, false, true); + + HttpResponse httpResponse = Request.Get(url[0]).execute().returnResponse(); + HttpEntity entity = httpResponse.getEntity(); + String result = EntityUtils.toString(entity); + VersionHistory history = new ObjectMapper().readValue(result, VersionHistory.class); + Assert.assertEquals(2, history.getVersionHistory().size()); + Version mostRecentVersion = history.getVersionHistory().get(0); + Assert.assertEquals(mostRecentVersion.getDescription(), "Test456"); + Version oldestVersion = history.getVersionHistory().get(1); + Assert.assertEquals(oldestVersion.getDescription(), "Test123"); + } + + @Test + public void testSetVersionHistoryWithoutBody() throws IOException { + final HashMap initializedNodeRefs = init(); + final VersionService versionService = alfrizcoVersionHistoryService; + + final boolean defaultAutoVersion = dictionaryService.getProperty(ContentModel.PROP_AUTO_VERSION) + .getDefaultValue().equals("true"); + final boolean defaultAutoVersionProps = dictionaryService.getProperty(ContentModel.PROP_AUTO_VERSION_PROPS) + .getDefaultValue().equals("true"); + final boolean defaultInitialVersion = dictionaryService.getProperty(ContentModel.PROP_INITIAL_VERSION) + .getDefaultValue().equals("true"); + + String versionHistoryUrl = createApixUrl("/versionhistory/%s/%s/%s", + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getStoreRefProtocol(), + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getStoreRefId(), + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getGuid()); + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + assertFalse(versionService + .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()))); + return null; + }, true, true); + + HttpResponse response = Request.Put(versionHistoryUrl) + .execute() + .returnResponse(); + assertEquals(200, response.getStatusLine().getStatusCode()); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + assertTrue(alfrizcoVersionHistoryService + .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()))); + assertEquals(defaultAutoVersion, nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), + ContentModel.PROP_AUTO_VERSION)); + assertEquals(defaultAutoVersionProps, nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), + ContentModel.PROP_AUTO_VERSION_PROPS)); + assertEquals(defaultInitialVersion, nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), + ContentModel.PROP_INITIAL_VERSION)); + assertEquals("1.0", nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), + ContentModel.PROP_VERSION_LABEL)); + return null; + }, true, true); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + assertFalse(alfrizcoVersionHistoryService + .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()))); + return null; + }, true, true); + + String versionHistoryUrl2 = createApixUrl("/versionhistory/%s/%s/%s", + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getStoreRefProtocol(), + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getStoreRefId(), + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getGuid()); + String requestBody = "{" + + "'autoVersion': false," + + "'autoVersionOnUpdateProps': true," + + "'initialVersion': false" + + "}"; + + HttpPut httpPut = new HttpPut(versionHistoryUrl2); + httpPut.setEntity(new StringEntity(json(requestBody), ContentType.APPLICATION_JSON)); + CloseableHttpResponse httpResponse = HttpClients.createDefault() + .execute(httpPut); + assertEquals(200, httpResponse + .getStatusLine() + .getStatusCode()); + + transactionService.getRetryingTransactionHelper() + .doInTransaction(() -> { + assertTrue(alfrizcoVersionHistoryService + .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()))); + assertEquals(false, nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), + ContentModel.PROP_AUTO_VERSION)); + assertEquals(true, nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), + ContentModel.PROP_AUTO_VERSION_PROPS)); + assertEquals(false, nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), + ContentModel.PROP_INITIAL_VERSION)); + assertEquals("1.0", nodeService + .getProperty(new org.alfresco.service.cmr.repository.NodeRef( + initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), + ContentModel.PROP_VERSION_LABEL)); + return null; + }, true, true); + } + + @After + public void cleanUp() { + this.removeMainTestFolder(); + } +} diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/VersionTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/VersionTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/VersionTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/VersionTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/resources/cyrillic_message.msg b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/resources/cyrillic_message.msg similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/resources/cyrillic_message.msg rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/resources/cyrillic_message.msg diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/temp/UploadFileTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/temp/UploadFileTest.java similarity index 64% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/temp/UploadFileTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/temp/UploadFileTest.java index 504c9d82..c518e137 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/temp/UploadFileTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/temp/UploadFileTest.java @@ -1,26 +1,15 @@ package eu.xenit.apix.rest.v1.tests.temp; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Before; import eu.xenit.apix.data.NodeRef; -import eu.xenit.apix.node.INodeService; import eu.xenit.apix.rest.v1.tests.RestV1BaseTest; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.PrintWriter; -import java.net.URL; import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.NodeService; -import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; @@ -29,37 +18,21 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; +import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - public class UploadFileTest extends RestV1BaseTest { - private final static Logger logger = LoggerFactory.getLogger(UploadFileTest.class); + private static final Logger logger = LoggerFactory.getLogger(UploadFileTest.class); private static final String LOCAL_TESTFILE_NAME = "test.txt"; - - @Autowired - ServiceRegistry serviceRegistry; - - @Autowired - INodeService nodeService; - private RetryingTransactionHelper transactionHelper; - private NodeService alfrescoNodeService; - private ContentService contentService; private NodeRef parentNodeRef; private Map initNodeRefArray; @org.junit.Before public void setUp() { - transactionHelper = this.serviceRegistry.getRetryingTransactionHelper(); - alfrescoNodeService = this.serviceRegistry.getNodeService(); - contentService = this.serviceRegistry.getContentService(); - initNodeRefArray = init(); this.parentNodeRef = initNodeRefArray.get(RestV1BaseTest.TESTFOLDER_NAME); } @@ -93,11 +66,11 @@ public void testUploadFileWhereFileAlreadyExists() throws IOException { @Test public void testUploadFileResultsInAccessDenied() throws IOException { String url = createUrl(RestV1BaseTest.USERWITHOUTRIGHTS, RestV1BaseTest.USERWITHOUTRIGHTS); - logger.debug(">>>>> URL: " + url); + logger.debug(">>>>> URL: {}", url); HttpEntity entity = createHttpEntity(initNodeRefArray.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME).toString(), LOCAL_TESTFILE_NAME); try (CloseableHttpResponse response = doPost(url, entity)) { String resultString = EntityUtils.toString(response.getEntity()); - logger.debug(" resultString: " + resultString); + logger.debug(" resultString: {}", resultString); assertEquals(403, response.getStatusLine().getStatusCode()); } } @@ -112,44 +85,44 @@ private String createUrl(String username, String password) { return urlBuilder.toString(); } - private HttpEntity createHttpEntity(String parentRef, String filename) throws IOException { - return MultipartEntityBuilder.create() - .addTextBody("parent", parentRef) - .addTextBody("type", ContentModel.TYPE_CONTENT.toString()) - .addBinaryBody("file", createTestFile(filename)) - .build(); - } - - private CloseableHttpResponse doPost(String url, HttpEntity entity) throws IOException { - CloseableHttpClient httpClient = HttpClients.createDefault(); - HttpPost httpPost = new HttpPost(url); - httpPost.setEntity(entity); - return httpClient.execute(httpPost); - } - @Test - /** Upload a file and simulteanously set its metadata. */ + /* Upload a file and simultaneously set its metadata. */ public void testUploadFileWithMetadata() throws IOException, JSONException { - String property_name = "{http://www.alfresco.org/model/content/1.0}title"; - String property_value = "Saifutsuhengoshiatsuken"; - String metadata = String.format("{ 'propertiesToSet': { '%s': ['%s'] }}", property_name, property_value); + String propertyName = "{http://www.alfresco.org/model/content/1.0}title"; + String propertyValue = "Saifutsuhengoshiatsuken"; + String metadata = String.format("{ 'propertiesToSet': { '%s': ['%s'] }}", propertyName, propertyValue); HttpEntity entity = MultipartEntityBuilder.create() - .addTextBody("parent", this.parentNodeRef.toString()) - .addTextBody("type", ContentModel.TYPE_CONTENT.toString()) + .addTextBody("parent", this.parentNodeRef.toString(), ContentType.TEXT_PLAIN) + .addTextBody("type", ContentModel.TYPE_CONTENT.toString(), ContentType.TEXT_PLAIN) .addBinaryBody("file", createTestFile(LOCAL_TESTFILE_NAME)) - .addTextBody("metadata", json(metadata)) + .addTextBody("metadata", json(metadata), ContentType.APPLICATION_JSON) .build(); CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(makeAlfrescoBaseurlAdmin() + "/apix/v1/nodes/upload"); httpPost.setEntity(entity); try (CloseableHttpResponse response = httpClient.execute(httpPost)) { - String resultString = EntityUtils.toString(response.getEntity()); + EntityUtils.toString(response.getEntity()); assertEquals(200, response.getStatusLine().getStatusCode()); } } + private HttpEntity createHttpEntity(String parentRef, String filename) throws IOException { + return MultipartEntityBuilder.create() + .addTextBody("parent", parentRef) + .addTextBody("type", ContentModel.TYPE_CONTENT.toString()) + .addBinaryBody("file", createTestFile(filename)) + .build(); + } + + private CloseableHttpResponse doPost(String url, HttpEntity entity) throws IOException { + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + httpPost.setEntity(entity); + return httpClient.execute(httpPost); + } + private File createTestFile(String pathName) throws IOException { File result = new File(pathName); @@ -166,31 +139,4 @@ private File createTestFile(String pathName) throws IOException { writer.close(); return result; } - - private File createTempMail() throws IOException { - ClassLoader classLoader = getClass().getClassLoader(); - logger.debug("ClassLoader classLoader = getClass().getClassLoader()"); - URL mail = classLoader.getResource("cyrillic_message.msg"); - logger.debug("Email: " + mail.getPath()); - InputStream inputStream = mail.openStream(); - logger.debug("Input stream is available:" + inputStream.available()); - logger.debug("InputStream inputStream = classLoader.getResourceAsStream(\"cyrillic_message.msg\")"); - - File tempFile = File.createTempFile("test", "msg"); - logger.debug("File tempFile = File.createTempFile(\"test\", \"msg\");"); - logger.debug("tempFile name: " + tempFile.getName()); - tempFile.deleteOnExit(); - logger.debug("tempFile.deleteOnExit()"); - FileOutputStream out = new FileOutputStream(tempFile); - logger.debug("FileOutputStream out = new FileOutputStream(tempFile);"); - IOUtils.copy(inputStream, out); - logger.debug("Copied content to tempFile"); - - out.flush(); - out.close(); - inputStream.close(); - - logger.debug("Flushed and closed input and output streams."); - return tempFile; - } } diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/temp/V1SearchWebscriptTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/temp/V1SearchWebscriptTest.java similarity index 96% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/temp/V1SearchWebscriptTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/temp/V1SearchWebscriptTest.java index b9df68c1..57a0f357 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/temp/V1SearchWebscriptTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v1/tests/temp/V1SearchWebscriptTest.java @@ -6,6 +6,7 @@ import java.io.IOException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -35,7 +36,7 @@ public void testSearch() throws IOException { " 'facets': { 'enabled': true } \n" + "}"); - checkoutHttppost.setEntity(new StringEntity(checkoutJsonString)); + checkoutHttppost.setEntity(new StringEntity(checkoutJsonString, ContentType.APPLICATION_JSON)); CloseableHttpResponse response = checkoutHttpclient.execute(checkoutHttppost); if (500 == response.getStatusLine().getStatusCode()) { diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/AllNodeInfoTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/AllNodeInfoTest.java similarity index 93% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/AllNodeInfoTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/AllNodeInfoTest.java index 4eb87f30..9a953566 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/AllNodeInfoTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/AllNodeInfoTest.java @@ -9,6 +9,7 @@ import org.apache.http.client.fluent.Request; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; @@ -67,7 +68,7 @@ public void testGetAllNodeInfoOfMultipleNodes() throws IOException, JSONExceptio final String url = makeAlfrescoBaseurl("admin", "admin") + "/apix/v2/nodes/nodeInfo"; logger.debug("url: " + url); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(200, response.getStatusLine().getStatusCode()); @@ -79,13 +80,13 @@ public void testGetAllNodeInfoOfMultipleNodes() throws IOException, JSONExceptio @Test public void testGetAllNodeInfoWithNoNodesListed() throws IOException { - String jsonString = json("{}"); + String jsonString = json(""); final CloseableHttpClient httpclient = HttpClients.createDefault(); final String url = makeAlfrescoBaseurl("admin", "admin") + "/apix/v2/nodes/nodeInfo"; logger.debug("url: " + url); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(400, response.getStatusLine().getStatusCode()); @@ -106,7 +107,7 @@ public void testGetAllNodeInfoForNodeWithoutPermissions() throws IOException { "]}"); final CloseableHttpClient httpclient = HttpClients.createDefault(); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(200, response.getStatusLine().getStatusCode()); @@ -133,7 +134,7 @@ public void testGetAllNodeInfoWithoutNodeWithoutPermissions() throws IOException RestV1BaseTest.USERWITHOUTRIGHTS, RestV1BaseTest.USERWITHOUTRIGHTS) + "/apix/v1/nodes/nodeInfo"; logger.debug("url: " + url); HttpPost httppost = new HttpPost(url); - httppost.setEntity(new StringEntity(jsonString)); + httppost.setEntity(new StringEntity(jsonString, ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(httppost)) { assertEquals(200, response.getStatusLine().getStatusCode()); diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/GroupTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/GroupTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/GroupTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/GroupTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/PeopleTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/PeopleTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/PeopleTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/PeopleTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/RestV2BaseTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/RestV2BaseTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/rest/v2/tests/RestV2BaseTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/rest/v2/tests/RestV2BaseTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/ApixImplBundleFilter.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/ApixImplBundleFilter.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/ApixImplBundleFilter.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/ApixImplBundleFilter.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/BaseTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/BaseTest.java similarity index 81% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/BaseTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/BaseTest.java index f419b204..9b8024bb 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/BaseTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/BaseTest.java @@ -2,13 +2,10 @@ import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; import eu.xenit.apix.util.SolrTestHelper; -import eu.xenit.apix.util.SolrTestHelperImpl; import eu.xenit.testing.integrationtesting.runner.AlfrescoTestRunner; import eu.xenit.testing.integrationtesting.runner.UseSpringContextOfBundle; import java.util.Properties; -import javax.sql.DataSource; import org.alfresco.model.ContentModel; -import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory; import org.alfresco.repo.model.Repository; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileFolderService; @@ -21,7 +18,6 @@ import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import java.io.File; import java.io.IOException; @@ -31,17 +27,17 @@ @RunWith(AlfrescoTestRunner.class) @UseSpringContextOfBundle(filter = ApixImplBundleFilter.class) -public abstract class BaseTest implements InitializingBean { +public abstract class BaseTest { //Apix Test model contstants - public final static String APIX_TESTCM_NAMESPACE = "http://test.apix.xenit.eu/model/content"; - public final static String APIX_TESTCM_PREFIX = "apixtest"; - public final static String APIX_TESTCM_PROP_SEARCHSERVICELIMITTEST_SHORTNAME = "searchServiceLimitTestProperty"; - public final static String APIX_TESTCM_PROP_SEARCHSERVICELIMITTEST_PREFIXED = + public static final String APIX_TESTCM_NAMESPACE = "http://test.apix.xenit.eu/model/content"; + public static final String APIX_TESTCM_PREFIX = "apixtest"; + public static final String APIX_TESTCM_PROP_SEARCHSERVICELIMITTEST_SHORTNAME = "searchServiceLimitTestProperty"; + public static final String APIX_TESTCM_PROP_SEARCHSERVICELIMITTEST_PREFIXED = APIX_TESTCM_PREFIX + ":" + APIX_TESTCM_PROP_SEARCHSERVICELIMITTEST_SHORTNAME; - private final static Logger logger = LoggerFactory.getLogger(BaseTest.class); - private final static String mainTestFolderName = "ApixMainTestFolder"; + private static final Logger logger = LoggerFactory.getLogger(BaseTest.class); + private static final String mainTestFolderName = "ApixMainTestFolder"; @Autowired protected ApixToAlfrescoConversion c; @@ -50,23 +46,15 @@ public abstract class BaseTest implements InitializingBean { @Autowired protected Repository repository; @Autowired - DataSource dataSource; - @Autowired - @Qualifier("Search") - SwitchableApplicationContextFactory searchSubSystem; - @Autowired @Qualifier("global-properties") Properties globalProperties; - - public SolrTestHelper solrHelper; - - public void afterPropertiesSet() { - String subsystem = globalProperties.getProperty("index.subsystem.name"); - String solrBaseUrl = subsystem.equals("solr4") ? "/solr4" : "/solr"; - solrHelper = new SolrTestHelperImpl(solrBaseUrl, dataSource, searchSubSystem); - } + @Autowired + protected SolrTestHelper solrHelper; protected NodeRef getNodeAtPath(String path) { + if("/app:company_home".equals(path)) { + return repository.getCompanyHome(); + } SearchService searchService = serviceRegistry.getSearchService(); StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; ResultSet resultSet = searchService.query(storeRef, SearchService.LANGUAGE_XPATH, path); diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/categories/CategoryServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/categories/CategoryServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/categories/CategoryServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/categories/CategoryServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/comments/CommentServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/comments/CommentServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/comments/CommentServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/comments/CommentServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/content/ContentServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/content/ContentServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/content/ContentServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/content/ContentServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/dictionary/DictionaryServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/dictionary/DictionaryServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/dictionary/DictionaryServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/dictionary/DictionaryServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/filefolder/FileFolderServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/filefolder/FileFolderServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/filefolder/FileFolderServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/filefolder/FileFolderServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/metadata/AlfrescoPropertyConvertorTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/metadata/AlfrescoPropertyConvertorTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/metadata/AlfrescoPropertyConvertorTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/metadata/AlfrescoPropertyConvertorTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/metadata/NodeServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/metadata/NodeServiceTest.java similarity index 99% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/metadata/NodeServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/metadata/NodeServiceTest.java index 76f3f491..add86c16 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/metadata/NodeServiceTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/metadata/NodeServiceTest.java @@ -270,14 +270,14 @@ public void TestGetMetadata() { NodeMetadata metadatas = service.getMetadata(c.apix(roots.iterator().next())); logger.debug(metadatas.toString().replaceAll(",", ",\n")); - assertEquals(c.apix(roots.iterator().next()), metadatas.id); + assertEquals(c.apix(roots.iterator().next()), metadatas.getId()); - assertTrue(metadatas.properties.containsKey(c.apix(ContentModel.PROP_NAME))); + assertTrue(metadatas.getProperties().containsKey(c.apix(ContentModel.PROP_NAME))); } private Boolean nodeIsInMetaList(NodeRef node, List metadatas) { for (NodeMetadata metadata : metadatas) { - if (metadata.id.equals(node.toString())) { + if (metadata.getId().equals(node.toString())) { return true; } } diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/people/PeopleServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/people/PeopleServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/people/PeopleServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/people/PeopleServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/permissions/PermissionServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/permissions/PermissionServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/permissions/PermissionServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/permissions/PermissionServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/properties/PropertyServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/properties/PropertyServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/properties/PropertyServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/properties/PropertyServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/FtsNodeVisitorTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/FtsNodeVisitorTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/FtsNodeVisitorTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/FtsNodeVisitorTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/QueryBuilderTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/QueryBuilderTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/QueryBuilderTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/QueryBuilderTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchNodeParsingTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchNodeParsingTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchNodeParsingTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchNodeParsingTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchServiceFacetsTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchServiceFacetsTest.java similarity index 70% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchServiceFacetsTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchServiceFacetsTest.java index e70dbc73..1996e0a0 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchServiceFacetsTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchServiceFacetsTest.java @@ -15,13 +15,10 @@ import org.alfresco.repo.search.impl.solr.facet.SolrFacetProperties; import org.alfresco.repo.search.impl.solr.facet.SolrFacetService; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.namespace.QName; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; /** @@ -31,7 +28,6 @@ */ public class SearchServiceFacetsTest extends SearchServiceTest { - private final static Logger logger = LoggerFactory.getLogger(SearchServiceFacetsTest.class); private static final String ADMIN_USER_NAME = "admin"; @Autowired @@ -42,54 +38,6 @@ public void Setup() { AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER_NAME); } - @Override - protected void withTestFacets(final RunAsWork work) throws Exception { - SolrFacetProperties languageFacet = new SolrFacetProperties.Builder() - .filterID("document_language") - .facetQName(APIXTEST_LANGUAGE) - .displayControl("Simple Filter") - .displayName("Language") - .maxFilters(10) - .hitThreshold(1) - .minFilterValueLength(1) - .sortBy("ASCENDING") - .scope("ALL") - .isEnabled(true) - .isDefault(true) - .build(); - - final SolrFacetProperties documentStatusFacet = new SolrFacetProperties.Builder() - .filterID("document_status") - .facetQName(APIXTEST_DOCUMENT_STATUS) - .displayControl("Simple Filter") - .displayName("Status") - .maxFilters(10) - .hitThreshold(1) - .minFilterValueLength(1) - .sortBy("ASCENDING") - .scope("ALL") - .isEnabled(true) - .isDefault(true) - .build(); - - withFacet(languageFacet, new RunAsWork() { - @Override - public Object doWork() throws Exception { - withFacet(documentStatusFacet, work); - return null; - } - }); - - } - private void withFacet(SolrFacetProperties facetProperties, AuthenticationUtil.RunAsWork work) throws Exception { - facetService.createFacetNode(facetProperties); - try { - work.doWork(); - } finally { - facetService.deleteFacet(facetProperties.getFilterID()); - } - } - @Test public void TestGetWithFacetsIncludesCustomFilterFacets() throws InterruptedException { solrHelper.waitForTransactionSync(); @@ -162,7 +110,7 @@ public void TestGetBucketedFacets() throws InterruptedException { query.setFacets(options); SearchQueryResult result = searchService.query(query); - // Search in results (Because of alf 4.2 source language level is 1.7. No lambdas to make this pretty 🙁) + // Search in results (Because of alf 4.2 source language level is 1.7. No lambdas to make this pretty) for (FacetSearchResult facetResult : result.getFacets()) { String facetName = facetResult.getName(); if (bucketedFacetNames.contains(facetName)) { diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchServiceTest.java similarity index 66% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchServiceTest.java index 03603f73..5f767e15 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchServiceTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchServiceTest.java @@ -11,17 +11,14 @@ import eu.xenit.apix.search.SearchQueryResult; import eu.xenit.apix.search.nodes.SearchSyntaxNode; import eu.xenit.apix.tests.BaseTest; -import eu.xenit.apix.util.SolrTestHelperImpl; -import java.io.IOException; import java.io.Serializable; import java.math.BigInteger; import java.util.HashMap; -import java.util.Locale; import java.util.Map; + import org.alfresco.model.ContentModel; import org.alfresco.repo.model.Repository; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileInfo; @@ -39,15 +36,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.surf.util.I18NUtil; -abstract public class SearchServiceTest extends BaseTest { +public abstract class SearchServiceTest extends BaseTest { private static final String LONG_MAX_VALUE = String.valueOf(Long.MAX_VALUE); private static final String LONG_MAX_VALUE_PLUS_ONE = new BigInteger(LONG_MAX_VALUE).add(new BigInteger("1")) .toString(); - private final static Logger logger = LoggerFactory.getLogger(SearchServiceTest.class); + private static final Logger logger = LoggerFactory.getLogger(SearchServiceTest.class); private static final String ADMIN_USER_NAME = "admin"; public static final String DESCRIPTION_SET_OF_1001 = "descriptionSetOf1001"; @Autowired @@ -64,12 +60,6 @@ abstract public class SearchServiceTest extends BaseTest { @Autowired NamespacePrefixResolver namespacePrefixResolver; - protected SolrTestHelperImpl solrTestHelper; - - final protected static String APIXTEST_NS = "http://test.apix.xenit.eu/model/content"; - final protected static QName APIXTEST_LANGUAGE = QName.createQName(APIXTEST_NS, "language"); - final protected static QName APIXTEST_DOCUMENT_STATUS = QName.createQName(APIXTEST_NS, "documentStatus"); - @Before public void Setup() { AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER_NAME); @@ -81,8 +71,7 @@ public void tearDown() { } @Test - public void TestGetWithoutFacets() throws IOException, InterruptedException { - solrHelper.waitForTransactionSync(); + public void TestGetWithoutFacets() { QueryBuilder builder = new QueryBuilder(); SearchSyntaxNode node = builder.term("type", "cm:folder").create(); @@ -96,8 +85,7 @@ public void TestGetWithoutFacets() throws IOException, InterruptedException { } @Test - public void TestGetWithFacets() throws IOException, InterruptedException { - solrHelper.waitForTransactionSync(); + public void TestGetWithFacets() { QueryBuilder builder = new QueryBuilder(); SearchSyntaxNode node = builder.term("type", "cm:content").create(); @@ -119,8 +107,7 @@ public void TestGetWithFacets() throws IOException, InterruptedException { } @Test - public void TestLimit() throws IOException, InterruptedException { - solrHelper.waitForTransactionSync(); + public void TestLimit() { QueryBuilder builder = new QueryBuilder(); SearchSyntaxNode node = builder.term("type", "cm:folder").create(); @@ -133,8 +120,7 @@ public void TestLimit() throws IOException, InterruptedException { } @Test - public void TestSkip() throws IOException, InterruptedException { - solrHelper.waitForTransactionSync(); + public void TestSkip() { QueryBuilder builder = new QueryBuilder(); eu.xenit.apix.search.nodes.SearchSyntaxNode node = builder.term("type", "cm:folder").create(); @@ -155,20 +141,17 @@ public void TestSkip() throws IOException, InterruptedException { } @Test - public void TestTotalCount() throws IOException, InterruptedException { + public void TestTotalCount() throws InterruptedException { transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public NodeRef execute() throws Throwable { + .doInTransaction((RetryingTransactionCallback) () -> { - NodeRef companyHomeRef = repository.getCompanyHome(); + NodeRef companyHomeRef = repository.getCompanyHome(); - FileInfo mainTestFolder = createMainTestFolder(companyHomeRef); - FileInfo testFolder = createTestFolder(mainTestFolder.getNodeRef(), "testFolder"); - FileInfo testNode = createTestNode(testFolder.getNodeRef(), "testNode"); - FileInfo testNode2 = createTestNode(testFolder.getNodeRef(), "testNode2"); - return null; - } + FileInfo mainTestFolder = createMainTestFolder(companyHomeRef); + FileInfo testFolder = createTestFolder(mainTestFolder.getNodeRef(), "testFolder"); + FileInfo testNode = createTestNode(testFolder.getNodeRef(), "testNode"); + FileInfo testNode2 = createTestNode(testFolder.getNodeRef(), "testNode2"); + return null; }, false, true); solrHelper.waitForTransactionSync(); @@ -176,24 +159,21 @@ public NodeRef execute() throws Throwable { Thread.sleep(15000); transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public NodeRef execute() throws Throwable { - QueryBuilder builder = new QueryBuilder(); - SearchSyntaxNode node = builder.property( - ContentModel.PROP_NAME.toPrefixString(namespacePrefixResolver), - "testNode", - false) - .create(); - - SearchQuery query = new SearchQuery(); - query.setQuery(node); - SearchQueryResult result = searchService.query(query); - - logger.debug("Total: " + result.getTotalResultCount()); - Assert.assertEquals(2, result.getTotalResultCount()); - return null; - } + .doInTransaction((RetryingTransactionCallback) () -> { + QueryBuilder builder = new QueryBuilder(); + SearchSyntaxNode node = builder.property( + ContentModel.PROP_NAME.toPrefixString(namespacePrefixResolver), + "testNode", + false) + .create(); + + SearchQuery query = new SearchQuery(); + query.setQuery(node); + SearchQueryResult result = searchService.query(query); + + logger.debug("Total: " + result.getTotalResultCount()); + assertEquals(2, result.getTotalResultCount()); + return null; }, false, true); } @@ -204,11 +184,11 @@ private void create1001TestDocs() throws InterruptedException { FileInfo mainTestFolder = createMainTestFolder(companyHomeRef); FileInfo testFolder = createTestFolder(mainTestFolder.getNodeRef(), "testFolderSetOf1001"); - Map props = new HashMap() {{ - put(QName.createQName(APIX_TESTCM_NAMESPACE, - APIX_TESTCM_PROP_SEARCHSERVICELIMITTEST_SHORTNAME), - DESCRIPTION_SET_OF_1001); - }}; + Map props = new HashMap<>(); + props.put( + QName.createQName(APIX_TESTCM_NAMESPACE, APIX_TESTCM_PROP_SEARCHSERVICELIMITTEST_SHORTNAME), + DESCRIPTION_SET_OF_1001 + ); for (int i = 0; i < 1001 ; i++) { FileInfo testNode = createTestNode(testFolder.getNodeRef(), "testNode-1001-" + i); nodeService.addProperties(testNode.getNodeRef(), props); @@ -283,8 +263,7 @@ public void TestLimitedByMaxPermissionChecks_eventual() throws InterruptedExcept } @Test - public void testPropertyRange() throws IOException, InterruptedException { - solrHelper.waitForTransactionSync(); + public void testPropertyRange() { QueryBuilder builder = new QueryBuilder(); SearchSyntaxNode node = builder .property("cm:created", "2010-01-01T00:00:00", "2015-01-01T00:00:00").create(); @@ -312,63 +291,55 @@ public static void waitAWhile(int nbSeconds) throws InterruptedException { * should always work. */ @Test - public void TestQueryConsistency_Transactional() throws InterruptedException { - solrHelper.waitForTransactionSync(); + public void TestQueryConsistency_Transactional() { final String theTitle = "The title to search for in SearchService.TestQueryConsistency_Transactional" + System.nanoTime(); final NodeRef theNewNode = transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public NodeRef execute() throws Throwable { - cleanUp(); - NodeRef companyHomeRef = repository.getCompanyHome(); - FileInfo mainTestFolder = createMainTestFolder(companyHomeRef); - - // create node - ChildAssociationRef testFolderAssoc = serviceRegistry.getNodeService() - .createNode(mainTestFolder.getNodeRef(), ContentModel.ASSOC_CONTAINS, - org.alfresco.service.namespace.QName - .createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "aNode.txt"), - ContentModel.TYPE_CONTENT); - NodeRef theNewNode = testFolderAssoc.getChildRef(); - - Map props = serviceRegistry - .getNodeService() - .getProperties(theNewNode); - props.put(ContentModel.PROP_TITLE, theTitle); - serviceRegistry.getNodeService().setProperties(theNewNode, props); - return theNewNode; - } + .doInTransaction(() -> { + cleanUp(); + NodeRef companyHomeRef = repository.getCompanyHome(); + FileInfo mainTestFolder = createMainTestFolder(companyHomeRef); + + // create node + ChildAssociationRef testFolderAssoc = serviceRegistry.getNodeService() + .createNode(mainTestFolder.getNodeRef(), ContentModel.ASSOC_CONTAINS, + QName + .createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "aNode.txt"), + ContentModel.TYPE_CONTENT); + NodeRef theNewNode1 = testFolderAssoc.getChildRef(); + + Map props = serviceRegistry + .getNodeService() + .getProperties(theNewNode1); + props.put(ContentModel.PROP_TITLE, theTitle); + serviceRegistry.getNodeService().setProperties(theNewNode1, props); + return theNewNode1; }, false, true); // Don't wait, immediately do a search transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Boolean execute() throws Throwable { - QueryBuilder builder = new QueryBuilder(); - eu.xenit.apix.search.nodes.SearchSyntaxNode node = builder - .property("cm:title", theTitle, true) - .create(); // Exact match is required - SearchQuery query = new SearchQuery(); - query.setQuery(node); - query.setConsistency(SearchQueryConsistency.TRANSACTIONAL); - SearchQueryResult result = searchService.query(query); - - assertTrue( - "Should find back the new node immediately when using transactional consistency!", - result.getNoderefs().contains(theNewNode.toString())); - - return Boolean.TRUE; - } + .doInTransaction(() -> { + QueryBuilder builder = new QueryBuilder(); + SearchSyntaxNode node = builder + .property("cm:title", theTitle, true) + .create(); // Exact match is required + SearchQuery query = new SearchQuery(); + query.setQuery(node); + query.setConsistency(SearchQueryConsistency.TRANSACTIONAL); + SearchQueryResult result = searchService.query(query); + + assertTrue( + "Should find back the new node immediately when using transactional consistency!", + result.getNoderefs().contains(theNewNode.toString())); + + return Boolean.TRUE; }, false, true); } @Test - public void TestExactMatchProperty() throws IOException, InterruptedException { - solrHelper.waitForTransactionSync(); + public void TestExactMatchProperty() { QueryBuilder builder = new QueryBuilder(); SearchSyntaxNode node = builder.property("cm:name", "Company Home", true).create(); // Exact match @@ -390,22 +361,6 @@ public void TestExactMatchProperty() throws IOException, InterruptedException { result.getNoderefs().get(0)); } - private void withLocale(Locale locale, AuthenticationUtil.RunAsWork work) throws Exception { - Locale prevLocale = I18NUtil.getLocale(); - I18NUtil.setLocale(locale); - - try { - work.doWork(); - } finally { - I18NUtil.setLocale(prevLocale); - } - } - - /** - * Implemented in alfresco version specific subclasses to set up and tear down solr facets. - */ - abstract protected void withTestFacets(AuthenticationUtil.RunAsWork work) throws Exception; - @Test public void validLong_onlyNode_doesNotThrowException() { diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchSyntaxPrinterTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchSyntaxPrinterTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/SearchSyntaxPrinterTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/SearchSyntaxPrinterTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/TermHitHighlightingTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/TermHitHighlightingTest.java similarity index 95% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/TermHitHighlightingTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/TermHitHighlightingTest.java index 8888a2f6..45a0984b 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/search/TermHitHighlightingTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/search/TermHitHighlightingTest.java @@ -33,16 +33,12 @@ @Ignore("Disabled for GHA build") public class TermHitHighlightingTest extends BaseTest { - private static final Logger log = LoggerFactory.getLogger(TermHitHighlightingTest.class); - @Autowired INodeService nodeService; @Autowired SearchService searchService; @Autowired RetryingTransactionHelper retryingTransactionHelper; - - private ObjectMapper mapper = new ObjectMapper(); private static final String FURIES_TXT = "" + "The furies are at home\nin the mirror; it is their address.\nEven the clearest water,\nif deep enough can drown.\n" + "\nNever think to surprise them.\nYour face approaching ever\nso friendly is the white flag\nthey ignore. There is no truce\n" @@ -72,7 +68,7 @@ public void cleanupAfterHighlightTest() { @Test /** Test all major parameters for term hit highlighting */ - public void searchResponseContainsHighlights() throws IOException, InterruptedException { + public void searchResponseContainsHighlights() throws InterruptedException { int initialCleanDocs = solrHelper.getNumberOfFtsStatusCleanDocs(); List expected = Arrays.asList(new HighlightResult("cm:content", Arrays.asList("" + "The !PREFIX!furies!SUFFIX! are at home\nin the mirror; it is their address.\n" diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/transaction/TransactionServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/transaction/TransactionServiceTest.java similarity index 95% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/transaction/TransactionServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/transaction/TransactionServiceTest.java index 7c99e252..76e55e6c 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/transaction/TransactionServiceTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/transaction/TransactionServiceTest.java @@ -21,7 +21,6 @@ public class TransactionServiceTest extends BaseTest { ITransactionService service; @Autowired IFileFolderService ffservice; - private int four = 4; //dummy variable to skip compile time checks. Dont change this please public FileInfo Setup() { this.cleanUp(); diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/translation/TranslationServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/translation/TranslationServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/translation/TranslationServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/translation/TranslationServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/versionhistory/VersionHistoryServiceTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/versionhistory/VersionHistoryServiceTest.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/versionhistory/VersionHistoryServiceTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/versionhistory/VersionHistoryServiceTest.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/WorkflowServiceBaseTest.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/WorkflowServiceBaseTest.java similarity index 99% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/WorkflowServiceBaseTest.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/WorkflowServiceBaseTest.java index 9ef11a5d..3be13881 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/WorkflowServiceBaseTest.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/WorkflowServiceBaseTest.java @@ -23,10 +23,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import java.io.Serializable; import java.util.*; + public abstract class WorkflowServiceBaseTest extends BaseTest { protected final static Logger logger = LoggerFactory.getLogger(WorkflowServiceBaseTest.class); @@ -49,6 +51,7 @@ public abstract class WorkflowServiceBaseTest extends BaseTest { @Autowired protected ServiceRegistry serviceRegistry; @Autowired + @Qualifier("eu.xenit.apix.workflow.IWorkflowService") protected IWorkflowService apixWorkflowService; protected RetryingTransactionHelper transactionHelper; protected MutableAuthenticationService authenticationService; diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_BaseMethod_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_BaseMethod_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_BaseMethod_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_BaseMethod_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelMyWorkflow_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelMyWorkflow_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelMyWorkflow_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelMyWorkflow_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflowAsAdmin_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflowAsAdmin_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflowAsAdmin_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflowAsAdmin_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflow_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflow_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflow_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_CancelSomebodyElseWorkflow_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPoolAsAdmin_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPoolAsAdmin_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPoolAsAdmin_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPoolAsAdmin_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPool_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPool_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPool_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnOtherPool_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnReviewTask_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnReviewTask_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnReviewTask_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimOnReviewTask_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimReleaseWorkflowTaskOnPooledTask_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimReleaseWorkflowTaskOnPooledTask_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimReleaseWorkflowTaskOnPooledTask_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ClaimReleaseWorkflowTaskOnPooledTask_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessToAsAdmin_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessToAsAdmin_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessToAsAdmin_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessToAsAdmin_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessTo_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessTo_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessTo_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIDontHaveAccessTo_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIHaveAccessTo_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIHaveAccessTo_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIHaveAccessTo_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetTaskByIDIHaveAccessTo_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessToAsAdmin_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessToAsAdmin_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessToAsAdmin_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessToAsAdmin_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessTo_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessTo_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessTo_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIDontHaveAccessTo_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIHaveAccessTo_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIHaveAccessTo_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIHaveAccessTo_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_GetWorkflowByIDIHaveAccessTo_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasksAsAdmin_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasksAsAdmin_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasksAsAdmin_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasksAsAdmin_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasks_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasks_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasks_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_ReleaseOnOtherClaimedTasks_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextAllTasks_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextAllTasks_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextAllTasks_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextAllTasks_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyPooledTasks_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyPooledTasks_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyPooledTasks_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyPooledTasks_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyTasks_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyTasks_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyTasks_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_SearchContextMyTasks_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionMyTask_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionMyTask_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionMyTask_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionMyTask_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTaskAsAdmin_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTaskAsAdmin_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTaskAsAdmin_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTaskAsAdmin_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTask_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTask_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTask_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_TransitionSomeoneElseTask_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateEmpty_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateEmpty_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateEmpty_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateEmpty_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateMixedProperties_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateMixedProperties_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateMixedProperties_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateMixedProperties_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateModifiableProperties_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateModifiableProperties_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateModifiableProperties_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateModifiableProperties_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateNull_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateNull_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateNull_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateNull_Test.java diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateUnmodifiableProperties_Test.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateUnmodifiableProperties_Test.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateUnmodifiableProperties_Test.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/tests/workflow/methods/WorkflowService_UpdateUnmodifiableProperties_Test.java diff --git a/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/AlfrescoServerInfo.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/AlfrescoServerInfo.java new file mode 100644 index 00000000..74398eb0 --- /dev/null +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/AlfrescoServerInfo.java @@ -0,0 +1,51 @@ +package eu.xenit.apix.util; + +import com.github.dynamicextensionsalfresco.osgi.OsgiService; +import org.alfresco.service.descriptor.DescriptorService; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +@Service +@OsgiService +public class AlfrescoServerInfo { + // Part of Alfresco Private API + private final DescriptorService descriptorService; + private final DataSource dataSource; + + public AlfrescoServerInfo(@Qualifier("descriptorComponent") DescriptorService descriptorServiceParam, + DataSource dataSourceParam) { + descriptorService = descriptorServiceParam; + dataSource = dataSourceParam; + } + + public String getAlfrescoVersion() { + return descriptorService + .getServerDescriptor() + .getVersion(); + } + + /** + * Tried using SearchTrackingComponent and NodeDAO to getMaxTxnId, but they are not as consistent and quick + * as getting it from alf_node table. + */ + public long getAlfTransactionIdDAO() { + try (Connection connection = dataSource.getConnection()) { + try(final Statement stmt = connection.createStatement()) { + try(final ResultSet rs = stmt.executeQuery("select max( transaction_id ) from alf_node")) { + if (rs.next()) { + return rs.getInt(1); + } + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return -1; + } +} diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrAdminClient.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrAdminClient.java similarity index 86% rename from apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrAdminClient.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrAdminClient.java index 677c6175..49852aee 100644 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrAdminClient.java +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrAdminClient.java @@ -12,9 +12,9 @@ public class SolrAdminClient { - private SwitchableApplicationContextFactory searchSubSystem; - private SolrAdminHTTPClient client; - private String baseUrl; + private final SwitchableApplicationContextFactory searchSubSystem; + private final String baseUrl; + private SolrAdminHTTPClient client = null; public SolrAdminClient(String baseUrl, SwitchableApplicationContextFactory searchSubSystem) { this.baseUrl = baseUrl; @@ -56,9 +56,11 @@ public JSONObject getSolrSummaryJson() throws JSONException { return object.getJSONObject("Summary"); } - public int getLastTransactionId() { + public long getLastTransactionId() { try { - return getSolrSummaryJson().getJSONObject("alfresco").getInt("Id for last TX in index"); + return getSolrSummaryJson() + .getJSONObject("alfresco") + .getLong("Id for last TX in index"); } catch (JSONException e) { e.printStackTrace(); return -1; diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrTestHelper.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrTestHelper.java similarity index 100% rename from apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrTestHelper.java rename to apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrTestHelper.java diff --git a/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java new file mode 100644 index 00000000..7404cc57 --- /dev/null +++ b/apix-integrationtests/alfresco/src/main/java/eu/xenit/apix/util/SolrTestHelperImpl.java @@ -0,0 +1,140 @@ +package eu.xenit.apix.util; + +import java.util.Properties; +import java.util.function.Supplier; + +import com.github.dynamicextensionsalfresco.osgi.OsgiService; +import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +/** + * This class should only be used by the integration tests. + */ +@Service +@OsgiService +public class SolrTestHelperImpl implements SolrTestHelper { + private static final Logger logger = LoggerFactory.getLogger(SolrTestHelperImpl.class); + + private static final String SOLR_SUMMARY_CORE_ALFRESCO = "alfresco"; + private static final String SOLR_SUMMARY_FTS = "FTS"; + private static final String SOLR_SUMMARY_FTS_DIRTY = "Node count whose content needs to be updated"; + private static final String SOLR_SUMMARY_FTS_CLEAN = "Node count whose content is in sync"; + private static final String SOLR_SUMMARY_FTS_NEW_6 = "Node count with FTSStatus New"; + private static final String SOLR_SUMMARY_FTS_DIRTY_6 = "Node count with FTSStatus Dirty"; + private static final String SOLR_SUMMARY_FTS_CLEAN_6 = "Node count with FTSStatus Clean"; + + private final AlfrescoServerInfo alfrescoServerInfo; + protected SolrAdminClient solrAdminClient; + + private static final int MAX_ATTEMPTS = 20; + + public SolrTestHelperImpl( + @Qualifier("global-properties") Properties globalProperties, + @Qualifier("Search") SwitchableApplicationContextFactory searchSubSystem, + + AlfrescoServerInfo alfrescoServerInfoParam) { + String subsystem = globalProperties.getProperty("index.subsystem.name"); + String solrBaseUrl = subsystem.equals("solr4") ? "/solr4" : "/solr"; + alfrescoServerInfo = alfrescoServerInfoParam; + solrAdminClient = new SolrAdminClient(solrBaseUrl, searchSubSystem); + } + + /** + * Beware of calling this method too frequently since it runs an aggregate query over the entire alf_nodes table + * + * @return true if the latest Alfresco transaction has been indexed by Solr + */ + @Override + public boolean areTransactionsSynced() { + long alfTransaction = alfrescoServerInfo.getAlfTransactionIdDAO(); + long solrTransaction = solrAdminClient.getLastTransactionId(); + logger.debug("alf transaction: {}, solr transaction: {}", alfTransaction, solrTransaction); + return alfTransaction <= solrTransaction; + } + + /** + * Wait until Solr has indexed the latest Alfresco transaction. + */ + @Override + public void waitForTransactionSync() throws InterruptedException { + waitForCompletion(this::areTransactionsSynced); + } + + @Override + public int getNumberOfFtsStatusCleanDocs() { + return (Integer) getSummaryFtsSection().get( + isAlfresco61() ? SOLR_SUMMARY_FTS_CLEAN_6 : SOLR_SUMMARY_FTS_CLEAN + ); + } + + @Override + public boolean isContentIndexed() { + return isContentIndexedImpl(null); + } + + @Override + public boolean isContentIndexed(int previousCleanCount) { + logger.debug("previousCleanCount: {}", previousCleanCount); + return isContentIndexedImpl(previousCleanCount); + } + + @Override + public void waitForContentSync() throws InterruptedException { + waitForCompletion(() -> isContentIndexedImpl(null)); + } + + @Override + public void waitForContentSync(int previousCleanCount) throws InterruptedException { + logger.debug("previousCleanCount: {}", previousCleanCount); + waitForCompletion(() -> isContentIndexedImpl(previousCleanCount)); + } + + protected JSONObject getSummaryFtsSection() { + return solrAdminClient + .getSolrSummaryJson() + .getJSONObject(SOLR_SUMMARY_CORE_ALFRESCO) + .getJSONObject(SOLR_SUMMARY_FTS); + } + + private void waitForCompletion(Supplier hasCompleted) throws InterruptedException { + System.out.print("Waiting 5 seconds for Solr to index content"); + for (int i = 0; i < MAX_ATTEMPTS; i++) { + if (hasCompleted.get()) { + return; + } + for (int j = 0; j < 5; j++) { + System.out.print("..." + ((i * 5) + j + 1)); + Thread.sleep(1000); + } + System.out.println(); + } + } + + private boolean isContentIndexedImpl(Integer previousCleanCount) { + JSONObject ftsBlock = getSummaryFtsSection(); + logger.debug("solrSummaryFTSBlock: {}", ftsBlock.toString(4)); + if(isAlfresco61()) { + return 0 == ((Integer) ftsBlock.get(SOLR_SUMMARY_FTS_NEW_6)) + && 0 == (Integer) ftsBlock.get(SOLR_SUMMARY_FTS_DIRTY_6) + && checkPreviousCount(previousCleanCount, (Integer) ftsBlock.get(SOLR_SUMMARY_FTS_CLEAN_6)); + } + return 0 == (Integer) ftsBlock.get(SOLR_SUMMARY_FTS_DIRTY) + && checkPreviousCount(previousCleanCount, (Integer) ftsBlock.get(SOLR_SUMMARY_FTS_CLEAN)); + } + + private boolean isAlfresco61() { + logger.debug("Alfresco version: {}", alfrescoServerInfo.getAlfrescoVersion()); + return alfrescoServerInfo.getAlfrescoVersion().startsWith("6.1"); + } + + private boolean checkPreviousCount(Integer previousCleanCount, Integer solrSummaryFtsCount) { + if(previousCleanCount == null) { + return true; // no need to check + } + return previousCleanCount < solrSummaryFtsCount; + } +} diff --git a/apix-integrationtests/src/main/resources/cyrillic_message.msg b/apix-integrationtests/alfresco/src/main/resources/cyrillic_message.msg similarity index 100% rename from apix-integrationtests/src/main/resources/cyrillic_message.msg rename to apix-integrationtests/alfresco/src/main/resources/cyrillic_message.msg diff --git a/apix-integrationtests/src/main/resources/facet-forms-config.json b/apix-integrationtests/alfresco/src/main/resources/facet-forms-config.json similarity index 100% rename from apix-integrationtests/src/main/resources/facet-forms-config.json rename to apix-integrationtests/alfresco/src/main/resources/facet-forms-config.json diff --git a/apix-integrationtests/alfresco/src/test/java/eu/xenit/apix/util/AlfrescoServerInfoTest.java b/apix-integrationtests/alfresco/src/test/java/eu/xenit/apix/util/AlfrescoServerInfoTest.java new file mode 100644 index 00000000..9b0a151e --- /dev/null +++ b/apix-integrationtests/alfresco/src/test/java/eu/xenit/apix/util/AlfrescoServerInfoTest.java @@ -0,0 +1,22 @@ +package eu.xenit.apix.util; + +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.descriptor.DescriptorService; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class AlfrescoServerInfoTest { + @Test + public void testVersionLowerThan6x() { + DescriptorService descriptorService = mock(DescriptorService.class); + Descriptor descriptor = mock(Descriptor.class); + doReturn("6.1.1 (3 rc25a8127-b23)").when(descriptor).getVersion(); + doReturn(descriptor).when(descriptorService).getServerDescriptor(); + AlfrescoServerInfo asi = new AlfrescoServerInfo(descriptorService, null); + String version = asi.getAlfrescoVersion(); + assertTrue(version.startsWith("6.")); + } +} diff --git a/apix-integrationtests/build.gradle b/apix-integrationtests/build.gradle deleted file mode 100644 index 6c2b97d1..00000000 --- a/apix-integrationtests/build.gradle +++ /dev/null @@ -1,123 +0,0 @@ -plugins { - id 'base' - id 'eu.xenit.de' version '2.1.3' apply false - id 'eu.xenit.alfresco' version '1.1.0' apply false - id 'eu.xenit.alfresco-remote-testrunner' version '2.0.1' apply false -} - -allprojects { - // Following needs to apply to shared (./src) project and version-specific (e.g. ./70) projects. - apply plugin: 'eu.xenit.de' - apply plugin: 'eu.xenit.alfresco-remote-testrunner' - apply plugin: 'eu.xenit.alfresco' - apply from: "${project.projectDir}/overrides.gradle" - - sourceSets { - main { - java { - srcDirs = [] - } - resources { - srcDirs = [] - } - } - integrationTest { - java { - srcDirs = ['./src/main/java'] - } - resources { - srcDirs = ['./src/main/resources'] - } - } - } - - dependencies { - alfrescoProvided project(":apix-rest-v1") - alfrescoProvided project(":apix-interface") - alfrescoProvided project(":apix-impl:apix-impl-${subproject_alfresco_version}") - - alfrescoProvided group: 'org.alfresco', name: 'alfresco-repository', version: alfresco_repo_version - alfrescoProvided group: 'org.alfresco', name: 'alfresco-remote-api', version: alfresco_remote_api_version - alfrescoProvided group: 'org.alfresco', name: 'alfresco-data-model', version: alfresco_dm_version - alfrescoProvided group: 'org.osgi', name: 'org.osgi.core', version: '4.3.1' - - // Below is a temporary fix to circumvent using commons-lang 1.0.0 JAR. This jar - // - is injected by alfresco-remote-testrunner - // - is not on Maven Central (only 1.0) is - // - nobody what it used for exactly - // - nobody knows where it came from - // Ergo, kill it with fire. - // The proper way is a fix of alfresco-remote-testrunner, but until then we use this workaround. - integrationTestImplementationLocal "commons-lang:commons-lang:1.0" - } -} - -subprojects { - sourceSets { - integrationTest { - java { - srcDirs = ['../src/main/java', './src/main/java'] - } - resources { - srcDirs = ['../src/main/resources', './src/main/resources'] - } - } - } - - def projectDocker = project(":apix-docker:docker-${subproject_alfresco_version}") - evaluationDependsOn(projectDocker.path) - def composeUpTask = projectDocker.tasks.getByName("composeUp") - def composeDownTask = projectDocker.tasks.getByName("composeDown") - - alfrescoIntegrationTest { - integrationTestPackages = ["eu.xenit.apix"] - } - - alfrescoDynamicExtensions { - repository { - endpoint { - protocol = project.hasProperty('protocol') ? project.protocol : 'http' - host = project.hasProperty('host') ? project.host : 'localhost' - port = project.hasProperty('port') ? project.port : 8080 - } - } - } - - installBundle.enabled = false - - installIntegrationTestingBundle { - dependsOn composeUpTask - doFirst { - def serviceInfo = composeUpTask.servicesInfos.get("alfresco-core") - if(serviceInfo != null) { - alfrescoDynamicExtensions { - repository { - endpoint { - host = serviceInfo.getHost() - port = serviceInfo.ports[8080] - } - } - } - } - } - } - - dependencies { - // This dependency is for code-highlighting in Intellij only, - // compileOnly is for the 'main' sourceSet, and is not inherited by - // the integrationTest sourceSet, so it does not actually affect the code that's - // being compiled. - // This is important, because else we would be adding a second version of all dependencies - // with the version as specified in the shared-sources override.gradle. That would then - // possibly cause compile- or runtime errors because Gradle automatically selects the - // latest version when duplicate dependencies are in the same configuration. - compileOnly project(':apix-integrationtests') - } - - integrationTest { - // After the tests, the docker setup should be stopped - finalizedBy composeDownTask - } - -} - diff --git a/apix-integrationtests-model-amp/README.md b/apix-integrationtests/model-amp/README.md similarity index 100% rename from apix-integrationtests-model-amp/README.md rename to apix-integrationtests/model-amp/README.md diff --git a/apix-integrationtests-model-amp/build.gradle b/apix-integrationtests/model-amp/build.gradle similarity index 100% rename from apix-integrationtests-model-amp/build.gradle rename to apix-integrationtests/model-amp/build.gradle diff --git a/apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest.properties b/apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest.properties similarity index 100% rename from apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest.properties rename to apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest.properties diff --git a/apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest_nl.properties b/apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest_nl.properties similarity index 100% rename from apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest_nl.properties rename to apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/messages/apixtest_nl.properties diff --git a/apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/model/apixtest-model.xml b/apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/model/apixtest-model.xml similarity index 100% rename from apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/model/apixtest-model.xml rename to apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/model/apixtest-model.xml diff --git a/apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/module-context.xml b/apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/module-context.xml similarity index 100% rename from apix-integrationtests-model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/module-context.xml rename to apix-integrationtests/model-amp/src/main/amp/config/alfresco/module/apix-integrationtests-model-amp/module-context.xml diff --git a/apix-integrationtests/overrides.gradle b/apix-integrationtests/overrides.gradle deleted file mode 100644 index eecad367..00000000 --- a/apix-integrationtests/overrides.gradle +++ /dev/null @@ -1,16 +0,0 @@ - -// This is a duplicate of the ./70/overrides.gradle to make code navigation work in shared code -ext { - subproject_alfresco_version = '70' - alfresco_version = alfresco_70_version - alfresco_dm_version = alfresco_70_dm_version - alfresco_repo_version = '8.424' - alfresco_remote_api_version = '8.424' -} - -dependencies { - integrationTestImplementationRemote(group: 'org.apache.httpcomponents', name: 'fluent-hc', version: http_version) { - exclude group: 'commons-logging' - exclude group: 'org.apache.httpcomponents', module: 'httpcore' - } -} \ No newline at end of file diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/NodeContentTest.java b/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/NodeContentTest.java deleted file mode 100644 index 541b364c..00000000 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/NodeContentTest.java +++ /dev/null @@ -1,228 +0,0 @@ -package eu.xenit.apix.rest.v1.tests; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import eu.xenit.apix.data.NodeRef; -import eu.xenit.apix.node.INodeService; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.transaction.TransactionService; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -/** - * Created by kenneth on 17.03.16. - */ -public class NodeContentTest extends RestV1BaseTest { - - private final static Logger logger = LoggerFactory.getLogger(NodeContentTest.class); - - @Autowired - INodeService nodeService; - - @Autowired - @Qualifier("TransactionService") - TransactionService transactionService; - - @Before - public void setup() { - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); - } - - @Test - public void testSetNodeContent() throws IOException { - final HashMap initializedNodeRefs = init(); - - final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), "/content", "admin", "admin"); - final CloseableHttpClient httpclient = HttpClients.createDefault(); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - HttpPut httpput = new HttpPut(url); - HttpEntity httpBody = MultipartEntityBuilder.create() - .addBinaryBody("file", "test content123".getBytes(), ContentType.TEXT_PLAIN, "abc.txt") - .build(); - httpput.setEntity(httpBody); - - try (CloseableHttpResponse response = httpclient.execute(httpput)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - } - - return null; - } - }, false, true); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - InputStream inputStream = nodeService - .getContent(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME)).getInputStream(); - assertEquals("test content123", IOUtils.toString(inputStream)); - inputStream.close(); - return null; - } - }, false, true); - - } - - @Test - public void testSetNodeContentReturnsAccesDenied() throws IOException { - final HashMap initializedNodeRefs = init(); - - final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME), "/content", RestV1BaseTest.USERWITHOUTRIGHTS, - RestV1BaseTest.USERWITHOUTRIGHTS); - final CloseableHttpClient httpclient = HttpClients.createDefault(); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(() -> { - HttpPut httpput = new HttpPut(url); - HttpEntity httpBody = MultipartEntityBuilder.create() - .addBinaryBody("file", "test content123".getBytes(), ContentType.TEXT_PLAIN, "abc.txt") - .build(); - httpput.setEntity(httpBody); - - try (CloseableHttpResponse response = httpclient.execute(httpput)) { - assertEquals(403, response.getStatusLine().getStatusCode()); - } - - return null; - }, false, true); - } - - @Test - public void testDeleteNodeContent() throws IOException { - final HashMap initializedNodeRefs = init(); - - final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), "/content", "admin", "admin"); - final CloseableHttpClient httpclient = HttpClients.createDefault(); - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - nodeService.setContent(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), - new ByteArrayInputStream("test contentabc".getBytes()), - "abc.txt"); - return null; - } - }, false, true); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - HttpDelete httpDelete = new HttpDelete(url); - - try (CloseableHttpResponse response = httpclient.execute(httpDelete)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - } - return null; - } - }, false, true); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - - assertNull(nodeService.getContent(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME))); - return null; - } - }, false, true); - } - - @Test - public void testDeleteNodeContentReturnsAccesDenied() throws IOException { - final HashMap initializedNodeRefs = init(); - - final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME), "/content", RestV1BaseTest.USERWITHOUTRIGHTS, - RestV1BaseTest.USERWITHOUTRIGHTS); - final CloseableHttpClient httpclient = HttpClients.createDefault(); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(() -> { - try (CloseableHttpResponse response = httpclient.execute(new HttpDelete(url))) { - assertEquals(403, response.getStatusLine().getStatusCode()); - } - return null; - }, false, true); - } - - @Test - public void testGetNodeContent() throws IOException { - final HashMap initializedNodeRefs = init(); - - final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), "/content", "admin", "admin"); - final CloseableHttpClient httpclient = HttpClients.createDefault(); - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - nodeService.setContent(initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME), - new ByteArrayInputStream("test contentdef".getBytes()), - "abc.txt"); - return null; - } - }, false, true); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - HttpGet httpGet = new HttpGet(url); - - try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - assertEquals(200, response.getStatusLine().getStatusCode()); - InputStream inputStream = response.getEntity().getContent(); - assertEquals("test contentdef", IOUtils.toString(inputStream)); - inputStream.close(); - } - return null; - } - }, false, true); - } - - @Test - public void testGetNodeContentReturnsAccesDenied() throws IOException { - final HashMap initializedNodeRefs = init(); - - final String url = makeNodesUrl(initializedNodeRefs.get(RestV1BaseTest.NOUSERRIGHTS_FILE_NAME), "/content", RestV1BaseTest.USERWITHOUTRIGHTS, - RestV1BaseTest.USERWITHOUTRIGHTS); - final CloseableHttpClient httpclient = HttpClients.createDefault(); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(() -> { - try (CloseableHttpResponse response = httpclient.execute(new HttpGet(url))) { - assertEquals(403, response.getStatusLine().getStatusCode()); - } - return null; - }, false, true); - } - - @After - public void cleanUp() { - this.removeMainTestFolder(); - } -} diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/VersionHistoryTest.java b/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/VersionHistoryTest.java deleted file mode 100644 index 62b09d4a..00000000 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/rest/v1/tests/VersionHistoryTest.java +++ /dev/null @@ -1,220 +0,0 @@ -package eu.xenit.apix.rest.v1.tests; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.fasterxml.jackson.databind.ObjectMapper; -import eu.xenit.apix.alfresco.ApixToAlfrescoConversion; -import eu.xenit.apix.data.NodeRef; -import eu.xenit.apix.versionhistory.Version; -import eu.xenit.apix.versionhistory.VersionHistory; -import java.io.IOException; -import java.util.HashMap; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.version.VersionBaseModel; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.version.VersionService; -import org.alfresco.service.transaction.TransactionService; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.fluent.Request; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - - -public class VersionHistoryTest extends RestV1BaseTest { - - private final static Logger logger = LoggerFactory.getLogger(VersionHistoryTest.class); - @Autowired - @Qualifier("TransactionService") - TransactionService transactionService; - @Autowired - @Qualifier("NodeService") - NodeService nodeService; - @Autowired - @Qualifier("DictionaryService") - DictionaryService dictionaryService; - @Autowired - private org.alfresco.service.cmr.version.VersionService alfrizcoVersionHistoryService; - @Autowired - private ApixToAlfrescoConversion c; - - @Before - public void setup() { - AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); - } - - // Holy - // - @Test - public void testGetVersionHistorySimpleNode() throws IOException { - final HashMap initializedNodeRefs = init(); - final String[] url = new String[1]; - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - NodeRef testNode = initializedNodeRefs.get(RestV1BaseTest.TESTFILE3_NAME); - url[0] = createApixUrl("/versionhistory/%s/%s/%s/versions", testNode.getStoreRefProtocol(), - testNode.getStoreRefId(), testNode.getGuid()); - HashMap versionProperties = new HashMap<>(); - versionProperties.put(VersionBaseModel.PROP_VERSION_TYPE, - org.alfresco.service.cmr.version.VersionType.MAJOR); - versionProperties.put(VersionBaseModel.PROP_DESCRIPTION, "Test123"); - org.alfresco.service.cmr.version.Version version = alfrizcoVersionHistoryService - .createVersion(c.alfresco(testNode), versionProperties); - logger.debug(" versioning 1 label: " + version.getVersionLabel()); - versionProperties.put(VersionBaseModel.PROP_DESCRIPTION, "Test456"); - org.alfresco.service.cmr.version.Version version2 = alfrizcoVersionHistoryService - .createVersion(c.alfresco(testNode), versionProperties); - logger.debug(" versioning 2 label: " + version2.getVersionLabel()); - return null; - } - }, false, true); - - HttpResponse httpResponse = Request.Get(url[0]).execute().returnResponse(); - HttpEntity entity = httpResponse.getEntity(); - String result = EntityUtils.toString(entity); - VersionHistory history = new ObjectMapper().readValue(result, VersionHistory.class); - Assert.assertEquals(2, history.getVersionHistory().size()); - Version mostRecentVersion = history.getVersionHistory().get(0); - Assert.assertEquals(mostRecentVersion.getDescription(), "Test456"); - Version oldestVersion = history.getVersionHistory().get(1); - Assert.assertEquals(oldestVersion.getDescription(), "Test123"); - } - - @Test - public void testSetVersionHistoryWithoutBody() throws IOException { - final HashMap initializedNodeRefs = init(); - final VersionService versionService = alfrizcoVersionHistoryService; - - final boolean defaultAutoVersion = dictionaryService.getProperty(ContentModel.PROP_AUTO_VERSION) - .getDefaultValue().equals("true"); - final boolean defaultAutoVersionProps = dictionaryService.getProperty(ContentModel.PROP_AUTO_VERSION_PROPS) - .getDefaultValue().equals("true"); - final boolean defaultInitialVersion = dictionaryService.getProperty(ContentModel.PROP_INITIAL_VERSION) - .getDefaultValue().equals("true"); - - String versionHistoryUrl = createApixUrl("/versionhistory/%s/%s/%s", - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getStoreRefProtocol(), - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getStoreRefId(), - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getGuid()); - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - public Object execute() throws Throwable { - assertFalse(versionService - .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()))); - return null; - } - }, true, true); - - int statusCode = Request.Put(versionHistoryUrl) - .execute() - .returnResponse() - .getStatusLine() - .getStatusCode(); - assertEquals(200, statusCode); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - public Object execute() throws Throwable { - assertTrue(alfrizcoVersionHistoryService - .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()))); - assertEquals(defaultAutoVersion, nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), - ContentModel.PROP_AUTO_VERSION)); - assertEquals(defaultAutoVersionProps, nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), - ContentModel.PROP_AUTO_VERSION_PROPS)); - assertEquals(defaultInitialVersion, nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), - ContentModel.PROP_INITIAL_VERSION)); - assertEquals("1.0", nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE_NAME).getValue()), - ContentModel.PROP_VERSION_LABEL)); - return null; - } - }, true, true); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - public Object execute() throws Throwable { - assertFalse(alfrizcoVersionHistoryService - .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()))); - return null; - } - }, true, true); - - String versionHistoryUrl2 = createApixUrl("/versionhistory/%s/%s/%s", - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getStoreRefProtocol(), - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getStoreRefId(), - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getGuid()); - String requestBody = "{" + - "'autoVersion': false," + - "'autoVersionOnUpdateProps': true," + - "'initialVersion': false" + - "}"; - - HttpPut httpPut = new HttpPut(versionHistoryUrl2); - httpPut.setEntity(new StringEntity(json(requestBody), ContentType.APPLICATION_JSON)); - int statusCode2 = HttpClients.createDefault() - .execute(httpPut) - .getStatusLine() - .getStatusCode(); - - assertEquals(200, statusCode2); - - transactionService.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - public Object execute() throws Throwable { - assertTrue(alfrizcoVersionHistoryService - .isVersioned(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()))); - assertEquals(false, nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), - ContentModel.PROP_AUTO_VERSION)); - assertEquals(true, nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), - ContentModel.PROP_AUTO_VERSION_PROPS)); - assertEquals(false, nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), - ContentModel.PROP_INITIAL_VERSION)); - assertEquals("1.0", nodeService - .getProperty(new org.alfresco.service.cmr.repository.NodeRef( - initializedNodeRefs.get(RestV1BaseTest.TESTFILE2_NAME).getValue()), - ContentModel.PROP_VERSION_LABEL)); - return null; - } - }, true, true); - } - - @After - public void cleanUp() { - this.removeMainTestFolder(); - } -} diff --git a/apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrTestHelperBaseImpl.java b/apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrTestHelperBaseImpl.java deleted file mode 100644 index 0b62e43e..00000000 --- a/apix-integrationtests/src/main/java/eu/xenit/apix/util/SolrTestHelperBaseImpl.java +++ /dev/null @@ -1,136 +0,0 @@ -package eu.xenit.apix.util; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import javax.sql.DataSource; -import org.alfresco.repo.management.subsystems.SwitchableApplicationContextFactory; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class should only be used by the integration tests. - */ -public abstract class SolrTestHelperBaseImpl implements SolrTestHelper { - private Logger logger = LoggerFactory.getLogger(SolrTestHelperBaseImpl.class); - - protected final String SOLR_SUMMARY_CORE_ALFRESCO = "alfresco"; - protected final String SOLR_SUMMARY_FTS = "FTS"; - protected final String SOLR_SUMMARY_FTS_DIRTY = "Node count whose content needs to be updated"; - protected final String SOLR_SUMMARY_FTS_CLEAN = "Node count whose content is in sync"; - - private DataSource dataSource; - protected SolrAdminClient solrAdminClient; - - private int maxTries = 20; - - public SolrTestHelperBaseImpl(String baseUrl, DataSource dataSource, SwitchableApplicationContextFactory searchSubSystem) { - this.dataSource = dataSource; - solrAdminClient = new SolrAdminClient(baseUrl, searchSubSystem); - } - - /** - * Beware of calling this method too frequently since it runs an aggregate query over the entire alf_nodes table - * - * @return true if the latest Alfresco transaction has been indexed by Solr - */ - @Override - public boolean areTransactionsSynced() { - int alfTransaction = getAlfTransactionId(); - int solrTransaction = solrAdminClient.getLastTransactionId(); - logger.debug("alf transaction: {}, solr transaction: {}", alfTransaction, solrTransaction); - return alfTransaction <= solrTransaction; - } - - /** - * Wait until Solr has indexed the latest Alfresco transaction. - */ - @Override - public void waitForTransactionSync() throws InterruptedException { - for (int i = 0; i < maxTries; i++) { - if (areTransactionsSynced()) { - return; - } - // These prints are here to send data over the wire while waiting. - // This prevents any http proxy from closing the connection due to timeouts - System.out.print("Waiting 5 seconds for Solr to index transactions"); - for (int j = 0; j < 5; j++) { - System.out.print("..." + ((i * 5) + j + 1)); - Thread.sleep(1000); - } - System.out.println(); - } - } - - @Override - public int getNumberOfFtsStatusCleanDocs() { - return (Integer) getSummaryFtsSection().get(SOLR_SUMMARY_FTS_CLEAN); - } - - @Override - public boolean isContentIndexed() { - JSONObject ftsBlock = getSummaryFtsSection(); - logger.debug("solrSummaryFTSBlock: {}", ftsBlock.toString(4)); - return 0 == (Integer) ftsBlock.get(SOLR_SUMMARY_FTS_DIRTY); - } - - @Override - public boolean isContentIndexed(int previousCleanCount) { - JSONObject ftsBlock = getSummaryFtsSection(); - logger.debug("solrSummaryFTSBlock: {}", ftsBlock.toString(4)); - return 0 == (Integer) ftsBlock.get(SOLR_SUMMARY_FTS_DIRTY) - && previousCleanCount < ((Integer) ftsBlock.get(SOLR_SUMMARY_FTS_CLEAN)); - } - - @Override - public void waitForContentSync() throws InterruptedException { - for (int i = 0; i < maxTries; i++) { - if (isContentIndexed()) { - return; - } - System.out.print("Waiting 5 seconds for Solr to index content"); - for (int j = 0; j < 5; j++) { - System.out.print("..." + ((i * 5) + j + 1)); - Thread.sleep(1000); - } - System.out.println(); - } - } - - @Override - public void waitForContentSync(int previousCleanCount) throws InterruptedException { - logger.debug("previousCleanCount: {}", previousCleanCount); - for (int i = 0; i < maxTries; i++) { - if (isContentIndexed(previousCleanCount)) { - return; - } - System.out.print("Waiting 5 seconds for Solr to index content"); - for (int j = 0; j < 5; j++) { - System.out.print("..." + ((i * 5) + j + 1)); - Thread.sleep(1000); - } - System.out.println(); - } - } - - private int getAlfTransactionId() { - try (Connection connection = dataSource.getConnection()) { - final Statement stmt = connection.createStatement(); - final ResultSet rs = stmt.executeQuery("select max( transaction_id ) from alf_node"); - - if (rs.next()) { - return rs.getInt(1); - } - rs.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - return -1; - } - - protected JSONObject getSummaryFtsSection() { - return solrAdminClient.getSolrSummaryJson().getJSONObject(SOLR_SUMMARY_CORE_ALFRESCO).getJSONObject(SOLR_SUMMARY_FTS); - } -} diff --git a/apix-interface/build.gradle b/apix-interface/build.gradle index c4db226d..5aeaf098 100644 --- a/apix-interface/build.gradle +++ b/apix-interface/build.gradle @@ -1,17 +1,15 @@ -description = 'Xenit API-X Java interface' +description = 'Xenit Alfred API Java interface' apply from: "${rootProject.projectDir}/publish.gradle" task sourcesJar(type: Jar) { from sourceSets.main.allJava - classifier = 'sources' + archiveClassifier = 'sources' } task javadocJar(type: Jar) { from javadoc - classifier = 'javadoc' - // Temporary workaround for https://bugs.openjdk.java.net/browse/JDK-8244171 - javadoc.options.addBooleanOption("-no-module-directories", true) + archiveClassifier = 'javadoc' } publishing { @@ -25,10 +23,8 @@ publishing { } dependencies { - compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jackson_version - compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: jackson_version - compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jackson_version - compile group: 'io.swagger', name: 'swagger-annotations', version: swagger_version - testCompile group: 'junit', name: 'junit', version: '4.12' + compileOnly "com.fasterxml.jackson.core:jackson-annotations:${jackson_version}" + compileOnly "com.fasterxml.jackson.core:jackson-databind:${jackson_version}" + testImplementation group: 'junit', name: 'junit', version: '4.12' } diff --git a/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFile.java b/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFile.java index c11f0867..2da86883 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFile.java +++ b/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFile.java @@ -17,6 +17,17 @@ public ConfigurationFile(NodeRef nodeRef, NodeMetadata metadata) { this.metadata = metadata; } + public ConfigurationFile() { + } + + public ConfigurationFile(String content, NodeRef nodeRef, NodeMetadata metadata, NodePath path, Object parsedContent) { + this.content = content; + this.nodeRef = nodeRef; + this.metadata = metadata; + this.path = path; + this.parsedContent = parsedContent; + } + public NodeRef getNodeRef() { return nodeRef; } diff --git a/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFileFlags.java b/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFileFlags.java index 492c7415..1cb5c49d 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFileFlags.java +++ b/apix-interface/src/main/java/eu/xenit/apix/configuration/ConfigurationFileFlags.java @@ -8,8 +8,11 @@ public class ConfigurationFileFlags { public boolean addMetadata; public boolean addNodeRef; + public ConfigurationFileFlags() { + } + public ConfigurationFileFlags(boolean addContent, boolean addPath, boolean addParsedContent, boolean addMetadata, - boolean addNodeRef) { + boolean addNodeRef) { this.addContent = addContent; this.addPath = addPath; this.addParsedContent = addParsedContent; diff --git a/apix-interface/src/main/java/eu/xenit/apix/configuration/Configurations.java b/apix-interface/src/main/java/eu/xenit/apix/configuration/Configurations.java index 866bd168..799219de 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/configuration/Configurations.java +++ b/apix-interface/src/main/java/eu/xenit/apix/configuration/Configurations.java @@ -1,17 +1,18 @@ package eu.xenit.apix.configuration; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.List; public class Configurations { private List files; + public Configurations() { + } + public Configurations(List files) { - this.files = new ArrayList(files); - Collections.sort(this.files,new ConfigurationFileComparator()); + this.files = new ArrayList<>(files); + this.files.sort(new ConfigurationFileComparator()); } public List getFiles() { diff --git a/apix-interface/src/main/java/eu/xenit/apix/data/NodeRef.java b/apix-interface/src/main/java/eu/xenit/apix/data/NodeRef.java index 0fd74ee3..fc0bc06e 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/data/NodeRef.java +++ b/apix-interface/src/main/java/eu/xenit/apix/data/NodeRef.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; + import java.io.Serializable; /** @@ -14,9 +15,11 @@ public class NodeRef implements Serializable { private static final String FORWARD_SLASH = "/"; private static final String SPACESTORE_DIVIDER = "://"; - private String value; + public NodeRef() { + } + @JsonCreator public NodeRef(String s) { value = s; diff --git a/apix-interface/src/main/java/eu/xenit/apix/data/QName.java b/apix-interface/src/main/java/eu/xenit/apix/data/QName.java index 8aa8679c..84d9ec96 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/data/QName.java +++ b/apix-interface/src/main/java/eu/xenit/apix/data/QName.java @@ -1,13 +1,14 @@ package eu.xenit.apix.data; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonValue; +import java.io.Serializable; + /** * Represents an unique name in alfresco for properties and types. */ -public class QName { +public class QName implements Serializable { private String value; diff --git a/apix-interface/src/main/java/eu/xenit/apix/filefolder/NodePath.java b/apix-interface/src/main/java/eu/xenit/apix/filefolder/NodePath.java index 576f17da..9ce72315 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/filefolder/NodePath.java +++ b/apix-interface/src/main/java/eu/xenit/apix/filefolder/NodePath.java @@ -9,6 +9,14 @@ public class NodePath { private String displayPath; private String qnamePath; + public NodePath() { + } + + public NodePath(String displayPath, String qnamePath) { + this.displayPath = displayPath; + this.qnamePath = qnamePath; + } + public String getDisplayPath() { return displayPath; } diff --git a/apix-interface/src/main/java/eu/xenit/apix/node/INodeService.java b/apix-interface/src/main/java/eu/xenit/apix/node/INodeService.java index 2cd5d472..538cc8c6 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/node/INodeService.java +++ b/apix-interface/src/main/java/eu/xenit/apix/node/INodeService.java @@ -6,16 +6,13 @@ import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.QName; import eu.xenit.apix.data.StoreRef; -import io.swagger.annotations.Api; import java.io.InputStream; -import java.util.Dictionary; import java.util.List; import java.util.Map; /** * Service for operations on nodes. */ -@Api(value = "/metadata", produces = "application/json") public interface INodeService { /** diff --git a/apix-interface/src/main/java/eu/xenit/apix/node/NodeMetadata.java b/apix-interface/src/main/java/eu/xenit/apix/node/NodeMetadata.java index f1f18a1f..af541cd6 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/node/NodeMetadata.java +++ b/apix-interface/src/main/java/eu/xenit/apix/node/NodeMetadata.java @@ -14,17 +14,15 @@ */ public class NodeMetadata { - public NodeRef id; - public QName type; // qname - public QName baseType; //qname - public long transactionId; - public Map> properties;//: { [k: string]: Translation[]; } - public List aspects; - //TODO: parent - - //Removed in favor of using the permissions endpoint public boolean canEditMetadata; // TODO: remove + private NodeRef id; + private QName type; // qname + private QName baseType; //qname + private long transactionId; + private Map> properties;//: { [k: string]: Translation[]; } + private List aspects; - //public NodeAssociation[] associations; + public NodeMetadata() { + } public NodeMetadata(NodeRef id, QName type, QName baseType, long transactionId, Map> properties, List aspects) { @@ -36,6 +34,55 @@ public NodeMetadata(NodeRef id, QName type, QName baseType, long transactionId, this.aspects = aspects; } + public NodeRef getId() { + return id; + } + + public void setId(NodeRef id) { + this.id = id; + } + + public QName getType() { + return type; + } + + public void setType(QName type) { + this.type = type; + } + + public QName getBaseType() { + return baseType; + } + + public void setBaseType(QName baseType) { + this.baseType = baseType; + } + + public long getTransactionId() { + return transactionId; + } + + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + + public Map> getProperties() { + return properties; + } + + public void setProperties(Map> properties) { + this.properties = properties; + } + //TODO: parent + + public List getAspects() { + return aspects; + } + + public void setAspects(List aspects) { + this.aspects = aspects; + } + @Override public String toString() { return "NodeMetadata{" + diff --git a/apix-interface/src/main/java/eu/xenit/apix/search/QueryBuilder.java b/apix-interface/src/main/java/eu/xenit/apix/search/QueryBuilder.java index e0d2fda7..888a8879 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/search/QueryBuilder.java +++ b/apix-interface/src/main/java/eu/xenit/apix/search/QueryBuilder.java @@ -1,9 +1,14 @@ package eu.xenit.apix.search; -import eu.xenit.apix.search.nodes.*; -import eu.xenit.apix.utils.java8.Consumer; +import eu.xenit.apix.search.nodes.InvertSearchNode; +import eu.xenit.apix.search.nodes.OperatorSearchNode; +import eu.xenit.apix.search.nodes.PropertySearchNode; +import eu.xenit.apix.search.nodes.RangeValue; +import eu.xenit.apix.search.nodes.SearchSyntaxNode; +import eu.xenit.apix.search.nodes.TermSearchNode; import java.util.ArrayList; +import java.util.function.Consumer; /** * Object used to build a search query. @@ -19,16 +24,9 @@ public class QueryBuilder { SearchSyntaxNode result = null; public QueryBuilder() { -// acceptNode = x -> { -// result = x; -// acceptNode = null; -// }; - acceptNode = new Consumer() { - @Override - public void accept(SearchSyntaxNode searchSyntaxNode) { - result = searchSyntaxNode; - acceptNode = null; - } + acceptNode = searchSyntaxNode -> { + result = searchSyntaxNode; + acceptNode = null; }; } @@ -108,17 +106,11 @@ public QueryBuilder startAnd() { } private QueryBuilder startOperator(OperatorSearchNode.Operator operator) { - final OperatorSearchNode node = new OperatorSearchNode(operator, new ArrayList()); + final OperatorSearchNode node = new OperatorSearchNode(operator, new ArrayList<>()); QueryBuilder subBuilder = new QueryBuilder(); subBuilder.result = node; -// subBuilder.acceptNode = x -> node.getChildren().add(x); - subBuilder.acceptNode = new Consumer() { - @Override - public void accept(SearchSyntaxNode searchSyntaxNode) { - node.getChildren().add(searchSyntaxNode); - } - }; + subBuilder.acceptNode = searchSyntaxNode -> node.getChildren().add(searchSyntaxNode); subBuilder.parent = this; return subBuilder; @@ -153,19 +145,9 @@ public QueryBuilder not() { final Consumer oldAccept = acceptNode; -// acceptNode = x -> { -// // This has to be before accept, non-elegant solution, because the result-accept sets acceptNode to 0 -// acceptNode = oldAccept; -// oldAccept.accept(new InvertSearchNode(x)); -// -// }; - - acceptNode = new Consumer() { - @Override - public void accept(SearchSyntaxNode searchSyntaxNode) { - acceptNode = oldAccept; - oldAccept.accept(new InvertSearchNode(searchSyntaxNode)); - } + acceptNode = searchSyntaxNode -> { + acceptNode = oldAccept; + oldAccept.accept(new InvertSearchNode(searchSyntaxNode)); }; return this; diff --git a/apix-interface/src/main/java/eu/xenit/apix/search/SearchQuery.java b/apix-interface/src/main/java/eu/xenit/apix/search/SearchQuery.java index 96b5cdd7..392c6a1b 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/search/SearchQuery.java +++ b/apix-interface/src/main/java/eu/xenit/apix/search/SearchQuery.java @@ -5,7 +5,6 @@ import eu.xenit.apix.data.QName; import eu.xenit.apix.data.StoreRef; import eu.xenit.apix.search.nodes.SearchSyntaxNode; -import io.swagger.annotations.ApiModelProperty; import java.util.ArrayList; import java.util.Arrays; @@ -24,7 +23,6 @@ */ public class SearchQuery { - @ApiModelProperty(required = true) private SearchSyntaxNode query; private PagingOptions paging = new PagingOptions(); private FacetOptions facets = new FacetOptions(); @@ -127,9 +125,9 @@ public void setSkip(int skip) { public static class FacetOptions { private boolean enabled; - @ApiModelProperty("Limits the number of values returned per facet") + private Integer limit = -1; - @ApiModelProperty("Return only facet values with count >= mincount") + private Integer mincount; public List custom; diff --git a/apix-interface/src/main/java/eu/xenit/apix/search/json/TypeResolver.java b/apix-interface/src/main/java/eu/xenit/apix/search/json/TypeResolver.java index 171d836a..cf4162fa 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/search/json/TypeResolver.java +++ b/apix-interface/src/main/java/eu/xenit/apix/search/json/TypeResolver.java @@ -28,12 +28,8 @@ public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType b @Override public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection subtypes) { - //if (!baseType.getRawClass().equals(SearchSyntaxNode.class)) throw new UnsupportedOperationException(); TypeNameIdResolver idRes = TypeNameIdResolver.construct(config, baseType, subtypes, false, true); - - SearchNodeTypeDeserializer s = new SearchNodeTypeDeserializer(baseType, idRes, null, false, null); - - return s; + return new SearchNodeTypeDeserializer(baseType, idRes, null, false, null); } @Override diff --git a/apix-interface/src/main/java/eu/xenit/apix/translation/PropertyTranslationValue.java b/apix-interface/src/main/java/eu/xenit/apix/translation/PropertyTranslationValue.java index 8d40d2df..62ff3f0a 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/translation/PropertyTranslationValue.java +++ b/apix-interface/src/main/java/eu/xenit/apix/translation/PropertyTranslationValue.java @@ -1,7 +1,6 @@ package eu.xenit.apix.translation; import eu.xenit.apix.data.QName; -import io.swagger.annotations.ApiModelProperty; import java.util.Map; @@ -11,7 +10,6 @@ */ public class PropertyTranslationValue extends TranslationValue { - @ApiModelProperty(dataType = "Map[string,string]") private Map values; public PropertyTranslationValue() { diff --git a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Consumer.java b/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Consumer.java deleted file mode 100644 index 1788597b..00000000 --- a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Consumer.java +++ /dev/null @@ -1,16 +0,0 @@ -package eu.xenit.apix.utils.java8; - -/** - * Copy of the java 8 comsumer interface - */ -public interface Consumer { - - /** - * Performs this operation on the given argument. - * - * @param t the input argument - */ - void accept(T t); - - -} diff --git a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Function.java b/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Function.java deleted file mode 100644 index 94a07c3e..00000000 --- a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Function.java +++ /dev/null @@ -1,8 +0,0 @@ -package eu.xenit.apix.utils.java8; - -/** - */ -public interface Function { - - R apply(T t); -} diff --git a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Optional.java b/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Optional.java deleted file mode 100644 index 29e1491e..00000000 --- a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Optional.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ -package eu.xenit.apix.utils.java8; - -import java.util.NoSuchElementException; -import java.util.Objects; - -/** - * Port to java 7 - */ -public final class Optional { - - /** - * Common instance for {@code empty()}. - */ - private static final Optional EMPTY = new Optional<>(); - - /** - * If non-null, the value; if null, indicates no value is present - */ - private final T value; - - /** - * Constructs an empty instance. - * - * Generally only one empty instance, {@link Optional#EMPTY}, should exist per VM. - */ - private Optional() { - this.value = null; - } - - /** - * Constructs an instance with the value present. - * - * @param value the non-null value to be present - * @throws NullPointerException if value is null - */ - private Optional(T value) { - this.value = Objects.requireNonNull(value); - } - - /** - * Returns an empty {@code Optional} instance. No value is present for this Optional. - * - * Though it may be tempting to do so, avoid testing if an object is empty by comparing with {@code ==} against - * instances returned by {@code Option.empty()}. There is no guarantee that it is a singleton. Instead, use {@link - * #isPresent()}. - * - * @param Type of the non-existent value - * @return an empty {@code Optional} - */ - public static Optional empty() { - @SuppressWarnings("unchecked") - Optional t = (Optional) EMPTY; - return t; - } - - /** - * Returns an {@code Optional} with the specified present non-null value. - * - * @param the class of the value - * @param value the value to be present, which must be non-null - * @return an {@code Optional} with the value present - * @throws NullPointerException if value is null - */ - public static Optional of(T value) { - return new Optional<>(value); - } - - /** - * Returns an {@code Optional} describing the specified value, if non-null, otherwise returns an empty {@code - * Optional}. - * - * @param the class of the value - * @param value the possibly-null value to describe - * @return an {@code Optional} with a present value if the specified value is non-null, otherwise an empty {@code - * Optional} - */ - public static Optional ofNullable(T value) { - return value == null ? (Optional) empty() : of(value); - } - - /** - * If a value is present in this {@code Optional}, returns the value, otherwise throws {@code - * NoSuchElementException}. - * - * @return the non-null value held by this {@code Optional} - * @throws NoSuchElementException if there is no value present - * @see Optional#isPresent() - */ - public T get() { - if (value == null) { - throw new NoSuchElementException("No value present"); - } - return value; - } - - /** - * Return {@code true} if there is a value present, otherwise {@code false}. - * - * @return {@code true} if there is a value present, otherwise {@code false} - */ - public boolean isPresent() { - return value != null; - } - - /** - * If a value is present, invoke the specified consumer with the value, otherwise do nothing. - * - * @param consumer block to be executed if a value is present - * @throws NullPointerException if value is present and {@code consumer} is null - */ - public void ifPresent(Consumer consumer) { - if (value != null) { - consumer.accept(value); - } - } - - /** - * If a value is present, and the value matches the given predicate, return an {@code Optional} describing the - * value, otherwise return an empty {@code Optional}. - * - * @param predicate a predicate to apply to the value, if present - * @return an {@code Optional} describing the value of this {@code Optional} if a value is present and the value - * matches the given predicate, otherwise an empty {@code Optional} - * @throws NullPointerException if the predicate is null - */ - public Optional filter(Predicate predicate) { - Objects.requireNonNull(predicate); - if (!isPresent()) { - return this; - } else { - return predicate.test(value) ? this : (Optional) empty(); - } - } - - /** - * If a value is present, apply the provided mapping function to it, and if the result is non-null, return an {@code - * Optional} describing the result. Otherwise return an empty {@code Optional}. - * - * This method supports post-processing on optional values, without the need to explicitly check for a return - * status. For example, the following code traverses a stream of file names, selects one that has not yet been - * processed, and then opens that file, returning an {@code Optional}: - * - *
{@code
-     *     Optional fis =
-     *         names.stream().filter(name -> !isProcessedYet(name))
-     *                       .findFirst()
-     *                       .map(name -> new FileInputStream(name));
-     * }
- * - * Here, {@code findFirst} returns an {@code Optional}, and then {@code map} returns an {@code - * Optional} for the desired file if one exists. - * - * @param The type of the result of the mapping function - * @param mapper a mapping function to apply to the value, if present - * @return an {@code Optional} describing the result of applying a mapping function to the value of this {@code - * Optional}, if a value is present, otherwise an empty {@code Optional} - * @throws NullPointerException if the mapping function is null - */ - public Optional map(Function mapper) { - Objects.requireNonNull(mapper); - if (!isPresent()) { - return empty(); - } else { - return (Optional) Optional.ofNullable(mapper.apply(value)); - } - } - - /** - * If a value is present, apply the provided {@code Optional}-bearing mapping function to it, return that result, - * otherwise return an empty {@code Optional}. This method is similar to {@link #map(Function)}, but the provided - * mapper is one whose result is already an {@code Optional}, and if invoked, {@code flatMap} does not wrap it with - * an additional {@code Optional}. - * - * @param The type parameter to the {@code Optional} returned by - * @param mapper a mapping function to apply to the value, if present the mapping function - * @return the result of applying an {@code Optional}-bearing mapping function to the value of this {@code - * Optional}, if a value is present, otherwise an empty {@code Optional} - * @throws NullPointerException if the mapping function is null or returns a null result - */ - public Optional flatMap(Function> mapper) { - Objects.requireNonNull(mapper); - if (!isPresent()) { - return empty(); - } else { - return Objects.requireNonNull(mapper.apply(value)); - } - } - - /** - * Return the value if present, otherwise return {@code other}. - * - * @param other the value to be returned if there is no value present, may be null - * @return the value, if present, otherwise {@code other} - */ - public T orElse(T other) { - return value != null ? value : other; - } - - /** - * Return the value if present, otherwise invoke {@code other} and return the result of that invocation. - * - * @param other a {@code Supplier} whose result is returned if no value is present - * @return the value if present otherwise the result of {@code other.get()} - * @throws NullPointerException if value is not present and {@code other} is null - */ - public T orElseGet(Supplier other) { - return value != null ? value : other.get(); - } - - /** - * Return the contained value, if present, otherwise throw an exception to be created by the provided supplier. - * - * A method reference to the exception constructor with an empty argument list can be used as the supplier. For - * example, {@code IllegalStateException::new} - * - * @param Type of the exception to be thrown - * @param exceptionSupplier The supplier which will return the exception to be thrown - * @return the present value - * @throws X if there is no value present - * @throws NullPointerException if no value is present and {@code exceptionSupplier} is null - */ - public T orElseThrow(Supplier exceptionSupplier) throws X { - if (value != null) { - return value; - } else { - throw exceptionSupplier.get(); - } - } - - /** - * Indicates whether some other object is "equal to" this Optional. The other object is considered equal if: - *
    - *
  • it is also an {@code Optional} and; - *
  • both instances have no value present or; - *
  • the present values are "equal to" each other via {@code equals()}. - *
- * - * @param obj an object to be tested for equality - * @return {code true} if the other object is "equal to" this object otherwise {@code false} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (!(obj instanceof Optional)) { - return false; - } - - Optional other = (Optional) obj; - return Objects.equals(value, other.value); - } - - /** - * Returns the hash code value of the present value, if any, or 0 (zero) if no value is present. - * - * @return hash code value of the present value or 0 if no value is present - */ - @Override - public int hashCode() { - return Objects.hashCode(value); - } - - /** - * Returns a non-empty string representation of this Optional suitable for debugging. The exact presentation format - * is unspecified and may vary between implementations and versions. - * - * If a value is present the result must include its string representation in the result. Empty and present - * Optionals must be unambiguously differentiable. - * - * @return the string representation of this instance - */ - @Override - public java.lang.String toString() { - return value != null - ? java.lang.String.format("Optional[%s]", value) - : "Optional.empty"; - } -} diff --git a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Predicate.java b/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Predicate.java deleted file mode 100644 index da04eb95..00000000 --- a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Predicate.java +++ /dev/null @@ -1,8 +0,0 @@ -package eu.xenit.apix.utils.java8; - -/** - */ -public interface Predicate { - - boolean test(T t); -} diff --git a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Supplier.java b/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Supplier.java deleted file mode 100644 index 75302151..00000000 --- a/apix-interface/src/main/java/eu/xenit/apix/utils/java8/Supplier.java +++ /dev/null @@ -1,8 +0,0 @@ -package eu.xenit.apix.utils.java8; - -/** - */ -public interface Supplier { - - T get(); -} diff --git a/apix-interface/src/main/java/eu/xenit/apix/workflow/model/WorkflowOrTaskChanges.java b/apix-interface/src/main/java/eu/xenit/apix/workflow/model/WorkflowOrTaskChanges.java index 00ec2423..f90c9f45 100644 --- a/apix-interface/src/main/java/eu/xenit/apix/workflow/model/WorkflowOrTaskChanges.java +++ b/apix-interface/src/main/java/eu/xenit/apix/workflow/model/WorkflowOrTaskChanges.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.Map; public class WorkflowOrTaskChanges { @@ -13,6 +14,9 @@ public WorkflowOrTaskChanges(@JsonProperty("propertiesToSet") Map getPropertiesToSet() { return propertiesToSet; } diff --git a/apix-rest-v1/META-INF/readme.txt b/apix-rest-v1/META-INF/readme.txt deleted file mode 100644 index de894c13..00000000 --- a/apix-rest-v1/META-INF/readme.txt +++ /dev/null @@ -1,18 +0,0 @@ -To facilitate OSGi bundle manifest generation, the archetype offers the choice of two maven plugins. - -a. SpringSource Bundlor Plugin -Home page: http://www.springsource.org/bundlor - -"SpringSource� Bundlor is a tool that automates the detection of dependencies and the creation of OSGi -manifest directives for JARs after their creation." - -The Bundlor tool is enabled by default. - -b. Apache Felix Bundle Plugin -Home page: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html - -"This plugin for Maven 2 is based on the BND tool from Peter Kriens. [...] The way you create a bundle -with BND is to tell it the content of the bundle's JAR file as a subset of the available classes." - - -To trigger the manifest generation (using either plugin), run: mvn package diff --git a/apix-rest-v1/build.gradle b/apix-rest-v1/build.gradle index 17e9d1d5..295dcbaa 100644 --- a/apix-rest-v1/build.gradle +++ b/apix-rest-v1/build.gradle @@ -1,35 +1,32 @@ -description = 'Xenit API-X Rest v1' -ext { - // This is the lowest supported Alfresco version - alfresco_repository_version = "6.2" +plugins { + id 'java-library' + id 'idea' + id 'eu.xenit.alfresco' version '1.1.0' } -dependencies { - // Alfresco dependency should be removed in the future - compileOnly(group: 'org.alfresco', name: 'alfresco-repository', version: alfresco_repository_version) { exclude group: 'maven-plugins' } - compileOnly project(':apix-interface') - // The REST API use one call from DE annotations-runtime for the bulk endpoint - // although annotations-runtime is internal - compileOnly(group: 'eu.xenit.de', name: 'annotations-runtime', version: de_version) { transitive = false } - compileOnly(group: 'eu.xenit.de', name: 'annotations', version: de_version) { transitive = false } - compileOnly(group: 'eu.xenit.de', name: 'webscripts', version: de_version) { transitive = false } +description = 'Xenit Alfred API Rest v1' + +apply from: "$rootProject.projectDir/alfresco/70/overrides.gradle" - compile(project(':de-swagger-reader')) +dependencies { + implementation platform("org.alfresco:acs-community-packaging:${alfresco_version}") + + // Alfresco dependency should be removed in the future + alfrescoProvided("org.alfresco:alfresco-repository") + alfrescoProvided project(':apix-interface') + alfrescoProvided('org.alfresco:alfresco-remote-api') - // Overriding the dependencies in swagger to use the same version everywhere, ideally we - // can switch to a recent swagger version and have updated jackson versions. - compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: jackson_version - compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-joda', version: jackson_version + alfrescoProvided "com.gradecak.alfresco-mvc:alfresco-mvc-rest:${mvc}" + alfrescoProvided "com.gradecak.alfresco-mvc:alfresco-mvc-aop:${mvc}" + alfrescoProvided "javax.servlet:javax.servlet-api:4.0.1" - testCompile group: 'org.springframework', name: 'spring-core', version: '3.2.10.RELEASE' - testCompile group: 'org.springframework', name: 'spring-test', version: '3.2.10.RELEASE' - testCompile group: 'org.alfresco.surf', name: 'spring-webscripts', version: alfresco_repository_version - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.25.1' - testCompile group: 'eu.xenit.de', name: 'annotations', version: de_version - testCompile group: 'eu.xenit.de', name: 'annotations-runtime', version: de_version - testCompile group: 'eu.xenit.de', name: 'webscripts', version: de_version - testCompile(group: 'org.alfresco', name: 'alfresco-repository', version: alfresco_repository_version) { exclude group: 'maven-plugins' } - testCompile(group: 'org.alfresco', name: 'alfresco-remote-api', version: alfresco_repository_version) { exclude group: 'maven-plugins' } - testCompile project(':apix-interface') - testCompile project(':apix-impl') + testImplementation (project(':apix-interface')) {transitive = false} + testImplementation project(':apix-impl') + testImplementation platform("org.alfresco:acs-community-packaging:${alfresco_version}") + testImplementation 'org.springframework:spring-core' + testImplementation 'org.springframework:spring-test' + testImplementation "org.alfresco.surf:spring-webscripts" + testImplementation 'org.alfresco:alfresco-repository' + testImplementation 'org.alfresco:alfresco-remote-api' + testImplementation group: 'org.mockito', name: 'mockito-core', version: '2.25.1' } diff --git a/apix-rest-v1/build.properties b/apix-rest-v1/build.properties deleted file mode 100644 index cfd2269e..00000000 --- a/apix-rest-v1/build.properties +++ /dev/null @@ -1,9 +0,0 @@ -source.. = src/main/java/,src/main/resources/,src/test/java,src/test/resources -output.. = target/classes/,target/test-classes -bin.includes = META-INF/,\ - .,\ - target/classes/ -src.includes = src/main/java/,\ - src/main/resources/,\ - src/test/java/,\ - src/test/resources/ diff --git a/apix-rest-v1/out/production/resources/META-INF/spring/bundle-context-osgi.Noxml b/apix-rest-v1/out/production/resources/META-INF/spring/bundle-context-osgi.Noxml deleted file mode 100644 index 3b18d880..00000000 --- a/apix-rest-v1/out/production/resources/META-INF/spring/bundle-context-osgi.Noxml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/apix-rest-v1/out/production/resources/META-INF/spring/bundle-context.NOxml b/apix-rest-v1/out/production/resources/META-INF/spring/bundle-context.NOxml deleted file mode 100644 index 309ae12d..00000000 --- a/apix-rest-v1/out/production/resources/META-INF/spring/bundle-context.NOxml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/apix-rest-v1/out/production/resources/log4j.properties b/apix-rest-v1/out/production/resources/log4j.properties deleted file mode 100644 index 18cfaa3f..00000000 --- a/apix-rest-v1/out/production/resources/log4j.properties +++ /dev/null @@ -1,8 +0,0 @@ -log4j.rootCategory=INFO, stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout.ConversionPattern=%t %p [%c] - %m%n -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.threshold=TRACE - -#log4j.logger.org.springframework.osgi=DEBUG -#log4j.logger.org.springframework=DEBUG \ No newline at end of file diff --git a/apix-rest-v1/readme.txt b/apix-rest-v1/readme.txt deleted file mode 100644 index de894c13..00000000 --- a/apix-rest-v1/readme.txt +++ /dev/null @@ -1,18 +0,0 @@ -To facilitate OSGi bundle manifest generation, the archetype offers the choice of two maven plugins. - -a. SpringSource Bundlor Plugin -Home page: http://www.springsource.org/bundlor - -"SpringSource� Bundlor is a tool that automates the detection of dependencies and the creation of OSGi -manifest directives for JARs after their creation." - -The Bundlor tool is enabled by default. - -b. Apache Felix Bundle Plugin -Home page: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html - -"This plugin for Maven 2 is based on the BND tool from Peter Kriens. [...] The way you create a bundle -with BND is to tell it the content of the bundle's JAR file as a subset of the available classes." - - -To trigger the manifest generation (using either plugin), run: mvn package diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/AlfredApiRestServletContext.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/AlfredApiRestServletContext.java new file mode 100644 index 00000000..27308d08 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/AlfredApiRestServletContext.java @@ -0,0 +1,138 @@ +package eu.xenit.apix.rest; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gradecak.alfresco.mvc.annotation.EnableAlfrescoMvcAop; +import com.gradecak.alfresco.mvc.rest.config.DefaultAlfrescoMvcServletContextConfiguration; +import eu.xenit.apix.rest.jackson.Jackson2ApixNodeRefDeserializer; +import eu.xenit.apix.rest.jackson.Jackson2ApixNodeRefSerializer; +import eu.xenit.apix.rest.jackson.Jackson2ApixQnameDeserializer; +import eu.xenit.apix.rest.jackson.Jackson2ApixQnameSerializer; +import eu.xenit.apix.rest.staging.workflow.WorkflowWebscript; +import eu.xenit.apix.rest.v0.categories.ClassificationGetWebscript; +import eu.xenit.apix.rest.v0.dictionary.DictionaryServiceChecksumWebscript; +import eu.xenit.apix.rest.v0.metadata.MetadataBulkWebscript; +import eu.xenit.apix.rest.v0.metadata.MetadataGetWebscript; +import eu.xenit.apix.rest.v0.metadata.MetadataPostWebscript; +import eu.xenit.apix.rest.v0.search.SearchWebScript0; +import eu.xenit.apix.rest.v1.GeneralWebscript; +import eu.xenit.apix.rest.v1.bulk.BulkWebscript1; +import eu.xenit.apix.rest.v1.categories.CategoryWebScript1; +import eu.xenit.apix.rest.v1.configuration.ConfigurationWebscript1; +import eu.xenit.apix.rest.v1.dictionary.DictionaryWebScript1; +import eu.xenit.apix.rest.v1.nodes.NodesWebscript1; +import eu.xenit.apix.rest.v1.people.PeopleWebscript1; +import eu.xenit.apix.rest.v1.properties.PropertiesWebScript1; +import eu.xenit.apix.rest.v1.search.SearchWebScript1; +import eu.xenit.apix.rest.v1.sites.SitesWebscript1; +import eu.xenit.apix.rest.v1.temp.LogsWebscript; +import eu.xenit.apix.rest.v1.temp.WIPWebscript; +import eu.xenit.apix.rest.v1.translation.TranslationsWebscript1; +import eu.xenit.apix.rest.v1.versionhistory.VersionHistoryWebScript1; +import eu.xenit.apix.rest.v1.workingcopies.WorkingcopiesWebscript1; +import eu.xenit.apix.rest.v2.groups.GroupsWebscript; +import eu.xenit.apix.rest.v2.nodes.NodesWebscriptV2; +import eu.xenit.apix.rest.v2.people.PeopleWebscript; +import eu.xenit.apix.search.json.SearchNodeJsonParser; +import org.alfresco.rest.framework.jacksonextensions.RestJsonModule; +import org.alfresco.service.namespace.NamespaceService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.PropertySource; +import org.springframework.web.multipart.MultipartResolver; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.List; + +@Configuration +@EnableWebMvc +@PropertySource(value = {"classpath:application.properties"}) +// should pick up other controllers from the same package by default +@ComponentScan(basePackages = {"eu.xenit.apix"}) +@EnableAlfrescoMvcAop(basePackageClasses = { + BulkWebscript1.class, + CategoryWebScript1.class, + ConfigurationWebscript1.class, + ClassificationGetWebscript.class, + DictionaryWebScript1.class, + DictionaryServiceChecksumWebscript.class, + GeneralWebscript.class, + GroupsWebscript.class, + LogsWebscript.class, + MetadataPostWebscript.class, + MetadataBulkWebscript.class, + MetadataGetWebscript.class, + NodesWebscriptV2.class, + NodesWebscript1.class, + PropertiesWebScript1.class, + PeopleWebscript1.class, + PeopleWebscript1.class, + PeopleWebscript.class, + SearchWebScript1.class, + SearchWebScript0.class, + SitesWebscript1.class, + TranslationsWebscript1.class, + VersionHistoryWebScript1.class, + WorkingcopiesWebscript1.class, + WorkflowWebscript.class, + WIPWebscript.class +}) +public class AlfredApiRestServletContext extends DefaultAlfrescoMvcServletContextConfiguration { + + + public AlfredApiRestServletContext(RestJsonModule alfrescoRestJsonModule, NamespaceService namespaceService) { + super(alfrescoRestJsonModule, namespaceService); + } + + @Override + protected List> customJsonDeserializers() { + return Arrays.asList( + new Jackson2ApixNodeRefDeserializer(), + new Jackson2ApixQnameDeserializer() + ); + } + + @Override + protected List> customJsonSerilizers() { + return Arrays.asList( + new Jackson2ApixNodeRefSerializer(), + new Jackson2ApixQnameSerializer() + ); + } + + @Bean + @Primary + @Override + public ObjectMapper objectMapper() { + ObjectMapper om = new SearchNodeJsonParser().getObjectMapper(); + om.setSerializationInclusion(JsonInclude.Include.NON_NULL); + jackson2ObjectMapperBuilder().configure(om); + return om; + } + + @Override + protected MultipartResolver createMultipartResolver() { + CommonsMultipartResolver resolver = new CommonsMultipartResolver() { + @Override + public boolean isMultipart(HttpServletRequest request) { + String method = request.getMethod().toLowerCase(); + //By default, only POST is allowed. Since this is an 'update' we should accept PUT. + if (!Arrays.asList("put", "post").contains(method)) { + return false; + } + String contentType = request.getContentType(); + return (contentType != null && contentType.toLowerCase().startsWith("multipart/")); + } + }; + resolver.setMaxUploadSize(-1); + resolver.setDefaultEncoding("utf-8"); + return resolver; + } +} \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/SpringConfig.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/SpringConfig.java new file mode 100644 index 00000000..44cefd38 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/SpringConfig.java @@ -0,0 +1,12 @@ +package eu.xenit.apix.rest; + +import com.gradecak.alfresco.mvc.rest.annotation.AlfrescoDispatcherWebscript; +import com.gradecak.alfresco.mvc.rest.annotation.EnableAlfrescoMvcRest; + +@EnableAlfrescoMvcRest( + @AlfrescoDispatcherWebscript( + name = "alfred.api", + servletContext = AlfredApiRestServletContext.class + ) +) +public class SpringConfig {} \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixAbstractSerializer.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixAbstractSerializer.java new file mode 100644 index 00000000..02d320b8 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixAbstractSerializer.java @@ -0,0 +1,29 @@ +package eu.xenit.apix.rest.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.io.Serializable; + +public abstract class Jackson2ApixAbstractSerializer extends StdSerializer { + + private static final long serialVersionUID = 1L; + private final Class supportedClass; + + protected Jackson2ApixAbstractSerializer(Class clazz) { + super(clazz); + this.supportedClass = clazz; + } + + @Override + public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeString(value.toString()); + } + + @Override + public Class handledType() { + return supportedClass; + } +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixNodeRefDeserializer.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixNodeRefDeserializer.java new file mode 100644 index 00000000..ff08204d --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixNodeRefDeserializer.java @@ -0,0 +1,29 @@ +package eu.xenit.apix.rest.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import eu.xenit.apix.data.NodeRef; +import org.springframework.core.convert.converter.Converter; + +import java.io.IOException; + +public class Jackson2ApixNodeRefDeserializer extends JsonDeserializer implements Converter { + + @Override + public Class handledType() { + return NodeRef.class; + } + + @Override + public NodeRef deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + return new NodeRef( + jp.getText() + ); + } + + @Override + public NodeRef convert(String nodeRef) { + return new NodeRef(nodeRef); + } +} \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixNodeRefSerializer.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixNodeRefSerializer.java new file mode 100644 index 00000000..176586e5 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixNodeRefSerializer.java @@ -0,0 +1,12 @@ +package eu.xenit.apix.rest.jackson; + +import eu.xenit.apix.data.NodeRef; + +public class Jackson2ApixNodeRefSerializer extends Jackson2ApixAbstractSerializer { + + private static final long serialVersionUID = 1L; + + public Jackson2ApixNodeRefSerializer() { + super(NodeRef.class); + } +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixQnameDeserializer.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixQnameDeserializer.java new file mode 100644 index 00000000..d1d5ff4e --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixQnameDeserializer.java @@ -0,0 +1,29 @@ +package eu.xenit.apix.rest.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import eu.xenit.apix.data.QName; +import org.springframework.core.convert.converter.Converter; + +import java.io.IOException; + +public class Jackson2ApixQnameDeserializer extends JsonDeserializer implements Converter { + + @Override + public Class handledType() { + return QName.class; + } + + @Override + public QName deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + return new QName( + jp.getText() + ); + } + + @Override + public QName convert(String qname) { + return new QName(qname); + } +} \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixQnameSerializer.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixQnameSerializer.java new file mode 100644 index 00000000..8d9504df --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/jackson/Jackson2ApixQnameSerializer.java @@ -0,0 +1,12 @@ +package eu.xenit.apix.rest.jackson; + +import eu.xenit.apix.data.QName; + +public class Jackson2ApixQnameSerializer extends Jackson2ApixAbstractSerializer { + + private static final long serialVersionUID = 1L; + + public Jackson2ApixQnameSerializer() { + super(QName.class); + } +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/ApixStagingWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/ApixStagingWebscript.java deleted file mode 100644 index 5e370003..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/ApixStagingWebscript.java +++ /dev/null @@ -1,34 +0,0 @@ -package eu.xenit.apix.rest.staging; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import eu.xenit.apix.data.NodeRef; -import eu.xenit.apix.filefolder.IFileFolderService; -import eu.xenit.apix.node.*; -import eu.xenit.apix.permissions.IPermissionService; -import eu.xenit.apix.permissions.PermissionValue; -import eu.xenit.apix.rest.v1.nodes.NodeInfo; -import org.alfresco.repo.admin.SysAdminParams; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ApixStagingWebscript { - - private final static Logger logger = LoggerFactory.getLogger(ApixStagingWebscript.class); - - protected void writeJsonResponse(WebScriptResponse response, Object object) throws IOException { - response.setContentType("application/json"); - response.setContentEncoding("utf-8"); - response.setHeader("Cache-Control", "no-cache"); - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.writeValue(response.getWriter(), object); - } -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/RestStagingConfig.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/RestStagingConfig.java deleted file mode 100644 index 715d50dd..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/RestStagingConfig.java +++ /dev/null @@ -1,8 +0,0 @@ -package eu.xenit.apix.rest.staging; - -public class RestStagingConfig { - - public static final String BaseUrl = "/apix/staging"; - public static final String ApixUrl = "/apix"; - public static final String Family = "Api-X Staging API (Unstable)"; -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowClaimsBody.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowClaimsBody.java new file mode 100644 index 00000000..fafc186d --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowClaimsBody.java @@ -0,0 +1,30 @@ +package eu.xenit.apix.rest.staging.workflow; + +public class WorkflowClaimsBody { + private String id; + private String userName; + + public WorkflowClaimsBody(String id, String userName) { + this.id = id; + this.userName = userName; + } + + public WorkflowClaimsBody() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowJsonParser.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowJsonParser.java deleted file mode 100644 index 13eb80e2..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowJsonParser.java +++ /dev/null @@ -1,10 +0,0 @@ -package eu.xenit.apix.rest.staging.workflow; - -import com.fasterxml.jackson.databind.ObjectMapper; - -public class WorkflowJsonParser { - - public ObjectMapper getObjectMapper() { - return new ObjectMapper(); - } -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowReleaseBody.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowReleaseBody.java new file mode 100644 index 00000000..93eed4f4 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowReleaseBody.java @@ -0,0 +1,27 @@ +package eu.xenit.apix.rest.staging.workflow; + +public class WorkflowReleaseBody { + private String id; + + public WorkflowReleaseBody() { + } + + public WorkflowReleaseBody(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String toString() { + return "WorkflowReleaseBody{" + + "id='" + id + '\'' + + '}'; + } +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowWebscript.java index 868b79d1..6a70a467 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/staging/workflow/WorkflowWebscript.java @@ -1,19 +1,6 @@ package eu.xenit.apix.rest.staging.workflow; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.util.ISO8601DateFormat; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; -import eu.xenit.apix.rest.staging.ApixStagingWebscript; -import eu.xenit.apix.rest.staging.RestStagingConfig; -import eu.xenit.apix.rest.v1.ExceptionObject; -import eu.xenit.apix.search.SearchQueryResult; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.workflow.IWorkflowService; import eu.xenit.apix.workflow.model.Task; import eu.xenit.apix.workflow.model.Workflow; @@ -23,297 +10,152 @@ import eu.xenit.apix.workflow.search.TaskOrWorkflowSearchResult; import eu.xenit.apix.workflow.search.TaskSearchQuery; import eu.xenit.apix.workflow.search.WorkflowSearchQuery; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -@WebScript(baseUri = RestStagingConfig.BaseUrl, families = RestStagingConfig.Family, defaultFormat = "json", - description = "Perform workflow and task operations", value = "Workflows") -@Authentication(AuthenticationType.USER) -@Qualifier("eu.xenit.apix.rest.staging.workflow.WorkflowWebscript") -@Component("eu.xenit.apix.rest.staging.workflow.WorkflowWebscript") -public class WorkflowWebscript extends ApixStagingWebscript { - - public static final ISO8601DateFormat DATE_FORMAT = new ISO8601DateFormat(); - - /// - private static final String WorkflowSearchQueryDocumentationSample = "{\n" + - "\t\"filters\": [],\n" + - "\t\"facets\": [\n" + - "\t\t\"{http://www.alfresco.org/model/content/1.0}owner\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}dueDate\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}startDate\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}endDate\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}workflowPriority\"\n" + - "\t],\n" + - "\t\"scope\": \"WorkflowsIveStarted\",\n" + - "\t\"paging\": {\n" + - "\t\t\"skip\": 0,\n" + - "\t\t\"limit\": 15\n" + - "\t}\n" + - "}"; - private static final String TaskSearchQueryDocumentationSample = "{\n" + - "\t\"facets\": [\n" + - "\t\t\"{http://www.alfresco.org/model/content/1.0}owner\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}priority\"\n" + - "\t],\n" + - "\t\"filters\": [],\n" + - "\t\"scope\": \"MyTasks\"|\"MyPooledTasks\"|\"AllTasks\",\n" + - "\t\"paging\": {\n" + - "\t\t\"skip\": 0,\n" + - "\t\t\"limit\": 15\n" + - "\t}\n" + - "}"; - private static final String WorkflowChangesOrTaskChangesDocumentationSample = "{\n" + - "\t\"propertiesToSet\": {\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}priority\": \"1\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}status\": \"On Hold\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}startDate\": \"2018-01-29T23:00:00.000Z\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}title\": \"Task 1\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}dueDate\": \"2018-01-24T23:00:00.000Z\",\n" + - "\t\t\"{http://www.alfresco.org/model/bpm/1.0}description\": \"Task allocated by colleague 2\"\n" + - "\t}\n" + - "}"; - /// +import java.io.Serializable; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; - @Qualifier("eu.xenit.apix.workflow.IWorkflowService") - @Autowired - IWorkflowService workflowService; +@RestController +public class WorkflowWebscript { + private static final Logger logger = LoggerFactory.getLogger(WorkflowWebscript.class); - Logger logger = LoggerFactory.getLogger(WorkflowWebscript.class); + private final IWorkflowService workflowService; - @Uri(value = "/workflows/definitions", method = HttpMethod.GET, defaultFormat = "json") - @ApiOperation(value = "Retrieve the definitions for all defined workflows") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = WorkflowDefinitionList.class)) - public void getWorkflowDefinitions(@RequestParam(delimiter = ",", required = false) - @ApiParam(value = "Comma separated definition names to exclude.") String[] exclude, - WebScriptResponse response) throws IOException { + public WorkflowWebscript(@Qualifier("eu.xenit.apix.workflow.IWorkflowService") IWorkflowService workflowService) { + this.workflowService = workflowService; + } + @AlfrescoTransaction(readOnly = true) + @GetMapping( + value = "/staging/workflows/definitions", + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity getWorkflowDefinitions( + @RequestParam(required = false) String[] exclude) { List definitions = workflowService.getAllDefinitions(); if (exclude != null && exclude.length > 0) { HashSet excludeSet = new HashSet<>(Arrays.asList(exclude)); - ListIterator iter = definitions.listIterator(); - while (iter.hasNext()) { - if (excludeSet.contains(iter.next().name)) { - iter.remove(); - } - } + definitions.removeIf(workflowDefinition -> excludeSet.contains(workflowDefinition.name)); } - writeJsonResponse(response, new WorkflowDefinitionList(definitions)); + return responseFrom(new WorkflowDefinitionList(definitions)); } - @Uri(value = "/workflows/definition/{name}", method = HttpMethod.GET) - @ApiOperation(value = "Retrieve the definition for the specified workflow name") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = WorkflowDefinition.class)) - public void getWorkflowDefinition(@UriVariable final String name, WebScriptResponse response) throws IOException { - WorkflowDefinition definition = workflowService.getWorkflowDefinition(name); - writeJsonResponse(response, definition); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/workflows/definition/{name}") + public ResponseEntity getWorkflowDefinition(@PathVariable final String name) { + return responseFrom(workflowService.getWorkflowDefinition(name)); } - @Uri(value = "/workflows/search", method = HttpMethod.POST) - @ApiOperation(value = "Returns a collection of workflow instances", - notes = "The result collection of workflow instances is sorted and filtered as requested in the provided" + - " WorkflowSearchQuery\nWorkflowSearchQuery Sample:\n" + WorkflowSearchQueryDocumentationSample) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = SearchQueryResult.class)) - @ApiImplicitParams({@ApiImplicitParam( - dataType = "eu.xenit.apix.workflow.WorkflowSearchQuery", - paramType = "body", - name = "body")}) - public void workflowsActiviti(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) - throws IOException { - ObjectMapper m = new WorkflowJsonParser().getObjectMapper(); - m.setDateFormat(DATE_FORMAT); - InputStream stream = webScriptRequest.getContent().getInputStream(); - WorkflowSearchQuery q = m.readValue(stream, WorkflowSearchQuery.class); - TaskOrWorkflowSearchResult result = workflowService.searchWorkflows(q); - logger.debug("Found results for workflows"); - logger.debug("Nb of results: " + result.results.size()); + @AlfrescoTransaction + @PostMapping(value = "/workflows/search") + public ResponseEntity workflowsActiviti(@RequestBody final WorkflowSearchQuery query) { + TaskOrWorkflowSearchResult result = workflowService.searchWorkflows(query); + logger.debug("Found results for workflows, # of results: {}", result.results.size()); result.getFacets().CheckValidity(); - writeJsonResponse(webScriptResponse, result); + return responseFrom(result); } - @Uri(value = "/tasks/search", method = HttpMethod.POST) - @ApiOperation(value = "Returns a collection of workflow tasks", - notes = "The result collection of workflow tasks is sorted and filtered as requested in the provided" + - " TaskSearchQuery\nTaskSearchQuery Sample:\n" + TaskSearchQueryDocumentationSample) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = SearchQueryResult.class)) - @ApiImplicitParams({@ApiImplicitParam( - dataType = "eu.xenit.apix.workflow.TaskSearchQuery", - paramType = "body", - name = "body")}) - public void tasksActiviti(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) - throws IOException { - ObjectMapper m = new WorkflowJsonParser().getObjectMapper(); - InputStream stream = webScriptRequest.getContent().getInputStream(); - TaskSearchQuery q = m.readValue(stream, TaskSearchQuery.class); - TaskOrWorkflowSearchResult result = workflowService.searchTasks(q); - writeJsonResponse(webScriptResponse, result); + @AlfrescoTransaction + @PostMapping(value = "/tasks/search") + public ResponseEntity tasksActiviti(@RequestBody final TaskSearchQuery query) { + return responseFrom(workflowService.searchTasks(query)); } - - @Uri(value = "/workflows/{id}", method = HttpMethod.GET) - @ApiOperation(value = "Retrieves a workflow with the provided id") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Task.class)) - public void workflow(@UriVariable final String id, WebScriptResponse response) throws IOException { - writeJsonResponse(response, workflowService.getWorkflowInfo(id)); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/workflows/{id}") + public ResponseEntity workflow(@PathVariable final String id) { + return responseFrom(workflowService.getWorkflowInfo(id)); } - @Uri(value = "/workflows/{id}/start", method = HttpMethod.POST) - public void startWorkflow(@UriVariable final String id, @RequestBody Map variables, - WebScriptResponse response) throws IOException { - for (Map.Entry e : variables.entrySet()) { - logger.debug("{}: {}", e.getKey(), e.getValue()); - } - - Workflow workflow = workflowService.startWorkflow(id, variables); - writeJsonResponse(response, workflow); + @AlfrescoTransaction + @PostMapping(value = "/workflows/{id}/start") + public ResponseEntity startWorkflow(@PathVariable final String id, + @RequestBody final Map variables) { + logger.debug("variables: {}", variables); + return responseFrom(workflowService.startWorkflow(id, variables)); } - - @Uri(value = "/tasks/{id}", method = HttpMethod.GET) - @ApiOperation(value = "Retrieves a workflow task with the provided id") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Task.class)) - public void task(@UriVariable final String id, WebScriptResponse response) throws IOException { - writeJsonResponse(response, workflowService.getTaskInfo(id)); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/tasks/{id}") + public ResponseEntity task(@PathVariable final String id) { + responseFrom(workflowService.getTaskInfo(id)); + return ResponseEntity.ok().build(); } - @Uri(value = "/workflows/{id}", method = HttpMethod.PUT) - @ApiOperation(value = "[Deprecated] Updates a workflow with the provided id, with the provided information from" + - " the provided WorkflowChanges\nWorkflowChanges Sample:\n" - + WorkflowChangesOrTaskChangesDocumentationSample) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Workflow.class)) - @ApiImplicitParams({@ApiImplicitParam( - dataType = "eu.xenit.apix.workflow.model.WorkflowChanges", - paramType = "body", - name = "body")}) - public void updateWorkflow( - @UriVariable String id, - WorkflowOrTaskChanges changes, - WebScriptRequest webScriptRequest, - WebScriptResponse webScriptResponse) throws IOException { - Workflow ret = workflowService.updateWorkflow(id, changes); - writeJsonResponse(webScriptResponse, ret); + @AlfrescoTransaction + @PutMapping(value = "/workflows/{id}") + public ResponseEntity updateWorkflow(@PathVariable final String id, + @RequestBody final WorkflowOrTaskChanges changes) { + return responseFrom(workflowService.updateWorkflow(id, changes)); } - @Uri(value = "/workflows/{id}", method = HttpMethod.DELETE) - @ApiOperation(value = "Cancels a workflow with the provided id") - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - public void cancelWorkflow( - @UriVariable String id, - WebScriptRequest webScriptRequest, - WebScriptResponse webScriptResponse) throws IOException { + @AlfrescoTransaction + @DeleteMapping(value = "/workflows/{id}") + public ResponseEntity cancelWorkflow(@PathVariable final String id) { workflowService.cancelWorkflow(id); + return ResponseEntity.ok().build(); } - @Uri(value = "/tasks/{id}", method = HttpMethod.PUT) - @ApiOperation(value = "Updates a workflow task with the provided id, with the provided information from" + - " the provided TaskChanges\nTaskChanges Sample:\n" + WorkflowChangesOrTaskChangesDocumentationSample) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Task.class)) - @ApiImplicitParams({@ApiImplicitParam( - dataType = "eu.xenit.apix.workflow.model.TaskChanges", - paramType = "body", - name = "body")}) - public void updateTask( - @UriVariable String id, - WorkflowOrTaskChanges changes, - WebScriptRequest webScriptRequest, - WebScriptResponse webScriptResponse - ) throws IOException { + @AlfrescoTransaction + @PutMapping(value = "/tasks/{id}") + public ResponseEntity updateTask(@PathVariable final String id, + @RequestBody final WorkflowOrTaskChanges changes) { try { - Task ret = workflowService.updateTask(id, changes); - writeJsonResponse(webScriptResponse, ret); - } catch (Error ex) { - webScriptResponse.setStatus(HttpStatus.SC_CONFLICT); - writeJsonResponse(webScriptResponse, new ExceptionObject(ex)); + return responseFrom(workflowService.updateTask(id, changes)); + } catch (Exception ex) { + return ResponseEntity.status(HttpStatus.SC_CONFLICT).build(); } } - @Uri(value = "/tasks/claim", method = HttpMethod.POST) - @ApiOperation(value = "Claims the task with the provided id for a user", - notes = "The user parameter is optional. If not provided, Alfred API will default to the current user") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = SearchQueryResult.class)) - @ApiImplicitParams({@ApiImplicitParam( - dataType = "String", - paramType = "body", - name = "body")}) - public void claimTask(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException { - ObjectMapper m = new WorkflowJsonParser().getObjectMapper(); - JsonNode input = m.readTree(webScriptRequest.getContent().getContent()); - logger.debug("Input: " + input); - JsonNode id = input.get("id"); - JsonNode userName = input.get("userName"); - + @AlfrescoTransaction + @PostMapping(value = "/staging/tasks/claim") + public ResponseEntity claimTask(@RequestBody final WorkflowClaimsBody workflowClaimsBody) { Task wfTask; - if (userName != null) { - logger.debug("Setting owner of task with id " + id.asText() + " to " + userName); - wfTask = workflowService.claimWorkflowTask(id.asText(), userName.asText()); + if (workflowClaimsBody.getUserName() != null) { + logger.debug("Setting owner of task with id {} to {}", workflowClaimsBody.getId(), workflowClaimsBody.getUserName()); + wfTask = workflowService.claimWorkflowTask(workflowClaimsBody.getId(), workflowClaimsBody.getUserName()); } else { logger.debug("Setting owner of task with id "); - wfTask = workflowService.claimWorkflowTask(id.asText()); + wfTask = workflowService.claimWorkflowTask(workflowClaimsBody.getId()); } - writeJsonResponse(webScriptResponse, wfTask); + return responseFrom(wfTask); } - @Uri(value = "/tasks/release", method = HttpMethod.POST) - @ApiOperation(value = "Releases the task with the provided id") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = SearchQueryResult.class)) - @ApiImplicitParams({@ApiImplicitParam( - dataType = "String", - paramType = "body", - name = "body")}) - public void releaseTask(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) - throws IOException { - ObjectMapper m = new WorkflowJsonParser().getObjectMapper(); - JsonNode input = m.readTree(webScriptRequest.getContent().getContent()); - logger.debug("Input: " + input); - JsonNode id = input.get("id"); - - logger.debug("Setting owner of task with id " + id.asText()); - Task wfTask = workflowService.releaseWorkflowTask(id.asText()); - writeJsonResponse(webScriptResponse, wfTask); + @AlfrescoTransaction + @PostMapping(value = "/staging/tasks/release") + public ResponseEntity releaseTask(@RequestBody final WorkflowReleaseBody workflowReleaseBody) { + logger.debug("Setting owner of task {}", workflowReleaseBody); + Task wfTask = workflowService.releaseWorkflowTask(workflowReleaseBody.getId()); + return responseFrom(wfTask); } - @Uri(value = "/tasks/{id}/end/{transition}", method = HttpMethod.POST) - @ApiOperation(value = "Ends the workflow task with the provided id with the provided transition as next") - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - public void transitionTask(@UriVariable String id, @UriVariable String transition) { + @AlfrescoTransaction + @PostMapping(value = "/staging/tasks/{id}/end/{transition}") + public void transitionTask(@PathVariable String id, @PathVariable String transition) { workflowService.endTask(id, transition); } - - @Uri(value = "/workflows/generate/{amount}/{username}", method = HttpMethod.GET) - @ApiOperation(value = "[DEV] Generate an amount of workflows for username") - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - public void generateWorkflow(@UriVariable final int amount, @UriVariable final String username) { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/staging/workflows/generate/{amount}/{username}") + public void generateWorkflow(@PathVariable final int amount, @PathVariable final String username) { workflowService.GenerateWorkflows(amount, username); } - //Include correct date format - protected void writeJsonResponse(WebScriptResponse response, Object object) throws IOException { - response.setContentType("application/json"); - response.setContentEncoding("utf-8"); - response.setHeader("Cache-Control", "no-cache"); - ObjectMapper mapper = new ObjectMapper(); - mapper.setDateFormat(new ISO8601DateFormat()); - //mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.writeValue(response.getWriter(), object); + protected ResponseEntity responseFrom(T object) { + return ResponseEntity.ok(object); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/RestV0Config.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/RestV0Config.java deleted file mode 100644 index 85d9ce6f..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/RestV0Config.java +++ /dev/null @@ -1,10 +0,0 @@ -package eu.xenit.apix.rest.v0; - -/** - * Created by Michiel Huygen on 09/03/2016. - */ -public class RestV0Config { - - //public static final String BaseUrl = "/apix/v1"; - public static final String Family = "Api-X v0"; -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/categories/ClassificationGetWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/categories/ClassificationGetWebscript.java index 43c77b7c..0bc99c70 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/categories/ClassificationGetWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/categories/ClassificationGetWebscript.java @@ -1,42 +1,34 @@ package eu.xenit.apix.rest.v0.categories; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import eu.xenit.apix.categories.Category; import eu.xenit.apix.categories.ICategoryService; import eu.xenit.apix.data.QName; -import eu.xenit.apix.rest.v0.RestV0Config; -import java.io.IOException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.AbstractWebScript; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; -/** - * Created by Michiel Huygen on 01/12/2015. - */ -@WebScript(families = {RestV0Config.Family}, defaultFormat = "json") -@Component("eu.xenit.apix.rest.v0.categories.ClassificationGetWebscript") -@Authentication(AuthenticationType.USER) -public class ClassificationGetWebscript extends AbstractWebScript { +import java.util.List; - @Autowired - private ICategoryService catService; +@RestController +public class ClassificationGetWebscript { - @Uri("/eu/xenit/classification/{aspectqname}") - @Override() - public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException { - - QName aspectQname = new QName(req.getServiceMatch().getTemplateVars().get("aspectqname")); - - ObjectMapper m = new ObjectMapper(); - String ret = m.writeValueAsString(catService.getCategoryTree(aspectQname)); - - res.getWriter().write(ret); + private final ICategoryService catService; + public ClassificationGetWebscript(ICategoryService catService) { + this.catService = catService; + } + @AlfrescoTransaction(readOnly = true) + @GetMapping( + value = "/classification/{aspectQName}", + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity> execute(@PathVariable final QName aspectQName) { + return ResponseEntity.ok( + catService.getCategoryTree(aspectQName) + ); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/dictionary/DictionaryServiceChecksumWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/dictionary/DictionaryServiceChecksumWebscript.java index ac5342b2..1dc5c1b8 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/dictionary/DictionaryServiceChecksumWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/dictionary/DictionaryServiceChecksumWebscript.java @@ -1,47 +1,35 @@ package eu.xenit.apix.rest.v0.dictionary; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.dictionary.IDictionaryService; -import eu.xenit.apix.rest.v0.RestV0Config; -import java.io.IOException; -import org.alfresco.service.ServiceRegistry; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.AbstractWebScript; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; -@Component("eu.xenit.apix.rest.v0.dictionary.DictionaryServiceChecksumWebscript") -@WebScript(families = {RestV0Config.Family}, defaultFormat = "json") -@Authentication(AuthenticationType.USER) -public class DictionaryServiceChecksumWebscript extends AbstractWebScript { +import java.util.Collections; +import java.util.Map; - @Autowired - private IDictionaryService service; - @Autowired - private ServiceRegistry serviceRegistry; +@RestController +public class DictionaryServiceChecksumWebscript { - @Override - @Uri(value = "/eu/xenit/dictionary/checksum", method = HttpMethod.GET) - public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException { + private final IDictionaryService service; - long checksum = service.getContentModelCheckSum(); - - ObjectMapper m = new ObjectMapper(); - - com.fasterxml.jackson.databind.node.ObjectNode ret = m.createObjectNode(); - ret.put("checksum", checksum); - webScriptResponse.setContentType("json"); - webScriptResponse.getWriter().write(ret.toString()); + public DictionaryServiceChecksumWebscript(IDictionaryService service) { + this.service = service; } - public void setServiceRegistry(ServiceRegistry serviceRegistry) { - this.serviceRegistry = serviceRegistry; + @AlfrescoTransaction(readOnly = true) + @GetMapping( + value = "/dictionary/checksum", + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity> execute() { + return ResponseEntity.ok( + Collections.singletonMap( + "checksum", + service.getContentModelCheckSum() + ) + ); } - } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataBulkWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataBulkWebscript.java index 34103809..ab10a20c 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataBulkWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataBulkWebscript.java @@ -1,76 +1,39 @@ package eu.xenit.apix.rest.v0.metadata; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.node.INodeService; import eu.xenit.apix.permissions.IPermissionService; -import eu.xenit.apix.rest.v0.RestV0Config; -import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import org.alfresco.service.ServiceRegistry; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.AbstractWebScript; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; -@WebScript(families = {RestV0Config.Family}, defaultFormat = "json") -@Component("eu.xenit.apix.rest.v0.metadata.MetadataBulkWebscript") -@Authentication(AuthenticationType.USER) -public class MetadataBulkWebscript extends AbstractWebScript { +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; - @Autowired - private INodeService service; - @Autowired - private IPermissionService permissionService; - @Autowired - private ServiceRegistry serviceRegistry; +@RestController +public class MetadataBulkWebscript { - @Uri(value = "/eu/xenit/metadata/bulk", method = HttpMethod.POST) - @Override - public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException { - ObjectMapper m = new ObjectMapper(); + private final INodeService service; + private final IPermissionService permissionService; - JsonNode input = m.readTree(webScriptRequest.getContent().getContent()); - if (!input.isArray()) { - throw new RuntimeException("Should be an array of noderefs"); - } - - List refs = new ArrayList<>(input.size()); - Iterator iterator = input.elements(); - while (iterator.hasNext()) { - refs.add(new NodeRef(iterator.next().asText())); - } + public MetadataBulkWebscript(INodeService service, IPermissionService permissionService) { + this.service = service; + this.permissionService = permissionService; + } + @AlfrescoTransaction + @PostMapping( + value = "/eu/xenit/metadata/bulk", + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity> execute(@RequestBody final List nodeRefs) { List metadatas = new ArrayList<>(); - - for (NodeRef el : refs) { + for (NodeRef el : nodeRefs) { metadatas.add(NodeMetadataV0.FromV1(service.getMetadata(el), permissionService)); } - - ArrayNode node = m.createArrayNode(); - - for (NodeMetadataV0 metadata : metadatas) { - node.add(m.valueToTree(metadata)); - } - - String retStr = node.toString(); - - webScriptResponse.setContentType("json"); - webScriptResponse.getWriter().write(retStr); + return ResponseEntity.ok(metadatas); } - - public void setServiceRegistry(ServiceRegistry serviceRegistry) { - this.serviceRegistry = serviceRegistry; - } - } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataGetWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataGetWebscript.java index 2ed6225d..99bd8357 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataGetWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataGetWebscript.java @@ -1,48 +1,34 @@ package eu.xenit.apix.rest.v0.metadata; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.node.INodeService; -import eu.xenit.apix.node.NodeMetadata; import eu.xenit.apix.permissions.IPermissionService; -import eu.xenit.apix.rest.v0.RestV0Config; -import java.io.IOException; -import org.alfresco.service.ServiceRegistry; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.AbstractWebScript; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -@WebScript(families = {RestV0Config.Family}, defaultFormat = "json") -@Component("eu.xenit.apix.rest.v0.metadata.MetadataGetWebscript") -@Authentication(AuthenticationType.USER) -public class MetadataGetWebscript extends AbstractWebScript { - @Autowired - private INodeService service; - @Autowired - private IPermissionService permissionService; - @Autowired - private ServiceRegistry serviceRegistry; +@RestController +public class MetadataGetWebscript { + private final INodeService service; + private final IPermissionService permissionService; - @Uri(value = "/eu/xenit/metadata", method = HttpMethod.GET) - @Override - public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException { - ObjectMapper m = new ObjectMapper(); - NodeRef noderef = new NodeRef(webScriptRequest.getParameter("noderef")); - NodeMetadata metadata = service.getMetadata(noderef); - String retStr = m.writeValueAsString(NodeMetadataV0.FromV1(metadata, permissionService)); - webScriptResponse.setContentType("json"); - webScriptResponse.getWriter().write(retStr); + public MetadataGetWebscript(INodeService service, IPermissionService permissionService) { + this.service = service; + this.permissionService = permissionService; } - - public void setServiceRegistry(ServiceRegistry serviceRegistry) { - this.serviceRegistry = serviceRegistry; + @AlfrescoTransaction(readOnly = true) + @GetMapping( + value = "/eu/xenit/metadata", + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity execute(@RequestParam final NodeRef nodeRef) { + return ResponseEntity.ok( + NodeMetadataV0.FromV1( + service.getMetadata(nodeRef), permissionService + ) + ); } - } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataPostWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataPostWebscript.java index 35c6cbfd..c0edf019 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataPostWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/MetadataPostWebscript.java @@ -1,51 +1,38 @@ package eu.xenit.apix.rest.v0.metadata; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.node.INodeService; -import eu.xenit.apix.node.NodeMetadata; import eu.xenit.apix.permissions.IPermissionService; -import eu.xenit.apix.rest.v0.RestV0Config; -import java.io.IOException; -import org.alfresco.service.ServiceRegistry; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.AbstractWebScript; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -@WebScript(families = {RestV0Config.Family}, defaultFormat = "json") -@Component("eu.xenit.apix.rest.v0.metadata.MetadataPostWebscript") -@Authentication(AuthenticationType.USER) -public class MetadataPostWebscript extends AbstractWebScript { +@RestController +public class MetadataPostWebscript { - @Autowired - private INodeService service; - @Autowired - private IPermissionService permissionService; - @Autowired - private ServiceRegistry serviceRegistry; + private final INodeService service; + private final IPermissionService permissionService; - @Uri(value = "/eu/xenit/metadata", method = HttpMethod.POST) - @Override - public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException { - ObjectMapper m = new ObjectMapper(); - NodeRef noderef = new NodeRef(webScriptRequest.getParameter("noderef")); - MetadataChangesV0 changes = m.readValue( - webScriptRequest.getContent().getInputStream(), MetadataChangesV0.class); - - NodeMetadata metadata = service.setMetadata(noderef, changes.ToV1()); - String retStr = m.writeValueAsString(NodeMetadataV0.FromV1(metadata, permissionService)); - webScriptResponse.setContentType("json"); - webScriptResponse.getWriter().write(retStr); + public MetadataPostWebscript(INodeService service, IPermissionService permissionService) { + this.service = service; + this.permissionService = permissionService; } - public void setServiceRegistry(ServiceRegistry serviceRegistry) { - this.serviceRegistry = serviceRegistry; + @AlfrescoTransaction + @PostMapping( + value = "/eu/xenit/metadata", + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity execute(@RequestParam final NodeRef nodeRef, + @RequestBody final MetadataChangesV0 changes) { + return ResponseEntity.ok( + NodeMetadataV0.FromV1( + service.setMetadata(nodeRef, changes.ToV1()), permissionService + ) + ); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/NodeMetadataV0.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/NodeMetadataV0.java index 1d0b770d..7504185c 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/NodeMetadataV0.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/metadata/NodeMetadataV0.java @@ -5,6 +5,7 @@ import eu.xenit.apix.node.NodeMetadata; import eu.xenit.apix.permissions.IPermissionService; import eu.xenit.apix.permissions.PermissionValue; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -16,21 +17,20 @@ */ public class NodeMetadataV0 { - public String id; - public String type; // qname + public boolean canEditMetadata; // TODO: remove + private String id; + private String type; // qname //public String qnamePath;// Whats difference between qname and qnamepath //public String icon16; // REMOVE //public String icon32; // REMOVE //public String mimetype; // REMOVE //public boolean isVersioned; // REMOVE - public long transactionId; + private long transactionId; //TODO: cleanup - public Map> properties;//: { [k: string]: Translation[]; } - public List aspects; + private Map> properties;//: { [k: string]: Translation[]; } //TODO: associations //TODO: parent - - public boolean canEditMetadata; // TODO: remove + private List aspects; //public NodeAssociation[] associations; @@ -39,13 +39,13 @@ public class NodeMetadataV0 { public static NodeMetadataV0 FromV1(NodeMetadata m, IPermissionService permissionService) { NodeMetadataV0 ret = new NodeMetadataV0(); - ret.id = m.id.getValue(); - ret.type = m.type.getValue(); - ret.transactionId = m.transactionId; + ret.id = m.getId().getValue(); + ret.type = m.getType().getValue(); + ret.transactionId = m.getTransactionId(); ret.properties = new HashMap<>(); ret.aspects = new ArrayList<>(); - for (Map.Entry> e : m.properties.entrySet()) { + for (Map.Entry> e : m.getProperties().entrySet()) { List ts = null; if (e != null) { ts = new ArrayList<>(); @@ -56,17 +56,65 @@ public static NodeMetadataV0 FromV1(NodeMetadata m, IPermissionService permissio ret.properties.put(e.getKey().getValue(), ts); } - for (QName a : m.aspects) { + for (QName a : m.getAspects()) { ret.aspects.add(a.getValue()); } - Map perms = permissionService.getPermissions(m.id); + Map perms = permissionService.getPermissions(m.getId()); ret.canEditMetadata = perms.containsKey(IPermissionService.WRITE) && perms.get(IPermissionService.WRITE) == PermissionValue.ALLOW; return ret; } + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public long getTransactionId() { + return transactionId; + } + + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + + public Map> getProperties() { + return properties; + } + + public void setProperties(Map> properties) { + this.properties = properties; + } + + public List getAspects() { + return aspects; + } + + public void setAspects(List aspects) { + this.aspects = aspects; + } + + public boolean isCanEditMetadata() { + return canEditMetadata; + } + + public void setCanEditMetadata(boolean canEditMetadata) { + this.canEditMetadata = canEditMetadata; + } + @Override public String toString() { return "NodeMetadata{" + diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchQueryV0.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchQueryV0.java index 462f4c0d..b1d797e6 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchQueryV0.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchQueryV0.java @@ -5,7 +5,6 @@ import eu.xenit.apix.data.QName; import eu.xenit.apix.search.SearchQuery; import eu.xenit.apix.search.nodes.SearchSyntaxNode; -import io.swagger.annotations.ApiModelProperty; import java.util.ArrayList; import java.util.List; @@ -85,9 +84,7 @@ public static class FacetOptions { //private ArrayList fields; private boolean retrieveDefaults = false; - @ApiModelProperty("Limits the number of values returned per facet") private Integer limit = -1; - @ApiModelProperty("Return only facet values with count >= mincount") private Integer mincount; public boolean isRetrieveDefaults() { diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchWebScript0.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchWebScript0.java index 4d31ee70..ec03d3fb 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchWebScript0.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v0/search/SearchWebScript0.java @@ -1,78 +1,37 @@ package eu.xenit.apix.rest.v0.search; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; -import eu.xenit.apix.rest.v0.RestV0Config; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.search.ISearchService; import eu.xenit.apix.search.SearchQueryResult; -import eu.xenit.apix.search.json.SearchNodeJsonParser; -import java.io.IOException; -import org.alfresco.service.ServiceRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; -@WebScript(families = {RestV0Config.Family}, defaultFormat = "json", value = "Search") -@Component("eu.xenit.apix.rest.v0.search.SearchWebScriptV0") -@Qualifier("eu.xenit.apix.rest.v0.search.SearchWebScriptV0") -@Authentication(AuthenticationType.USER) +@RestController public class SearchWebScript0 { + private static final Logger logger = LoggerFactory.getLogger(SearchWebScript0.class); + private final ISearchService service; - Logger logger = LoggerFactory.getLogger(SearchWebScript0.class); - @Autowired - private ISearchService service; - @Autowired - private ServiceRegistry serviceRegistry; - - @Uri(value = "/eu/xenit/search", method = HttpMethod.POST) - public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException { - - logger.debug(webScriptRequest.toString()); + public SearchWebScript0(ISearchService service) { + this.service = service; + } - ObjectMapper m = new SearchNodeJsonParser().getObjectMapper(); - SearchQueryV0 q; - try { - q = m.readValue(webScriptRequest.getContent().getInputStream(), SearchQueryV0.class); - } catch (Exception e) { - webScriptResponse.setStatus(500); - webScriptResponse.getWriter().write("Error occured during reading of search query"); - webScriptResponse.getWriter().write(e.getMessage()); - webScriptResponse.getWriter().write(e.getStackTrace().toString()); - return; - } + @AlfrescoTransaction + @PostMapping( + value = "/eu/xenit/search", + produces = MediaType.APPLICATION_JSON_VALUE + ) + public ResponseEntity execute(@RequestBody final SearchQueryV0 query){ + logger.debug("query: {}", query); SearchQueryResult result; - try { - result = service.query(q.toV1()); - } catch (Exception e) { - webScriptResponse.setStatus(500); - webScriptResponse.getWriter().write("Error occured while searching"); - webScriptResponse.getWriter().write(e.getMessage()); - webScriptResponse.getWriter().write(e.getStackTrace().toString()); - return; - } - + result = service.query(query.toV1()); for (eu.xenit.apix.search.FacetSearchResult f : result.getFacets()) { f.setName("@" + f.getName()); } - - ObjectMapper mapper = new ObjectMapper(); - com.fasterxml.jackson.databind.ObjectWriter wr = mapper.writerWithType(SearchQueryResult.class); - String retStr = wr.writeValueAsString(result); - - webScriptResponse.setContentType("json"); - webScriptResponse.getWriter().write(retStr); + return ResponseEntity.ok(result); } - - public void setServiceRegistry(ServiceRegistry serviceRegistry) { - this.serviceRegistry = serviceRegistry; - } - } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/ApixSwaggerDescription.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/ApixSwaggerDescription.java deleted file mode 100644 index fdbf3ac0..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/ApixSwaggerDescription.java +++ /dev/null @@ -1,31 +0,0 @@ -package eu.xenit.apix.rest.v1; - -import io.swagger.annotations.Contact; -import io.swagger.annotations.Info; -import io.swagger.annotations.License; -import io.swagger.annotations.SwaggerDefinition; -import io.swagger.annotations.Tag; - -/** - * Created by Michiel Huygen on 14/03/2016. - */ -@SwaggerDefinition( - info = @Info( - description = "This is the swagger specification for Api-X REST API" + - "\n\n" + - "Examples can be found at: https://docs.xenit.eu/alfred-api", - version = "2.0.0", - title = "Api-X REST API", - //termsOfService = "http://swagger.io/terms/", - contact = @Contact(name = "XeniT", email = "engineering@xenit.eu", url = "http://www.xenit.eu"), - license = @License(name = "GNU Lesser General Public License v3", url = "https://www.gnu.org/licenses/lgpl-3.0.txt") - ), - //consumes = {"application/json", "application/xml"}, - produces = {"application/json"}, - schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}, - tags = {@Tag(name = "WIP", description = "Dont use in production!")} - //tags = {@Tag(name = "metadata", description = "Operations on node metadata")} -) -public class ApixSwaggerDescription { - -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/ApixV1Webscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/ApixV1Webscript.java index 508c69b1..a2086e4e 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/ApixV1Webscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/ApixV1Webscript.java @@ -1,8 +1,6 @@ package eu.xenit.apix.rest.v1; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.util.ISO8601DateFormat; + import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.filefolder.IFileFolderService; import eu.xenit.apix.node.ChildParentAssociation; @@ -13,63 +11,48 @@ import eu.xenit.apix.permissions.IPermissionService; import eu.xenit.apix.permissions.PermissionValue; import eu.xenit.apix.rest.v1.nodes.NodeInfo; -import java.io.IOException; +import eu.xenit.apix.rest.v1.nodes.NodeInfoRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; + import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; -import org.alfresco.repo.admin.SysAdminParams; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; +import java.util.stream.Collectors; -/** - * Created by kenneth on 14.03.16. - */ public class ApixV1Webscript { - private final static Logger logger = LoggerFactory.getLogger(ApixV1Webscript.class); - - @Autowired - SysAdminParams sysAdminParams; + private static final Logger logger = LoggerFactory.getLogger(ApixV1Webscript.class); - protected void writeJsonResponse(WebScriptResponse response, Object object) throws IOException { - response.setContentType("application/json"); - response.setContentEncoding("utf-8"); - response.setHeader("Cache-Control", "no-cache"); - ObjectMapper mapper = new ObjectMapper(); - mapper.setDateFormat(new ISO8601DateFormat()); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.writeValue(response.getWriter(), object); + protected ResponseEntity writeJsonResponse(T object) { + return ResponseEntity.ok(object); } protected NodeRef createNodeRef(String space, String store, String guid) { return new NodeRef(space, store, guid); } - public String removeEscapeCharacters(String str) { - return str.replace("\\", ""); - } - - protected List nodeRefToNodeInfo(List nodeRefs, - IFileFolderService fileFolderService, - INodeService nodeService, - IPermissionService permissionService, - boolean retrievePath, - boolean retrieveMetadata, - boolean retrievePermissions, - boolean retrieveAssocs, - boolean retrieveChildAssocs, - boolean retrieveParentAssocs, - boolean retrieveTargetAssocs, - boolean retrieveSourceAssocs + protected List nodeRefToNodeInfo(NodeInfoRequest nodeInfoRequest, + IFileFolderService fileFolderService, + INodeService nodeService, + IPermissionService permissionService ) { List nodeInfoList = new ArrayList<>(); + List nodeRefs = nodeInfoRequest.getNoderefs().stream().map(NodeRef::new).collect(Collectors.toList()); for (NodeRef nodeRef : nodeRefs) { - NodeInfo nodeInfo = nodeRefToNodeInfo(nodeRef, fileFolderService, nodeService, permissionService, - retrievePath, retrieveMetadata, retrievePermissions, retrieveAssocs, retrieveChildAssocs, - retrieveParentAssocs, retrieveTargetAssocs, retrieveSourceAssocs); + NodeInfo nodeInfo = nodeRefToNodeInfo(nodeRef, + fileFolderService, + nodeService, + permissionService, + nodeInfoRequest.getRetrievePath(), + nodeInfoRequest.getRetrieveMetadata(), + nodeInfoRequest.getRetrievePermissions(), + nodeInfoRequest.getRetrieveAssocs(), + nodeInfoRequest.getRetrieveChildAssocs(), + nodeInfoRequest.getRetrieveParentAssocs(), + nodeInfoRequest.getRetrieveTargetAssocs(), + nodeInfoRequest.getRetrieveSourceAssocs()); if (nodeInfo == null) { continue; } @@ -81,24 +64,24 @@ protected List nodeRefToNodeInfo(List nodeRefs, } protected NodeInfo nodeRefToNodeInfo(NodeRef nodeRef, IFileFolderService fileFolderService, - INodeService nodeService, IPermissionService permissionService) { + INodeService nodeService, IPermissionService permissionService) { return nodeRefToNodeInfo(nodeRef, fileFolderService, nodeService, permissionService, true, true, true, true, true, true, true, true); } protected NodeInfo nodeRefToNodeInfo(NodeRef nodeRef, - IFileFolderService fileFolderService, - INodeService nodeService, - IPermissionService permissionService, - boolean retrievePath, - boolean retrieveMetadata, - boolean retrievePermissions, - boolean retrieveAssocs, - boolean retrieveChildAssocs, - boolean retrieveParentAssocs, - boolean retrieveTargetAssocs, - boolean retrieveSourceAssocs) { + IFileFolderService fileFolderService, + INodeService nodeService, + IPermissionService permissionService, + boolean retrievePath, + boolean retrieveMetadata, + boolean retrievePermissions, + boolean retrieveAssocs, + boolean retrieveChildAssocs, + boolean retrieveParentAssocs, + boolean retrieveTargetAssocs, + boolean retrieveSourceAssocs) { if (!permissionService.hasPermission(nodeRef, IPermissionService.READ)) { logger.warn("Excluding node {} from results due to insufficient permissions", nodeRef); return null; diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/GeneralWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/GeneralWebscript.java index 8946c65c..f70edcf8 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/GeneralWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/GeneralWebscript.java @@ -1,43 +1,27 @@ package eu.xenit.apix.rest.v1; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.version.IVersionService; import eu.xenit.apix.version.VersionDescription; -import eu.xenit.apix.web.IWebUtils; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; -@WebScript(baseUri = RestV1Config.BaseUrl, families = {RestV1Config.Family}, defaultFormat = "json", - description = "General API operations", value = "General") -@Component("eu.xenit.apix.rest.v1.GeneralWebscript") -@Authentication(AuthenticationType.USER) -public class GeneralWebscript extends ApixV1Webscript {//implements BeanFactoryAware{ +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController +public class GeneralWebscript extends ApixV1Webscript { - Logger logger = LoggerFactory.getLogger(GeneralWebscript.class); + private final IVersionService versionService; - IVersionService versionService; - - @Autowired - public GeneralWebscript(IVersionService versionService, IWebUtils webUtils) { + public GeneralWebscript(IVersionService versionService) { this.versionService = versionService; } - @Uri(value = "/version", method = HttpMethod.GET) - @ApiOperation("Access the version information for Api-X") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = VersionDescription.class)) - public void getApixVersion(WebScriptResponse response) throws IOException { - writeJsonResponse(response, versionService.getVersionDescription()); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/version") + public ResponseEntity getApixVersion() { + return writeJsonResponse(versionService.getVersionDescription()); } - } \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkSubResult.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkSubResult.java index 61782ca4..f88bdd12 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkSubResult.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkSubResult.java @@ -1,7 +1,6 @@ package eu.xenit.apix.rest.v1.bulk; import com.fasterxml.jackson.databind.JsonNode; -import io.swagger.annotations.ApiModelProperty; import java.util.HashMap; import java.util.Map; @@ -10,17 +9,15 @@ */ public class BulkSubResult { - @ApiModelProperty(value = "The HTTP status code of the sub call", example = "200") private int statusCode; - @ApiModelProperty(value = "JSON result body of the sub call", dataType = "object") - private JsonNode body; + private Object body; private Map headers; public BulkSubResult() { } - public BulkSubResult(int statusCode, JsonNode body, Map headers) { + public BulkSubResult(int statusCode, Object body, Map headers) { this.statusCode = statusCode; this.body = body; this.headers = headers; @@ -34,7 +31,7 @@ public void setStatusCode(int statusCode) { this.statusCode = statusCode; } - public JsonNode getBody() { + public Object getBody() { return body; } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkWebscript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkWebscript1.java index abe81f64..ae4919d1 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkWebscript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkWebscript1.java @@ -1,157 +1,138 @@ package eu.xenit.apix.rest.v1.bulk; -import com.fasterxml.jackson.core.io.JsonStringEncoder; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.WebScriptUriRegistry; -import com.github.dynamicextensionsalfresco.webscripts.annotations.FormatStyle; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.webscript.DispatcherWebscript; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import eu.xenit.apix.rest.v1.bulk.request.BulkHttpServletRequest; +import eu.xenit.apix.rest.v1.bulk.request.BulkRequest; +import eu.xenit.apix.rest.v1.bulk.request.IntermediateRequest; +import eu.xenit.apix.rest.v1.bulk.response.IntermediateResponse; +import java.nio.charset.StandardCharsets; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; -import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.surf.util.Content; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.extensions.surf.util.URLDecoder; -import org.springframework.extensions.webscripts.Match; import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; - -/** - * Created by kenneth on 18.03.16. Largely rewritten by roel on 13.03.18 - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Perform multiple operations in a single call", value = "Bulk") -@Component("eu.xenit.apix.rest.v1.BulkWebscript") -public class BulkWebscript1 extends ApixV1Webscript { +import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; - private final static Logger logger = LoggerFactory.getLogger(BulkWebscript1.class); - @Autowired - ServiceRegistry serviceRegistry; - // Dynamic Extensions is needed to provide the object that can be autowired in here - @Autowired - WebScriptUriRegistry wsuriRegistry; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; - public BulkWebscript1() { - } - public BulkWebscript1(ServiceRegistry serviceRegistry, WebScriptUriRegistry registry) { - this.serviceRegistry = serviceRegistry; - this.wsuriRegistry = registry; - } +@RestController +public class BulkWebscript1 extends ApixV1Webscript { - public static BulkSubResult createBulkResult(ObjectMapper mapper, IntermediateResponse resp) throws IOException { - int status = resp.getStatus(); - JsonNode body; - String strbody = resp.getWriter().toString(); + private static final Logger logger = LoggerFactory.getLogger(BulkWebscript1.class); - if (strbody == null || strbody.equals("")) { - body = mapper.createObjectNode(); - } else { - body = mapper.readTree(resp.getWriter().toString()); - } - - return new BulkSubResult(status, body, resp.getHeaders()); - } + private final ServiceRegistry serviceRegistry; + final ObjectMapper mapper = new ObjectMapper(); + private final DispatcherWebscript dispatcherWebscript; - public static BulkSubResult create404subResult(ObjectMapper mapper, String url) throws IOException { - return new BulkSubResult(HttpStatus.SC_NOT_FOUND /* = 404 */, - mapper.readTree("\"No Webscript found for url " + - new String(JsonStringEncoder.getInstance().quoteAsString(url)) + "\""), - new HashMap(0)); + public BulkWebscript1(ServiceRegistry serviceRegistry, + @Qualifier("alfred.api") DispatcherWebscript dispatcherWebscript) { + this.serviceRegistry = serviceRegistry; + this.dispatcherWebscript = dispatcherWebscript; } - @Uri(value = "/bulk", method = HttpMethod.POST, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation("Performs multiple Api-X operations in a single rest call") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = BulkSubResult[].class)) - public void bulk(final BulkRequest[] bulkRequests, final WebScriptRequest req, final WebScriptResponse response) - throws IOException { + @AlfrescoTransaction + @PostMapping(value = "/v1/bulk") + public ResponseEntity> bulk(@RequestBody final BulkRequest[] bulkRequests, + final HttpServletRequest req) { + final WebScriptRequest wsReq = ((DispatcherWebscript.WebscriptRequestWrapper) req).getWebScriptServletRequest(); - final WebScriptUriRegistry registry = wsuriRegistry; - final ObjectMapper mapper = new ObjectMapper(); final List bulkResults = new ArrayList<>(); - for (int i = 0; i < bulkRequests.length; i++) { - BulkRequest request = bulkRequests[i]; + for (BulkRequest request : bulkRequests) { // v1 bulk only gets to send to other v1 endpoints :( - // We need to url decode this so we can find the match and parse any @UriVariable - final String url = URLDecoder.decode("/apix/v1" + request.getUrl()); + // We need to url decode this, so we can find the match and parse any @UriVariable - logger.debug("Evaluating {} with body {}", url, request.getBody()); + BulkHttpServletRequest bulkHttpServletRequest = new BulkHttpServletRequest(req, + URLDecoder.decode("/alfresco/service/apix/v1" + request.getUrl()), + request.getMethod().toUpperCase(), request.getBody()); + logger.debug("Evaluating {} with body {} and method {}", bulkHttpServletRequest.getRequestURI(), + bulkHttpServletRequest.getBody(), + bulkHttpServletRequest.getMethod()); - String strippedUrl = url; - if(url.contains("?")) { - strippedUrl = url.substring(0, url.indexOf("?")); - } - - //The Match created in this method does not split off the querystring arguments correctly, - //therefore we will remove the queryString from the url for this method. Later in the call, - //the url with query string is still passed to have it handled. - final Match m = registry.findWebScript(request.getMethod(), strippedUrl); - if (m == null || m.getWebScript() == null) { - logger.debug("Could not find any webscript bound to {}. Injecting 404 subresponse.", url); - bulkResults.add(create404subResult(mapper, url)); - continue; - } + final IntermediateRequest intermediateRequest = new IntermediateRequest(wsReq, bulkHttpServletRequest); - logger.debug("Matched on {}", m.getWebScript().getClass().toString()); + final IntermediateResponse intermediateResponse = new IntermediateResponse(); + // ALFREDAPI-520: Due to the bulk script circumventing the spring framework config + // through the alfresco-mvc dispatcher servlet, the encoding of the response can be mangled. + // Setting it here avoids this issue. + // This is not considered breaking since the original implementation did not have a mechanism for the client + // to request certain encodings. + intermediateResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + final WebScriptServletResponse webScriptServletResponse = new WebScriptServletResponse(wsReq.getRuntime(), + intermediateResponse); - // These Intermediate... classes are simple container objects, standing in for HTTP requests and responses - // They are used to execute the WebScript directly, providing arguments and retrieving the result - Content intermediateContent = new IntermediateContent(req.getContent(), request.getBody()); - final WebScriptRequest intermediateRequest = new IntermediateRequest(req, url, intermediateContent, m); - final IntermediateResponse intermediateResponse = new IntermediateResponse(req.getRuntime()); // Each subrequest gets to run in its own transaction context, because even a caught exception can mark a // transaction as "needs to be rolled back". We don't want a previous subrequest to be rolled back because // the next subrequest used some exception-driven code. Also, please don't use exceptions as flow control. BulkSubResult subRes = (BulkSubResult) serviceRegistry.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - try { - // Actual execution of called WebScript happens here - m.getWebScript().execute(intermediateRequest, intermediateResponse); - logger.debug("Resp body is '{}'", intermediateResponse.getWriter().toString()); - // Convert the response container to json + statuscode + headers - return createBulkResult(mapper, intermediateResponse); - } catch (Exception e) { - // Catching all exceptions to just print a stacktrace isn't super clean... - // But we want the bulk to continue with the other requests even if this one fails. - logger.error("Error in bulk call to " + url, e); - return new BulkSubResult(500, - new ObjectMapper().valueToTree("Exception found: " + e.getMessage()), - new HashMap()); - } + .doInTransaction((RetryingTransactionHelper.RetryingTransactionCallback) () -> { + try { + // Actual execution of called WebScript happens here + dispatcherWebscript.execute(intermediateRequest, webScriptServletResponse); + logger.debug("Resp body is '{}'", intermediateResponse.getWriter()); + // Convert the response container to json + statuscode + headers + return createBulkResult(mapper, intermediateResponse); + } catch (Exception e) { + // Catching all exceptions to just print a stacktrace isn't super clean... + // But we want the bulk to continue with the other requests even if this one fails. + logger.error("Error in bulk call to {}", intermediateRequest.getHttpServletRequest().getRequestURI(), e); + return new BulkSubResult(500, + mapper.valueToTree("Exception found: " + e.getMessage()), + new HashMap<>()); } }, false, true); if (subRes == null) { - logger.warn("bulkSubResult is null for request {}.", url); + logger.warn("bulkSubResult is null for request {}.", intermediateRequest.getHttpServletRequest().getRequestURI()); } else { bulkResults.add(subRes); } } - if (bulkResults.size() > 0) { + if (!bulkResults.isEmpty()) { // Write out the list of BulkSubResults which becomes nice json - writeJsonResponse(response, bulkResults); + return writeJsonResponse(bulkResults); + } + logger.warn("No results for bulk operation"); + return ResponseEntity.noContent().build(); + } + + private static BulkSubResult createBulkResult(ObjectMapper mapper, IntermediateResponse resp) throws IOException { + int status = resp.getStatus(); + Object body; + String strBody = resp.getContentAsString(); + if (strBody == null || strBody.equals("")) { + body = mapper.createObjectNode(); } else { - logger.warn("No results for bulk operation"); + try { + body = mapper.readTree(strBody); + } catch (JsonParseException exception) { + body = strBody; + } } + + return new BulkSubResult(status, body, resp.getHeaderNames().stream().collect( + Collectors.toMap(Function.identity(), resp::getHeader) + )); } + } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateContent.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateContent.java deleted file mode 100644 index e22875ca..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateContent.java +++ /dev/null @@ -1,53 +0,0 @@ -package eu.xenit.apix.rest.v1.bulk; - -import com.fasterxml.jackson.databind.JsonNode; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import org.apache.commons.io.IOUtils; -import org.springframework.extensions.surf.util.Content; - -public class IntermediateContent implements Content { - - // According to /etc/mime.types, this is the only one - private final String mimetype = "application/json"; - private String json; - // Should always be UTF-8 but since our json comes from the parent Content, - // we'll get the encoding from there too - private String encoding; - - public IntermediateContent(Content parent, JsonNode content) { - json = (content != null) ? content.toString() : "{}"; - encoding = parent.getEncoding(); - } - - public String getContent() { - return json; - } - - @Override - public String getMimetype() { - return mimetype; - } - - @Override - public String getEncoding() { - return encoding; - } - - @Override - public long getSize() { - return json.length(); - } - - @Override - public InputStream getInputStream() { - return IOUtils.toInputStream(json); - } - - @Override - public Reader getReader() throws IOException { - return new StringReader(json); - } -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateRequest.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateRequest.java deleted file mode 100644 index 22d5bbd3..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateRequest.java +++ /dev/null @@ -1,72 +0,0 @@ -package eu.xenit.apix.rest.v1.bulk; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.springframework.extensions.surf.util.Content; -import org.springframework.extensions.webscripts.Match; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptRequestURLImpl; - -public class IntermediateRequest extends WebScriptRequestURLImpl { - - private String serverPath; - private Map> headers; - private String agent; - private Content content; - - public IntermediateRequest(WebScriptRequest bulkWebScriptRequest, String URL, Content content, Match match) { - super(bulkWebScriptRequest.getRuntime(), splitURL(bulkWebScriptRequest.getContextPath(), URL), match); - serverPath = bulkWebScriptRequest.getServerPath(); - this.content = content; - agent = bulkWebScriptRequest.getAgent(); - headers = new HashMap<>(); - for (String headerName : bulkWebScriptRequest.getHeaderNames()) { - String[] values = bulkWebScriptRequest.getHeaderValues(headerName); - List list = Arrays.asList(values); - headers.put(headerName, list); - } - } - - @Override - public String getServerPath() { - return serverPath; - } - - @Override - public String[] getHeaderNames() { - Set names = headers.keySet(); - return names.toArray(new String[names.size()]); - } - - @Override - public String getHeader(String name) { - List list = headers.get(name); - if (list == null || list.size() == 0) { - return null; - } - return list.get(0); - } - - @Override - public String[] getHeaderValues(String name) { - List list = headers.get(name); - if (list == null || list.size() == 0) { - return null; - } - return list.toArray(new String[list.size()]); - } - - @Override - public Content getContent() { - return content; - } - - @Override - public String getAgent() { - return agent; - } - -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateResponse.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateResponse.java deleted file mode 100644 index d214d831..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/IntermediateResponse.java +++ /dev/null @@ -1,80 +0,0 @@ -package eu.xenit.apix.rest.v1.bulk; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringWriter; -import java.io.Writer; -import java.util.HashMap; -import java.util.Map; -import org.springframework.extensions.webscripts.Cache; -import org.springframework.extensions.webscripts.Runtime; -import org.springframework.extensions.webscripts.WebScriptResponseImpl; - -public class IntermediateResponse extends WebScriptResponseImpl { - - private Writer out; - private int status = 200; // Webscripts default to success, explicit set status on failure - private Map headers; - - public IntermediateResponse(Runtime runtime) { - super(runtime); - out = new StringWriter(); - headers = new HashMap<>(); - } - - public int getStatus() { - return status; - } - - public void setStatus(int status) { - this.status = status; - } - - public OutputStream getOutputStream() throws IOException { - // TODO: implement this? - return null; - } - - public Writer getWriter() throws IOException { - return out; - } - - public Map getHeaders() { - return headers; - } - - public void setHeader(String name, String value) { - headers.put(name, value); - } - - public void addHeader(String name, String value) { - setHeader(name, value); - } - - public String encodeScriptUrl(String url) { - // not supported - throw new UnsupportedOperationException(); - } - - public String getEncodeScriptUrlFunction(String name) { - // not supported - throw new UnsupportedOperationException(); - } - - public void reset() { - // not supported - } - - public void setCache(Cache cache) { - // not supported - } - - public void setContentType(String contentType) { - // not supported - } - - public void setContentEncoding(String contentEncoding) { - // not supported - } - -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/BulkHttpServletRequest.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/BulkHttpServletRequest.java new file mode 100644 index 00000000..6cdec4eb --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/BulkHttpServletRequest.java @@ -0,0 +1,80 @@ +package eu.xenit.apix.rest.v1.bulk.request; + +import org.springframework.util.StringUtils; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.IOException; + +public class BulkHttpServletRequest extends HttpServletRequestWrapper { + private static final String HTTP = "http"; + + private static final String HTTPS = "https"; + private static final String ALFRESCO = "/alfresco"; + private static final String SERVICE = "/service"; + + /** + * Constructs a request object wrapping the given request. + * + * @param request the {@link HttpServletRequest} to be wrapped. + * @throws IllegalArgumentException if the request is null + */ + public BulkHttpServletRequest(HttpServletRequest request, String url, String method, Object body) { + super(request); + this.url = url; + this.method = method; + this.body = body; + } + + final String url; + final String method; + final Object body; + + @Override + public String getRequestURI() { + return url; + } + + @Override + public String getContextPath() { + return ALFRESCO; + } + + @Override + public String getServletPath() { + return SERVICE; + } + + @Override + public String getMethod() { + return method; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return new IntermediateContent(body); + } + + public Object getBody() { + return body; + } + + @Override + public StringBuffer getRequestURL() { + String scheme = getScheme(); + String server = getServerName(); + int port = getServerPort(); + String uri = getRequestURI(); + + StringBuffer sb = new StringBuffer(scheme).append("://").append(server); + if (port > 0 && ((HTTP.equalsIgnoreCase(scheme) && port != 80) || + (HTTPS.equalsIgnoreCase(scheme) && port != 443))) { + sb.append(':').append(port); + } + if (StringUtils.hasText(uri)) { + sb.append(uri); + } + return sb; + } +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkRequest.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/BulkRequest.java similarity index 71% rename from apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkRequest.java rename to apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/BulkRequest.java index a4b62a2e..a3ec58a3 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/BulkRequest.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/BulkRequest.java @@ -1,18 +1,14 @@ -package eu.xenit.apix.rest.v1.bulk; +package eu.xenit.apix.rest.v1.bulk.request; import com.fasterxml.jackson.databind.JsonNode; -import io.swagger.annotations.ApiModelProperty; /** * Represents a single request in a bulk requests call Created by kenneth on 18.03.16. */ public class BulkRequest { - @ApiModelProperty(required = true, allowableValues = "get,put,post,delete") private String method; - @ApiModelProperty(example = "/version?alf_ticket=TICKET_4654...", required = true) private String url; - @ApiModelProperty(dataType = "object", notes = "Only allowed for PUT and POST") private JsonNode body; public BulkRequest() { diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/IntermediateContent.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/IntermediateContent.java new file mode 100644 index 00000000..7887cdbf --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/IntermediateContent.java @@ -0,0 +1,41 @@ +package eu.xenit.apix.rest.v1.bulk.request; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class IntermediateContent extends ServletInputStream { + IntermediateContent(Object body) throws JsonProcessingException { + mapper = new ObjectMapper(); + delegate = new ByteArrayInputStream(mapper.writeValueAsBytes(body)); + } + + final ObjectMapper mapper; + final InputStream delegate; + + @Override + public int read() throws IOException { + return delegate.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) { + //will not be implemented + } + +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/IntermediateRequest.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/IntermediateRequest.java new file mode 100644 index 00000000..e8a32d05 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/request/IntermediateRequest.java @@ -0,0 +1,32 @@ +package eu.xenit.apix.rest.v1.bulk.request; + +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest; + +import javax.servlet.http.HttpServletRequest; + +public class IntermediateRequest extends WebScriptServletRequest { + public static final String MATCH = "apix/"; + + public IntermediateRequest(WebScriptRequest bulkWebScriptRequest, HttpServletRequest request) { + super(bulkWebScriptRequest.getRuntime(), request, null, null); + } + + + @Override + public String getExtensionPath() { + String uri = getHttpServletRequest().getRequestURI(); + + if (uri.contains(MATCH)) { + uri = uri.substring(uri.indexOf(MATCH) + MATCH.length()); + } + + if (uri.contains("?")) { + uri = uri.substring(0, uri.indexOf("?")); + } + + return uri; + } + + +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/DelegatingServletOutputStream.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/DelegatingServletOutputStream.java new file mode 100644 index 00000000..759a73e9 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/DelegatingServletOutputStream.java @@ -0,0 +1,61 @@ +package eu.xenit.apix.rest.v1.bulk.response; + + +import org.springframework.util.Assert; + +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import java.io.IOException; +import java.io.OutputStream; + +public class DelegatingServletOutputStream extends ServletOutputStream { + + private final OutputStream targetStream; + + + /** + * Create a DelegatingServletOutputStream for the given target stream. + * + * @param targetStream the target stream (never {@code null}) + */ + public DelegatingServletOutputStream(OutputStream targetStream) { + Assert.notNull(targetStream, "Target OutputStream must not be null"); + this.targetStream = targetStream; + } + + /** + * Return the underlying target stream (never {@code null}). + */ + public final OutputStream getTargetStream() { + return this.targetStream; + } + + + @Override + public void write(int b) throws IOException { + this.targetStream.write(b); + } + + @Override + public void flush() throws IOException { + super.flush(); + this.targetStream.flush(); + } + + @Override + public void close() throws IOException { + super.close(); + this.targetStream.close(); + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + throw new UnsupportedOperationException(); + } + +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/HeaderValueHolder.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/HeaderValueHolder.java new file mode 100644 index 00000000..e6364e24 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/HeaderValueHolder.java @@ -0,0 +1,64 @@ +package eu.xenit.apix.rest.v1.bulk.response; + + +import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class HeaderValueHolder { + + private final List values = new LinkedList<>(); + + + void setValue(@Nullable Object value) { + this.values.clear(); + if (value != null) { + this.values.add(value); + } + } + + void addValue(Object value) { + this.values.add(value); + } + + void addValues(Collection values) { + this.values.addAll(values); + } + + void addValueArray(Object values) { + CollectionUtils.mergeArrayIntoCollection(values, this.values); + } + + List getValues() { + return Collections.unmodifiableList(this.values); + } + + List getStringValues() { + List stringList = new ArrayList<>(this.values.size()); + for (Object value : this.values) { + stringList.add(value.toString()); + } + return Collections.unmodifiableList(stringList); + } + + @Nullable + Object getValue() { + return (!this.values.isEmpty() ? this.values.get(0) : null); + } + + @Nullable + String getStringValue() { + return (!this.values.isEmpty() ? String.valueOf(this.values.get(0)) : null); + } + + @Override + public String toString() { + return this.values.toString(); + } + +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/IntermediateCookie.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/IntermediateCookie.java new file mode 100644 index 00000000..b9190a86 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/IntermediateCookie.java @@ -0,0 +1,150 @@ +package eu.xenit.apix.rest.v1.bulk.response; + +import org.springframework.core.style.ToStringCreator; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import javax.servlet.http.Cookie; +import java.time.DateTimeException; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + + +public class IntermediateCookie extends Cookie { + + private static final long serialVersionUID = 4312531139502726325L; + + + @Nullable + private ZonedDateTime expires; + + @Nullable + private String sameSite; + + + /** + * Construct a new {@link IntermediateCookie} with the supplied name and value. + * + * @param name the name + * @param value the value + * @see Cookie#Cookie(String, String) + */ + public IntermediateCookie(String name, String value) { + super(name, value); + } + + /** + * Set the "Expires" attribute for this cookie. + * + * @since 5.1.11 + */ + public void setExpires(@Nullable ZonedDateTime expires) { + this.expires = expires; + } + + /** + * Get the "Expires" attribute for this cookie. + * + * @return the "Expires" attribute for this cookie, or {@code null} if not set + * @since 5.1.11 + */ + @Nullable + public ZonedDateTime getExpires() { + return this.expires; + } + + /** + * Set the "SameSite" attribute for this cookie. + *

This limits the scope of the cookie such that it will only be attached + * to same-site requests if the supplied value is {@code "Strict"} or cross-site + * requests if the supplied value is {@code "Lax"}. + * + * @see RFC6265 bis + */ + public void setSameSite(@Nullable String sameSite) { + this.sameSite = sameSite; + } + + /** + * Get the "SameSite" attribute for this cookie. + * + * @return the "SameSite" attribute for this cookie, or {@code null} if not set + */ + @Nullable + public String getSameSite() { + return this.sameSite; + } + + + /** + * Factory method that parses the value of the supplied "Set-Cookie" header. + * + * @param setCookieHeader the "Set-Cookie" value; never {@code null} or empty + * @return the created cookie + */ + public static IntermediateCookie parse(String setCookieHeader) { + Assert.notNull(setCookieHeader, "Set-Cookie header must not be null"); + String[] cookieParts = setCookieHeader.split("\\s*=\\s*", 2); + Assert.isTrue(cookieParts.length == 2, () -> "Invalid Set-Cookie header '" + setCookieHeader + "'"); + + String name = cookieParts[0]; + String[] valueAndAttributes = cookieParts[1].split("\\s*;\\s*", 2); + String value = valueAndAttributes[0]; + String[] attributes = + (valueAndAttributes.length > 1 ? valueAndAttributes[1].split("\\s*;\\s*") : new String[0]); + + IntermediateCookie cookie = new IntermediateCookie(name, value); + for (String attribute : attributes) { + if (StringUtils.startsWithIgnoreCase(attribute, "Domain")) { + cookie.setDomain(extractAttributeValue(attribute, setCookieHeader)); + } else if (StringUtils.startsWithIgnoreCase(attribute, "Max-Age")) { + cookie.setMaxAge(Integer.parseInt(extractAttributeValue(attribute, setCookieHeader))); + } else if (StringUtils.startsWithIgnoreCase(attribute, "Expires")) { + try { + cookie.setExpires(ZonedDateTime.parse(extractAttributeValue(attribute, setCookieHeader), + DateTimeFormatter.RFC_1123_DATE_TIME)); + } catch (DateTimeException ex) { + // ignore invalid date formats + } + } else if (StringUtils.startsWithIgnoreCase(attribute, "Path")) { + cookie.setPath(extractAttributeValue(attribute, setCookieHeader)); + } else if (StringUtils.startsWithIgnoreCase(attribute, "Secure")) { + cookie.setSecure(true); + } else if (StringUtils.startsWithIgnoreCase(attribute, "HttpOnly")) { + cookie.setHttpOnly(true); + } else if (StringUtils.startsWithIgnoreCase(attribute, "SameSite")) { + cookie.setSameSite(extractAttributeValue(attribute, setCookieHeader)); + } else if (StringUtils.startsWithIgnoreCase(attribute, "Comment")) { + cookie.setComment(extractAttributeValue(attribute, setCookieHeader)); + } + } + return cookie; + } + + private static String extractAttributeValue(String attribute, String header) { + String[] nameAndValue = attribute.split("="); + Assert.isTrue(nameAndValue.length == 2, + () -> "No value in attribute '" + nameAndValue[0] + "' for Set-Cookie header '" + header + "'"); + return nameAndValue[1]; + } + + @Override + public String toString() { + return new ToStringCreator(this) + .append("name", getName()) + .append("value", getValue()) + .append("Path", getPath()) + .append("Domain", getDomain()) + .append("Version", getVersion()) + .append("Comment", getComment()) + .append("Secure", getSecure()) + .append("HttpOnly", isHttpOnly()) + .append("SameSite", this.sameSite) + .append("Max-Age", getMaxAge()) + .append("Expires", (this.expires != null ? + DateTimeFormatter.RFC_1123_DATE_TIME.format(this.expires) : null)) + .toString(); + } + +} \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/IntermediateResponse.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/IntermediateResponse.java new file mode 100644 index 00000000..a240c31f --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/bulk/response/IntermediateResponse.java @@ -0,0 +1,634 @@ +package eu.xenit.apix.rest.v1.bulk.response; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.util.StringUtils; +import org.springframework.web.util.WebUtils; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + + +public class IntermediateResponse implements HttpServletResponse { + + private static final String CHARSET_PREFIX = "charset="; + + private static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; + + private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); + + + //--------------------------------------------------------------------- + // ServletResponse properties + //--------------------------------------------------------------------- + + + private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING; + + /** + * {@code true} if the character encoding has been explicitly set through + * {@link HttpServletResponse} methods or through a {@code charset} parameter + * on the {@code Content-Type}. + */ + private boolean characterEncodingSet = false; + + private final ByteArrayOutputStream content = new ByteArrayOutputStream(1024); + + private final ServletOutputStream outputStream = new ResponseServletOutputStream(this.content); + + @Nullable + private PrintWriter writer; + + + @Nullable + private String contentType; + + private int bufferSize = 4096; + + private boolean committed; + + private Locale locale = Locale.getDefault(); + + + //--------------------------------------------------------------------- + // HttpServletResponse properties + //--------------------------------------------------------------------- + + + private final Map headers = new LinkedCaseInsensitiveMap<>(); + + private int status = HttpServletResponse.SC_OK; + + + //--------------------------------------------------------------------- + // ServletResponse interface + //--------------------------------------------------------------------- + + + @Override + public void setCharacterEncoding(String characterEncoding) { + setExplicitCharacterEncoding(characterEncoding); + updateContentTypePropertyAndHeader(); + } + + private void setExplicitCharacterEncoding(String characterEncoding) { + Assert.notNull(characterEncoding, "'characterEncoding' must not be null"); + this.characterEncoding = characterEncoding; + this.characterEncodingSet = true; + } + + private void updateContentTypePropertyAndHeader() { + if (this.contentType != null) { + String value = this.contentType; + if (this.characterEncodingSet && !value.toLowerCase().contains(CHARSET_PREFIX)) { + value += ';' + CHARSET_PREFIX + getCharacterEncoding(); + this.contentType = value; + } + doAddHeaderValue(HttpHeaders.CONTENT_TYPE, value, true); + } + } + + @Override + public String getCharacterEncoding() { + return this.characterEncoding; + } + + @Override + public ServletOutputStream getOutputStream() { + boolean outputStreamAccessAllowed = true; + Assert.state(outputStreamAccessAllowed, "OutputStream access not allowed"); + return this.outputStream; + } + + @Override + public PrintWriter getWriter() throws UnsupportedEncodingException { + boolean writerAccessAllowed = true; + Assert.state(writerAccessAllowed, "Writer access not allowed"); + if (this.writer == null) { + Writer targetWriter = new OutputStreamWriter(this.content, getCharacterEncoding()); + this.writer = new ResponsePrintWriter(targetWriter); + } + return this.writer; + } + + + /** + * Get the content of the response body as a {@code String}, using the charset + * specified for the response by the application, either through + * {@link HttpServletResponse} methods or through a charset parameter on the + * {@code Content-Type}. If no charset has been explicitly defined, they + * will be used. + * + * @return the content as a {@code String} + * @throws UnsupportedEncodingException if the character encoding is not supported + * @see #setCharacterEncoding(String) + * @see #setContentType(String) + */ + public String getContentAsString() throws UnsupportedEncodingException { + return this.content.toString(getCharacterEncoding()); + } + + + @Override + public void setContentLength(int contentLength) { + doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true); + } + + + @Override + public void setContentLengthLong(long contentLength) { + doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true); + } + + + @Override + public void setContentType(@Nullable String contentType) { + this.contentType = contentType; + if (contentType != null) { + try { + MediaType mediaType = MediaType.parseMediaType(contentType); + if (mediaType.getCharset() != null) { + setExplicitCharacterEncoding(mediaType.getCharset().name()); + } + } catch (Exception ex) { + // Try to get charset value anyway + int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX); + if (charsetIndex != -1) { + setExplicitCharacterEncoding(contentType.substring(charsetIndex + CHARSET_PREFIX.length())); + } + } + updateContentTypePropertyAndHeader(); + } + } + + @Override + @Nullable + public String getContentType() { + return this.contentType; + } + + @Override + public void setBufferSize(int bufferSize) { + this.bufferSize = bufferSize; + } + + @Override + public int getBufferSize() { + return this.bufferSize; + } + + @Override + public void flushBuffer() { + setCommitted(true); + } + + @Override + public void resetBuffer() { + Assert.state(!isCommitted(), "Cannot reset buffer - response is already committed"); + this.content.reset(); + } + + private void setCommittedIfBufferSizeExceeded() { + int bufSize = getBufferSize(); + if (bufSize > 0 && this.content.size() > bufSize) { + setCommitted(true); + } + } + + public void setCommitted(boolean committed) { + this.committed = committed; + } + + @Override + public boolean isCommitted() { + return this.committed; + } + + @Override + public void reset() { + resetBuffer(); + this.characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING; + this.characterEncodingSet = false; + this.contentType = null; + this.locale = Locale.getDefault(); + this.headers.clear(); + this.status = HttpServletResponse.SC_OK; + } + + @Override + public void setLocale(@Nullable Locale locale) { + // Although the Javadoc for javax.servlet.ServletResponse.setLocale(Locale) does not + // state how a null value for the supplied Locale should be handled, both Tomcat and + // Jetty simply ignore a null value. So we do the same here. + if (locale == null) { + return; + } + this.locale = locale; + doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, locale.toLanguageTag(), true); + } + + @Override + public Locale getLocale() { + return this.locale; + } + + + //--------------------------------------------------------------------- + // HttpServletResponse interface + //--------------------------------------------------------------------- + + @Override + public void addCookie(Cookie cookie) { + Assert.notNull(cookie, "Cookie must not be null"); + doAddHeaderValue(HttpHeaders.SET_COOKIE, getCookieHeader(cookie), false); + } + + private String getCookieHeader(Cookie cookie) { + StringBuilder buf = new StringBuilder(); + buf.append(cookie.getName()).append('=').append(cookie.getValue() == null ? "" : cookie.getValue()); + if (StringUtils.hasText(cookie.getPath())) { + buf.append("; Path=").append(cookie.getPath()); + } + if (StringUtils.hasText(cookie.getDomain())) { + buf.append("; Domain=").append(cookie.getDomain()); + } + int maxAge = cookie.getMaxAge(); + ZonedDateTime expires = (cookie instanceof IntermediateCookie ? ((IntermediateCookie) cookie).getExpires() : null); + if (maxAge >= 0) { + buf.append("; Max-Age=").append(maxAge); + buf.append("; Expires="); + if (expires != null) { + buf.append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)); + } else { + HttpHeaders headers = new HttpHeaders(); + headers.setExpires(maxAge > 0 ? System.currentTimeMillis() + 1000L * maxAge : 0); + buf.append(headers.getFirst(HttpHeaders.EXPIRES)); + } + } else if (expires != null) { + buf.append("; Expires="); + buf.append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME)); + } + + if (cookie.getSecure()) { + buf.append("; Secure"); + } + if (cookie.isHttpOnly()) { + buf.append("; HttpOnly"); + } + if (cookie instanceof IntermediateCookie) { + IntermediateCookie intermediateCookie = (IntermediateCookie) cookie; + if (StringUtils.hasText(intermediateCookie.getSameSite())) { + buf.append("; SameSite=").append(intermediateCookie.getSameSite()); + } + } + if (StringUtils.hasText(cookie.getComment())) { + buf.append("; Comment=").append(cookie.getComment()); + } + return buf.toString(); + } + + + @Override + public boolean containsHeader(String name) { + return this.headers.containsKey(name); + } + + /** + * Return the names of all specified headers as a Set of Strings. + *

As of Servlet 3.0, this method is also defined in {@link HttpServletResponse}. + * + * @return the {@code Set} of header name {@code Strings}, or an empty {@code Set} if none + */ + @Override + public Collection getHeaderNames() { + return this.headers.keySet(); + } + + /** + * Return the primary value for the given header as a String, if any. + * Will return the first value in case of multiple values. + *

As of Servlet 3.0, this method is also defined in {@link HttpServletResponse}. + * As of Spring 3.1, it returns a stringified value for Servlet 3.0 compatibility. + * + * @param name the name of the header + * @return the associated header value, or {@code null} if none + */ + @Override + @Nullable + public String getHeader(String name) { + HeaderValueHolder header = this.headers.get(name); + return (header != null ? header.getStringValue() : null); + } + + /** + * Return all values for the given header as a List of Strings. + *

As of Servlet 3.0, this method is also defined in {@link HttpServletResponse}. + * As of Spring 3.1, it returns a List of stringified values for Servlet 3.0 compatibility. + * + * @param name the name of the header + * @return the associated header values, or an empty List if none + */ + @Override + public List getHeaders(String name) { + HeaderValueHolder header = this.headers.get(name); + if (header != null) { + return header.getStringValues(); + } else { + return Collections.emptyList(); + } + } + + /** + * The default implementation returns the given URL String as-is. + *

Can be overridden in subclasses, appending a session id or the like. + */ + @Override + public String encodeURL(String url) { + return url; + } + + /** + * The default implementation delegates to {@link #encodeURL}, + * returning the given URL String as-is. + *

Can be overridden in subclasses, appending a session id or the like + * in a redirect-specific fashion. For general URL encoding rules, + * override the common {@link #encodeURL} method instead, applying + * to redirect URLs as well as to general URLs. + */ + @Override + public String encodeRedirectURL(String url) { + return encodeURL(url); + } + + @Override + @Deprecated + public String encodeUrl(String url) { + return encodeURL(url); + } + + @Override + @Deprecated + public String encodeRedirectUrl(String url) { + return encodeRedirectURL(url); + } + + @Override + public void sendError(int status, String errorMessage) throws UnsupportedEncodingException { + Assert.state(!isCommitted(), "Cannot set error status - response is already committed"); + this.status = status; + getWriter().println(errorMessage); + setCommitted(true); + } + + @Override + public void sendError(int status) { + Assert.state(!isCommitted(), "Cannot set error status - response is already committed"); + this.status = status; + setCommitted(true); + } + + @Override + public void sendRedirect(String url) { + Assert.state(!isCommitted(), "Cannot send redirect - response is already committed"); + Assert.notNull(url, "Redirect URL must not be null"); + setHeader(HttpHeaders.LOCATION, url); + setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + setCommitted(true); + } + + + @Override + public void setDateHeader(String name, long value) { + setHeaderValue(name, formatDate(value)); + } + + @Override + public void addDateHeader(String name, long value) { + addHeaderValue(name, formatDate(value)); + } + + + private String formatDate(long date) { + return newDateFormat().format(new Date(date)); + } + + private DateFormat newDateFormat() { + SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.US); + dateFormat.setTimeZone(GMT); + return dateFormat; + } + + @Override + public void setHeader(String name, @Nullable String value) { + setHeaderValue(name, value); + } + + @Override + public void addHeader(String name, @Nullable String value) { + addHeaderValue(name, value); + } + + @Override + public void setIntHeader(String name, int value) { + setHeaderValue(name, value); + } + + @Override + public void addIntHeader(String name, int value) { + addHeaderValue(name, value); + } + + private void setHeaderValue(String name, @Nullable Object value) { + if (value == null) { + return; + } + boolean replaceHeader = true; + if (setSpecialHeader(name, value, replaceHeader)) { + return; + } + doAddHeaderValue(name, value, replaceHeader); + } + + private void addHeaderValue(String name, @Nullable Object value) { + if (value == null) { + return; + } + boolean replaceHeader = false; + if (setSpecialHeader(name, value, replaceHeader)) { + return; + } + doAddHeaderValue(name, value, replaceHeader); + } + + private boolean setSpecialHeader(String name, Object value, boolean replaceHeader) { + if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) { + setContentType(value.toString()); + return true; + } else if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) { + setContentLength(value instanceof Number ? ((Number) value).intValue() : Integer.parseInt(value.toString())); + return true; + } else if (HttpHeaders.CONTENT_LANGUAGE.equalsIgnoreCase(name)) { + String contentLanguages = value.toString(); + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_LANGUAGE, contentLanguages); + Locale language = headers.getContentLanguage(); + setLocale(language != null ? language : Locale.getDefault()); + // Since setLocale() sets the Content-Language header to the given + // single Locale, we have to explicitly set the Content-Language header + // to the user-provided value. + doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, contentLanguages, true); + return true; + } else if (HttpHeaders.SET_COOKIE.equalsIgnoreCase(name)) { + IntermediateCookie cookie = IntermediateCookie.parse(value.toString()); + if (replaceHeader) { + setCookie(cookie); + } else { + addCookie(cookie); + } + return true; + } else { + return false; + } + } + + private void doAddHeaderValue(String name, Object value, boolean replace) { + Assert.notNull(value, "Header value must not be null"); + HeaderValueHolder header = this.headers.computeIfAbsent(name, key -> new HeaderValueHolder()); + if (replace) { + header.setValue(value); + } else { + header.addValue(value); + } + } + + /** + * Set the {@code Set-Cookie} header to the supplied {@link Cookie}, + * overwriting any previous cookies. + * + * @param cookie the {@code Cookie} to set + * @see #addCookie(Cookie) + * @since 5.1.10 + */ + private void setCookie(Cookie cookie) { + Assert.notNull(cookie, "Cookie must not be null"); + doAddHeaderValue(HttpHeaders.SET_COOKIE, getCookieHeader(cookie), true); + } + + @Override + public void setStatus(int status) { + if (!this.isCommitted()) { + this.status = status; + } + } + + @Override + @Deprecated + public void setStatus(int status, String errorMessage) { + if (!this.isCommitted()) { + this.status = status; + try { + getWriter().println(errorMessage); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public int getStatus() { + return this.status; + } + + + /** + * Inner class that adapts the ServletOutputStream to mark the + * response as committed once the buffer size is exceeded. + */ + private class ResponseServletOutputStream extends DelegatingServletOutputStream { + + public ResponseServletOutputStream(OutputStream out) { + super(out); + } + + @Override + public void write(int b) throws IOException { + super.write(b); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + @Override + public void flush() throws IOException { + super.flush(); + setCommitted(true); + } + } + + /** + * Inner class that adapts the PrintWriter to mark the + * response as committed once the buffer size is exceeded. + */ + private class ResponsePrintWriter extends PrintWriter { + + public ResponsePrintWriter(Writer out) { + super(out, true); + } + + @Override + public void write(char[] buf, int off, int len) { + super.write(buf, off, len); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + @Override + public void write(String s, int off, int len) { + super.write(s, off, len); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + @Override + public void write(int c) { + super.write(c); + super.flush(); + setCommittedIfBufferSizeExceeded(); + } + + @Override + public void flush() { + super.flush(); + setCommitted(true); + } + + @Override + public void close() { + super.flush(); + super.close(); + setCommitted(true); + } + } + +} \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/categories/CategoryWebScript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/categories/CategoryWebScript1.java index 95a5fd78..1cfd9bad 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/categories/CategoryWebScript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/categories/CategoryWebScript1.java @@ -1,44 +1,33 @@ package eu.xenit.apix.rest.v1.categories; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.categories.Category; import eu.xenit.apix.categories.ICategoryService; import eu.xenit.apix.data.QName; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; -@WebScript(baseUri = RestV1Config.BaseUrl + "/category", families = RestV1Config.Family, defaultFormat = "json", - description = "Retrieves Category information", value = "Category") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v1.categories.CategoryWebScript1") +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class CategoryWebScript1 extends ApixV1Webscript { - Logger logger = LoggerFactory.getLogger(CategoryWebScript1.class); - @Autowired - private ICategoryService categoryService; + private final ICategoryService categoryService; - @Uri(value = "/aspect/{qname}", method = HttpMethod.GET) - @ApiOperation(value = "Return the categories available for an aspect", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Categories.class)) - public void getCategoriesForAspect(@UriVariable final String qname, WebScriptResponse webScriptResponse) - throws IOException { + public CategoryWebScript1(ICategoryService categoryService) { + this.categoryService = categoryService; + } + + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/category/aspect/{qname}") + public ResponseEntity getCategoriesForAspect(@PathVariable final String qname) { QName apixQName = new QName(qname); List categories = categoryService.getCategoryTree(apixQName); - writeJsonResponse(webScriptResponse, new Categories(categories)); + return writeJsonResponse(new Categories(categories)); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/configuration/ConfigurationWebscript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/configuration/ConfigurationWebscript1.java index 2da850c0..ca9377de 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/configuration/ConfigurationWebscript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/configuration/ConfigurationWebscript1.java @@ -3,73 +3,59 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.ExceptionHandler; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; -import com.sun.star.auth.InvalidArgumentException; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; +import com.gradecak.alfresco.mvc.webscript.DispatcherWebscript; import eu.xenit.apix.configuration.ConfigurationFileFlags; import eu.xenit.apix.configuration.ConfigurationService; import eu.xenit.apix.configuration.Configurations; -import eu.xenit.apix.content.IContentService; -import eu.xenit.apix.filefolder.IFileFolderService; -import eu.xenit.apix.node.INodeService; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; - -import java.io.IOException; -import java.io.Writer; -import java.util.Arrays; -import java.util.List; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Retrieves configuration data files from the datadictionary", value = "Configuration") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v1.configuration.ConfigurationWebscript1") +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class ConfigurationWebscript1 extends ApixV1Webscript { private static final Logger log = LoggerFactory.getLogger(ConfigurationWebscript1.class); - @Autowired - IFileFolderService fileFolderService; - - @Autowired - INodeService nodeService; + private final ObjectMapper mapper; - @Autowired - IContentService contentService; - @Autowired - ConfigurationService configurationService; + private final ConfigurationService configurationService; + public ConfigurationWebscript1( + @Qualifier("eu.xenit.apix.configuration.ConfigurationService") ConfigurationService configurationService) { + this.configurationService = configurationService; + mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + mapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false); + } - @Uri(value = "/configuration", method = HttpMethod.GET, defaultFormat = "json") - - @ApiOperation("Returns configuration files information and content") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Configurations.class)) - - public void getConfigurationFiles( - @RequestParam(defaultValue = "content,nodeRef", delimiter = ",") @ApiParam(value = "Comma separated field names to include.", defaultValue = "content,nodeRef", allowableValues = "content,nodeRef,path,metadata,parsedContent") String[] fields, - @RequestParam @ApiParam("The directory to search for configuration files, relative to the data dictionary") String searchDirectory, - @RequestParam(value = "filter.name", required = false) @ApiParam(name = "filter.name", value = "Regular expression that the node name should match.") String nameFilter, - @RequestParam(required = false) @ApiParam("Javascript callback function") String callback, - WebScriptRequest webScriptRequest, - WebScriptResponse webScriptResponse + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/configuration", produces = {"application/js", MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getJsConfigurationFiles( + @RequestParam(defaultValue = "content,nodeRef", required = false) String[] fields, + @RequestParam String searchDirectory, + @RequestParam(value = "filter.name", required = false) String nameFilter, + @RequestParam(required = false) String callback, final HttpServletRequest req ) throws IOException { + final WebScriptRequest wsReq = ((DispatcherWebscript.WebscriptRequestWrapper) req).getWebScriptServletRequest(); List fieldsList = Arrays.asList(fields); ConfigurationFileFlags configurationFileFlags = new ConfigurationFileFlags( fieldsList.contains("content"), @@ -79,35 +65,36 @@ public void getConfigurationFiles( fieldsList.contains("nodeRef")); Configurations configurations = configurationService .getConfigurationFiles(searchDirectory, nameFilter, configurationFileFlags); - - if (webScriptRequest.getFormat().equalsIgnoreCase("js")) { - writeJsResponse(callback, webScriptResponse, configurations); - } else { - writeJsonResponse(webScriptResponse, configurations); + if ("js".equalsIgnoreCase(wsReq.getFormat()) || + "js".equalsIgnoreCase( + getAcceptSubType( + req.getHeader("Accept")))) { + return ResponseEntity.ok() + .contentType(new MediaType("application", "js")) + .body( + String.format("%s(%s)", callback, + mapper.writeValueAsString(configurations))); } + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_JSON) + .body(configurations); } - private void writeJsResponse(String callback, WebScriptResponse webScriptResponse, Configurations configurations) - throws IOException { - webScriptResponse.setContentType("application/js"); - webScriptResponse.setContentEncoding("utf-8"); - webScriptResponse.setHeader("Cache-Control", "no-cache"); - Writer writer = webScriptResponse.getWriter(); - writer.write(callback); - writer.write("("); - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false); - mapper.writeValue(writer, configurations); - writer.write(");"); - writer.flush(); - writer.close(); + @Nullable + private static String getAcceptSubType(String accept) { + String acceptSubType = null; + if (accept != null) { + String[] acceptSplit = accept.split("/"); + if (acceptSplit.length > 1) { + acceptSubType = acceptSplit[1]; + } + } + return acceptSubType; } @ExceptionHandler(IllegalArgumentException.class) - private void writeBadRequestResponse(IllegalArgumentException exception, WebScriptResponse response) throws IOException{ - log.debug("Bad input;", exception); - response.setStatus(400); - writeJsonResponse(response, exception.getMessage()); + private ResponseEntity writeBadRequestResponse(IllegalArgumentException exception) { + log.debug("Bad input", exception); + return ResponseEntity.badRequest().body(exception.getMessage()); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/dictionary/DictionaryWebScript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/dictionary/DictionaryWebScript1.java index 61904866..7f1551c7 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/dictionary/DictionaryWebScript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/dictionary/DictionaryWebScript1.java @@ -1,13 +1,8 @@ package eu.xenit.apix.rest.v1.dictionary; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.FormatStyle; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.data.QName; import eu.xenit.apix.dictionary.IDictionaryService; import eu.xenit.apix.dictionary.aspects.AspectDefinition; @@ -18,109 +13,93 @@ import eu.xenit.apix.properties.Properties; import eu.xenit.apix.properties.PropertyDefinition; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -@WebScript(baseUri = RestV1Config.BaseUrl + "/dictionary", families = RestV1Config.Family, defaultFormat = "json", - description = "Retrieves Dictionary information", value = "Dictionary") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v1.property.DictionaryWebScript1") +import javax.servlet.http.HttpServletRequest; + +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class DictionaryWebScript1 extends ApixV1Webscript { - Logger logger = LoggerFactory.getLogger(DictionaryWebScript1.class); - @Autowired - IDictionaryService dictionaryService; + private static final Logger logger = LoggerFactory.getLogger(DictionaryWebScript1.class); + private final IDictionaryService dictionaryService; + public DictionaryWebScript1( + @Qualifier(("eu.xenit.apix.dictionary.IDictionaryService")) IDictionaryService dictionaryService) { + this.dictionaryService = dictionaryService; + } - @Uri(value = "/properties/{qname}", method = HttpMethod.GET, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation(value = "Return the definition of a property", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = PropertyDefinition.class)) - public void getPropertyDefinition(@UriVariable final String qname, WebScriptResponse webScriptResponse) - throws IOException { - eu.xenit.apix.data.QName apixQName = new eu.xenit.apix.data.QName(qname); - PropertyDefinition propDef = dictionaryService.GetPropertyDefinition(apixQName); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/dictionary/properties/**") + public ResponseEntity getPropertyDefinition(HttpServletRequest request) { + QName qname = extractQNameFromUrlPath(request, "/v1/dictionary/properties/"); + PropertyDefinition propDef = dictionaryService.GetPropertyDefinition(qname); if (propDef == null) { - webScriptResponse.setStatus(HttpStatus.SC_NOT_FOUND); + return ResponseEntity.notFound().build(); } - writeJsonResponse(webScriptResponse, propDef); + return writeJsonResponse(propDef); } - - @Uri(value = "/properties", method = HttpMethod.GET, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation(value = "Return properties", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Properties.class)) - public void getProperties( - WebScriptResponse webScriptResponse) throws IOException { - Properties properties = dictionaryService.getProperties(); - writeJsonResponse(webScriptResponse, properties); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/dictionary/properties") + public ResponseEntity getProperties() { + return writeJsonResponse(dictionaryService.getProperties()); } - - @Uri(value = "/types", method = HttpMethod.GET, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation(value = "Return the definitions of types", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Types.class)) - public void getSubTypeDefinitions(@RequestParam(defaultValue = "sys:base", required = false) final String parent, - WebScriptResponse webScriptResponse) throws IOException { - QName apixQName = new QName(parent); - Types types = dictionaryService.GetSubTypeDefinitions(apixQName, true); - writeJsonResponse(webScriptResponse, types); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/dictionary/types") + public ResponseEntity getSubTypeDefinitions(@RequestParam(defaultValue = "sys:base", required = false) final String parent) { + return writeJsonResponse( + dictionaryService.GetSubTypeDefinitions( + new QName(parent), true + ) + ); } - @Uri(value = "/types/{qname}", method = HttpMethod.GET, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation(value = "Return the definition of a type", notes = "") - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = TypeDefinition.class), - @ApiResponse(code = 404, message = "Not Found")}) - public void getTypeDefinition(@UriVariable final String qname, WebScriptResponse webScriptResponse) - throws IOException { - logger.debug("Received type qname %s", qname); - eu.xenit.apix.data.QName apixQName = new eu.xenit.apix.data.QName(qname); - TypeDefinition classDef = dictionaryService.GetTypeDefinition(apixQName); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/dictionary/types/**") + public ResponseEntity getTypeDefinition(HttpServletRequest request) { + QName qname = extractQNameFromUrlPath(request, "/v1/dictionary/types/"); + logger.debug("Received type qname {}", qname); + TypeDefinition classDef = dictionaryService.GetTypeDefinition(qname); if (classDef == null) { - webScriptResponse.setStatus(HttpStatus.SC_NOT_FOUND); + return ResponseEntity.notFound().build(); } - writeJsonResponse(webScriptResponse, classDef); + return writeJsonResponse(classDef); } - @Uri(value = "/aspects/{qname}", method = HttpMethod.GET, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation(value = "Return the definition of a aspect", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = AspectDefinition.class)) - public void getAspectDefinition(@UriVariable final String qname, WebScriptResponse webScriptResponse) - throws IOException { - logger.debug("Received aspect qname %s", qname); - eu.xenit.apix.data.QName apixQName = new eu.xenit.apix.data.QName(qname); - AspectDefinition classDef = dictionaryService.GetAspectDefinition(apixQName); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/dictionary/aspects/**") + public ResponseEntity getAspectDefinition(HttpServletRequest request) { + QName qname = extractQNameFromUrlPath(request, "/v1/dictionary/aspects/"); + logger.debug("Received aspect qname {}", qname); + AspectDefinition classDef = dictionaryService.GetAspectDefinition(qname); if (classDef == null) { - webScriptResponse.setStatus(HttpStatus.SC_NOT_FOUND); + return ResponseEntity.notFound().build(); } - writeJsonResponse(webScriptResponse, classDef); + return writeJsonResponse(classDef); } - - @Uri(value = "/aspects", method = HttpMethod.GET, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation(value = "Return apects", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Aspects.class)) - public void getAspects(WebScriptResponse webScriptResponse) throws IOException { - Aspects aspects = dictionaryService.getAspects(); - writeJsonResponse(webScriptResponse, aspects); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/dictionary/aspects") + public ResponseEntity getAspects() { + return writeJsonResponse(dictionaryService.getAspects()); } - @Uri(value = "/namespaces", method = HttpMethod.GET, formatStyle = FormatStyle.ARGUMENT) - @ApiOperation(value = "Returns the namespaces", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Namespaces.class)) - public void getNamespaces(WebScriptResponse webScriptResponse) throws IOException { - Namespaces namespaces = dictionaryService.getNamespaces(); - writeJsonResponse(webScriptResponse, namespaces); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/dictionary/namespaces") + public ResponseEntity getNamespaces() { + return writeJsonResponse(dictionaryService.getNamespaces()); } - + private QName extractQNameFromUrlPath(HttpServletRequest request, String path) { + String qnameValue = request.getRequestURI().split(request.getContextPath() + path)[1]; + return new QName(qnameValue); + } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/internal/ApixArgumentResolver.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/internal/ApixArgumentResolver.java deleted file mode 100644 index bf04c99e..00000000 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/internal/ApixArgumentResolver.java +++ /dev/null @@ -1,47 +0,0 @@ -package eu.xenit.apix.rest.v1.internal; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.arguments.ArgumentResolver; -import java.io.IOException; -import java.lang.annotation.Annotation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; - -/** - * Created by Michiel Huygen on 14/03/2016. - */ -@Component -public class ApixArgumentResolver implements ArgumentResolver { - - private Logger logger = LoggerFactory.getLogger(ApixArgumentResolver.class); - - @Override - public final boolean supports(final Class parameterType, final Class annotationType) { - /* Determine if using Class.isAssignableFrom() breaks backwards compatibility. */ - return parameterType.getCanonicalName().startsWith("eu.xenit.apix") && !Exception.class.isAssignableFrom(parameterType); - } - - @Override - public final Object resolveArgument(final Class argumentType, final Annotation parameterAnnotation, - final String name, final WebScriptRequest request, final WebScriptResponse response) { - /*final Class expectedParameterType = getExpectedArgumentType(); - if (argumentType.equals(expectedParameterType) == false) { - throw new IllegalArgumentException(String.format("Incorrect parameter type %s, expected type %s", - argumentType, expectedParameterType)); - }*/ - - try { - //TODO: inject apix object mapper? - ObjectMapper map = new ObjectMapper(); - return map.readValue(request.getContent().getContent(), argumentType); - } catch (IOException e) { - logger.warn("Cannot convert webscript argument with type from package eu.xenit.apix to json", e); - throw new RuntimeException("Cannot convert webscript argument (" + name + ") " + - "with type from package eu.xenit.apix to json - " + argumentType.getCanonicalName(), e); - } - } - -} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/AncestorsObject.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/AncestorsObject.java index 8e513f93..2b15a185 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/AncestorsObject.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/AncestorsObject.java @@ -1,6 +1,7 @@ package eu.xenit.apix.rest.v1.nodes; import eu.xenit.apix.data.NodeRef; + import java.util.List; public class AncestorsObject { @@ -8,16 +9,27 @@ public class AncestorsObject { private NodeRef node; private List ancestors; + public AncestorsObject(NodeRef node, List ancestors) { + this.node = node; + this.ancestors = ancestors; + } + + public AncestorsObject() { + } + public NodeRef getNode() { return node; } + public void setNode(NodeRef node) { + this.node = node; + } + public List getAncestors() { return ancestors; } - public AncestorsObject(NodeRef node, List ancestors) { - this.node = node; + public void setAncestors(List ancestors) { this.ancestors = ancestors; } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeAclsOptions.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeAclsOptions.java index 1cc69fdd..c59a1d2e 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeAclsOptions.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeAclsOptions.java @@ -2,19 +2,20 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; + import java.util.Set; public class ChangeAclsOptions { private boolean inheritFromParent; private Set ownAccessList; - private ObjectMapper mapper = new ObjectMapper(); + + public ChangeAclsOptions() { + } @JsonCreator public ChangeAclsOptions(@JsonProperty("inheritFromParent") boolean inheritFromParent, - @JsonProperty("ownAccessList") Set ownAccessList) throws IOException { + @JsonProperty("ownAccessList") Set ownAccessList) { this.ownAccessList = ownAccessList; this.inheritFromParent = inheritFromParent; } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeParentOptions.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeParentOptions.java index 880e881e..38b31df2 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeParentOptions.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/ChangeParentOptions.java @@ -2,22 +2,28 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModelProperty; /** * Created by Michiel Huygen on 12/05/2016. */ class ChangeParentOptions { - @ApiModelProperty(required = true) - public String parent; + private String parent; + @JsonCreator public ChangeParentOptions(@JsonProperty("parent") String parent) { this.parent = parent; } + public ChangeParentOptions() { + } + public String getParent() { return parent; } + + public void setParent(String parent) { + this.parent = parent; + } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateAssociationOptions.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateAssociationOptions.java index ae15659e..37a70cdf 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateAssociationOptions.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateAssociationOptions.java @@ -2,18 +2,21 @@ import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.QName; -import io.swagger.annotations.ApiModelProperty; /** * Created by Michiel Huygen on 23/05/2016. */ public class CreateAssociationOptions { - - @ApiModelProperty(required = true) private NodeRef target; - @ApiModelProperty("Defaults to cm:content") private QName type = new QName("{http://www.alfresco.org/model/content/1.0}content"); + public CreateAssociationOptions(NodeRef target, QName type) { + this.target = target; + this.type = type; + } + public CreateAssociationOptions() { + } + public NodeRef getTarget() { return target; } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateNodeOptions.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateNodeOptions.java index ec0e66da..bc941cc5 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateNodeOptions.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/CreateNodeOptions.java @@ -2,13 +2,12 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; import eu.xenit.apix.data.QName; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; +import org.alfresco.model.ContentModel; + import java.util.HashMap; import java.util.Map; -import org.alfresco.model.ContentModel; +import java.util.Objects; /** * Created by Michiel Huygen on 12/05/2016. @@ -16,25 +15,28 @@ public class CreateNodeOptions { public static final QName PROP_NAME_QNAME = new QName(ContentModel.PROP_NAME.toString()); - @ApiModelProperty(required = true) - public String parent; - public String name; - public String type; - public Map properties; + private String parent; + private String name; + private String type; + private Map properties; + private QName[] aspectsToAdd; + private QName[] aspectsToRemove; + private String copyFrom; - public QName[] aspectsToAdd; - public QName[] aspectsToRemove; - public String copyFrom; - private ObjectMapper mapper = new ObjectMapper(); + public CreateNodeOptions() { + this.properties = new HashMap<>(1); + this.aspectsToRemove = new QName[0]; + this.aspectsToAdd = new QName[0]; + } @JsonCreator public CreateNodeOptions(@JsonProperty("parent") String parent, - @JsonProperty("name") String name, - @JsonProperty("type") String type, - @JsonProperty("properties") Map properties, - @JsonProperty("aspectsToAdd") QName[] aspectsToAdd, - @JsonProperty("aspectsToRemove") QName[] aspectsToRemove, - @JsonProperty("copyFrom") String copyFrom) throws IOException { + @JsonProperty("name") String name, + @JsonProperty("type") String type, + @JsonProperty("properties") Map properties, + @JsonProperty("aspectsToAdd") QName[] aspectsToAdd, + @JsonProperty("aspectsToRemove") QName[] aspectsToRemove, + @JsonProperty("copyFrom") String copyFrom) { this.parent = parent; this.name = name; this.type = type; @@ -42,23 +44,13 @@ public CreateNodeOptions(@JsonProperty("parent") String parent, if (this.properties == null) { this.properties = new HashMap<>(1); } - if ((name != null && !this.properties.containsKey(PROP_NAME_QNAME))) { + if ((name != null && !getProperties().containsKey(PROP_NAME_QNAME))) { this.properties.put(PROP_NAME_QNAME, new String[]{name}); } - if (aspectsToAdd == null) { - this.aspectsToAdd = new QName[0]; - } - else { - this.aspectsToAdd = aspectsToAdd; - } + this.aspectsToAdd = Objects.requireNonNullElseGet(aspectsToAdd, () -> new QName[0]); - if (aspectsToRemove == null) { - this.aspectsToRemove = new QName[0]; - } - else { - this.aspectsToRemove = aspectsToRemove; - } + this.aspectsToRemove = Objects.requireNonNullElseGet(aspectsToRemove, () -> new QName[0]); this.copyFrom = copyFrom; } @@ -67,27 +59,55 @@ public String getParent() { return parent; } + public void setParent(String parent) { + this.parent = parent; + } + public String getName() { return name; } + public void setName(String name) { + this.name = name; + } + public String getType() { return type; } + public void setType(String type) { + this.type = type; + } + public Map getProperties() { return properties; } + public void setProperties(Map properties) { + this.properties = properties; + } + public QName[] getAspectsToAdd() { return aspectsToAdd; } + public void setAspectsToAdd(QName[] aspectsToAdd) { + this.aspectsToAdd = aspectsToAdd; + } + public QName[] getAspectsToRemove() { return aspectsToRemove; } + public void setAspectsToRemove(QName[] aspectsToRemove) { + this.aspectsToRemove = aspectsToRemove; + } + public String getCopyFrom() { return copyFrom; } + + public void setCopyFrom(String copyFrom) { + this.copyFrom = copyFrom; + } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/InheritFromParent.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/InheritFromParent.java index 1e85bc16..5ecda553 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/InheritFromParent.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/InheritFromParent.java @@ -2,12 +2,14 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModelProperty; class InheritFromParent { - @ApiModelProperty(required = true) - public boolean inheritFromParent; + private boolean inheritFromParent; + + public InheritFromParent() { + + } @JsonCreator public InheritFromParent(@JsonProperty("inheritFromParent") boolean inheritFromParent) { @@ -15,11 +17,10 @@ public InheritFromParent(@JsonProperty("inheritFromParent") boolean inheritFromP } public boolean isInheritFromParent() { - return this.inheritFromParent; + return inheritFromParent; } public void setInheritFromParent(boolean inheritFromParent) { this.inheritFromParent = inheritFromParent; } - } \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodeInfo.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodeInfo.java index d67c4503..8f0aff3c 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodeInfo.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodeInfo.java @@ -5,6 +5,7 @@ import eu.xenit.apix.node.NodeAssociations; import eu.xenit.apix.node.NodeMetadata; import eu.xenit.apix.permissions.PermissionValue; + import java.util.Map; /** @@ -12,11 +13,11 @@ */ public class NodeInfo { - public NodeRef noderef; - public NodeMetadata metadata; - public Map permissions; - public NodeAssociations associations; - public NodePath path; + private NodeRef noderef; + private NodeMetadata metadata; + private Map permissions; + private NodeAssociations associations; + private NodePath path; public NodeInfo() { @@ -33,4 +34,44 @@ public NodeInfo(NodeRef nodeRef, this.associations = associations; this.path = path; } + + public NodeRef getNoderef() { + return noderef; + } + + public void setNoderef(NodeRef noderef) { + this.noderef = noderef; + } + + public NodeMetadata getMetadata() { + return metadata; + } + + public void setMetadata(NodeMetadata metadata) { + this.metadata = metadata; + } + + public Map getPermissions() { + return permissions; + } + + public void setPermissions(Map permissions) { + this.permissions = permissions; + } + + public NodeAssociations getAssociations() { + return associations; + } + + public void setAssociations(NodeAssociations associations) { + this.associations = associations; + } + + public NodePath getPath() { + return path; + } + + public void setPath(NodePath path) { + this.path = path; + } } \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodeInfoRequest.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodeInfoRequest.java new file mode 100644 index 00000000..992ef582 --- /dev/null +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodeInfoRequest.java @@ -0,0 +1,119 @@ +package eu.xenit.apix.rest.v1.nodes; + +import java.util.List; + +public class NodeInfoRequest { + Boolean retrieveMetadata; + Boolean retrievePath; + Boolean retrievePermissions; + Boolean retrieveAssocs; + Boolean retrieveChildAssocs; + Boolean retrieveParentAssocs; + Boolean retrieveTargetAssocs; + Boolean retrieveSourceAssocs; + List noderefs; + + NodeInfoRequest() { + } + + public NodeInfoRequest(Boolean retrieveMetadata, + Boolean retrievePath, + Boolean retrievePermissions, + Boolean retrieveAssocs, + Boolean retrieveChildAssocs, + Boolean retrieveParentAssocs, + Boolean retrieveTargetAssocs, + Boolean retrieveSourceAssocs, + List noderefs) { + this.retrieveMetadata = retrieveMetadata; + this.retrievePath = retrievePath; + this.retrievePermissions = retrievePermissions; + this.retrieveAssocs = retrieveAssocs; + this.retrieveChildAssocs = retrieveChildAssocs; + this.retrieveParentAssocs = retrieveParentAssocs; + this.retrieveTargetAssocs = retrieveTargetAssocs; + this.retrieveSourceAssocs = retrieveSourceAssocs; + this.noderefs = noderefs; + } + + public boolean getRetrieveMetadata() { + return !Boolean.FALSE.equals(retrieveMetadata); + + } + + public void setRetrieveMetadata(Boolean retrieveMetadata) { + this.retrieveMetadata = retrieveMetadata; + } + + public boolean getRetrievePath() { + return !Boolean.FALSE.equals(retrievePath); + } + + public void setRetrievePath(Boolean retrievePath) { + this.retrievePath = retrievePath; + } + + public boolean getRetrievePermissions() { + return !Boolean.FALSE.equals(retrievePermissions); + + } + + public void setRetrievePermissions(Boolean retrievePermissions) { + this.retrievePermissions = retrievePermissions; + } + + public boolean getRetrieveAssocs() { + return !Boolean.FALSE.equals(retrieveAssocs); + + } + + public void setRetrieveAssocs(Boolean retrieveAssocs) { + this.retrieveAssocs = retrieveAssocs; + } + + public boolean getRetrieveChildAssocs() { + return !Boolean.FALSE.equals(retrieveChildAssocs); + + } + + public void setRetrieveChildAssocs(Boolean retrieveChildAssocs) { + this.retrieveChildAssocs = retrieveChildAssocs; + } + + public boolean getRetrieveParentAssocs() { + + return !Boolean.FALSE.equals(retrieveParentAssocs); + + } + + public void setRetrieveParentAssocs(Boolean retrieveParentAssocs) { + this.retrieveParentAssocs = retrieveParentAssocs; + + } + + public boolean getRetrieveTargetAssocs() { + return !Boolean.FALSE.equals(retrieveTargetAssocs); + + } + + public void setRetrieveTargetAssocs(Boolean retrieveTargetAssocs) { + this.retrieveTargetAssocs = retrieveTargetAssocs; + } + + public boolean getRetrieveSourceAssocs() { + return !Boolean.FALSE.equals(retrieveSourceAssocs); + + } + + public void setRetrieveSourceAssocs(Boolean retrieveSourceAssocs) { + this.retrieveSourceAssocs = retrieveSourceAssocs; + } + + public List getNoderefs() { + return noderefs; + } + + public void setNoderefs(List noderefs) { + this.noderefs = noderefs; + } +} diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodesWebscript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodesWebscript1.java index 209f1e1c..e18e4385 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodesWebscript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/nodes/NodesWebscript1.java @@ -1,13 +1,6 @@ package eu.xenit.apix.rest.v1.nodes; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.ExceptionHandler; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Transaction; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.comments.Comment; import eu.xenit.apix.comments.Conversation; import eu.xenit.apix.comments.ICommentService; @@ -27,344 +20,240 @@ import eu.xenit.apix.permissions.NodePermission; import eu.xenit.apix.permissions.PermissionValue; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; import eu.xenit.apix.rest.v1.nodes.ChangeAclsOptions.Access; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.io.InputStream; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; import org.alfresco.model.ContentModel; -import org.alfresco.repo.model.Repository; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.security.PermissionService; -import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.extensions.webscripts.WrappingWebScriptResponse; -import org.springframework.extensions.webscripts.servlet.FormData; -import org.springframework.stereotype.Component; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Access operations on nodes", value = "Nodes") -@Transaction(readOnly = false) -@Component("eu.xenit.apix.rest.v1.NodesWebscript") +@AlfrescoTransaction +@RestController public class NodesWebscript1 extends ApixV1Webscript { - private final static Logger logger = LoggerFactory.getLogger(NodesWebscript1.class); + private static final Logger logger = LoggerFactory.getLogger(NodesWebscript1.class); - @Autowired - INodeService nodeService; + private final INodeService nodeService; - @Autowired - IPermissionService permissionService; + private final IPermissionService permissionService; - @Autowired - IFileFolderService fileFolderService; + private final IFileFolderService fileFolderService; - @Autowired - ICommentService commentService; + private final ICommentService commentService; - @Autowired - ServiceRegistry serviceRegistry; + private final ServiceRegistry serviceRegistry; - @Autowired - Repository repository; + public NodesWebscript1(INodeService nodeService, IPermissionService permissionService, + IFileFolderService fileFolderService, ICommentService commentService, + ServiceRegistry serviceRegistry) { + this.nodeService = nodeService; + this.permissionService = permissionService; + this.fileFolderService = fileFolderService; + this.commentService = commentService; + this.serviceRegistry = serviceRegistry; + } - @Uri(value = "/nodes/{space}/{store}/{guid}/metadata", method = HttpMethod.POST) - @ApiOperation(value = "Change node metadata", - notes = "Example to set a node's title and add the sys:versionable aspect:\n" + - "\n" + - "```\n" + - "POST /apix/v1/nodes/workspace/SpacesStore/b54287de-381e-44b1-b6d1-e6c9a9d632fd/metadata\n" + - "{\n" + - " \"aspectsToAdd\": [\"{http://www.alfresco.org/model/system/1.0}temporary\"],\n" + - " \"propertiesToSet\": {\"{http://www.alfresco.org/model/content/1.0}title\":[\"My new title\"]}\n" - + - "}\n" + - "```\n" + - "\n" + - "When you generalize the type of a node instead of specializing, the default properties of the " + - "initial **type** that are not present in the new type are removed, however, the default aspects " + - "are not. This is the default behaviour. If you also want to remove these aspects on type " + - "generalization, add the parameter **cleanUpAspectsOnGeneralization** to the request body.\n" + - "\n" + - "Example for cleaning up aspects on type generalization:\n" + - "\n" + - "```\n" + - "POST /apix/v1/nodes/workspace/SpacesStore/b54287de-381e-44b1-b6d1-e6c9a9d632fd/metadata\n" + - "{\n" + - " \"type\": \"{http://www.alfresco.org/model/content/1.0}content\",\n" + - " \"cleanUpAspectsOnGeneralization\": true\n" + - "}\n" + - "```\n" + - "\n" + - "Changing the cm:name property will also update the qname path of the node so it is in sync with it.\n" - + - "This only applies to nodes of type or subtype cm:content or cm:folder but not of type or subtype of cm:systemfolder.") - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = NodeMetadata.class), - @ApiResponse(code = 403, message = "Not Authorized") - }) - public void setMetadata(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, - final MetadataChanges changes, final WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes/{space}/{store}/{guid}/metadata") + public ResponseEntity setMetadata(@PathVariable final String space, @PathVariable final String store, + @PathVariable final String guid, @RequestBody final MetadataChanges changes) { NodeRef nodeRef = createNodeRef(space, store, guid); NodeMetadata nodeMetadata = nodeService.setMetadata(nodeRef, changes); if (nodeMetadata == null) { - response.setStatus(404); + ResponseEntity.notFound(); } - writeJsonResponse(response, nodeMetadata); - - } - - @ApiOperation("Retrieve node metadata") - @Uri(value = "/nodes/{space}/{store}/{guid}/metadata", method = HttpMethod.GET) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = NodeMetadata.class), - @ApiResponse(code = 403, message = "Not Authorized") - }) - public void getMetadata(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - - NodeRef nodeRef = this.createNodeRef(space, store, guid); - NodeMetadata nodeMetadata = nodeService.getMetadata(nodeRef); + return writeJsonResponse(nodeMetadata); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/metadata") + public ResponseEntity getMetadata(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { + NodeMetadata nodeMetadata = nodeService.getMetadata( + this.createNodeRef(space, store, guid) + ); if (nodeMetadata == null) { - response.setStatus(404); + return ResponseEntity.notFound().build(); } else { - writeJsonResponse(response, nodeMetadata); + return writeJsonResponse(nodeMetadata); } - } - - @ApiOperation("Delete a node") - @Uri(value = "/nodes/{space}/{store}/{guid}", method = HttpMethod.DELETE) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found")}) - public void deleteNode(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, - @RequestParam(required = false) final String permanently, final WebScriptResponse response) - throws IOException { - logger.debug(" permanently: " + permanently); + @AlfrescoTransaction + @DeleteMapping(value = "/v1/nodes/{space}/{store}/{guid}") + public ResponseEntity deleteNode(@PathVariable final String space, @PathVariable final String store, + @PathVariable final String guid, + @RequestParam(required = false) final String permanently) { + logger.debug(" permanently: {}", permanently); boolean deletePermanently = permanently != null && permanently.equals("true"); - logger.debug(" deletePermanently: " + deletePermanently); + logger.debug(" deletePermanently: {}", deletePermanently); NodeRef nodeRef = createNodeRef(space, store, guid); - boolean success = nodeService.deleteNode(nodeRef, deletePermanently); - if (success) { + if (nodeService.deleteNode(nodeRef, deletePermanently)) { logger.debug("node {} deleted", nodeRef); - response.setStatus(200); - writeJsonResponse(response, "Node deleted."); - } else { - logger.debug("Failed to delete node, node does not exist: {}", nodeRef); - response.setStatus(404); - writeJsonResponse(response, "Failed to delete node, node does not exist: " + nodeRef.toString()); + return ResponseEntity.ok("Node deleted."); } - - } - - @ApiOperation("Retrieve node associations.\nVersionstore does not support sourceAssocs. For version nodes, an empty list is returned for this component of the result.") - @Uri(value = "/nodes/{space}/{store}/{guid}/associations", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = NodeAssociations.class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void getAssociations(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - NodeAssociations associations = this.nodeService.getAssociations(nodeRef); - writeJsonResponse(response, associations); - - } - - @ApiOperation("Create new association with given node as source") - @Uri(value = "/nodes/{space}/{store}/{guid}/associations", method = HttpMethod.POST) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void createAssociation(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - CreateAssociationOptions options, - WebScriptResponse response) throws IOException { - NodeRef source = this.createNodeRef(space, store, guid); - nodeService.createAssociation(source, options.getTarget(), options.getType()); - - //writeJsonResponse(response, associations); - response.setStatus(200); - } - - @ApiOperation("Deletes an association with given node as source") - @Uri(value = "/nodes/{space}/{store}/{guid}/associations", method = HttpMethod.DELETE) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - public void deleteAssociation(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - @RequestParam String target, @RequestParam String type, - WebScriptResponse response) throws IOException { - NodeRef source = this.createNodeRef(space, store, guid); - NodeRef targetNode = new NodeRef(URLDecoder.decode(target, StandardCharsets.UTF_8.toString())); - - nodeService.removeAssociation(source, - targetNode, - new QName(type)); - response.setStatus(200); - - } - - - @ApiOperation("Retrieve node parent associations") - @Uri(value = "/nodes/{space}/{store}/{guid}/associations/parents", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = ChildParentAssociation[].class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void getParentAssociations(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - List associations = this.nodeService.getParentAssociations(nodeRef); - - writeJsonResponse(response, associations); - - } - - @ApiOperation("Retrieve node child associations") - @Uri(value = "/nodes/{space}/{store}/{guid}/associations/children", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = ChildParentAssociation[].class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void getChildAssociations(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - List associations = this.nodeService.getChildAssociations(nodeRef); - - writeJsonResponse(response, associations); - - } - - @ApiOperation("Retrieve node peer associations with given node being the source") - @Uri(value = "/nodes/{space}/{store}/{guid}/associations/targets", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = NodeAssociation[].class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void getSourcePeerAssociations(@UriVariable String space, @UriVariable String store, - @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - List associations = this.nodeService.getTargetAssociations(nodeRef); - - writeJsonResponse(response, associations); - - } - - @ApiOperation(value = "Retrieve current user's permissions for a node", - notes = "Returns a key-value map of permissions keys to a value of 'DENY' or 'ALLOW'. " + - "Possible keys are: Read, Write, Delete, CreateChildren, ReadPermissions, ChangePermissions, " + - "or custom permissions") - @Uri(value = "/nodes/{space}/{store}/{guid}/permissions", method = HttpMethod.GET) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = PermissionValue.class, responseContainer = "Map")}) + logger.debug("Failed to delete node, node does not exist: {}", nodeRef); + return ResponseEntity + .status(HttpStatus.SC_NOT_FOUND) + .body(String.format("Failed to delete node, node does not exist: %s", nodeRef.toString())); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/associations") + public ResponseEntity getAssociations(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + this.nodeService.getAssociations( + this.createNodeRef(space, store, guid) + ) + ); + } + + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes/{space}/{store}/{guid}/associations") + public ResponseEntity createAssociation(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, + @RequestBody CreateAssociationOptions options) { + nodeService.createAssociation( + this.createNodeRef(space, store, guid), + options.getTarget(), + options.getType() + ); + return ResponseEntity.ok().build(); + } + + @AlfrescoTransaction + @DeleteMapping(value = "/v1/nodes/{space}/{store}/{guid}/associations") + public ResponseEntity deleteAssociation(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @RequestParam NodeRef target, + @RequestParam String type) { + nodeService.removeAssociation( + this.createNodeRef(space, store, guid), + target, + new QName(type) + ); + return ResponseEntity.ok().build(); + } + + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/associations/parents") + public ResponseEntity> getParentAssociations(@PathVariable String space, + @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + this.nodeService.getParentAssociations( + this.createNodeRef(space, store, guid) + ) + ); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/associations/children") + public ResponseEntity> getChildAssociations(@PathVariable String space, + @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + this.nodeService.getChildAssociations( + this.createNodeRef(space, store, guid) + ) + ); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/associations/targets") + public ResponseEntity> getSourcePeerAssociations(@PathVariable String space, + @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + this.nodeService.getTargetAssociations( + this.createNodeRef(space, store, guid) + ) + ); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/permissions") // It would seem permissions can always be retrieved? - public void getPermissions(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - Map permissions = this.permissionService.getPermissions(nodeRef); - writeJsonResponse(response, permissions); - - } - - @ApiOperation(value = "sets a user a permission for a node.") - @Uri(value = "/nodes/{space}/{store}/{guid}/permissions/authority/{authority}/permission/{permission}", method = HttpMethod.POST) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - public void setPermission(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - @UriVariable String authority, @UriVariable String permission, WebScriptResponse response) - throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - this.permissionService.setPermission(nodeRef, authority, permission); - response.setStatus(200); - - } - - @ApiOperation(value = "removes a user its permission for a node.") - @Uri(value = "/nodes/{space}/{store}/{guid}/permissions/authority/{authority}/permission/{permission}", method = HttpMethod.DELETE) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - public void deletePermission(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - @UriVariable String authority, @UriVariable String permission, WebScriptResponse response) - throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - this.permissionService.deletePermission(nodeRef, authority, permission); - response.setStatus(200); - - } - - @ApiOperation(value = "Gets the ACLs for a node.") - @Uri(value = "/nodes/{space}/{store}/{guid}/acl", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = NodePermission.class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void getAcls(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - NodePermission permissions = this.permissionService.getNodePermissions(nodeRef); - response.setStatus(200); - writeJsonResponse(response, permissions); - - } - - @Uri(value = "/nodes/{space}/{store}/{guid}/acl/inheritFromParent", method = HttpMethod.POST) - @ApiResponses({@ApiResponse(code = 403, message = "Not Authorized")}) - public void setInheritParentPermissions(@UriVariable String space, @UriVariable String store, - @UriVariable String guid, final InheritFromParent inherit) { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - this.permissionService.setInheritParentPermissions(nodeRef, inherit.inheritFromParent); - } - - @ApiOperation(value = "Sets the ACL for a node.", notes = "Example:\n" + - "\n" + - "```\n" + - "{\n" + - " \"inheritFromParent\": false,\n" + - " \"ownAccessList\": [\n" + - " {\n" + - " \"allowed\": true,\n" + - " \"authority\": \"MYGROUP\",\n" + - " \"permission\": \"Collaborator\"\n" + - " }\n" + - "]}\n" + - "```", consumes = "application/json") - @Uri(value = "/nodes/{space}/{store}/{guid}/acl", method = HttpMethod.PUT) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - public void setAcls(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - final ChangeAclsOptions changeAclsOptions, WebScriptRequest request, WebScriptResponse response) - throws JSONException, IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - + public ResponseEntity> getPermissions(@PathVariable String space, + @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + this.permissionService.getPermissions( + this.createNodeRef(space, store, guid) + ) + ); + } + + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes/{space}/{store}/{guid}/permissions/authority/{authority}/permission/{permission}") + public ResponseEntity setPermission(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @PathVariable String authority, + @PathVariable String permission) { + this.permissionService.setPermission( + this.createNodeRef(space, store, guid), authority, permission + ); + return ResponseEntity.ok().build(); + } + + @AlfrescoTransaction + @DeleteMapping(value = "/v1/nodes/{space}/{store}/{guid}/permissions/authority/{authority}/permission/{permission}") + public ResponseEntity deletePermission(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @PathVariable String authority, + @PathVariable String permission) { + this.permissionService.deletePermission(this.createNodeRef(space, store, guid), authority, permission); + return ResponseEntity.ok().build(); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/acl") + public ResponseEntity getAcls(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + this.permissionService.getNodePermissions( + this.createNodeRef(space, store, guid) + ) + ); + } + + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes/{space}/{store}/{guid}/acl/inheritFromParent") + public void setInheritParentPermissions(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @RequestBody final InheritFromParent inherit) { + this.permissionService.setInheritParentPermissions( + this.createNodeRef(space, store, guid), inherit.isInheritFromParent() + ); + } + + @AlfrescoTransaction + @PutMapping(value = "/v1/nodes/{space}/{store}/{guid}/acl") + public ResponseEntity setAcls(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, + @RequestBody final ChangeAclsOptions changeAclsOptions) { NodePermission permissions = new NodePermission(); - permissions.setInheritFromParent(changeAclsOptions.isInheritFromParent()); Set accessList = new HashSet<>(); permissions.setOwnAccessList(accessList); Set ownAccessList = changeAclsOptions.getOwnAccessList(); if (ownAccessList == null) { - response.setStatus(400); - return; + return ResponseEntity.badRequest().build(); } for (Access access : changeAclsOptions.getOwnAccessList()) { NodePermission.Access a = new NodePermission.Access(); @@ -373,195 +262,61 @@ public void setAcls(@UriVariable String space, @UriVariable String store, @UriVa a.setAuthority(access.authority); a.setPermission(access.permission); } - this.permissionService.setNodePermissions(nodeRef, permissions); - - response.setStatus(200); - - } - - @ApiOperation("Returns path of the node") - @Uri(value = "/nodes/{space}/{store}/{guid}/path", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = NodePath.class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void getPath(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - - NodePath path = this.fileFolderService.getPath(nodeRef); - writeJsonResponse(response, path); - - } - - @ApiOperation("Returns combined information of a node.\nNote: versionstore does not support sourceAssocs. For version nodes, an empty list added to the result") - @Uri(value = "/nodes/{space}/{store}/{guid}", method = HttpMethod.GET) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = NodeInfo.class), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found") - }) - public void getAllInfoOfNode(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = this.createNodeRef(space, store, guid); - + this.permissionService.setNodePermissions( + this.createNodeRef(space, store, guid), permissions + ); + return ResponseEntity.ok().build(); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/path") + public ResponseEntity getPath(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + this.fileFolderService.getPath( + this.createNodeRef(space, store, guid) + ) + ); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}") + public ResponseEntity getAllInfoOfNode(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { try { // This method will return a null result if user has insufficient permissions - NodeInfo nodeInfo = this - .nodeRefToNodeInfo(nodeRef, this.fileFolderService, this.nodeService, this.permissionService); + NodeInfo nodeInfo = this.nodeRefToNodeInfo( + this.createNodeRef(space, store, guid), + this.fileFolderService, this.nodeService, this.permissionService + ); if (nodeInfo != null) { - writeJsonResponse(response, nodeInfo); - } else { - String message = String.format("Insufficient permissions for node %s", nodeRef); - throw new AccessDeniedException(message); + return writeJsonResponse(nodeInfo); } + return ResponseEntity + .status(HttpStatus.SC_UNAUTHORIZED) + .body(String.format("Insufficient permissions for node %s://%s/%s", space, store, guid)); } catch (InvalidNodeRefException invalidNodeRefException) { - logger.debug("Failed to get node info, node does not exist: {}", invalidNodeRefException); - response.setStatus(404); - writeJsonResponse(response, "Failed to get node info, node does not exist."); + logger.debug("Failed to get node info, node does not exist: {}://{}/{}", space, store, guid, + invalidNodeRefException); + return ResponseEntity + .status(HttpStatus.SC_NOT_FOUND) + .body("Failed to get node info, node does not exist."); } - } - @ApiOperation(value = "Returns combined information of multiple nodes. " - + "Nodes errors or without appropriate permissions will not be included in return, " - + "and will not cause failure of the call.", - notes = "Example to get combined information of multiple nodes:\n" + - "\n" + - "```\n" + - "POST /apix/v1/nodes/nodeInfo\n" + - "{\n" + - "\"retrieveMetadata\" : true, \n" + - "\"retrievePath\" : true, \n" + - "\"retrievePermissions\" : true, \n" + - "\"retrieveAssocs\" : true, \n" + - "\"retrieveChildAssocs\" : true, \n" + - "\"retrieveParentAssocs\" : true, \n" + - "\"retrieveTargetAssocs\" : true, \n" + - "\"noderefs\": [ \n" + - " \"workspace://SpacesStore/123456789\", \n" + - " \"workspace://SpacesStore/147258369\", \n" + - " \"workspace://SpacesStore/478159236\" \n" + - "]}\n" + - "```" + - "\n" + - "'retrieveMetadata', 'retrievePath', 'retrievePermissions', 'retrieveAssocs', 'retrieveChildAssocs',\n" - + - "'retrieveParentAssocs', 'retrieveTargetAssocs' are optional parameters.\n" + - "Set 'retrieveMetadata' to false to omit the aspects and properties from the result.\n" + - "Set 'retrievePath' to false to omit the path from the result.\n" + - "Set 'retrievePermissions' to false to omit the permissions from the result.\n" + - "Set 'retrieveAssocs' to false to omit the associations(parent associations, child associations, peer associations) from the result.\n" - + - "Set 'retrieveChildAssocs' to false to omit the child associations from the result.\n" + - "Set 'retrieveParentAssocs' to false to omit the parent associations from the result.\n" + - "Set 'retrieveTargetAssocs' to false to omit the peer target associations from the result.\n" + - "Set 'retrieveSourceAssocs' to false to omit the peer source associations from the result. Note: versionstore does not support sourceAssocs. For version nodes, an empty list added to the result\n") - @Uri(value = "/nodes/nodeInfo", method = HttpMethod.POST) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = NodeInfo[].class), - @ApiResponse(code = 400, message = "Bad Request") - }) - public void getAllInfoOfNodes(WebScriptRequest request, WebScriptResponse response) - throws IOException, JSONException { - String requestString = request.getContent().getContent(); - logger.debug("request content: " + requestString); - JSONObject jsonObject = new JSONObject(requestString); - if (jsonObject == null) { - response.setStatus(400); - String message = String - .format("Malfromed body: request string could not be parsed to jsonObject: %s", requestString); - logger.debug(message); - writeJsonResponse(response, message); - } - logger.debug("json: " + jsonObject.toString()); - - boolean retrieveMetadata = true; - boolean retrievePath = true; - boolean retrievePermissions = true; - boolean retrieveAssocs = true; - boolean retrieveChildAssocs = true; - boolean retrieveParentAssocs = true; - boolean retrieveTargetAssocs = true; - boolean retrieveSourceAssocs = true; - - List nodeRefs = new ArrayList(); - try { - if (jsonObject.has("retrieveMetadata")) { - retrieveMetadata = jsonObject.getBoolean("retrieveMetadata"); - } - if (jsonObject.has("retrievePath")) { - retrievePath = jsonObject.getBoolean("retrievePath"); - } - if (jsonObject.has("retrievePermissions")) { - retrievePermissions = jsonObject.getBoolean("retrievePermissions"); - } - if (jsonObject.has("retrieveAssocs")) { - retrieveAssocs = jsonObject.getBoolean("retrieveAssocs"); - } - if (jsonObject.has("retrieveChildAssocs")) { - retrieveChildAssocs = jsonObject.getBoolean("retrieveChildAssocs"); - } - if (jsonObject.has("retrieveParentAssocs")) { - retrieveParentAssocs = jsonObject.getBoolean("retrieveParentAssocs"); - } - if (jsonObject.has("retrieveTargetAssocs")) { - retrieveTargetAssocs = jsonObject.getBoolean("retrieveTargetAssocs"); - } - if (jsonObject.has("retrieveSourceAssocs")) { - retrieveSourceAssocs = jsonObject.getBoolean("retrieveSourceAssocs"); - } - - JSONArray nodeRefsJsonArray = jsonObject.getJSONArray("noderefs"); - if (nodeRefsJsonArray == null) { - response.setStatus(400); - String message = String.format("Could not retrieve target noderefs from body: %s", jsonObject); - logger.debug(message); - writeJsonResponse(response, message); - } - int nodeRefsJsonArrayLength = nodeRefsJsonArray.length(); - logger.debug("nodeRefsJsonArrayLength: " + nodeRefsJsonArrayLength); - for (int i = 0; i < nodeRefsJsonArrayLength; i++) { - String nodeRefString = (String) nodeRefsJsonArray.get(i); - logger.debug("nodeRefString: " + nodeRefString); - NodeRef nodeRef = new NodeRef(nodeRefString); - nodeRefs.add(nodeRef); - } - } catch (JSONException e) { - logger.error("Error deserializing json body", e); - String message = String.format("Malformed json body {}", jsonObject); - response.setStatus(400); - writeJsonResponse(response, message); - } - - //logger.error("done parsing request data"); - //logger.error("start nodeRefToNodeInfo"); - List nodeInfoList = this.nodeRefToNodeInfo(nodeRefs, + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes/nodeInfo") + public ResponseEntity getAllInfoOfNodes(@RequestBody final NodeInfoRequest nodeInfoRequest) { + List nodeInfoList = this.nodeRefToNodeInfo(nodeInfoRequest, this.fileFolderService, this.nodeService, - this.permissionService, - retrievePath, - retrieveMetadata, - retrievePermissions, - retrieveAssocs, - retrieveChildAssocs, - retrieveParentAssocs, - retrieveTargetAssocs, - retrieveSourceAssocs); - //logger.error("end nodeRefToNodeInfo"); - - //logger.error("start writeJsonResponse"); - writeJsonResponse(response, nodeInfoList); - //logger.error("end writeJsonResponse"); - } - - @ApiOperation(value = "Retrieves the ancestors of the nodes", - notes = "It is possible to add \"root\" as a request parameter.\n" - + "It is the node reference up to which point ancestors will be retrieved.\n" - + "The default root reference will be the reference of Company Home") - @Uri(value = "/nodes/{space}/{store}/{guid}/ancestors", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = AncestorsObject.class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void retrieveAncestors(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - @RequestParam(required = false) String root, WebScriptResponse response) throws IOException { + this.permissionService + ); + return writeJsonResponse(nodeInfoList); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/ancestors") + public ResponseEntity retrieveAncestors(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, + @RequestParam(required = false) String root) { NodeRef nodeRef = this.createNodeRef(space, store, guid); NodeRef rootRef = null; if (root != null) { @@ -571,132 +326,93 @@ public void retrieveAncestors(@UriVariable String space, @UriVariable String sto try { List ancestors = nodeService.getAncestors(nodeRef, rootRef); AncestorsObject ancestorsObject = new AncestorsObject(nodeRef, ancestors); - writeJsonResponse(response, ancestorsObject); + return writeJsonResponse(ancestorsObject); } catch (InvalidNodeRefException ex) { - logger.error("noderef does not exist"); - response.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(response, ex.getMessage()); + logger.error("noderef does not exist", ex); + return ResponseEntity.status(HttpStatus.SC_NOT_FOUND) + .body(ex.getMessage()); } catch (AccessDeniedException ex) { - logger.error("access denied on noderef"); - response.setStatus(HttpStatus.SC_FORBIDDEN); - writeJsonResponse(response, ex.getMessage()); + logger.error("access denied on noderef", ex); + return ResponseEntity.status(HttpStatus.SC_FORBIDDEN) + .body(ex.getMessage()); } } - @ApiOperation(value = "Creates or copies a node", - notes = "Example of POST body:\n" - + "\n" - + "```\n" - + "POST /apix/v1/nodes\n" - + "{\n" - + "\"parent\" : \"workspace://SpacesStore/d5dac928-e581-4507-9be7-9a2416adc318\", \n" - + "\"name\" : \"mydocument.txt\", \n" - + "\"type\" : \"{http://www.alfresco.org/model/content/1.0}content\", \n" - + "\"properties\" : {\n" - + " \"{namespace}property1\": [\n" - + " \"string\"\n" - + " ],\n" - + " \"{namespace}property2\": [\n" - + " \"string\"\n" - + " ],\n" - + " \"{namespace}property3\": [\n" - + " \"string\"\n" - + " ]\n" - + "}, \n" - + "\"aspectsToAdd\" : [\n" - + " \"{namespace}aspect1\"\n" - + "], \n" - + "\"aspectsToRemove\" : [\n" - + " \"{namespace}aspect1\"\n" - + "], \n" - + "\"copyFrom\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\", \n" - + "}\n" - + "```" - + "\n" - + "\"aspectsToRemove\" is only relevant when copying a node.\n") - @Uri(value = "/nodes", method = HttpMethod.POST) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = NodeInfo.class), - @ApiResponse(code = 403, message = "Not Authorized")}) - public void createNode(final CreateNodeOptions createNodeOptions, WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes") + public ResponseEntity createNode(@RequestBody final CreateNodeOptions createNodeOptions) { try { Object resultObject = serviceRegistry.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - NodeRef parent = new NodeRef(createNodeOptions.parent); + .doInTransaction(() -> { + NodeRef parent = new NodeRef(createNodeOptions.getParent()); - if (!nodeService.exists(parent)) { - writeNotFoundResponse(response, parent); - return null; - } + if (!nodeService.exists(parent)) { + return writeNotFoundResponse(parent); + } - NodeRef nodeRef; - NodeRef copyFrom = null; - if (createNodeOptions.copyFrom == null) { - nodeRef = nodeService - .createNode(parent, createNodeOptions.name, - new QName(createNodeOptions.type)); - } else { - copyFrom = new NodeRef(createNodeOptions.copyFrom); - if (!nodeService.exists(copyFrom)) { - response.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(response, "CopyFrom does not exist"); - return null; - } - nodeRef = nodeService.copyNode(copyFrom, parent, true); + NodeRef nodeRef; + NodeRef copyFrom = null; + if (createNodeOptions.getCopyFrom() == null) { + nodeRef = nodeService + .createNode(parent, createNodeOptions.getName(), + new QName(createNodeOptions.getType())); + } else { + copyFrom = new NodeRef(createNodeOptions.getCopyFrom()); + if (!nodeService.exists(copyFrom)) { + return writeNotFoundResponse(copyFrom); } + nodeRef = nodeService.copyNode(copyFrom, parent, true); + } - MetadataChanges metadataChanges; - QName type; - if (createNodeOptions.type != null) { - type = new QName(createNodeOptions.type); - } else if (createNodeOptions.type == null && createNodeOptions.copyFrom != null) { - type = nodeService.getMetadata(copyFrom).type; - } else { - response.setStatus(HttpStatus.SC_BAD_REQUEST); - writeJsonResponse(response, - "Please provide parameter \"type\" when creating a new node"); - return null; - } + MetadataChanges metadataChanges; + QName type; + if (createNodeOptions.getType() != null) { + type = new QName(createNodeOptions.getType()); + } else if (createNodeOptions.getType() == null && createNodeOptions.getCopyFrom() != null) { + type = nodeService.getMetadata(copyFrom).getType(); + } else { + return ResponseEntity.status(HttpStatus.SC_BAD_REQUEST) + .body("Please provide parameter \"type\" when creating a new node"); + } - metadataChanges = new MetadataChanges(type, - createNodeOptions.aspectsToAdd, - createNodeOptions.aspectsToRemove, - createNodeOptions.properties); - nodeService.setMetadata(nodeRef, metadataChanges); + metadataChanges = new MetadataChanges(type, + createNodeOptions.getAspectsToAdd(), + createNodeOptions.getAspectsToRemove(), + createNodeOptions.getProperties()); + nodeService.setMetadata(nodeRef, metadataChanges); - return nodeRef; - } + return nodeRef; }, false, true); if (resultObject == null) { - return; + return ResponseEntity.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).build(); } NodeRef resultRef = new NodeRef(resultObject.toString()); NodeInfo nodeInfo = this .nodeRefToNodeInfo(resultRef, this.fileFolderService, this.nodeService, this.permissionService); - writeJsonResponse(response, nodeInfo); + return writeJsonResponse(nodeInfo); } catch (org.alfresco.service.cmr.model.FileExistsException fileExistsException) { throw new FileExistsException( - new NodeRef(createNodeOptions.copyFrom), + new NodeRef(createNodeOptions.getCopyFrom()), new NodeRef(fileExistsException.getParentNodeRef().toString()), fileExistsException.getName()); } - } - @ApiOperation("Moves a node by changing its parent") - @Uri(value = "/nodes/{space}/{store}/{guid}/parent", method = HttpMethod.PUT) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - public void setParent(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, final ChangeParentOptions location, WebScriptResponse response) - throws IOException { - NodeRef parent = new NodeRef(location.parent); + @AlfrescoTransaction + @PutMapping(value = "/v1/nodes/{space}/{store}/{guid}/parent") + public ResponseEntity setParent(@PathVariable final String space, @PathVariable final String store, + @PathVariable final String guid, @RequestBody ChangeParentOptions location) { NodeRef nodeToMove = createNodeRef(space, store, guid); try { - nodeService.moveNode(nodeToMove, parent); + return ResponseEntity.ok( + nodeService.moveNode( + nodeToMove, + new NodeRef(location.getParent()) + ) + ); } catch (org.alfresco.service.cmr.model.FileExistsException fileExistsException) { throw new FileExistsException( nodeToMove, @@ -704,257 +420,142 @@ public void setParent(@UriVariable final String space, @UriVariable final String fileExistsException.getName()); } } - - @ApiOperation(value = "Retrieves all comments for a given node") - @Uri(value = "/nodes/{space}/{store}/{guid}/comments", method = HttpMethod.GET) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found") - }) - public void getComments(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - @RequestParam(defaultValue = "0") int skipcount, @RequestParam(defaultValue = "10") int pagesize, - WebScriptResponse response) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/comments") + public ResponseEntity getComments(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @RequestParam(defaultValue = "0") int skipcount, + @RequestParam(defaultValue = "10") int pagesize) { final NodeRef target = this.createNodeRef(space, store, guid); - if (nodeService.exists(target)) { - if (permissionService.hasPermission(target, PermissionService.READ)) { - Conversation comments = commentService.getComments(target, skipcount, pagesize); - boolean canCreate = permissionService.hasPermission(target, PermissionService.CREATE_CHILDREN); - comments.setCreatable(canCreate); - response.setStatus(HttpStatus.SC_OK); - writeJsonResponse(response, comments); - } else { - throw new AccessDeniedException("User does not have permission to read parent node"); - } - } else { - writeNotFoundResponse(response, target); + if (!nodeService.exists(target)) { + return writeNotFoundResponse(target); } - } - - @ApiOperation(value = "Appends a new comment to the given node.") - @Uri(value = "/nodes/{space}/{store}/{guid}/comments", method = HttpMethod.POST) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found") - }) - public void addComment(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - final Comment newComment, WebScriptRequest request, WebScriptResponse response) throws IOException { - final NodeRef target = new NodeRef(space, store, guid); - if (nodeService.exists(target)) { - Comment responseComment = commentService.addNewComment(target, newComment.getContent()); - response.setStatus(HttpStatus.SC_OK); - writeJsonResponse(response, responseComment); - } else { - writeNotFoundResponse(response, target); + if (permissionService.hasPermission(target, PermissionService.READ)) { + Conversation comments = commentService.getComments(target, skipcount, pagesize); + boolean canCreate = permissionService.hasPermission(target, PermissionService.CREATE_CHILDREN); + comments.setCreatable(canCreate); + return writeJsonResponse(comments); } + return writeNotAuthorizedResponse(new AccessDeniedException(target.getValue())); } - @ApiOperation(value = "Returns the comment with the given id.") - @Uri(value = "/comments/{space}/{store}/{guid}", - method = HttpMethod.GET) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found") - }) - public void getComment(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptRequest request, WebScriptResponse response) throws IOException { - final NodeRef targetComment = new NodeRef(space, store, guid); - if (nodeService.exists(targetComment)) { - if (permissionService.hasPermission(targetComment, PermissionService.READ)) { - Comment comment = commentService.getComment(targetComment); - response.setStatus(HttpStatus.SC_OK); - writeJsonResponse(response, comment); - } else { - throw new AccessDeniedException(String.format("User does not have permission " + - "to read the comment node %s", targetComment.toString())); - } + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes/{space}/{store}/{guid}/comments") + public ResponseEntity addComment(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @RequestBody final Comment newComment) { + final NodeRef target = this.createNodeRef(space, store, guid); + if (!nodeService.exists(target)) { + return writeNotFoundResponse(target); + } + Comment responseComment = commentService.addNewComment(target, newComment.getContent()); + return writeJsonResponse(responseComment); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/comments/{space}/{store}/{guid}") + public ResponseEntity getComment(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { + final NodeRef targetComment = this.createNodeRef(space, store, guid); + if (!nodeService.exists(targetComment)) { + return writeNotFoundResponse(targetComment); + } + if (permissionService.hasPermission(targetComment, PermissionService.READ)) { + return writeJsonResponse(commentService.getComment(targetComment)); } else { - writeNotFoundResponse(response, targetComment); + throw new AccessDeniedException(String.format("User does not have permission " + + "to read the comment node %s", targetComment.toString())); } } - @ApiOperation(value = "Updates the comment with the given id.") - @Uri(value = "/comments/{space}/{store}/{guid}", - method = HttpMethod.PUT) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found") - }) - public void updateComment(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - final Comment newComment, WebScriptRequest request, WebScriptResponse response) throws IOException { - final NodeRef targetComment = new NodeRef(space, store, guid); - if (nodeService.exists(targetComment)) { - Comment updatedComment = commentService.updateComment(targetComment, newComment.getContent()); - response.setStatus(HttpStatus.SC_OK); - writeJsonResponse(response, updatedComment); - } else { - writeNotFoundResponse(response, targetComment); + @AlfrescoTransaction + @PutMapping(value = "/v1/comments/{space}/{store}/{guid}") + public ResponseEntity updateComment(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @RequestBody final Comment newComment) { + final NodeRef targetComment = this.createNodeRef(space, store, guid); + if (!nodeService.exists(targetComment)) { + return writeNotFoundResponse(targetComment); } + Comment updatedComment = commentService.updateComment(targetComment, newComment.getContent()); + return writeJsonResponse(updatedComment); } - @ApiOperation(value = "Deletes the comment with the given id.") - @Uri(value = "/comments/{space}/{store}/{guid}", - method = HttpMethod.DELETE) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found") - }) - public void deleteComment(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptRequest request, WebScriptResponse response) throws IOException { - final NodeRef targetComment = new NodeRef(space, store, guid); - if (nodeService.exists(targetComment)) { - commentService.deleteComment(targetComment); - response.setStatus(HttpStatus.SC_OK); - writeJsonResponse(response, String.format("Comment %s deleted", targetComment.toString())); - } else { - writeNotFoundResponse(response, targetComment); + @AlfrescoTransaction + @DeleteMapping(value = "/v1/comments/{space}/{store}/{guid}") + public ResponseEntity deleteComment(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { + final NodeRef targetComment = this.createNodeRef(space, store, guid); + if (!nodeService.exists(targetComment)) { + return writeNotFoundResponse(targetComment); } + commentService.deleteComment(targetComment); + return writeJsonResponse(String.format("Comment %s deleted", targetComment)); } - - @ApiOperation(value = "Downloads content file for given node") - @Uri(value = "/nodes/{space}/{store}/{guid}/content", method = HttpMethod.GET) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success"), - @ApiResponse(code = 403, message = "Not Authorized"), - @ApiResponse(code = 404, message = "Not Found")}) - public void getContent(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - final NodeRef nodeRef = new NodeRef(space, store, guid); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/content") + public ResponseEntity getContent(@PathVariable String space, @PathVariable String store, @PathVariable String guid) { + final NodeRef nodeRef = this.createNodeRef(space, store, guid); ContentInputStream contentInputStream = nodeService.getContent(nodeRef); if (contentInputStream == null) { - response.setStatus(404); - return; - } - response.setContentType(contentInputStream.getMimetype()); - response.addHeader("Content-Length", Objects.toString(contentInputStream.getSize())); - - // Unwrap the webscript responses, because we don't want any caching when writing the file - WebScriptResponse resp = response; - while (resp instanceof WrappingWebScriptResponse) { - resp = ((WrappingWebScriptResponse) resp).getNext(); - } - - InputStream inputStream = contentInputStream.getInputStream(); - - try { - IOUtils.copyLarge(inputStream, resp.getOutputStream()); - } finally { - IOUtils.closeQuietly(inputStream); + return writeNotFoundResponse(nodeRef); } + return ResponseEntity.ok() + .body(new InputStreamResource(contentInputStream.getInputStream())); } - @ApiOperation(value = "Sets or updates the content for given node. If no file is given the content will be set to empty.", consumes = "multipart/form-data") - @Uri(value = "/nodes/{space}/{store}/{guid}/content", method = HttpMethod.PUT) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - @ApiImplicitParams({@ApiImplicitParam(name = "file", paramType = "form", dataType = "file", required = false)}) - public void setContent(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - final WebScriptRequest multiPart, WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @PutMapping(value = "/v1/nodes/{space}/{store}/{guid}/content") + public ResponseEntity setContent(@PathVariable String space, @PathVariable String store, + @PathVariable String guid, @RequestPart final MultipartFile file) { final NodeRef finalDestination = this.createNodeRef(space, store, guid); RetryingTransactionHelper transactionHelper = serviceRegistry.getRetryingTransactionHelper(); - - org.springframework.extensions.webscripts.servlet.FormData.FormField content = null; - - org.springframework.extensions.webscripts.servlet.FormData formData = (org.springframework.extensions.webscripts.servlet.FormData) multiPart - .parseContent(); - final org.springframework.extensions.webscripts.servlet.FormData.FormField[] fields = formData.getFields(); - for (org.springframework.extensions.webscripts.servlet.FormData.FormField field : fields) { - if (field.getName().equals("file") && field.getIsFile()) { - content = field; - } - } - - final org.springframework.extensions.webscripts.servlet.FormData.FormField finalContent = content; - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - - nodeService - .setContent(finalDestination, finalContent != null ? finalContent.getInputStream() : null, - finalContent != null ? finalContent.getFilename() : null); - return null; - } + transactionHelper.doInTransaction(() -> { + nodeService + .setContent(finalDestination, file != null ? file.getInputStream() : null, + file != null ? file.getOriginalFilename() : null); + return null; }, false, true); - - //writeJsonResponse(response, new Message("Successfully set content")); - + return ResponseEntity.ok().build(); } - - @ApiOperation(value = "Sets the content for given node to empty") - @Uri(value = "/nodes/{space}/{store}/{guid}/content", method = HttpMethod.DELETE) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - public void deleteContent(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - final WebScriptRequest multiPart, WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @DeleteMapping(value = "/v1/nodes/{space}/{store}/{guid}/content") + public ResponseEntity deleteContent(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { RetryingTransactionHelper transactionHelper = serviceRegistry.getRetryingTransactionHelper(); final NodeRef finalDestination = this.createNodeRef(space, store, guid); - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - - nodeService.setContent(finalDestination, null, null); - return null; - } + transactionHelper.doInTransaction(() -> { + nodeService.setContent(finalDestination, null, null); + return null; }, false, true); - - } - - - @ApiOperation(value = "Checks if the given node exists") - @Uri(value = "/nodes/{space}/{store}/{guid}/exists", method = HttpMethod.GET) - @ApiResponses({@ApiResponse(code = 200, message = "Success"), @ApiResponse(code = 403, message = "Not Authorized")}) - public void exists(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { - NodeRef nodeRef = new NodeRef(space, store, guid); - writeJsonResponse(response, nodeService.exists(nodeRef)); - - } - - - @ApiOperation(value = "Creates a new node with given content", consumes = "multipart/form-data") - @Uri(value = "/nodes/upload", method = HttpMethod.POST) - @ApiResponses({@ApiResponse(code = 200, message = "Success", response = NodeInfo.class), - @ApiResponse(code = 403, message = "Not Authorized")}) - @ApiImplicitParams({ - @ApiImplicitParam(value = "Noderef of parent for the new node", name = "parent", paramType = "form", dataType = "string", required = true), - //TODO: Datatype doesnt work here? - @ApiImplicitParam(value = "QName type for the new node", name = "type", paramType = "form", dataType = "string", required = false), - @ApiImplicitParam(name = "file", paramType = "form", dataType = "file", required = true), - @ApiImplicitParam(value = "Metadata for this file", name = "metadata", paramType = "form", dataType = "string", required = false), - @ApiImplicitParam(value = "Enable metadata extraction from the content, for example for msg files", name = "extractMetadata", paramType = "form", dataType = "boolean", required = false), - }) - - public void uploadNode(final WebScriptRequest multiPart, final WebScriptResponse response) throws IOException { + return ResponseEntity.ok().build(); + } + + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/exists") + public ResponseEntity exists(@PathVariable String space, @PathVariable String store, + @PathVariable String guid) { + return writeJsonResponse( + nodeService.exists( + this.createNodeRef(space, store, guid) + ) + ); + } + + @AlfrescoTransaction + @PostMapping(value = "/v1/nodes/upload") + public ResponseEntity uploadNode( + @RequestParam(required = false) String type, + @RequestParam(required = false) String parent, + @RequestParam(required = false) Boolean extractMetadata, + @RequestPart(required = false) MetadataChanges metadata, + @RequestPart final MultipartFile file) { RetryingTransactionHelper transactionHelper = serviceRegistry.getRetryingTransactionHelper(); // Note the difference between: // * metadata = The metadata the user annotates the file with. // * extractMetadata = Whether the users wants metadata automatically extracted from the file. - // Both setting metadata and extracting metadata are optional. They can happen (or not) independently from each other. - String type = ContentModel.TYPE_CONTENT.toString(); - String parent = null; - FormData.FormField file = null; - Boolean extractMetadata = false; - MetadataChanges metadata = null; - - FormData formData = (FormData) multiPart.parseContent(); - for (FormData.FormField field : formData.getFields()) { - if (field.getName().equals("parent")) { - parent = field.getValue(); - } else if (field.getName().equals("type")) { - type = field.getValue(); - } else if (field.getName().equals("file") && field.getIsFile()) { - file = field; - } else if (field.getName().equals("metadata")) { - ObjectMapper mapper = new ObjectMapper(); - metadata = mapper.readValue(field.getValue(), MetadataChanges.class); - } else if (field.getName().equals("extractMetadata")) { - extractMetadata = Boolean.parseBoolean(field.getValue()); - } - } + // Both setting metadata and extracting metadata are optional. + // They can happen (or not) independently from each other. + type = type == null ? ContentModel.TYPE_CONTENT.toString() : type; + extractMetadata = Boolean.TRUE.equals(extractMetadata); if (file == null) { throw new IllegalArgumentException("Content must be supplied as a multipart 'file' field"); @@ -964,14 +565,13 @@ public void uploadNode(final WebScriptRequest multiPart, final WebScriptResponse } final String finalParent = parent; - final FormData.FormField finalFile = file; final String finalType = type; final MetadataChanges finalMetadata = metadata; final Boolean finalExtractMetadata = extractMetadata; - NodeRef resultRef = null; + NodeRef resultRef; try { resultRef = transactionHelper - .doInTransaction(() -> createNodeForUpload(finalParent, finalFile, finalType, finalMetadata, + .doInTransaction(() -> createNodeForUpload(finalParent, file, finalType, finalMetadata, finalExtractMetadata), false, true); } catch (org.alfresco.service.cmr.model.FileExistsException fileExistsException) { throw new FileExistsException( @@ -980,25 +580,25 @@ public void uploadNode(final WebScriptRequest multiPart, final WebScriptResponse fileExistsException.getName()); } NodeInfo nodeInfo = this - .nodeRefToNodeInfo((NodeRef) resultRef, fileFolderService, nodeService, permissionService); - writeJsonResponse(response, nodeInfo); + .nodeRefToNodeInfo(resultRef, fileFolderService, nodeService, permissionService); + return writeJsonResponse(nodeInfo); } public NodeRef createNodeForUpload(String finalParent, - FormData.FormField finalFile, - String finalType, - MetadataChanges finalMetadata, - Boolean finalExtractMetadata) { + MultipartFile file, + String finalType, + MetadataChanges finalMetadata, + Boolean finalExtractMetadata) throws IOException { NodeRef newNode = nodeService - .createNode(new NodeRef(finalParent), finalFile.getFilename(), + .createNode(new NodeRef(finalParent), file.getOriginalFilename(), new QName(finalType)); - nodeService.setContent(newNode, finalFile.getInputStream(), finalFile.getFilename()); + nodeService.setContent(newNode, file.getInputStream(), file.getOriginalFilename()); if (finalMetadata != null) { nodeService.setMetadata(newNode, finalMetadata); } - if (finalExtractMetadata) { + if (Boolean.TRUE.equals(finalExtractMetadata)) { try { nodeService.extractMetadata(newNode); logger.debug("Metadata extracted"); @@ -1010,26 +610,27 @@ public NodeRef createNodeForUpload(String finalParent, } @ExceptionHandler(AccessDeniedException.class) - private void writeNotAuthorizedResponse(AccessDeniedException exception, WebScriptResponse response) - throws IOException { - logger.debug("Not Authorized: ", exception); - response.setStatus(403); - writeJsonResponse(response, "Not authorised to execute this operation"); + private ResponseEntity writeNotAuthorizedResponse(AccessDeniedException ex) { + logger.debug("Not Authorized", ex); + return ResponseEntity + .status(HttpStatus.SC_FORBIDDEN) + .body("Not authorised to execute this operation"); } @ExceptionHandler(FileExistsException.class) - private void writeFileExistsResponse(FileExistsException fileExistsException, WebScriptResponse response) - throws IOException { + private ResponseEntity writeFileExistsResponse(FileExistsException fileExistsException) { String message = fileExistsException.toString(); - logger.debug(message); - response.setStatus(400); - writeJsonResponse(response, message); + logger.debug(message, fileExistsException); + return ResponseEntity + .status(HttpStatus.SC_BAD_REQUEST) + .body(message); } - private void writeNotFoundResponse(WebScriptResponse response, NodeRef requestedNode) throws IOException { + private ResponseEntity writeNotFoundResponse(NodeRef requestedNode) { String message = String.format("Node Not Found: %s", requestedNode); logger.debug(message); - response.setStatus(404); - writeJsonResponse(response, message); + return ResponseEntity + .status(HttpStatus.SC_NOT_FOUND) + .body(message); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/people/PeopleWebscript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/people/PeopleWebscript1.java index a4727196..4ff512af 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/people/PeopleWebscript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/people/PeopleWebscript1.java @@ -1,74 +1,66 @@ package eu.xenit.apix.rest.v1.people; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.people.IPeopleService; -import eu.xenit.apix.people.Person; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.util.NoSuchElementException; -import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.NoSuchElementException; -/** - * Created by Jasperhilven on 24-Oct-16. - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Retrieves person information", value = "People") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v1.people.PeopleWebscript") +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class PeopleWebscript1 extends ApixV1Webscript { - Logger logger = LoggerFactory.getLogger(eu.xenit.apix.rest.v1.people.PeopleWebscript1.class); - @Autowired - IPeopleService personService; + private static final Logger logger = LoggerFactory.getLogger(PeopleWebscript1.class); + private final IPeopleService personService; - @Uri(value = "/people/{space}/{store}/{guid}", method = HttpMethod.GET) - @ApiOperation(value = "Returns person information", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Person.class)) - public void getPerson(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, WebScriptResponse webScriptResponse) throws IOException { - logger.debug("Asked person with guid: " + guid); + public PeopleWebscript1(IPeopleService personService) { + this.personService = personService; + } + + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/people/{space}/{store}/{guid}") + public ResponseEntity getPerson(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid) { + logger.debug("Asked person with guid: {}", guid); try { - Person p = personService.GetPerson(createNodeRef(space, store, guid)); - writeJsonResponse(webScriptResponse, p); + return writeJsonResponse( + personService.GetPerson( + createNodeRef(space, store, guid) + ) + ); } catch (NoSuchElementException noSuchElementException) { - webScriptResponse.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(webScriptResponse, noSuchElementException.getMessage()); + return ResponseEntity.status(org.springframework.http.HttpStatus.NOT_FOUND) + .body(noSuchElementException.getMessage()); } catch (IllegalArgumentException illegalArgumentException) { - webScriptResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - writeJsonResponse(webScriptResponse, illegalArgumentException.getMessage()); + return ResponseEntity.status(org.springframework.http.HttpStatus.BAD_REQUEST) + .body(illegalArgumentException.getMessage()); } } - @Uri(value = "/people", method = HttpMethod.GET) - @ApiOperation(value = "Returns person information given a userName", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Person.class)) - public void getPersonViaUserName(@RequestParam final String userName, WebScriptResponse webScriptResponse) - throws IOException { - logger.debug("Asked person with name: " + userName); - try{ - Person p = personService.GetPerson(userName); - writeJsonResponse(webScriptResponse, p); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/people") + public ResponseEntity getPersonViaUserName(@RequestParam final String userName) { + logger.debug("Asked person with name: {}", userName); + try { + return writeJsonResponse( + personService.GetPerson(userName) + ); } catch (NoSuchElementException noSuchElementException) { - webScriptResponse.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(webScriptResponse, noSuchElementException.getMessage()); + return ResponseEntity.status(org.springframework.http.HttpStatus.NOT_FOUND) + .body(noSuchElementException.getMessage()); } catch (IllegalArgumentException illegalArgumentException) { - webScriptResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - writeJsonResponse(webScriptResponse, illegalArgumentException.getMessage()); + return ResponseEntity.status(org.springframework.http.HttpStatus.BAD_REQUEST) + .body(illegalArgumentException.getMessage()); } } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/properties/PropertiesWebScript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/properties/PropertiesWebScript1.java index 0dd28859..c1137461 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/properties/PropertiesWebScript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/properties/PropertiesWebScript1.java @@ -1,60 +1,43 @@ package eu.xenit.apix.rest.v1.properties; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; +import eu.xenit.apix.data.QName; import eu.xenit.apix.dictionary.properties.IPropertyService; import eu.xenit.apix.properties.PropertyDefinition; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** - * Created by Jasperhilven on 13-Jan-17. - * - * @deprecated Use DictionaryWebScript1 instead + * @deprecated since Oct 2015, use DictionaryWebScript1 instead */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Retrieves Property information", value = "Properties") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v1.property.PropertiesWebScript1") +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController +@Deprecated(since = "Deprecated since Oct 2015, use DictionaryWebScript1 instead") public class PropertiesWebScript1 extends ApixV1Webscript { - Logger logger = LoggerFactory.getLogger(PropertiesWebScript1.class); - - @Autowired - IPropertyService propertyService; + private final IPropertyService propertyService; + public PropertiesWebScript1(IPropertyService propertyService) { + this.propertyService = propertyService; + } - @Uri(value = "/properties/{qname}", method = HttpMethod.GET) - @ApiOperation(value = "Return the definition of a property", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = PropertyDefinition.class)) + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/properties/{qname}") //Use qname with slash to avoid //https://stackoverflow.com/questions/13482020/encoded-slash-2f-with-spring-requestmapping-path-param-gives-http-400 - public void getPropertyDefinition(@UriVariable final String qname, - @RequestParam(required = false) String qnameWithSlash, WebScriptResponse webScriptResponse) - throws IOException { - String qnameUsed = qnameWithSlash != null ? qnameWithSlash : qname; - String decoded = java.net.URLDecoder.decode(qnameUsed, "UTF-8"); - logger.debug("Asked versionhistory for node with guid: " + decoded); - eu.xenit.apix.data.QName apixQName = new eu.xenit.apix.data.QName(qnameUsed); + public ResponseEntity getPropertyDefinition(@PathVariable final QName qname, + @RequestParam(required = false) QName qnameWithSlash) { + QName apixQName = qnameWithSlash != null ? qnameWithSlash : qname; PropertyDefinition propDef = propertyService.GetPropertyDefinition(apixQName); if (propDef == null) { - webScriptResponse.setStatus(HttpStatus.NOT_FOUND.value()); + return ResponseEntity.notFound().build(); } - writeJsonResponse(webScriptResponse, propDef); + return writeJsonResponse(propDef); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/search/SearchWebScript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/search/SearchWebScript1.java index 7133be7d..4028fe1c 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/search/SearchWebScript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/search/SearchWebScript1.java @@ -1,156 +1,33 @@ package eu.xenit.apix.rest.v1.search; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; import eu.xenit.apix.search.ISearchService; import eu.xenit.apix.search.SearchQuery; -import eu.xenit.apix.search.SearchQueryResult; -import eu.xenit.apix.search.json.SearchNodeJsonParser; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.io.InputStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; -/** - * Created by stan on 5/2/16. - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Perform search queries", value = "Search") -@Authentication(AuthenticationType.USER) -@Qualifier("eu.xenit.apix.rest.v1.search.SearchWebScriptV1") -@Component("eu.xenit.apix.rest.v1.search.SearchWebScriptV1") +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class SearchWebScript1 extends ApixV1Webscript { - Logger logger = LoggerFactory.getLogger(SearchWebScript1.class); - @Autowired - private ISearchService service; + private final ISearchService service; - @Uri(value = "/search", method = HttpMethod.POST) - @ApiOperation(value = "Performs a search for nodes", notes ="# Request components\n" - + "\n" - + "## query\n" - + "Object containing subcomponents that build the requested query.\n" - + "Info about the Search query syntax can be found here: https://docs.xenit.eu/alfred-api/stable-user/rest-api\n" - + "### special search terms:\n" - + "- type: searches for nodes of that type (for example: \"type\" : \"cm:content\")\n" - + "- aspect: searches for nodes with that aspect (for example: \"aspect\" : \"cm:titled\")\n" - + "- noderef: searches the node with that noderef (for example: \"noderef\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\")\n" - + "- parent: searches the nodes with that parent (for example: \"parent\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\")\n" - + "- path: searches the nodes with that path (for example: \"path\" : \"/\")\n" - + "- category: searches the nodes with that category (for example: \"category\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\")\n" - + "- text: searches the nodes with content containing that text (for example: \"text\" : \"this text\")\n" - + "- all: searches the nodes with content, cm:name, cm:creator, cm:modifier or cm:author containing the value (for example: \"all\" : \"search term\")\n" - + "- isunset: searches the nodes with where the value of the property is not set (for example: \"isunset\" : \"cm:author\")\n" - + "- isnull: searches the nodes with where the value of the property is null (for example: \"isnull\" : \"cm:author\")\n" - + "- isnotnull: searches the nodes with where the value of the property is not null (for example: \"isnotnull\" : \"cm:author\")\n" - + "- exists: searches the nodes that have the property (for example: \"exists\" : \"cm:author\")\n" - + "\n" - + "## paging\n" - + "`Optional`\n" - + "\n" - + "Options to skip over results starting from the top of the result and to limit the total number of results.\n" - + "\n" - + "## facets\n" - + "`Optional`\n" - + "\n" - + "Options to enable, limit the total amount, minimum number of hits and customize input of facets.\n" - + "\n" - + "Note: facets with 0 hits are not returned in the result\n" - + "\n" - + "## orderBy\n" - + "`Optional`\n" - + "\n" - + "Options to select the property to order by and the direction of sorting.\n" - + "\n" - + "## consistency\n" - + "`Optional`\n" - + "\n" - + "Option to request specific consistency\n" - + "\n" - + "## locale\n" - + "`Optional`\n" - + "\n" - + "Options to request specific locale and encoding options\n" - + "\n" - + "## workspace\n" - + "`Optional`\n" - + "\n" - + "Options to change the target alfresco workspace\n" - + "\n" - + "## highlight\n" - + "`5.2 and up`\n" - + "\n" - + "`Optional`\n" - + "\n" - + "Options to change the highlight configuration.\n" - + "Minimal requirement is the `fields` array, which takes object containing at least the `field` property. Each list element specifies a property on which higlighting needs to be applied. When no fields are specified, the call defaults to `cm:content` as field.\n" - + "Full documentation can be found on the alfresco [documentation](https://docs.alfresco.com/5.2/concepts/search-api-highlight.html) page.\n" - + "\n" - + "# Examples\n" - + "\n" - + "Search for the first 10 nodes in the `cm:content` namespace:\n" - + "```json\n" - + "{\n" - + " \"query\": {\"type\":\"{http://www.alfresco.org/model/content/1.0}content\"},\n" - + " \"paging\": {\n" - + " \"limit\": 10,\n" - + " \"skip\": 0\n" - + " },\n" - + " \"facets\": {\n" - + " \"enabled\": false\n" - + " }\n" - + "}\n" - + "```\n" - + "\n" - + "Search for all nodes with the term 'budget' in the `cm:content` property (fulltext), and show two highlighted hits with delimiter \\\\:\n" - + "```json\n" - + "{\n" - + " \"query\": {\n" - + " \"property\": {\n" - + " \"exact\": false,\n" - + " \"name\": \"cm:content\",\n" - + " \"value\": \"budget\"\n" - + " }\n" - + " },\n" - + " \"highlight\":{\n" - + " \"prefix\":\"\",\n" - + " \"postfix\":\"\",\n" - + " \"snippetCount\":2,\n" - + "\t\t\"fields\":[{\"field\":\"cm:content\"}]\n" - + " }\n" - + "}\n" - + "```") - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = SearchQueryResult.class), - @ApiResponse(code = 400, message = "Failure")}) - @ApiImplicitParams({ - @ApiImplicitParam(dataType = "eu.xenit.apix.search.SearchQuery", paramType = "body", name = "body")}) - public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException { - ObjectMapper m = new SearchNodeJsonParser().getObjectMapper(); - InputStream stream = webScriptRequest.getContent().getInputStream(); - SearchQueryResult result = null; + public SearchWebScript1(ISearchService service) { + this.service = service; + } + + @AlfrescoTransaction + @PostMapping(value = "/v1/search") + public ResponseEntity execute(@RequestBody final SearchQuery query) { try { - result = service.query(m.readValue(stream, SearchQuery.class)); - writeJsonResponse(webScriptResponse, result); + return writeJsonResponse(service.query(query)); } catch (IllegalArgumentException illegalArgumentException) { - webScriptResponse.setStatus(400); - webScriptResponse.getWriter().write(illegalArgumentException.getMessage()); + return ResponseEntity.status(400).body(illegalArgumentException.getMessage()); } } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/sites/SitesWebscript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/sites/SitesWebscript1.java index dda51332..7fd3a686 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/sites/SitesWebscript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/sites/SitesWebscript1.java @@ -1,88 +1,67 @@ package eu.xenit.apix.rest.v1.sites; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Transaction; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.filefolder.IFileFolderService; import eu.xenit.apix.node.INodeService; import eu.xenit.apix.permissions.IPermissionService; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; import eu.xenit.apix.rest.v1.nodes.NodeInfo; import eu.xenit.apix.sites.ISite; import eu.xenit.apix.sites.ISiteService; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.security.AuthenticationService; -import org.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Access operations on sites", value = "Sites") -@Transaction(readOnly = false) -@Component("eu.xenit.apix.rest.v1.SitesWebscript") +@AlfrescoTransaction +@RestController public class SitesWebscript1 extends ApixV1Webscript { - private final static Logger logger = LoggerFactory.getLogger(SitesWebscript1.class); + private static final Logger logger = LoggerFactory.getLogger(SitesWebscript1.class); - @Autowired - INodeService nodeService; + private final INodeService nodeService; - @Autowired - IPermissionService permissionService; + private final IPermissionService permissionService; - @Autowired - IFileFolderService fileFolderService; + private final IFileFolderService fileFolderService; - @Autowired - ISiteService siteService; + private final ISiteService siteService; - @Autowired - ServiceRegistry serviceRegistry; + private final ServiceRegistry serviceRegistry; - @ApiOperation(value = "Retrieves information about the available sites of the current user", - notes = "Returns a list of sites. For each site the node reference, short name, title, description,\n" - + "site visibility and list of site components (document libray, links, data lists, wiki,\n" - + "discussions) are returned.\n" - + "\n" - + "There are no mandatory request parameters. However, there are optional ones:\n" - + "Set 'retrieveMetadata' to true to return the aspects and properties of the sites.\n" - + "Set 'retrievePath' to true to return the path of the sites.\n" - + "Set 'retrievePermissions' to true to return the permissions of the sites.\n" - + "Set 'retrieveChildAssocs' to true to return the child associations of the sites.\n" - + "Set 'retrieveParentAssocs' to true to return the parent associations of the sites.\n" - + "Set 'retrieveTargetAssocs' to true to return the target peer associations of the sites.\n" - + "Set 'retrieveSourceAssocs' to true to return the source peer associations of the sites.\n") - @Uri(value = "/sites/mySites", method = HttpMethod.GET) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = SiteInfo[].class)) - public void getMySites(@RequestParam(required = false, defaultValue = "false") Boolean retrieveMetadata, - @RequestParam(required = false, defaultValue = "false") boolean retrievePath, - @RequestParam(required = false, defaultValue = "false") boolean retrievePermissions, - @RequestParam(required = false, defaultValue = "false") boolean retrieveChildAssocs, - @RequestParam(required = false, defaultValue = "false") boolean retrieveParentAssocs, - @RequestParam(required = false, defaultValue = "false") boolean retrieveTargetAssocs, - @RequestParam(required = false, defaultValue = "false") boolean retrieveSourceAssocs, - WebScriptResponse response) - throws IOException { - logger.debug("retrieveMetadata: " + retrieveMetadata); - logger.debug("retrievePath: " + retrievePath); - logger.debug("retrievePermissions: " + retrievePermissions); - logger.debug("retrieveChildAssocs: " + retrieveChildAssocs); - logger.debug("retrieveParentAssocs: " + retrieveParentAssocs); - logger.debug("retrieveTargetAssocs: " + retrieveTargetAssocs); - logger.debug("retrieveSourceAssocs: " + retrieveSourceAssocs); + public SitesWebscript1(INodeService nodeService, IPermissionService permissionService, + IFileFolderService fileFolderService, ISiteService siteService, + ServiceRegistry serviceRegistry) { + this.nodeService = nodeService; + this.permissionService = permissionService; + this.fileFolderService = fileFolderService; + this.siteService = siteService; + this.serviceRegistry = serviceRegistry; + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/sites/mySites") + public ResponseEntity> getMySites( + @RequestParam(required = false, defaultValue = "false") Boolean retrieveMetadata, + @RequestParam(required = false, defaultValue = "false") boolean retrievePath, + @RequestParam(required = false, defaultValue = "false") boolean retrievePermissions, + @RequestParam(required = false, defaultValue = "false") boolean retrieveChildAssocs, + @RequestParam(required = false, defaultValue = "false") boolean retrieveParentAssocs, + @RequestParam(required = false, defaultValue = "false") boolean retrieveTargetAssocs, + @RequestParam(required = false, defaultValue = "false") boolean retrieveSourceAssocs) { + logger.debug("retrieveMetadata: {}", retrieveMetadata); + logger.debug("retrievePath: {}", retrievePath); + logger.debug("retrievePermissions: {}", retrievePermissions); + logger.debug("retrieveChildAssocs: {}", retrieveChildAssocs); + logger.debug("retrieveParentAssocs: {}", retrieveParentAssocs); + logger.debug("retrieveTargetAssocs: {}", retrieveTargetAssocs); + logger.debug("retrieveSourceAssocs: {}", retrieveSourceAssocs); AuthenticationService authService = serviceRegistry.getAuthenticationService(); List sites = siteService.getUserSites(authService.getCurrentUserName()); @@ -96,6 +75,6 @@ public void getMySites(@RequestParam(required = false, defaultValue = "false") B siteInfoList.add(siteInfo); } - writeJsonResponse(response, siteInfoList); + return writeJsonResponse(siteInfoList); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/Charsets.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/Charsets.java index a34807d0..c40c02ab 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/Charsets.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/Charsets.java @@ -20,67 +20,6 @@ import java.nio.charset.UnsupportedCharsetException; public class Charsets { - // - // This class should only contain Charset instances for required encodings. This guarantees that it will load - // correctly and without delay on all Java platforms. - // - - /** - * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1. - *

- * Every implementation of the Java platform is required to support this character encoding. - * - * @see Standard charsets - */ - public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); - /** - *

- * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. - * - *

- * Every implementation of the Java platform is required to support this character encoding. - * - * @see Standard charsets - */ - public static final Charset US_ASCII = Charset.forName("US-ASCII"); - /** - *

- * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark - * (either order accepted on input, big-endian used on output) - *

- * Every implementation of the Java platform is required to support this character encoding. - * - * @see Standard charsets - */ - public static final Charset UTF_16 = Charset.forName("UTF-16"); - /** - *

- * Sixteen-bit Unicode Transformation Format, big-endian byte order. - *

- * Every implementation of the Java platform is required to support this character encoding. - * - * @see Standard charsets - */ - public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); - /** - *

- * Sixteen-bit Unicode Transformation Format, little-endian byte order. - *

- * Every implementation of the Java platform is required to support this character encoding. - * - * @see Standard charsets - */ - public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); - /** - *

- * Eight-bit Unicode Transformation Format. - *

- * Every implementation of the Java platform is required to support this character encoding. - * - * @see Standard charsets - */ - public static final Charset UTF_8 = Charset.forName("UTF-8"); - /** * Returns the given Charset or the default Charset if the given Charset is null. * diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/LogsWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/LogsWebscript.java index 068b8f7c..cd25bb1a 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/LogsWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/LogsWebscript.java @@ -1,48 +1,39 @@ package eu.xenit.apix.rest.v1.temp; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.Api; import java.io.File; import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -/** - * Created by Michiel Huygen on 03/05/2016. - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Display logs") -@Api(hidden = true) -@Component("eu.xenit.apix.rest.v1.temp.LogsWebscript") +@RestController public class LogsWebscript extends ApixV1Webscript { - @Uri(value = "/tmp/log", defaultFormat = "text") - @Authentication(AuthenticationType.ADMIN) - public void showLog(@RequestParam(defaultValue = "200") int lines, WebScriptResponse resp) throws IOException { - ArrayList output = new ArrayList(); + private final String logPath; - try (ReversedLinesFileReader reader = new ReversedLinesFileReader( - new File("/opt/alfresco/tomcat/logs/catalina.out"))) { + public LogsWebscript(Environment env) { + logPath = env.resolvePlaceholders("$CATALINA_HOME/logs/catalina.out"); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/tmp/log", produces = { MediaType.TEXT_PLAIN_VALUE }) + @AlfrescoAuthentication(AuthenticationType.ADMIN) + public ResponseEntity showLog(@RequestParam(defaultValue = "200") int lines) throws IOException { + StringBuilder log = new StringBuilder(); + File logFile = new File(logPath); + try (ReversedLinesFileReader reader = new ReversedLinesFileReader(logFile)) { for (int i = 0; i < lines; i++) { - output.add(reader.readLine() + "\n"); + log.append(reader.readLine()) + .append("\n"); } } - - Writer writer = resp.getWriter(); - - for (int i = lines - 1; i >= 0; i--) { - writer.append(output.get(i)); - } - - + return ResponseEntity.ok(log.toString()); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/ReversedLinesFileReader.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/ReversedLinesFileReader.java index 8a51e3d2..d498a7d6 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/ReversedLinesFileReader.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/ReversedLinesFileReader.java @@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; @@ -63,10 +64,10 @@ public ReversedLinesFileReader(final File file) throws IOException { /** * Creates a ReversedLinesFileReader with the given block size and encoding. * - * @param file the file to be read + * @param file the file to be read * @param blockSize size of the internal buffer (for ideal performance this should match with the block size of the - * underlying file system). - * @param encoding the encoding of the file + * underlying file system). + * @param encoding the encoding of the file * @throws IOException if an I/O error occurs * @since 2.3 */ @@ -94,7 +95,7 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars if (maxBytesPerChar == 1f) { // all one byte encodings are no problem byteDecrement = 1; - } else if (charset == Charset.forName("UTF-8")) { + } else if (charset.equals(StandardCharsets.UTF_8)) { // UTF-8 works fine out of the box, for multibyte sequences a second UTF-8 byte can never be a newline byte // http://en.wikipedia.org/wiki/UTF-8 byteDecrement = 1; @@ -102,11 +103,11 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars // Same as for UTF-8 // http://www.herongyang.com/Unicode/JIS-Shift-JIS-Encoding.html byteDecrement = 1; - } else if (charset == Charset.forName("UTF-16BE") || charset == Charset.forName("UTF-16LE")) { + } else if (charset.equals(StandardCharsets.UTF_16BE) || charset.equals(StandardCharsets.UTF_16LE)) { // UTF-16 new line sequences are not allowed as second tuple of four byte sequences, // however byte order has to be specified byteDecrement = 2; - } else if (charset == Charset.forName("UTF-16")) { + } else if (charset.equals(StandardCharsets.UTF_16)) { throw new UnsupportedEncodingException( "For UTF-16, you need to specify the byte order (use UTF-16BE or UTF-16LE)"); } else { @@ -122,13 +123,13 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars /** * Creates a ReversedLinesFileReader with the given block size and encoding. * - * @param file the file to be read + * @param file the file to be read * @param blockSize size of the internal buffer (for ideal performance this should match with the block size of the - * underlying file system). - * @param encoding the encoding of the file - * @throws IOException if an I/O error occurs + * underlying file system). + * @param encoding the encoding of the file + * @throws IOException if an I/O error occurs * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the - * encoding is not supported. + * encoding is not supported. */ public ReversedLinesFileReader(final File file, final int blockSize, final String encoding) throws IOException { this(file, blockSize, Charsets.toCharset(encoding)); @@ -184,8 +185,8 @@ private class FilePart { /** * ctor * - * @param no the part number - * @param length its length + * @param no the part number + * @param length its length * @param leftOverOfLastFilePart remainder * @throws IOException if there is a problem reading the file */ @@ -315,7 +316,7 @@ private void createLeftOver() { * Finds the new-line sequence and return its length. * * @param data buffer to scan - * @param i start offset in buffer + * @param i start offset in buffer * @return length of newline sequence or 0 if none found */ private int getNewLineMatchByteCount(byte[] data, int i) { diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/WIPWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/WIPWebscript.java index d515a988..ca274bd8 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/WIPWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/temp/WIPWebscript.java @@ -1,42 +1,31 @@ package eu.xenit.apix.rest.v1.temp; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.WIP.IWIPService; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; -/** - * Created by Michiel Huygen on 27/05/2016. - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Work In Progress - UNSTABLE", value = "WIP") -@Component("eu.xenit.apix.rest.v1.temp.WIPWebscript") +@RestController("eu.xenit.apix.rest.v1.temp.WIPWebscript") public class WIPWebscript extends ApixV1Webscript { - @Autowired - IWIPService WipService; + private final IWIPService wipService; - @ApiOperation(value = "Downloads preview file for given node") - @Uri(value = "/nodes/{space}/{store}/{guid}/content/previews/pdf", method = HttpMethod.GET) - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - @ApiImplicitParams({@ApiImplicitParam(name = "file", paramType = "form", dataType = "file", required = true)}) - public void getPreviewPdf(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - final WebScriptRequest multiPart, WebScriptResponse response) throws IOException { + public WIPWebscript(IWIPService wipService) { + this.wipService = wipService; + } + + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/nodes/{space}/{store}/{guid}/content/previews/pdf") + public ResponseEntity getPreviewPdf(@PathVariable String space, + @PathVariable String store, + @PathVariable String guid) { final NodeRef nodeRef = new NodeRef(space, store, guid); //TODO: from /searchapp/download + return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).build(); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/translation/TranslationsWebscript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/translation/TranslationsWebscript1.java index b7906360..ee4d0228 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/translation/TranslationsWebscript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/translation/TranslationsWebscript1.java @@ -1,61 +1,45 @@ package eu.xenit.apix.rest.v1.translation; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; import eu.xenit.apix.translation.ITranslationService; import eu.xenit.apix.translation.Translations; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + import java.util.Locale; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; -/** - * Created by Stan on 30-Mar-16. - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Retrieve translations", value = "Translations") -@Component("eu.xenit.apix.rest.v1.translation.TranslationsWebscript1") +@RestController public class TranslationsWebscript1 extends ApixV1Webscript { - @Autowired - ITranslationService translationService; + private final ITranslationService translationService; + public TranslationsWebscript1(ITranslationService translationService) { + this.translationService = translationService; + } - @Uri(value = "/translations/{locale}/checksum", method = HttpMethod.GET) - @ApiOperation("Retrieve a checksum of all translations for given locale") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = TranslationChecksum.class)) - public void getChecksum(@UriVariable final String locale, WebScriptResponse response) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/translations/{locale}/checksum") + public ResponseEntity getChecksum(@PathVariable final String locale) { Locale language = Locale.forLanguageTag(locale); - Long checksum = translationService.getTranslationsCheckSum(language); - TranslationChecksum checksumObj = new TranslationChecksum(checksum); - - writeJsonResponse(response, checksumObj); + return writeJsonResponse(checksumObj); } - @Uri(value = "/translations/{locale}", method = HttpMethod.GET) - @ApiOperation("Get all available translations for given locale") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Translations.class)) - public void getTranslations(@UriVariable final String locale, WebScriptResponse response) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/translations/{locale}") + public ResponseEntity getTranslations(@PathVariable final String locale) { Locale language = Locale.forLanguageTag(locale); - Translations translations = translationService.getTranslations(language); - - writeJsonResponse(response, translations); + return writeJsonResponse(translations); } public static class TranslationChecksum { - public Long checksum; + private Long checksum; public TranslationChecksum(Long checksum) { this.checksum = checksum; @@ -69,6 +53,4 @@ public void setChecksum(Long checksum) { this.checksum = checksum; } } - - } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionHistoryWebScript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionHistoryWebScript1.java index 5b10e93f..1c3738bd 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionHistoryWebScript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionHistoryWebScript1.java @@ -1,112 +1,93 @@ package eu.xenit.apix.rest.v1.versionhistory; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.data.QName; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; import eu.xenit.apix.versionhistory.IVersionHistoryService; import eu.xenit.apix.versionhistory.Version; import eu.xenit.apix.versionhistory.VersionHistory; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.io.InputStream; import java.io.Serializable; import java.util.HashMap; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.surf.util.Content; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; - -/** - * Created by stan on 5/2/16. - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Retrieves version history information", value = "VersionHistory") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v1.versionhistory.VersionHistoryWebScript1") +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class VersionHistoryWebScript1 extends ApixV1Webscript { - private final QName PROP_VERSION_LABEL = new QName("{http://www.alfresco.org/model/content/1.0}versionLabel"); - private final QName PROP_INITIAL_VERSION = new QName("{http://www.alfresco.org/model/content/1.0}initialVersion"); - private final QName PROP_AUTO_VERSION = new QName("{http://www.alfresco.org/model/content/1.0}autoVersion"); - private final QName PROP_AUTO_VERSION_PROPS = new QName( - "{http://www.alfresco.org/model/content/1.0}autoVersionOnUpdateProps"); - private final QName PROP_VERSION_TYPE = new QName("{http://www.alfresco.org/model/content/1.0}versionType"); - - //@Autowired - //private ISearchService service; + private static final Logger logger = LoggerFactory.getLogger(VersionHistoryWebScript1.class); - Logger logger = LoggerFactory.getLogger(VersionHistoryWebScript1.class); - @Autowired - IVersionHistoryService versionHistoryService; + private static final QName PROP_INITIAL_VERSION = + new QName("{http://www.alfresco.org/model/content/1.0}initialVersion"); + private static final QName PROP_AUTO_VERSION = + new QName("{http://www.alfresco.org/model/content/1.0}autoVersion"); + private static final QName PROP_AUTO_VERSION_PROPS = + new QName( + "{http://www.alfresco.org/model/content/1.0}autoVersionOnUpdateProps"); + private final IVersionHistoryService versionHistoryService; - @Uri(value = "/versionhistory/{space}/{store}/{guid}/versions", method = HttpMethod.GET) - @ApiOperation(value = "Returns list of chronological version information for give node", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = VersionHistory.class)) - public void getVersionHistory(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, WebScriptResponse webScriptResponse) throws IOException { - logger.debug("Asked versionhistory for node with guid: " + guid); - VersionHistory vH = versionHistoryService.GetVersionHistory(createNodeRef(space, store, guid)); - writeJsonResponse(webScriptResponse, vH); + public VersionHistoryWebScript1(IVersionHistoryService versionHistoryService) { + this.versionHistoryService = versionHistoryService; } - - @Uri(value = "/versionhistory/{space}/{store}/{guid}/root", method = HttpMethod.GET) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Version.class)) - @ApiOperation(value = "Returns the root (oldest) version") - public void getVersionHistoryRoot(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, WebScriptResponse webScriptResponse) throws IOException { - Version oldest = versionHistoryService.getRootVersion(createNodeRef(space, store, guid)); - writeJsonResponse(webScriptResponse, oldest); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/versionhistory/{space}/{store}/{guid}/versions") + public ResponseEntity getVersionHistory(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid) { + logger.debug("Asked versionhistory for node with guid: {}", guid); + return writeJsonResponse( + versionHistoryService.GetVersionHistory(createNodeRef(space, store, guid)) + ); } - - @Uri(value = "/versionhistory/{space}/{store}/{guid}/head", method = HttpMethod.GET) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Version.class)) - @ApiOperation(value = "Returns the head (newest) version") - public void getVersionHistoryHead(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, WebScriptResponse webScriptResponse) throws IOException { - Version newest = versionHistoryService.getHeadVersion(createNodeRef(space, store, guid)); - writeJsonResponse(webScriptResponse, newest); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/versionhistory/{space}/{store}/{guid}/root") + public ResponseEntity getVersionHistoryRoot(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid) { + return writeJsonResponse( + versionHistoryService.getRootVersion(createNodeRef(space, store, guid)) + ); + } + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/versionhistory/{space}/{store}/{guid}/head") + public ResponseEntity getVersionHistoryHead(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid) { + return writeJsonResponse( + versionHistoryService.getHeadVersion(createNodeRef(space, store, guid)) + ); } - @Uri(value = "/versionhistory/{space}/{store}/{guid}", method = HttpMethod.DELETE) - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - //No method available to disable versioning. deleting will merely reset version history, starting a new history upon a new version change - @ApiOperation(value = "Permanently emoves version history") - public void deleteVersionHistory(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, WebScriptResponse webScriptResponse) throws IOException { + @AlfrescoTransaction + @DeleteMapping(value = "/v1/versionhistory/{space}/{store}/{guid}") + //No method available to disable versioning. deleting will merely reset version history, + // starting a new history upon a new version change + public ResponseEntity deleteVersionHistory(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid) { versionHistoryService.deleteVersionHistory(createNodeRef(space, store, guid)); + return ResponseEntity.ok().build(); } - @Uri(value = "/versionhistory/{space}/{store}/{guid}", method = HttpMethod.PUT) - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - @ApiImplicitParams({ - @ApiImplicitParam(dataType = "eu.xenit.apix.rest.v1.versionhistory.VersionOptions", paramType = "body", name = "body")}) - @ApiOperation(value = "Enables versioning for this node, creating an initial version") - public void setVersionHistory(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) - throws IOException { + @AlfrescoTransaction + @PutMapping(value = "/v1/versionhistory/{space}/{store}/{guid}") + public ResponseEntity setVersionHistory(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid, + @RequestBody(required = false) final VersionOptions versionOptions) { HashMap versionProperties = new HashMap<>(); - - Content requestContent = webScriptRequest.getContent(); - InputStream requestInputStream = requestContent.getInputStream(); - - try { - ObjectMapper objectMapper = new ObjectMapper(); - VersionOptions versionOptions = objectMapper.readValue(requestInputStream, VersionOptions.class); + if(versionOptions != null) { if (versionOptions.getAutoVersion() != null) { versionProperties.put(PROP_AUTO_VERSION, versionOptions.getAutoVersion()); } @@ -116,35 +97,28 @@ public void setVersionHistory(@UriVariable final String space, @UriVariable fina if (versionOptions.getInitialVersion() != null) { versionProperties.put(PROP_INITIAL_VERSION, versionOptions.getInitialVersion()); } - } catch (JsonMappingException ex) { - boolean isFirstChar = ex.getLocation().getLineNr() == 1 && ex.getLocation().getColumnNr() == 0; - boolean isAtStart = ex.getLocation().getByteOffset() == 0 || ex.getLocation().getCharOffset() == 0; - isAtStart |= ex.getLocation().getByteOffset() == -1 && isFirstChar; - if (!isAtStart) { - throw ex; - } - // Else, this is an exception because there is no body passed to the request } - versionHistoryService.ensureVersioningEnabled(createNodeRef(space, store, guid), versionProperties); + return ResponseEntity.ok().build(); } - @Uri(value = "/versionhistory/{space}/{store}/{guid}/versions/{label}/revert", method = HttpMethod.POST) - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - @ApiOperation(value = "(Shallow) Revert the node to version with given label") - public void revertVersionHistory(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, @UriVariable final String label, WebScriptResponse webScriptResponse) - throws IOException { + @AlfrescoTransaction + @PostMapping(value = "/v1/versionhistory/{space}/{store}/{guid}/versions/{label}/revert") + public ResponseEntity revertVersionHistory(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid, + @PathVariable final String label) { versionHistoryService.revert(createNodeRef(space, store, guid), label); + return ResponseEntity.ok().build(); } - @Uri(value = "/versionhistory/{space}/{store}/{guid}/versions/{label}", method = HttpMethod.DELETE) - @ApiResponses(@ApiResponse(code = 200, message = "Success")) - @ApiOperation(value = "Permanently remove version with given label") - public void deleteVersion(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, @UriVariable final String label, WebScriptResponse webScriptResponse) - throws IOException { + @AlfrescoTransaction + @DeleteMapping(value = "/v1/versionhistory/{space}/{store}/{guid}/versions/{label}") + public ResponseEntity deleteVersion(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid, + @PathVariable final String label) { versionHistoryService.deleteVersion(createNodeRef(space, store, guid), label); + return ResponseEntity.ok().build(); } - } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionOptions.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionOptions.java index cae4256b..285f1114 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionOptions.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/versionhistory/VersionOptions.java @@ -6,6 +6,15 @@ public class VersionOptions { private Boolean autoVersion; private Boolean autoVersionOnUpdateProps; + public VersionOptions() { + } + + public VersionOptions(Boolean initialVersion, Boolean autoVersion, Boolean autoVersionOnUpdateProps) { + this.initialVersion = initialVersion; + this.autoVersion = autoVersion; + this.autoVersionOnUpdateProps = autoVersionOnUpdateProps; + } + public Boolean getInitialVersion() { return initialVersion; } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckinBody.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckinBody.java index 22a493f4..af53d8e9 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckinBody.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckinBody.java @@ -13,11 +13,14 @@ class CheckinBody { @JsonCreator public CheckinBody(@JsonProperty("comment") String comment, - @JsonProperty("majorVersion") boolean majorVersion) { + @JsonProperty("majorVersion") boolean majorVersion) { this.comment = comment; this.majorVersion = majorVersion; } + public CheckinBody() { + } + public String getComment() { return comment; } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckoutBody.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckoutBody.java index d764c641..aaef1c6a 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckoutBody.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/CheckoutBody.java @@ -3,22 +3,21 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import eu.xenit.apix.data.NodeRef; -import io.swagger.annotations.ApiModelProperty; /** * Created by Michiel Huygen on 12/05/2016. */ class CheckoutBody { - @ApiModelProperty(required = true) public NodeRef original; - @ApiModelProperty("Optional, if not specified uses the original node's folder. " + - "If current user does not have permissions for this folder, the working copy is created in the user's home folder") public NodeRef destinationFolder; + public CheckoutBody() { + } + @JsonCreator public CheckoutBody(@JsonProperty("original") NodeRef original, - @JsonProperty("destinationFolder") NodeRef destinationFolder) { + @JsonProperty("destinationFolder") NodeRef destinationFolder) { this.original = original; this.destinationFolder = destinationFolder; } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/WorkingcopiesWebscript1.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/WorkingcopiesWebscript1.java index d25bf3c4..31ab6b5b 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/WorkingcopiesWebscript1.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v1/workingcopies/WorkingcopiesWebscript1.java @@ -1,125 +1,84 @@ package eu.xenit.apix.rest.v1.workingcopies; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.data.NodeRef; -import eu.xenit.apix.filefolder.IFileFolderService; import eu.xenit.apix.node.INodeService; -import eu.xenit.apix.permissions.IPermissionService; import eu.xenit.apix.rest.v1.ApixV1Webscript; -import eu.xenit.apix.rest.v1.RestV1Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import org.alfresco.service.ServiceRegistry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; -/** - * Created by Michiel Huygen on 09/03/2016. - */ -@WebScript(baseUri = RestV1Config.BaseUrl, families = RestV1Config.Family, defaultFormat = "json", - description = "Access operations on working copies", value = "Workingcopies") -@Component("eu.xenit.apix.rest.v1.WorkingcopiesWebscript1") +@RestController public class WorkingcopiesWebscript1 extends ApixV1Webscript { - private final static Logger logger = LoggerFactory.getLogger(WorkingcopiesWebscript1.class); + private final INodeService nodeService; - @Autowired - INodeService nodeService; - - @Autowired - IPermissionService permissionService; - - @Autowired - IFileFolderService fileFolderService; - - @Autowired - ServiceRegistry serviceRegistry; + public WorkingcopiesWebscript1(INodeService nodeService) { + this.nodeService = nodeService; + } - @Uri(value = "/workingcopies", method = HttpMethod.POST) - @ApiOperation("Checks out a new working copy for given node") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = NoderefResult.class)) - public void createWorkingcopy(CheckoutBody checkoutBody, WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @PostMapping(value = "/v1/workingcopies") + public ResponseEntity createWorkingcopy(@RequestBody CheckoutBody checkoutBody) { final NodeRef originalRef = checkoutBody.getOriginal(); NodeRef destinationRef = checkoutBody.getDestinationFolder(); + if (!nodeService.exists(originalRef)) { + return respondDoesNotExist(originalRef); + } - if (nodeService.exists(originalRef)) { - // if a destinationRef was specified, it must exist, but nodeservice.checkout(..., null) works fine. - if (destinationRef == null || nodeService.exists(destinationRef)) { - NodeRef workingCopyRef = nodeService.checkout(originalRef, destinationRef); - writeJsonResponse(response, new NoderefResult(workingCopyRef)); - } else { - response.setStatus(404); - response.getWriter().write(String.format("Destination noderef %s does not exist", destinationRef)); - } - } else { - response.setStatus(404); - response.getWriter().write(String.format("Original noderef %s does not exist.", originalRef)); + // if a destinationRef was specified, it must exist, but nodeservice.checkout(..., null) works fine. + if (destinationRef == null || nodeService.exists(destinationRef)) { + NodeRef workingCopyRef = nodeService.checkout(originalRef, destinationRef); + return writeJsonResponse(new NoderefResult(workingCopyRef)); } + + return respondDoesNotExist(destinationRef); } - @ApiOperation(value = "Checks in given working copy and removes it", notes = "Returns the noderef of the original node") - @Uri(value = "/workingcopies/{space}/{store}/{guid}/checkin", method = HttpMethod.POST) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = NoderefResult.class), - @ApiResponse(code = 404, message = "Not found") - }) - public void checkinWorkingcopy(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, - final CheckinBody checkinBody, WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @PostMapping(value = "/v1/workingcopies/{space}/{store}/{guid}/checkin") + public ResponseEntity checkinWorkingcopy(@PathVariable final String space, @PathVariable final String store, + @PathVariable final String guid, + @RequestBody final CheckinBody checkinBody) { final NodeRef nodeRef = createNodeRef(space, store, guid); if (nodeService.exists(nodeRef)) { NodeRef originalRef = nodeService.checkin(nodeRef, checkinBody.getComment(), checkinBody.getMajorVersion()); - writeJsonResponse(response, new NoderefResult(originalRef)); - } else { - response.setStatus(404); - response.getWriter().write(String.format("Noderef %s does not exist.", nodeRef)); + return writeJsonResponse(new NoderefResult(originalRef)); } + return respondDoesNotExist(nodeRef); } - @ApiOperation(value = "Cancels and removes a working copy", notes = "Returns the noderef of the original node") - @Uri(value = "/workingcopies/{space}/{store}/{guid}", method = HttpMethod.DELETE) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = NoderefResult.class), - @ApiResponse(code = 404, message = "Not found") - }) - public void cancelWorkingcopy(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, - WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @DeleteMapping(value = "/v1/workingcopies/{space}/{store}/{guid}") + public ResponseEntity cancelWorkingcopy(@PathVariable final String space, @PathVariable final String store, + @PathVariable final String guid) { final NodeRef workingCopyRef = createNodeRef(space, store, guid); if (nodeService.exists(workingCopyRef)) { NodeRef originalRef = nodeService.cancelCheckout(workingCopyRef); - writeJsonResponse(response, new NoderefResult(originalRef)); - } else { - response.setStatus(404); - response.getWriter().write(String.format("Noderef %s does not exist.", workingCopyRef)); + return writeJsonResponse(new NoderefResult(originalRef)); } + return respondDoesNotExist(workingCopyRef); } - - @ApiOperation("Returns the original node for given working copy") - @Uri(value = "/workingcopies/{space}/{store}/{guid}/original", method = HttpMethod.GET) - @ApiResponses({ - @ApiResponse(code = 200, message = "Success", response = NoderefResult.class), - @ApiResponse(code = 404, message = "Not Found") - }) - public void getWorkingCopySource(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, - WebScriptResponse response) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v1/workingcopies/{space}/{store}/{guid}/original") + public ResponseEntity getWorkingCopySource(@PathVariable final String space, @PathVariable final String store, + @PathVariable final String guid) { NodeRef workingCopyRef = createNodeRef(space, store, guid); if (nodeService.exists(workingCopyRef)) { NodeRef originalRef = nodeService.getWorkingCopySource(workingCopyRef); - writeJsonResponse(response, new NoderefResult(originalRef)); - } else { - response.setStatus(404); - response.getWriter().write(String.format("Noderef %s does not exist.", workingCopyRef)); + return writeJsonResponse(new NoderefResult(originalRef)); } + + return respondDoesNotExist(workingCopyRef); } + private ResponseEntity respondDoesNotExist(NodeRef nodeRef) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(String.format(String.format("%s does not exist.", nodeRef))); + } } \ No newline at end of file diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/ApixV2Webscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/ApixV2Webscript.java index 73089f8f..daabacd5 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/ApixV2Webscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/ApixV2Webscript.java @@ -2,112 +2,116 @@ import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.filefolder.IFileFolderService; -import eu.xenit.apix.node.*; +import eu.xenit.apix.filefolder.NodePath; +import eu.xenit.apix.node.ChildParentAssociation; +import eu.xenit.apix.node.INodeService; +import eu.xenit.apix.node.NodeAssociation; +import eu.xenit.apix.node.NodeAssociations; +import eu.xenit.apix.node.NodeMetadata; import eu.xenit.apix.permissions.IPermissionService; import eu.xenit.apix.permissions.PermissionValue; import eu.xenit.apix.rest.v1.ApixV1Webscript; import eu.xenit.apix.rest.v1.nodes.NodeInfo; +import eu.xenit.apix.rest.v1.nodes.NodeInfoRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * Created by jasper on 16/02/17. */ public class ApixV2Webscript extends ApixV1Webscript { - private final static Logger logger = LoggerFactory.getLogger(ApixV2Webscript.class); + private static final Logger logger = LoggerFactory.getLogger(ApixV2Webscript.class); + + protected List nodeRefToNodeInfo(NodeInfoRequest nodeInfoRequest, IFileFolderService fileFolderService, + INodeService nodeService, IPermissionService permissionService) { + List nodeRefs = nodeInfoRequest.getNoderefs().stream().map(NodeRef::new).collect(Collectors.toList()); + + return nodeRefs.stream().filter(nodeRef -> { + boolean hasPermission = permissionService.hasPermission(nodeRef, IPermissionService.READ); + if (!hasPermission) { + logger.warn("Excluding node {} from results due to insufficient permissions", nodeRef); + } + return hasPermission; + + }) + .filter(nodeRef -> { + boolean exists = nodeService.exists(nodeRef); + if (!exists) { + logger.debug("Excluding node {} from results because it does not exist", nodeRef); + } + return exists; + + }).map(nodeRef -> { + logger.debug("######################################################"); + logger.debug("start new NodeInfo"); + NodeInfo nodeInfo = new NodeInfo(nodeRef, + getNodeMetadata(nodeInfoRequest, nodeService, nodeRef), + getNodePermissions(nodeInfoRequest, permissionService, nodeRef), + getNodeAssociations(nodeInfoRequest, nodeService, nodeRef) + , getNodePath(nodeInfoRequest, fileFolderService, nodeRef)); + logger.debug("end new NodeInfo"); + return nodeInfo; + }).collect(Collectors.toList()); + } - protected List nodeRefToNodeInfo(List nodeRefs, IFileFolderService fileFolderService, - INodeService nodeService, IPermissionService permissionService) { - List nodeInfoList = new ArrayList(); - for (NodeRef nodeRef : nodeRefs) { - eu.xenit.apix.filefolder.NodePath path = fileFolderService.getPath(nodeRef); - NodeMetadata nodeMetadata = nodeService.getMetadata(nodeRef); - Map permissions = permissionService.getPermissionsFast(nodeRef); - NodeAssociations associations = nodeService.getAssociations(nodeRef); - NodeInfo nodeInfo = new NodeInfo(nodeRef, nodeMetadata, permissions, associations, path); - nodeInfoList.add(nodeInfo); + private Map getNodePermissions(NodeInfoRequest nodeInfoRequest, IPermissionService permissionService, NodeRef nodeRef) { + logger.debug("start getPermissions"); + Map permissions = null; + if (nodeInfoRequest.getRetrievePermissions()) { + permissions = permissionService.getPermissionsFast(nodeRef); } - - return nodeInfoList; + logger.debug("end getPermissions"); + return permissions; } - protected List nodeRefToNodeInfo(List nodeRefs, IFileFolderService fileFolderService, - INodeService nodeService, IPermissionService permissionService, - boolean retrievePath, boolean retrieveMetadata, - boolean retrievePermissions, boolean retrieveAssocs, - boolean retrieveChildAssocs, boolean retrieveParentAssocs, - boolean retrieveTargetAssocs, boolean retrieveSourceAssocs) { - List nodeInfoList = new ArrayList(); - for (NodeRef nodeRef : nodeRefs) { - - if (!permissionService.hasPermission(nodeRef, IPermissionService.READ)) { - logger.warn("Excluding node {} from results due to insufficient permissions", nodeRef); - continue; - } - - if (!nodeService.exists(nodeRef)) { - logger.debug("Excluding node {} from results because it does not exist", nodeRef); - continue; - } + private NodeMetadata getNodeMetadata(NodeInfoRequest nodeInfoRequest, INodeService nodeService, NodeRef nodeRef) { + logger.debug("start getMetadata"); + NodeMetadata nodeMetadata = null; + if (nodeInfoRequest.getRetrieveMetadata()) { + nodeMetadata = nodeService.getMetadata(nodeRef); + } + logger.debug("end getMetadata"); + return nodeMetadata; + } - logger.debug("######################################################"); + private NodePath getNodePath(NodeInfoRequest nodeInfoRequest, IFileFolderService fileFolderService, NodeRef nodeRef) { + logger.debug("start getPath"); + NodePath path = null; + if (nodeInfoRequest.getRetrievePath()) { + path = fileFolderService.getPath(nodeRef); + } + logger.debug("end getPath"); + return path; + } - logger.debug("start getPath"); - eu.xenit.apix.filefolder.NodePath path = null; - if (retrievePath) { - path = fileFolderService.getPath(nodeRef); + private NodeAssociations getNodeAssociations(NodeInfoRequest nodeInfoRequest, INodeService nodeService, NodeRef nodeRef) { + logger.debug("start getAssociations"); + NodeAssociations associations = null; + if (nodeInfoRequest.getRetrieveAssocs()) { + List childAssocs = null; + if (nodeInfoRequest.getRetrieveChildAssocs()) { + childAssocs = nodeService.getChildAssociations(nodeRef); } - logger.debug("end getPath"); - - logger.debug("start getMetadata"); - NodeMetadata nodeMetadata = null; - if (retrieveMetadata) { - nodeMetadata = nodeService.getMetadata(nodeRef); + List parentAssociations = null; + if (nodeInfoRequest.getRetrieveParentAssocs()) { + parentAssociations = nodeService.getParentAssociations(nodeRef); } - logger.debug("end getMetadata"); - - logger.debug("start getPermissions"); - Map permissions = null; - if (retrievePermissions) { - permissions = permissionService.getPermissionsFast(nodeRef); + List targetAssociations = null; + if (nodeInfoRequest.getRetrieveTargetAssocs()) { + targetAssociations = nodeService.getTargetAssociations(nodeRef); } - logger.debug("end getPermissions"); - - logger.debug("start getAssociations"); - NodeAssociations associations = null; - if (retrieveAssocs) { - List childAssocs = null; - if (retrieveChildAssocs) { - childAssocs = nodeService.getChildAssociations(nodeRef); - } - List parentAssociations = null; - if (retrieveParentAssocs) { - parentAssociations = nodeService.getParentAssociations(nodeRef); - } - List targetAssociations = null; - if (retrieveTargetAssocs) { - targetAssociations = nodeService.getTargetAssociations(nodeRef); - } - List sourceAssociations = null; - if (retrieveSourceAssocs) { - targetAssociations = nodeService.getSourceAssociations(nodeRef); - } - associations = new NodeAssociations(childAssocs, parentAssociations, targetAssociations, sourceAssociations); + List sourceAssociations = null; + if (nodeInfoRequest.getRetrieveSourceAssocs()) { + sourceAssociations = nodeService.getSourceAssociations(nodeRef); } - logger.debug("end getAssociations"); - - logger.debug("start new NodeInfo"); - NodeInfo nodeInfo = new NodeInfo(nodeRef, nodeMetadata, permissions, associations, path); - logger.debug("end new NodeInfo"); - - nodeInfoList.add(nodeInfo); + associations = new NodeAssociations(childAssocs, parentAssociations, targetAssociations, sourceAssociations); } - - return nodeInfoList; + logger.debug("end getAssociations"); + return associations; } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/GroupsWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/GroupsWebscript.java index e4a08f29..2cc6d795 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/GroupsWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/GroupsWebscript.java @@ -1,90 +1,78 @@ package eu.xenit.apix.rest.v2.groups; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.groups.Group; import eu.xenit.apix.people.IPeopleService; import eu.xenit.apix.people.Person; import eu.xenit.apix.rest.v2.ApixV2Webscript; -import eu.xenit.apix.rest.v2.RestV2Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; - -/** - * Created by jasper on 14/03/17. - */ - -@WebScript(baseUri = RestV2Config.BaseUrl, families = RestV2Config.Family, defaultFormat = "json", - description = "Retrieves group information and links users/groups to parent groups", value = "Groups") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v2.groups.GroupsWebscript") +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + + +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class GroupsWebscript extends ApixV2Webscript { - Logger logger = LoggerFactory.getLogger(GroupsWebscript.class); - @Autowired - IPeopleService personService; + private static final Logger logger = LoggerFactory.getLogger(GroupsWebscript.class); + private final IPeopleService personService; - @Uri(value = "/groups", method = HttpMethod.GET) - @ApiOperation(value = "Returns a list containing all groups", notes = "") - @ApiResponses(@ApiResponse(code = HttpStatus.SC_OK, message = "Success", response = Group[].class)) - public void GetAllGroups(WebScriptResponse webScriptResponse) throws IOException { - writeJsonResponse(webScriptResponse, personService.GetGroups()); + public GroupsWebscript(IPeopleService personService) { + this.personService = personService; } - @Uri(value = "/groups/{name}/people", method = HttpMethod.GET) - @ApiOperation(value = "Returns the persons within a specific group", notes = "") - @ApiResponses(@ApiResponse(code = HttpStatus.SC_OK, message = "Success", response = Person[].class)) - public void GetPeopleOfGroup(@UriVariable final String name, @RequestParam(required = false) Boolean immediate, - WebScriptResponse webScriptResponse) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/groups") + public ResponseEntity> GetAllGroups() { + return writeJsonResponse(personService.GetGroups()); + } + + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/groups/{name}/people") + public ResponseEntity GetPeopleOfGroup(@PathVariable final String name, + @RequestParam(required = false) Boolean immediate) { if (immediate == null) { immediate = false; } List people = personService.GetUsersOfGroup(name, immediate); if (people == null) { - giveNoGroup404(webScriptResponse, name); - return; + return giveNoGroup404(name); } - writeJsonResponse(webScriptResponse, people); + return ResponseEntity.ok(people); } - @Uri(value = "/groups/{name}/groups", method = HttpMethod.GET) - @ApiOperation(value = "Returns the groups within a specific group", notes = "") - @ApiResponses(@ApiResponse(code = HttpStatus.SC_OK, message = "Success", response = Group[].class)) - public void GetGroupsOfGroup(@UriVariable final String name, @RequestParam(required = false) Boolean immediate, - WebScriptResponse webScriptResponse) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/groups/{name}/groups") + public ResponseEntity GetGroupsOfGroup(@PathVariable final String name, + @RequestParam(required = false) Boolean immediate) { if (immediate == null) { immediate = false; } List groups = personService.GetSubgroupsInGroup(name, immediate); if (groups == null) { - giveNoGroup404(webScriptResponse, name); - return; + return giveNoGroup404(name); } - writeJsonResponse(webScriptResponse, groups); + return writeJsonResponse(groups); } - @Uri(value = "/groups/{name}/people", method = HttpMethod.PUT) - @ApiOperation(value = "Sets the complete list of people as direct members of this group", notes = "") - @ApiResponses(@ApiResponse(code = HttpStatus.SC_OK, message = "Success", response = Group[].class)) - public void SetPeopleInGroup(@UriVariable final String name, SetUsersInGroupOptions options, - WebScriptResponse webScriptResponse) throws IOException { + @AlfrescoTransaction + @PutMapping(value = "/v2/groups/{name}/people") + public ResponseEntity SetPeopleInGroup(@PathVariable final String name, + @RequestBody SetUsersInGroupOptions options) { // We want to replace all of the users in group {name} by a new list of users // We're going to avoid unlinking and re-linking the same user, because iterating over the list to check for // duplicates is going to be cheaper than unnecessarily invoking all of Alfresco's internal safety checking @@ -93,8 +81,7 @@ public void SetPeopleInGroup(@UriVariable final String name, SetUsersInGroupOpti logger.debug("Setting new list of users for {}", name); // error handling, if {name} isn't a group if (linkedUsers == null) { - giveNoGroup404(webScriptResponse, name); - return; + return giveNoGroup404(name); } List oldUsers = new ArrayList<>(); @@ -104,14 +91,13 @@ public void SetPeopleInGroup(@UriVariable final String name, SetUsersInGroupOpti List newUsers = Arrays.asList(options.getUsers()); replaceAuthorities(name, oldUsers, newUsers); + return ResponseEntity.ok().build(); } - - @Uri(value = "/groups/{name}/groups", method = HttpMethod.PUT) - @ApiOperation(value = "Sets the complete list of direct subgroups for this group", notes = "") - @ApiResponses(@ApiResponse(code = HttpStatus.SC_OK, message = "Success", response = Group[].class)) - public void SetGroupsOfGroup(@UriVariable final String name, SetSubgroupOptions options, - WebScriptResponse webScriptResponse) throws IOException { + @AlfrescoTransaction + @PutMapping(value = "/v2/groups/{name}/groups") + public ResponseEntity SetGroupsOfGroup(@PathVariable final String name, + @RequestBody SetSubgroupOptions options) { // We want to replace all of the subgroups of {name} by a new list of subgroups // We're going to avoid unlinking and re-linking the same group, because iterating over the list to check for // duplicates is going to be cheaper than unnecessarily invoking all of Alfresco's internal safety checking @@ -120,8 +106,7 @@ public void SetGroupsOfGroup(@UriVariable final String name, SetSubgroupOptions logger.debug("Setting new list of subgroups for {}", name); // error handling, if {name} isn't a group if (linkedGroups == null) { - giveNoGroup404(webScriptResponse, name); - return; + return giveNoGroup404(name); } List oldGroups = new ArrayList<>(); @@ -131,11 +116,12 @@ public void SetGroupsOfGroup(@UriVariable final String name, SetSubgroupOptions List newGroups = Arrays.asList(options.getSubgroups()); replaceAuthorities(name, oldGroups, newGroups); + return ResponseEntity.ok().build(); } - private void giveNoGroup404(WebScriptResponse response, String name) throws IOException { - response.setStatus(HttpStatus.SC_NOT_FOUND); // 404 - response.getWriter().write("Group " + name + " does not exist"); + private ResponseEntity giveNoGroup404(String name) { + return ResponseEntity.status(HttpStatus.SC_NOT_FOUND) + .body("Group " + name + " does not exist"); } private void replaceAuthorities(String parentGroup, List oldOnes, List newOnes) { diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetSubgroupOptions.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetSubgroupOptions.java index bb2a2af6..891ced1f 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetSubgroupOptions.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetSubgroupOptions.java @@ -2,11 +2,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModelProperty; class SetSubgroupOptions { - @ApiModelProperty(required = true) private String[] subgroups; @JsonCreator @@ -14,8 +12,10 @@ public SetSubgroupOptions(@JsonProperty("subgroups") String[] subgroups) { this.subgroups = subgroups; } + public SetSubgroupOptions() { + } + public String[] getSubgroups() { return subgroups; } - } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetUsersInGroupOptions.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetUsersInGroupOptions.java index 1f7c700a..25be3c3b 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetUsersInGroupOptions.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/groups/SetUsersInGroupOptions.java @@ -2,13 +2,14 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModelProperty; class SetUsersInGroupOptions { - @ApiModelProperty(required = true) private String[] users; + public SetUsersInGroupOptions() { + } + @JsonCreator public SetUsersInGroupOptions(@JsonProperty("users") String[] users) { this.users = users; diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/nodes/NodesWebscriptV2.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/nodes/NodesWebscriptV2.java index bbfda9fa..9f0effbe 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/nodes/NodesWebscriptV2.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/nodes/NodesWebscriptV2.java @@ -1,10 +1,7 @@ package eu.xenit.apix.rest.v2.nodes; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Transaction; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.data.QName; import eu.xenit.apix.filefolder.IFileFolderService; @@ -14,265 +11,142 @@ import eu.xenit.apix.permissions.PermissionValue; import eu.xenit.apix.rest.v1.nodes.CreateNodeOptions; import eu.xenit.apix.rest.v1.nodes.NodeInfo; +import eu.xenit.apix.rest.v1.nodes.NodeInfoRequest; import eu.xenit.apix.rest.v2.ApixV2Webscript; -import eu.xenit.apix.rest.v2.RestV2Config; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; import org.apache.http.HttpStatus; -import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; -@WebScript(baseUri = RestV2Config.BaseUrl, families = RestV2Config.Family, defaultFormat = "json", - description = "Access operations on nodes", value = "Nodes") -@Transaction( - readOnly = false -) -@Component("eu.xenit.apix.rest.v2.NodesWebscript") +@AlfrescoAuthentication +@RestController public class NodesWebscriptV2 extends ApixV2Webscript { - private final static Logger logger = LoggerFactory.getLogger(NodesWebscriptV2.class); + private static final Logger logger = LoggerFactory.getLogger(NodesWebscriptV2.class); + + private final INodeService nodeService; - @Autowired - INodeService nodeService; + private final IPermissionService permissionService; - @Autowired - IPermissionService permissionService; + private final IFileFolderService fileFolderService; - @Autowired - IFileFolderService fileFolderService; + private final ServiceRegistry serviceRegistry; - @Autowired - ServiceRegistry serviceRegistry; + public NodesWebscriptV2(INodeService nodeService, IPermissionService permissionService, + IFileFolderService fileFolderService, ServiceRegistry serviceRegistry) { + this.nodeService = nodeService; + this.permissionService = permissionService; + this.fileFolderService = fileFolderService; + this.serviceRegistry = serviceRegistry; + } - @ApiOperation("Returns combined information of a node.\nNote: versionstore does not support sourceAssocs. For version nodes, an empty list added to the result") - @Uri(value = "/nodes/{space}/{store}/{guid}", method = HttpMethod.GET) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = NodeInfo.class)) - public void getAllInfo(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/nodes/{space}/{store}/{guid}") + public ResponseEntity getAllInfo(@PathVariable String space, + @PathVariable String store, + @PathVariable String guid) { NodeRef nodeRef = this.createNodeRef(space, store, guid); NodeInfo nodeInfo = this .nodeRefToNodeInfo(nodeRef, this.fileFolderService, this.nodeService, this.permissionService); - writeJsonResponse(response, nodeInfo); + return writeJsonResponse(nodeInfo); } - @ApiOperation(value = "Returns combined information of multiple nodes", - notes = "Example to get combined information of multiple nodes:\n" + - "\n" + - "```\n" + - "POST /apix/v1/nodes/nodeInfo\n" + - "{\n" + - "\"retrieveMetadata\" : true, \n" + - "\"retrievePath\" : true, \n" + - "\"retrievePermissions\" : true, \n" + - "\"retrieveAssocs\" : true, \n" + - "\"retrieveChildAssocs\" : true, \n" + - "\"retrieveParentAssocs\" : true, \n" + - "\"retrieveTargetAssocs\" : true, \n" + - "\"noderefs\": [ \n" + - " \"workspace://SpacesStore/123456789\", \n" + - " \"workspace://SpacesStore/147258369\", \n" + - " \"workspace://SpacesStore/478159236\" \n" + - "]}\n" + - "```" + - "\n" + - "'retrieveMetadata', 'retrievePath', 'retrievePermissions', 'retrieveAssocs', 'retrieveChildAssocs',\n" - + - "'retrieveParentAssocs', 'retrieveTargetAssocs' are optional parameters.\n" + - "Set 'retrieveMetadata' to false to omit the aspects and properties from the result.\n" + - "Set 'retrievePath' to false to omit the path from the result.\n" + - "Set 'retrievePermissions' to false to omit the permissions from the result.\n" + - "Set 'retrieveAssocs' to false to omit the associations (parent associations, child associations, peer associations) from the result.\n" - + - "Set 'retrieveChildAssocs' to false to omit the child associations from the result.\n" + - "Set 'retrieveParentAssocs' to false to omit the parent associations from the result.\n" + - "Set 'retrieveTargetAssocs' to false to omit the peer target associations from the result.\n" + - "Set 'retrieveSourceAssocs' to false to omit the peer source associations from the result. Note: versionstore does not support sourceAssocs. For version nodes, an empty list added to the result\n") - @Uri(value = "/nodes/nodeInfo", method = HttpMethod.POST) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = NodeInfo[].class)) - public void getAllInfos(WebScriptRequest request, WebScriptResponse response) throws IOException, JSONException { - logger.debug("entered getAllInfo method"); - String requestString = request.getContent().getContent(); - logger.debug("request content: " + requestString); - JSONObject jsonObject = new JSONObject(requestString); - if (jsonObject == null) { - response.setStatus(400); - String message = String - .format("Malfromed body: request string could not be parsed to jsonObject: %s", requestString); - logger.debug(message); - writeJsonResponse(response, message); - } - logger.debug("json: " + jsonObject.toString()); - - boolean retrieveMetadata = true; - boolean retrievePath = true; - boolean retrievePermissions = true; - boolean retrieveAssocs = true; - boolean retrieveChildAssocs = true; - boolean retrieveParentAssocs = true; - boolean retrieveTargetAssocs = true; - boolean retrieveSourceAssocs = true; - - List nodeRefs = new ArrayList(); - try { - if (jsonObject.has("retrieveMetadata")) { - retrieveMetadata = jsonObject.getBoolean("retrieveMetadata"); - } - if (jsonObject.has("retrievePath")) { - retrievePath = jsonObject.getBoolean("retrievePath"); - } - if (jsonObject.has("retrievePermissions")) { - retrievePermissions = jsonObject.getBoolean("retrievePermissions"); - } - if (jsonObject.has("retrieveAssocs")) { - retrieveAssocs = jsonObject.getBoolean("retrieveAssocs"); - } - if (jsonObject.has("retrieveChildAssocs")) { - retrieveChildAssocs = jsonObject.getBoolean("retrieveChildAssocs"); - } - if (jsonObject.has("retrieveParentAssocs")) { - retrieveParentAssocs = jsonObject.getBoolean("retrieveParentAssocs"); - } - if (jsonObject.has("retrieveTargetAssocs")) { - retrieveTargetAssocs = jsonObject.getBoolean("retrieveTargetAssocs"); - } - if (jsonObject.has("retrieveSourceAssocs")) { - retrieveSourceAssocs = jsonObject.getBoolean("retrieveSourceAssocs"); - } - - JSONArray nodeRefsJsonArray = jsonObject.getJSONArray("noderefs"); - if (nodeRefsJsonArray == null) { - response.setStatus(400); - String message = String.format("Could not retrieve target noderefs from body: %s", jsonObject); - logger.debug(message); - writeJsonResponse(response, message); - } - int nodeRefsJsonArrayLength = nodeRefsJsonArray.length(); - logger.debug("nodeRefsJsonArrayLength: " + nodeRefsJsonArrayLength); - for (int i = 0; i < nodeRefsJsonArrayLength; i++) { - String nodeRefString = (String) nodeRefsJsonArray.get(i); - logger.debug("nodeRefString: " + nodeRefString); - NodeRef nodeRef = new NodeRef(nodeRefString); - nodeRefs.add(nodeRef); - } - } catch (JSONException e) { - logger.error("Error deserializing json body", e); - String message = String.format("Malformed json body {}", jsonObject); - response.setStatus(400); - writeJsonResponse(response, message); - } - - logger.debug("done parsing request data"); - logger.debug("start nodeRefToNodeInfo"); + @AlfrescoTransaction + @PostMapping(value = "/v2/nodes/nodeInfo") + public ResponseEntity> getAllInfos(@RequestBody final NodeInfoRequest nodeInfoRequest) throws JSONException { List nodeInfoList = this.nodeRefToNodeInfo( - nodeRefs, + nodeInfoRequest, this.fileFolderService, this.nodeService, - this.permissionService, - retrievePath, - retrieveMetadata, - retrievePermissions, - retrieveAssocs, - retrieveChildAssocs, - retrieveParentAssocs, - retrieveTargetAssocs, - retrieveSourceAssocs); - logger.debug("end nodeRefToNodeInfo"); - - logger.debug("start writeJsonResponse"); - writeJsonResponse(response, nodeInfoList); - logger.debug("end writeJsonResponse"); + this.permissionService); + return writeJsonResponse(nodeInfoList); } - @ApiOperation(value = "Retrieve current user's permissions for a node", - notes = "Returns a key-value map of permissions keys to a value of 'DENY' or 'ALLOW'. " + - "Possible keys are: Read, Write, Delete, CreateChildren, ReadPermissions, ChangePermissions, " + - "or custom permissions") - @Uri(value = "/nodes/{space}/{store}/{guid}/permissions", method = HttpMethod.GET) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = PermissionValue.class, responseContainer = "Map")) - public void getPermissions(@UriVariable String space, @UriVariable String store, @UriVariable String guid, - WebScriptResponse response) throws IOException { + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/nodes/{space}/{store}/{guid}/permissions") + public ResponseEntity> getPermissions(@PathVariable String space, + @PathVariable String store, + @PathVariable String guid) { NodeRef nodeRef = this.createNodeRef(space, store, guid); - - Map permissions = this.permissionService.getPermissionsFast(nodeRef); - writeJsonResponse(response, permissions); + return writeJsonResponse( + this.permissionService.getPermissionsFast(nodeRef) + ); } - @ApiOperation("Creates or copies a node") - @Uri(value = "/nodes", method = HttpMethod.POST) - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = NodeInfo.class)) - public void createNode(final CreateNodeOptions createNodeOptions, WebScriptResponse response) throws IOException { + @AlfrescoTransaction + @PostMapping(value = "/v2/nodes") + public ResponseEntity createNode(@RequestBody final CreateNodeOptions createNodeOptions) { + final StringBuilder errorMessage = new StringBuilder(); + final AtomicInteger errorCode = new AtomicInteger(); Object resultObject = serviceRegistry.getRetryingTransactionHelper() - .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { - @Override - public Object execute() throws Throwable { - NodeRef parent = new NodeRef(createNodeOptions.parent); - - if (!nodeService.exists(parent)) { - response.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(response, "Parent does not exist"); - return null; - } + .doInTransaction(() -> { + NodeRef parent = new NodeRef(createNodeOptions.getParent()); - NodeRef nodeRef; - NodeRef copyFrom = null; - if (createNodeOptions.copyFrom == null) { - nodeRef = nodeService - .createNode(parent, createNodeOptions.name, - new QName(createNodeOptions.type)); - } else { - copyFrom = new NodeRef(createNodeOptions.copyFrom); - if (!nodeService.exists(copyFrom)) { - response.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(response, "CopyFrom does not exist"); - return null; - } - nodeRef = nodeService.copyNode(copyFrom, parent, true); - } + if (!nodeService.exists(parent)) { + errorCode.addAndGet(HttpStatus.SC_NOT_FOUND); + errorMessage.append("Parent does not exist"); + return null; + } - MetadataChanges metadataChanges; - QName type; - if (createNodeOptions.type != null) { - type = new QName(createNodeOptions.type); - } else if ( createNodeOptions.type == null && createNodeOptions.copyFrom != null ) { - type = nodeService.getMetadata(copyFrom).type; - } else { - response.setStatus(HttpStatus.SC_BAD_REQUEST); - writeJsonResponse(response, - "Please provide parameter \"type\" when creating a new node"); + NodeRef nodeRef; + NodeRef copyFrom = null; + if (createNodeOptions.getCopyFrom() == null) { + nodeRef = nodeService + .createNode(parent, createNodeOptions.getName(), + new QName(createNodeOptions.getType())); + } else { + copyFrom = new NodeRef(createNodeOptions.getCopyFrom()); + if (!nodeService.exists(copyFrom)) { + errorCode.addAndGet(HttpStatus.SC_NOT_FOUND); + errorMessage.append("CopyFrom does not exist"); return null; } - metadataChanges = new MetadataChanges(type, null, null, - createNodeOptions.properties); - nodeService.setMetadata(nodeRef, metadataChanges); + nodeRef = nodeService.copyNode(copyFrom, parent, true); + } - return nodeRef; + MetadataChanges metadataChanges; + QName type; + if (createNodeOptions.getType() != null) { + type = new QName(createNodeOptions.getType()); + } else if (createNodeOptions.getCopyFrom() != null) { + type = nodeService.getMetadata(copyFrom).getType(); + } else { + errorCode.addAndGet(HttpStatus.SC_BAD_REQUEST); + errorMessage.append( + "Please provide parameter \"type\" when creating a new node" + ); + return null; } + metadataChanges = new MetadataChanges(type, null, null, + createNodeOptions.getProperties()); + nodeService.setMetadata(nodeRef, metadataChanges); + + return nodeRef; }, false, true); + if (resultObject == null) { + return ResponseEntity.status(errorCode.get()) + .body(errorMessage.toString()); + } + NodeRef resultRef = new NodeRef(resultObject.toString()); NodeInfo nodeInfo = this .nodeRefToNodeInfo(resultRef, this.fileFolderService, this.nodeService, this.permissionService); - writeJsonResponse(response, nodeInfo); + return writeJsonResponse(nodeInfo); } } diff --git a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/people/PeopleWebscript.java b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/people/PeopleWebscript.java index 8e0d4358..5bebba5c 100644 --- a/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/people/PeopleWebscript.java +++ b/apix-rest-v1/src/main/java/eu/xenit/apix/rest/v2/people/PeopleWebscript.java @@ -1,102 +1,87 @@ package eu.xenit.apix.rest.v2.people; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AlfrescoTransaction; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; import eu.xenit.apix.people.IPeopleService; import eu.xenit.apix.people.Person; import eu.xenit.apix.rest.v2.ApixV2Webscript; -import eu.xenit.apix.rest.v2.RestV2Config; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; -import org.apache.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.extensions.webscripts.WebScriptResponse; -import org.springframework.stereotype.Component; -/** - * Created by Jasperhilven - */ -@WebScript(baseUri = RestV2Config.BaseUrl, families = RestV2Config.Family, defaultFormat = "json", - description = "Retrieves person information", value = "People") -@Authentication(AuthenticationType.USER) -@Component("eu.xenit.apix.rest.v2.people.PeopleWebscript") +@AlfrescoAuthentication(AuthenticationType.USER) +@RestController public class PeopleWebscript extends ApixV2Webscript { - Logger logger = LoggerFactory.getLogger(PeopleWebscript.class); - @Autowired - IPeopleService personService; + private static final Logger logger = LoggerFactory.getLogger(PeopleWebscript.class); + private final IPeopleService personService; + + public PeopleWebscript(IPeopleService personService) { + this.personService = personService; + } - @Uri(value = "/people/id/{space}/{store}/{guid}", method = HttpMethod.GET) - @ApiOperation(value = "Returns person information", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Person.class)) - public void getPerson(@UriVariable final String space, @UriVariable final String store, - @UriVariable final String guid, WebScriptResponse webScriptResponse) throws IOException { - logger.debug("Asked person with guid: " + guid); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/people/id/{space}/{store}/{guid}") + public ResponseEntity getPerson(@PathVariable final String space, + @PathVariable final String store, + @PathVariable final String guid) { + logger.debug("Asked person with guid: {}", guid); try { - Person p = personService.GetPerson(createNodeRef(space, store, guid)); - writeJsonResponse(webScriptResponse, p); + return writeJsonResponse( + personService.GetPerson(createNodeRef(space, store, guid)) + ); } catch (NoSuchElementException noSuchElementException) { - webScriptResponse.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(webScriptResponse, noSuchElementException); + return ResponseEntity.status(org.springframework.http.HttpStatus.NOT_FOUND) + .body(noSuchElementException.getMessage()); } catch (IllegalArgumentException illegalArgumentException) { - webScriptResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - writeJsonResponse(webScriptResponse, illegalArgumentException); + return ResponseEntity.status(org.springframework.http.HttpStatus.BAD_REQUEST) + .body(illegalArgumentException.getMessage()); } } - @Uri(value = "/people", method = HttpMethod.GET) - @ApiOperation(value = "Returns all people", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Person[].class)) - public void getAllPeople(WebScriptResponse webScriptResponse) throws IOException { - List people = personService.GetPeople(); - writeJsonResponse(webScriptResponse, people); - return; + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/people") + public ResponseEntity> getAllPeople() { + return writeJsonResponse(personService.GetPeople()); } - @Uri(value = "/people/-me-", method = HttpMethod.GET) - @ApiOperation(value = "Returns current user information", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Person.class)) - public void getPersonCurrentUser(WebScriptResponse webScriptResponse) throws IOException { - getPersonWithName("-me-", webScriptResponse); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/people/-me-") + public ResponseEntity getPersonCurrentUser() { + return getPersonWithName("-me-"); } - @Uri(value = "/people/{name}", method = HttpMethod.GET) - @ApiOperation(value = "Returns person information", notes = "") - @ApiResponses(@ApiResponse(code = 200, message = "Success", response = Person.class)) - public void getPersonWithName(@UriVariable final String name, WebScriptResponse webScriptResponse) - throws IOException { - logger.debug("Asked person with name: " + name); - try{ - Person p = personService.GetPerson(name); - writeJsonResponse(webScriptResponse, p); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/people/{name}") + public ResponseEntity getPersonWithName(@PathVariable final String name) { + logger.debug("Asked person with name: {}", name); + try { + return writeJsonResponse( + personService.GetPerson(name) + ); } catch (NoSuchElementException noSuchElementException) { - webScriptResponse.setStatus(HttpStatus.SC_NOT_FOUND); - writeJsonResponse(webScriptResponse, noSuchElementException.getMessage()); + return ResponseEntity.status(org.springframework.http.HttpStatus.NOT_FOUND) + .body(noSuchElementException.getMessage()); } catch (IllegalArgumentException illegalArgumentException) { - webScriptResponse.setStatus(HttpStatus.SC_BAD_REQUEST); - writeJsonResponse(webScriptResponse, illegalArgumentException.getMessage()); + return ResponseEntity.status(org.springframework.http.HttpStatus.BAD_REQUEST) + .body(illegalArgumentException.getMessage()); } } - @Uri(value = "/people/containergroups/{name}", method = HttpMethod.GET) - @ApiOperation(value = "Returns container groups of person", notes = "") - @ApiResponses(value = @ApiResponse(code = 200, message = "Success", response = String[].class)) - public void getContainerGroupsOf(@UriVariable final String name, WebScriptResponse webScriptResponse) - throws IOException { - logger.debug("Asked containergroups for person with name: " + name); + @AlfrescoTransaction(readOnly = true) + @GetMapping(value = "/v2/ people/containergroups/{name}") + public ResponseEntity> getContainerGroupsOf(@PathVariable final String name) { + logger.debug("Asked containergroups for person with name: {}", name); Set result = personService.GetContainerGroups(name); - writeJsonResponse(webScriptResponse, new ArrayList(result)); + return writeJsonResponse(new ArrayList<>(result)); } } diff --git a/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.delete.desc.xml b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.delete.desc.xml new file mode 100644 index 00000000..558adb88 --- /dev/null +++ b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.delete.desc.xml @@ -0,0 +1,11 @@ + + Alfred API Delete + /apix/{path} + user + none + + + + true + + \ No newline at end of file diff --git a/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.get.desc.xml b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.get.desc.xml new file mode 100644 index 00000000..4fe7cea4 --- /dev/null +++ b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.get.desc.xml @@ -0,0 +1,11 @@ + + Alfred API Get + /apix/{path} + user + none + + + + true + + \ No newline at end of file diff --git a/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.post.desc.xml b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.post.desc.xml new file mode 100644 index 00000000..4bc20aa2 --- /dev/null +++ b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.post.desc.xml @@ -0,0 +1,11 @@ + + Alfred API Post + /apix/{path} + user + none + + + + true + + \ No newline at end of file diff --git a/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.put.desc.xml b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.put.desc.xml new file mode 100644 index 00000000..d7e5611d --- /dev/null +++ b/apix-rest-v1/src/main/resources/alfresco/extension/templates/webscripts/alfred/api.put.desc.xml @@ -0,0 +1,11 @@ + + Alfred API Put + /apix/{path} + user + none + + + + true + + \ No newline at end of file diff --git a/apix-rest-v1/src/main/resources/alfresco/module/alfred-api/module-context.xml b/apix-rest-v1/src/main/resources/alfresco/module/alfred-api/module-context.xml new file mode 100644 index 00000000..df7b823b --- /dev/null +++ b/apix-rest-v1/src/main/resources/alfresco/module/alfred-api/module-context.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/apix-rest-v1/src/main/resources/application.properties b/apix-rest-v1/src/main/resources/application.properties new file mode 100644 index 00000000..71ec92ff --- /dev/null +++ b/apix-rest-v1/src/main/resources/application.properties @@ -0,0 +1,2 @@ +management.endpoint.shutdown.enabled=true +management.endpoints.web.exposure.include=* \ No newline at end of file diff --git a/apix-rest-v1/src/main/resources/log4j.properties b/apix-rest-v1/src/main/resources/log4j.properties index 18cfaa3f..7fa621cb 100644 --- a/apix-rest-v1/src/main/resources/log4j.properties +++ b/apix-rest-v1/src/main/resources/log4j.properties @@ -5,4 +5,5 @@ log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.threshold=TRACE #log4j.logger.org.springframework.osgi=DEBUG -#log4j.logger.org.springframework=DEBUG \ No newline at end of file +#log4j.logger.org.springframework=DEBUG +#log4j.logger.org.springframework.web.servlet.mvc.support=DEBUG \ No newline at end of file diff --git a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/NodeInfoSerializationTest.java b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/NodeInfoSerializationTest.java deleted file mode 100644 index 1f24dd8b..00000000 --- a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/NodeInfoSerializationTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package eu.xenit.apix.rest; - -import com.fasterxml.jackson.databind.ObjectMapper; -import eu.xenit.apix.data.NodeRef; -import eu.xenit.apix.data.QName; -import eu.xenit.apix.node.ChildParentAssociation; -import eu.xenit.apix.node.NodeAssociations; -import eu.xenit.apix.rest.v1.nodes.NodeInfo; -import java.io.IOException; -import java.util.ArrayList; -import org.junit.Test; - -/** - * Created by Michiel Huygen on 19/05/2016. - */ -public class NodeInfoSerializationTest { - - @Test - - public void TestDeserializeNodeInfoJson() throws IOException { - NodeInfo nodeinfo = new NodeInfo(); - nodeinfo.associations = new NodeAssociations(); - nodeinfo.associations.setParents(new ArrayList()); - nodeinfo.associations.getParents().add(new ChildParentAssociation(new NodeRef("workspace://SpacesStore/7987"), - new NodeRef("workspace://SpacesStore/7987"), new QName("hello"), false)); - - new ObjectMapper().readValue(new ObjectMapper().writeValueAsString(nodeinfo), NodeInfo.class); - } -} diff --git a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/NodesWebscript1Test.java b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/NodesWebscript1Test.java index c820485e..c9a55d1b 100644 --- a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/NodesWebscript1Test.java +++ b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/NodesWebscript1Test.java @@ -11,16 +11,16 @@ import eu.xenit.apix.data.NodeRef; import eu.xenit.apix.node.INodeService; import eu.xenit.apix.rest.v1.nodes.NodesWebscript1; + +import java.io.IOException; import java.io.InputStream; -import org.springframework.extensions.webscripts.servlet.FormData; import org.junit.Test; -import org.mockito.internal.util.reflection.FieldSetter; -import org.springframework.extensions.webscripts.servlet.FormData.FormField; +import org.springframework.web.multipart.MultipartFile; public class NodesWebscript1Test { @Test - public void test_uploadNode_triggerMetadataExtract() throws NoSuchFieldException { + public void test_uploadNode_triggerMetadataExtract() throws IOException { //Setup mocks INodeService nodeServiceMock = mock(NodeService.class); when(nodeServiceMock.createNode(any(), any(), any())) @@ -28,11 +28,11 @@ public void test_uploadNode_triggerMetadataExtract() throws NoSuchFieldException doNothing().when(nodeServiceMock).setContent(any(), any(), any()); doNothing().when(nodeServiceMock).extractMetadata(any()); - NodesWebscript1 nodesWebscript = new NodesWebscript1(); - FieldSetter.setField(nodesWebscript, NodesWebscript1.class.getDeclaredField("nodeService"), nodeServiceMock); + NodesWebscript1 nodesWebscript = new NodesWebscript1(nodeServiceMock, + null, null, null, null); - FormField fileMock = mock(FormData.FormField.class); - when(fileMock.getFilename()).thenReturn("testFile"); + MultipartFile fileMock = mock(MultipartFile.class); + when(fileMock.getOriginalFilename()).thenReturn("testFile"); InputStream inputStreamMock = mock(InputStream.class); when(fileMock.getInputStream()).thenReturn(inputStreamMock); diff --git a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/SearchWebscriptTest.java b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/SearchWebscriptTest.java index 5650188f..609df2e7 100644 --- a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/SearchWebscriptTest.java +++ b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/SearchWebscriptTest.java @@ -3,12 +3,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import eu.xenit.apix.search.SearchQuery; import eu.xenit.apix.search.json.SearchNodeJsonParser; +import org.junit.Assert; +import org.junit.Test; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import org.junit.Assert; -import org.junit.Test; /** * Created by jasper on 04/04/17. @@ -22,6 +23,6 @@ public void Test() throws IOException { String jsonInput = "{\"query\":{\"property\":{\"name\":\"cm:name\",\"value\":\"Company Home\"}},\"paging\":{\"limit\":1,\"skip\":0},\"facets\":{\"enabled\":false}}"; InputStream stream = new ByteArrayInputStream(jsonInput.getBytes(StandardCharsets.UTF_8)); SearchQuery q = m.readValue(stream, SearchQuery.class); - Assert.assertTrue(q != null); + Assert.assertNotNull(q); } } diff --git a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/TestDEWebscript1.java b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/TestDEWebscript1.java deleted file mode 100644 index 8b31a582..00000000 --- a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/TestDEWebscript1.java +++ /dev/null @@ -1,26 +0,0 @@ -package eu.xenit.apix.rest; - -import com.github.dynamicextensionsalfresco.webscripts.annotations.Authentication; -import com.github.dynamicextensionsalfresco.webscripts.annotations.AuthenticationType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.HttpMethod; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; - -/** - * Created by Michiel Huygen on 29/03/2016. - */ -@WebScript(baseUri = "/base", families = "My Family", value = "TestDEWebscript") -@Authentication(AuthenticationType.GUEST) -public class TestDEWebscript1 { - - @Uri("/method") - public void testGet() { - - } - - @Authentication(AuthenticationType.ADMIN) - @Uri(value = "/method/{param}", method = HttpMethod.POST) - public void testPost() { - - } -} diff --git a/apix-rest-v1/src/test/java/eu/xenit/apix/rest/TestWebscript1.java b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/TestWebscript1.java new file mode 100644 index 00000000..ef91fea3 --- /dev/null +++ b/apix-rest-v1/src/test/java/eu/xenit/apix/rest/TestWebscript1.java @@ -0,0 +1,23 @@ +package eu.xenit.apix.rest; + +import com.gradecak.alfresco.mvc.annotation.AlfrescoAuthentication; +import com.gradecak.alfresco.mvc.annotation.AuthenticationType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@AlfrescoAuthentication(AuthenticationType.GUEST) +public class TestWebscript1 { + + @GetMapping("/method") + public void testGet() { + + } + + @AlfrescoAuthentication(AuthenticationType.ADMIN) + @PostMapping(value = "/method/{param}") + public void testPost() { + + } +} diff --git a/apix-rest-v1/template.mf b/apix-rest-v1/template.mf deleted file mode 100644 index 94121f6f..00000000 --- a/apix-rest-v1/template.mf +++ /dev/null @@ -1,4 +0,0 @@ -Excluded-Exports: - *.internal* -Unversioned-Imports: - * diff --git a/build.gradle b/build.gradle index 4f4cf836..ef191e23 100644 --- a/build.gradle +++ b/build.gradle @@ -1,95 +1,48 @@ -def bundleClassPath(configuration) { - def list = ['.'] - configuration.each { - list += 'lib/' + it.name - } - return list.join(',') +plugins { + // Centralize plugin version management + id 'be.vbgn.ci-detect' version '0.5.0' apply false + id 'eu.xenit.de' version '3.1.0' apply false + id 'eu.xenit.amp' version '1.1.0' apply false + id 'eu.xenit.alfresco' version '1.1.0' apply false + id 'eu.xenit.docker-alfresco' version '5.3.1' apply false + id 'eu.xenit.docker-compose' version '5.3.1' apply false + id 'eu.xenit.alfresco-remote-testrunner' version '2.0.1' apply false } -def includeResource(configuration) { - def list = [] - configuration.each { - //We have to replace the backslashes with forward slashes because on windows the path will just be unseparated. - //For example when you have path "C\:Users\Someone\.gradle\caches" it will become "C\:UsersSomeone.gradlecaches" - def normalizedPath = it.path.replace("\\", "/") - list += 'lib/' + it.name + "=" + normalizedPath - } - return list.join(',') -} - -def getVersionQualifier(String branch_name) { - if (branch_name.startsWith('release')) +def static getVersionQualifier(String branch_name) { + if(branch_name.startsWith('release')) return '' - //(DEVEM-463, ALFREDAPI-514): The current osgi version (6.0.0) in DE (3.0.0) has issues with version strings containing '-' - // We've tried replacing the '-' with at '.', but that breaks maven-style dependency resolution. - // We consider correct dependency resolution of snapshots a more important feature than DE, so we opt for the '-' - // Dynamic extensions bundles of snapshots are therefore broken. return '-SNAPSHOT' } -buildscript { - repositories { - mavenCentral() - maven { - url "https://maven.alfresco.com/nexus/content/groups/public" - } - } -} - ext { - versionWithoutQualifier = '4.0.1' + versionWithoutQualifier = '5.0.0' + de_version = '3.1.0' // Only used for integration testing + mvc = '8.0.0' jackson_version = '2.8.3' - swagger_version = "1.5.7" - de_version = "3.0.0" - - http_core_version = "4.3.3" - http_version = "4.3.4" - - alfresco_62_version = "6.2" - alfresco_70_version = "7.0" - alfresco_71_version = "7.1" - alfresco_72_version = "7.2" - alfresco_73_version = "7.3" - - // alfresco-data-model might have a different version number than the main war it's included in - // ref: https://bitbucket.org/xenit/alfresco-bom/src/master/repo/eu/xenit/alfresco/ for the bom of vanilla alfresco - alfresco_62_dm_version = "8.50.18" - alfresco_70_dm_version = "8.424" - alfresco_71_dm_version = "12.23" - alfresco_72_dm_version = "14.142" - alfresco_73_dm_version = "17.175" - care4alfVersion = '2.3.0' + http_version = '4.3.4' // Used by integration tests } subprojects { apply plugin: 'java' - apply plugin: 'maven' - group = 'eu.xenit.apix' + group = 'eu.xenit.alfred.api' version = versionWithoutQualifier + getVersionQualifier(System.env.BRANCH_NAME ?: 'local') - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 11 + targetCompatibility = 11 repositories { mavenCentral() maven { url "https://artifacts.alfresco.com/nexus/content/groups/public" } - - maven { - url "https://repo.xenit.eu/basic/legacy/maven/" - credentials { - username System.env.CLOUDSMITH_USER ?: property('eu.xenit.cloudsmith.username') - password System.env.CLOUDSMITH_APIKEY ?: property('eu.xenit.cloudsmith.password') - } - } // This private repository provides Xenit with Alfresco enterprise artefacts. // External developers should replace it with their own library repository. maven { url 'https://artifacts.alfresco.com/nexus/content/groups/private' credentials { - username System.env.ALFRESCO_NEXUS_USERNAME ?: property('org.alfresco.maven.nexus.username') - password System.env.ALFRESCO_NEXUS_PASSWORD ?: property('org.alfresco.maven.nexus.password') + username System.env.ALFRESCO_NEXUS_USERNAME ?: property("org.alfresco.maven.nexus.username") + password System.env.ALFRESCO_NEXUS_PASSWORD ?: property("org.alfresco.maven.nexus.password") } } } diff --git a/de-swagger-reader/build.gradle b/de-swagger-reader/build.gradle deleted file mode 100644 index 21d5f48b..00000000 --- a/de-swagger-reader/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -group = 'eu.xenit.swagger' -description = 'Xenit Dynamic-extensions Swagger Reader' - -apply plugin: 'osgi' - -dependencies { - compile group: 'commons-io', name: 'commons-io', version: '2.0.1' - compile(group: 'io.swagger', name: 'swagger-core', version: swagger_version) { - exclude group: 'org.slf4j', module: 'slf4j-api' - } - compileOnly group: 'org.slf4j', name: 'slf4j-api', version: '1.7.22' - compile group: 'io.swagger', name: 'swagger-annotations', version: swagger_version - compileOnly(group: 'eu.xenit.de', name: 'annotations', version: de_version) { - transitive = false - } - compileOnly(group: 'org.springframework', name: 'spring-core', version: '3.2.10.RELEASE') - compileOnly(group: 'org.springframework.extensions.surf', name: 'spring-webscripts', version: '5.0.d') -} diff --git a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/DESwaggerExtension.java b/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/DESwaggerExtension.java deleted file mode 100644 index 0972d366..00000000 --- a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/DESwaggerExtension.java +++ /dev/null @@ -1,73 +0,0 @@ -package eu.xenit.swagger.reader; - -import com.fasterxml.jackson.databind.type.ArrayType; -import com.fasterxml.jackson.databind.type.SimpleType; -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import eu.xenit.swagger.reader.ext.AbstractSwaggerExtension; -import eu.xenit.swagger.reader.ext.SwaggerExtension; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -/** - * Created by Michiel Huygen on 10/03/2016. - */ -public class DESwaggerExtension extends AbstractSwaggerExtension { - - @Override - public List extractParameters(List annotations, Type type, Set typesToSkip, - Iterator chain) { - Class cls = null; - if (type instanceof SimpleType) { - cls = ((SimpleType) type).getRawClass(); // No clue why this happens - } else if (type instanceof Class) { - cls = ((Class) type); - } else if (type instanceof ArrayType) { - cls = ((ArrayType) type).getRawClass(); - } - - if (cls.isAssignableFrom(WebScriptRequest.class) - || cls.isAssignableFrom(WebScriptResponse.class)) { - typesToSkip.add(type); - return Collections.emptyList(); - - } - - for (Annotation ann : annotations) { - - if (ann.annotationType().equals(RequestParam.class)) { - RequestParam c = (RequestParam) ann; - - QueryParameter parameter = new QueryParameter(); - parameter.setRequired(c.required()); - parameter.setName(c.value()); - parameter.setType("string"); //TODO - - return Collections.singletonList((Parameter) parameter); - - } - if (ann.annotationType().equals(UriVariable.class)) { - UriVariable c = (UriVariable) ann; - PathParameter parameter = new PathParameter(); - parameter.setRequired(c.required()); - parameter.setName(c.value()); - parameter.setType("string"); - return Collections.singletonList((Parameter) parameter); - } - } - - return Collections.emptyList(); - // SHould i call parent? - // return super.extractParameters(annotations, type, typesToSkip, chain); - } -} diff --git a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/Reader.java b/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/Reader.java deleted file mode 100644 index 5191e4b0..00000000 --- a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/Reader.java +++ /dev/null @@ -1,1137 +0,0 @@ -/** - * Copyright 2016 SmartBear Software - *

- * 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. - */ - -package eu.xenit.swagger.reader; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; -import eu.xenit.swagger.reader.ext.SwaggerExtension; -import eu.xenit.swagger.reader.ext.SwaggerExtensions; -import eu.xenit.swagger.reader.utils.ReaderUtils; -import io.swagger.annotations.*; -import io.swagger.annotations.Info; -import io.swagger.converter.ModelConverters; -import io.swagger.models.Contact; -import io.swagger.models.ExternalDocs; -import io.swagger.models.License; -import io.swagger.models.*; -import io.swagger.models.Tag; -import io.swagger.models.parameters.*; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.RefProperty; -import io.swagger.util.BaseReaderUtils; -import io.swagger.util.ParameterProcessor; -import io.swagger.util.PathUtils; -import io.swagger.util.ReflectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; - -//import javax.ws.rs.Consumes; -//import javax.ws.rs.HttpMethod; -//import javax.ws.rs.Produces; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.*; - -public class Reader { - - private static final Logger LOGGER = LoggerFactory.getLogger(Reader.class); - private static final String SUCCESSFUL_OPERATION = "successful operation"; - private static final String PATH_DELIMITER = "/"; - - //private final ReaderConfig config; - private Swagger swagger; - private String[] ignoredRoutes = new String[0]; - - public Reader() { - this(null); - } - - public Reader(Swagger swagger) { - this.swagger = swagger == null ? new Swagger() : swagger; - } - - private static Class getClassArgument(Type cls) { - if (cls instanceof ParameterizedType) { - final ParameterizedType parameterized = (ParameterizedType) cls; - final Type[] args = parameterized.getActualTypeArguments(); - if (args.length != 1) { - LOGGER.error(String.format("Unexpected class definition: %s", cls)); - return null; - } - final Type first = args[0]; - if (first instanceof Class) { - return (Class) first; - } else { - return null; - } - } else { - LOGGER.error(String.format("Unknown class definition: %s", cls)); - return null; - } - } - -// public Reader(Swagger swagger){//, ReaderConfig config) { -// this.swagger = swagger == null ? new Swagger() : swagger; -// //this.config = new DefaultReaderConfig(config); -// } - - private static Set parseSchemes(String schemes) { - final Set result = EnumSet.noneOf(Scheme.class); - for (String item : StringUtils.trimToEmpty(schemes).split(",")) { - final Scheme scheme = Scheme.forValue(StringUtils.trimToNull(item)); - if (scheme != null) { - result.add(scheme); - } - } - return result; - } - - private static boolean isVoid(Type type) { - final Class cls = TypeFactory.defaultInstance().constructType(type).getRawClass(); - return Void.class.isAssignableFrom(cls) || Void.TYPE.isAssignableFrom(cls); - } - - private static boolean isValidResponse(Type type) { - if (type == null) { - return false; - } - final JavaType javaType = TypeFactory.defaultInstance().constructType(type); - if (isVoid(javaType)) { - return false; - } - final Class cls = javaType.getRawClass(); - //TODO: return !javax.ws.rs.core.Response.class.isAssignableFrom(cls) && !isResourceClass(cls); - return !isResourceClass(cls); - } - - private static boolean isResourceClass(Class cls) { - return cls.getAnnotation(Api.class) != null; - } - - /** - * Scans a set of classes for both ReaderListeners and Swagger annotations. All found listeners will - * be instantiated before any of the classes are scanned for Swagger annotations - so they can be invoked - * accordingly. - * - * @param classes a set of classes to scan - * @return the generated Swagger definition - */ - - public Swagger read(Set> classes) { - // process SwaggerDefinitions first - so we get tags in desired order - for (Class cls : classes) { - SwaggerDefinition swaggerDefinition = cls.getAnnotation(SwaggerDefinition.class); - if (swaggerDefinition != null) { - readSwaggerConfig(cls, swaggerDefinition); - } - } - - for (Class cls : classes) { - read(cls, "", null, false, new String[0], new String[0], new HashMap(), - new ArrayList(), new HashSet>()); - } - - return swagger; - } - - public Swagger getSwagger() { - return this.swagger; - } - - /** - * Scans a single class for Swagger annotations - does not invoke ReaderListeners - */ - - public Swagger read(Class cls) { - - SwaggerDefinition swaggerDefinition = cls.getAnnotation(SwaggerDefinition.class); - if (swaggerDefinition != null) { - readSwaggerConfig(cls, swaggerDefinition); - } - - return read(cls, "", null, false, new String[0], new String[0], new HashMap(), - new ArrayList(), new HashSet>()); - } - - protected Swagger read(Class cls, String parentPath, String parentMethod, boolean isSubresource, - String[] parentConsumes, String[] parentProduces, Map parentTags, - List parentParameters) { - return read(cls, parentPath, parentMethod, isSubresource, parentConsumes, parentProduces, parentTags, - parentParameters, new HashSet>()); - } - - private Swagger read(Class cls, String parentPath, String parentMethod, boolean isSubresource, - String[] parentConsumes, String[] parentProduces, Map parentTags, - List parentParameters, Set> scannedResources) { - Map globalScopes = new HashMap(); - - Map tags = new HashMap(); - List securities = new ArrayList(); - - String[] consumes = new String[0]; - String[] produces = new String[0]; - final Set globalSchemes = EnumSet.noneOf(Scheme.class); - - Api api = (Api) cls.getAnnotation(Api.class); - WebScript webscript = (WebScript) cls.getAnnotation(WebScript.class); - - boolean hasPathAnnotation = false;//TODO: (ReflectionUtils.getAnnotation(cls, javax.ws.rs.Path.class) != null); - boolean hasApiAnnotation = (api != null); - boolean isApiHidden = hasApiAnnotation && api.hidden(); - boolean hasDEWebscript = webscript != null; - - // class readable only if annotated with @Path or isSubresource, or and @Api not hidden - boolean classReadable = (hasPathAnnotation || isSubresource) && !isApiHidden; - - // readable if classReadable or (scanAllResources true in config and @Api not hidden) - boolean readable = classReadable || hasDEWebscript;//&& config.isScanAllResources()); - - if (isApiHidden) { - return swagger; - } - - if (!readable) { - return swagger; - } - Tag webscriptTag = null; - - if (webscript != null) { - - parentPath += webscript.baseUri(); - - Tag tag = new Tag(); - tag.setDescription(webscript.description()); - -// cls.getAnnotation(webscript) -// String webscriptTagName = cls.getSimpleName(); -// String[] cutOff = {"webscript","webscript1"}; -// for(int i =0;i 0; - String name = tagHasValidNameProperty ? webscript.value() : cls.getSimpleName(); - tag.setName(name); - swagger.addTag(tag); - webscriptTag = tag; - - - } - if (hasApiAnnotation && api.tags() != null && api.tags().length > 0) { - webscriptTag = null; // Don't use webscripttags when custom is specified - } - - // api readable only if @Api present; cannot be hidden because checked in classReadable. - boolean apiReadable = hasApiAnnotation; - - if (apiReadable) { - // the value will be used as a tag for 2.0 UNLESS a Tags annotation is present - Set tagStrings = extractTags(api); - for (String tagString : tagStrings) { - Tag tag = new Tag().name(tagString); - tags.put(tagString, tag); - } - for (String tagName : tags.keySet()) { - swagger.tag(tags.get(tagName)); - } - - if (!api.produces().isEmpty()) { - produces = new String[]{api.produces()}; - /*} else if (cls.getAnnotation(Produces.class) != null) { - produces = ReaderUtils.splitContentValues(cls.getAnnotation(Produces.class).value());*/ - } - if (!api.consumes().isEmpty()) { - consumes = new String[]{api.consumes()}; - /*} else if (cls.getAnnotation(Consumes.class) != null) { - consumes = ReaderUtils.splitContentValues(cls.getAnnotation(Consumes.class).value());*/ - } - globalSchemes.addAll(parseSchemes(api.protocols())); - Authorization[] authorizations = api.authorizations(); - - for (Authorization auth : authorizations) { - if (auth.value() != null && !"".equals(auth.value())) { - SecurityRequirement security = new SecurityRequirement(); - security.setName(auth.value()); - AuthorizationScope[] scopes = auth.scopes(); - for (AuthorizationScope scope : scopes) { - if (scope.scope() != null && !"".equals(scope.scope())) { - security.addScope(scope.scope()); - } - } - securities.add(security); - } - } - } - - if (readable) { - - if (isSubresource) { - if (parentTags != null) { - tags.putAll(parentTags); - } - } - // merge consumes, produces - - // look for method-level annotated properties - - // handle sub-resources by looking at return type - - final List globalParameters = new ArrayList(); - - // look for constructor-level annotated properties - globalParameters.addAll(ReaderUtils.collectConstructorParameters(cls, swagger)); - - // look for field-level annotated properties - globalParameters.addAll(ReaderUtils.collectFieldParameters(cls, swagger)); - - // parse the method - //final javax.ws.rs.Path apiPath = ReflectionUtils.getAnnotation(cls, javax.ws.rs.Path.class); - Method methods[] = cls.getMethods(); - for (Method method : methods) { - if (ReflectionUtils.isOverriddenMethod(method, cls)) { - continue; - } - //javax.ws.rs.Path methodPath = ReflectionUtils.getAnnotation(method, javax.ws.rs.Path.class); - - String operationPath = getPath(method, parentPath); - Map regexMap = new HashMap(); - operationPath = PathUtils.parsePath(operationPath, regexMap); - if (operationPath != null) { - if (isIgnored(operationPath)) { - continue; - } - - final ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class); - String httpMethod = extractOperationMethod(apiOperation, method, SwaggerExtensions.chain()); - - Operation operation = null; - //if(apiOperation != null || config.isScanAllResources() || httpMethod != null || methodPath != null) { - if (apiOperation != null || httpMethod != null) {//|| methodPath != null) { - operation = parseMethod(cls, method, globalParameters); - } - if (operation == null) { - continue; - } - if (parentParameters != null) { - for (Parameter param : parentParameters) { - operation.parameter(param); - } - } - for (Parameter param : operation.getParameters()) { - if (regexMap.get(param.getName()) != null) { - String pattern = regexMap.get(param.getName()); - param.setPattern(pattern); - } - } - - if (apiOperation != null) { - for (Scheme scheme : parseSchemes(apiOperation.protocols())) { - operation.scheme(scheme); - } - } - - if (operation.getSchemes() == null || operation.getSchemes().isEmpty()) { - for (Scheme scheme : globalSchemes) { - operation.scheme(scheme); - } - } - - String[] apiConsumes = consumes; - if (parentConsumes != null) { - Set both = new HashSet(Arrays.asList(apiConsumes)); - both.addAll(new HashSet(Arrays.asList(parentConsumes))); - if (operation.getConsumes() != null) { - both.addAll(new HashSet(operation.getConsumes())); - } - apiConsumes = both.toArray(new String[both.size()]); - } - - String[] apiProduces = produces; - if (parentProduces != null) { - Set both = new HashSet(Arrays.asList(apiProduces)); - both.addAll(new HashSet(Arrays.asList(parentProduces))); - if (operation.getProduces() != null) { - both.addAll(new HashSet(operation.getProduces())); - } - apiProduces = both.toArray(new String[both.size()]); - } - final Class subResource = getSubResourceWithJaxRsSubresourceLocatorSpecs(method); - if (subResource != null && !scannedResources.contains(subResource)) { - scannedResources.add(subResource); - read(subResource, operationPath, httpMethod, true, apiConsumes, apiProduces, tags, - operation.getParameters(), scannedResources); - // remove the sub resource so that it can visit it later in another path - // but we have a room for optimization in the future to reuse the scanned result - // by caching the scanned resources in the reader instance to avoid actual scanning - // the the resources again - scannedResources.remove(subResource); - } - - // can't continue without a valid http method - httpMethod = httpMethod == null ? parentMethod : httpMethod; - if (httpMethod != null) { - if (apiOperation != null) { - boolean hasExplicitTag = false; - for (String tag : apiOperation.tags()) { - if (!"".equals(tag)) { - operation.tag(tag); - swagger.tag(new Tag().name(tag)); - } - } - - operation.getVendorExtensions() - .putAll(BaseReaderUtils.parseExtensions(apiOperation.extensions())); - } - - if (operation.getConsumes() == null) { - for (String mediaType : apiConsumes) { - operation.consumes(mediaType); - } - } - if (operation.getProduces() == null) { - for (String mediaType : apiProduces) { - operation.produces(mediaType); - } - } - - if (operation.getTags() == null) { - for (String tagString : tags.keySet()) { - operation.tag(tagString); - } - } - - if (webscriptTag != null) { - operation.tag(webscriptTag.getName()); - } - - // Only add global @Api securities if operation doesn't already have more specific securities - if (operation.getSecurity() == null) { - for (SecurityRequirement security : securities) { - operation.security(security); - } - } - - Path path = swagger.getPath(operationPath); - if (path == null) { - path = new Path(); - swagger.path(operationPath, path); - } - path.set(httpMethod, operation); - - readImplicitParameters(method, operation); - } - } - } - } - - return swagger; - } - - private void readImplicitParameters(Method method, Operation operation) { - ApiImplicitParams implicitParams = method.getAnnotation(ApiImplicitParams.class); - if (implicitParams != null && implicitParams.value().length > 0) { - for (ApiImplicitParam param : implicitParams.value()) { - Parameter p = readImplicitParam(param); - if (p != null) { - operation.addParameter(p); - } - } - } - } - - protected Parameter readImplicitParam(ApiImplicitParam param) { - final Parameter p; - if (param.paramType().equalsIgnoreCase("path")) { - p = new PathParameter(); - } else if (param.paramType().equalsIgnoreCase("query")) { - p = new QueryParameter(); - } else if (param.paramType().equalsIgnoreCase("form") || param.paramType().equalsIgnoreCase("formData")) { - p = new FormParameter(); - } else if (param.paramType().equalsIgnoreCase("body")) { - p = null; - } else if (param.paramType().equalsIgnoreCase("header")) { - p = new HeaderParameter(); - } else { - LOGGER.warn("Unkown implicit parameter type: [" + param.paramType() + "]"); - return null; - } - - Type type; - - try { - if (param.dataType().equals("file")) { - type = java.io.File.class; - } else { - type = Class.forName(param.dataType()); - } - } catch (ClassNotFoundException e) { - LOGGER.debug("Class not found, using native swagger method. dataType: " + param.dataType()); - type = ReflectionUtils.typeFromString(param.dataType()); - } - - return ParameterProcessor.applyAnnotations(swagger, p, type == null ? String.class : type, - Arrays.asList(param)); - } - - protected void readSwaggerConfig(Class cls, SwaggerDefinition config) { - - if (!config.basePath().isEmpty()) { - swagger.setBasePath(config.basePath()); - } - - if (!config.host().isEmpty()) { - swagger.setHost(config.host()); - } - - readInfoConfig(config); - - for (String consume : config.consumes()) { - if (StringUtils.isNotEmpty(consume)) { - swagger.addConsumes(consume); - } - } - - for (String produce : config.produces()) { - if (StringUtils.isNotEmpty(produce)) { - swagger.addProduces(produce); - } - } - - if (!config.externalDocs().value().isEmpty()) { - ExternalDocs externalDocs = swagger.getExternalDocs(); - if (externalDocs == null) { - externalDocs = new ExternalDocs(); - swagger.setExternalDocs(externalDocs); - } - - externalDocs.setDescription(config.externalDocs().value()); - - if (!config.externalDocs().url().isEmpty()) { - externalDocs.setUrl(config.externalDocs().url()); - } - } - - for (io.swagger.annotations.Tag tagConfig : config.tags()) { - if (!tagConfig.name().isEmpty()) { - Tag tag = new Tag(); - tag.setName(tagConfig.name()); - tag.setDescription(tagConfig.description()); - - if (!tagConfig.externalDocs().value().isEmpty()) { - tag.setExternalDocs(new ExternalDocs(tagConfig.externalDocs().value(), - tagConfig.externalDocs().url())); - } - - tag.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(tagConfig.extensions())); - - swagger.addTag(tag); - } - } - - for (SwaggerDefinition.Scheme scheme : config.schemes()) { - if (scheme != SwaggerDefinition.Scheme.DEFAULT) { - swagger.addScheme(Scheme.forValue(scheme.name())); - } - } - } - - protected void readInfoConfig(SwaggerDefinition config) { - Info infoConfig = config.info(); - io.swagger.models.Info info = swagger.getInfo(); - if (info == null) { - info = new io.swagger.models.Info(); - swagger.setInfo(info); - } - - if (!infoConfig.description().isEmpty()) { - info.setDescription(infoConfig.description()); - } - - if (!infoConfig.termsOfService().isEmpty()) { - info.setTermsOfService(infoConfig.termsOfService()); - } - - if (!infoConfig.title().isEmpty()) { - info.setTitle(infoConfig.title()); - } - - if (!infoConfig.version().isEmpty()) { - info.setVersion(infoConfig.version()); - } - - if (!infoConfig.contact().name().isEmpty()) { - Contact contact = info.getContact(); - if (contact == null) { - contact = new Contact(); - info.setContact(contact); - } - - contact.setName(infoConfig.contact().name()); - if (!infoConfig.contact().email().isEmpty()) { - contact.setEmail(infoConfig.contact().email()); - } - - if (!infoConfig.contact().url().isEmpty()) { - contact.setUrl(infoConfig.contact().url()); - } - } - - if (!infoConfig.license().name().isEmpty()) { - License license = info.getLicense(); - if (license == null) { - license = new License(); - info.setLicense(license); - } - - license.setName(infoConfig.license().name()); - if (!infoConfig.license().url().isEmpty()) { - license.setUrl(infoConfig.license().url()); - } - } - - info.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(infoConfig.extensions())); - } - - protected Class getSubResource(Method method) { - final Class rawType = method.getReturnType(); - final Class type; - if (Class.class.equals(rawType)) { - type = getClassArgument(method.getGenericReturnType()); - if (type == null) { - return null; - } - } else { - type = rawType; - } - - if (type.getAnnotation(Api.class) != null) { - return type; - } - - // For sub-resources that are not annotated with @Api, look for any HttpMethods. - for (Method m : type.getMethods()) { - if (extractOperationMethod(null, m, null) != null) { - return type; - } - } - - return null; - } - - //TODO: -// String getPath(javax.ws.rs.Path classLevelPath, javax.ws.rs.Path methodLevelPath, String parentPath) { -// if (classLevelPath == null && methodLevelPath == null && StringUtils.isEmpty(parentPath)) { -// return null; -// } -// StringBuilder b = new StringBuilder(); -// if (parentPath != null && !"".equals(parentPath) && !"/".equals(parentPath)) { -// if (!parentPath.startsWith("/")) { -// parentPath = "/" + parentPath; -// } -// if (parentPath.endsWith("/")) { -// parentPath = parentPath.substring(0, parentPath.length() - 1); -// } -// -// b.append(parentPath); -// } -// if (classLevelPath != null) { -// b.append(classLevelPath.value()); -// } -// if (methodLevelPath != null && !"/".equals(methodLevelPath.value())) { -// String methodPath = methodLevelPath.value(); -// if (!methodPath.startsWith("/") && !b.toString().endsWith("/")) { -// b.append("/"); -// } -// if (methodPath.endsWith("/")) { -// methodPath = methodPath.substring(0, methodPath.length() - 1); -// } -// b.append(methodPath); -// } -// String output = b.toString(); -// if (!output.startsWith("/")) { -// output = "/" + output; -// } -// if (output.endsWith("/") && output.length() > 1) { -// return output.substring(0, output.length() - 1); -// } else { -// return output; -// } -// } - - protected Class getSubResourceWithJaxRsSubresourceLocatorSpecs(Method method) { - final Class rawType = method.getReturnType(); - final Class type; - if (Class.class.equals(rawType)) { - type = getClassArgument(method.getGenericReturnType()); - if (type == null) { - return null; - } - } else { - type = rawType; - } - - //TODO: DE? - /*if (method.getAnnotation(javax.ws.rs.Path.class) != null) { - if (extractOperationMethod(null, method, null) == null) { - return type; - } - }*/ - return null; - } - - protected Set extractTags(Api api) { - Set output = new LinkedHashSet(); - - boolean hasExplicitTags = false; - for (String tag : api.tags()) { - if (!"".equals(tag)) { - hasExplicitTags = true; - output.add(tag); - } - } - if (!hasExplicitTags) { - // derive tag from api path + description - String tagString = api.value().replace("/", ""); - if (!"".equals(tagString)) { - output.add(tagString); - } - } - return output; - } - - private String getPath(Method method, String parentPath) { - // DE - Uri uri = (Uri) method.getAnnotation(Uri.class); - if (uri == null) { - return null; - } - - if (uri.value().length != 1) { - throw new RuntimeException("Should have a single path for uri!" + uri.value()); - } - return parentPath + uri.value()[0]; -// -// if (classLevelPath == null && methodLevelPath == null && StringUtils.isEmpty(parentPath)) { -// return null; -// } -// StringBuilder b = new StringBuilder(); -// if (parentPath != null && !"".equals(parentPath) && !"/".equals(parentPath)) { -// if (!parentPath.startsWith("/")) { -// parentPath = "/" + parentPath; -// } -// if (parentPath.endsWith("/")) { -// parentPath = parentPath.substring(0, parentPath.length() - 1); -// } -// -// b.append(parentPath); -// } -// if (classLevelPath != null) { -// b.append(classLevelPath.value()); -// } -// if (methodLevelPath != null && !"/".equals(methodLevelPath.value())) { -// String methodPath = methodLevelPath.value(); -// if (!methodPath.startsWith("/") && !b.toString().endsWith("/")) { -// b.append("/"); -// } -// if (methodPath.endsWith("/")) { -// methodPath = methodPath.substring(0, methodPath.length() - 1); -// } -// b.append(methodPath); -// } -// String output = b.toString(); -// if (!output.startsWith("/")) { -// output = "/" + output; -// } -// if (output.endsWith("/") && output.length() > 1) { -// return output.substring(0, output.length() - 1); -// } else { -// return output; -// } - } - - private Map parseResponseHeaders(ResponseHeader[] headers) { - Map responseHeaders = null; - if (headers != null && headers.length > 0) { - for (ResponseHeader header : headers) { - String name = header.name(); - if (!"".equals(name)) { - if (responseHeaders == null) { - responseHeaders = new HashMap(); - } - String description = header.description(); - Class cls = header.response(); - - if (!isVoid(cls)) { - final Property property = ModelConverters.getInstance().readAsProperty(cls); - if (property != null) { - Property responseProperty = ContainerWrapper - .wrapContainer(header.responseContainer(), property, - ContainerWrapper.ARRAY, ContainerWrapper.LIST, ContainerWrapper.SET); - responseProperty.setDescription(description); - responseHeaders.put(name, responseProperty); - appendModels(cls); - } - } - } - } - } - return responseHeaders; - } - - /*private String getHttpMethodFromCustomAnnotations(Method method) { - for(Annotation methodAnnotation : method.getAnnotations()){ - HttpMethod httpMethod = methodAnnotation.annotationType().getAnnotation(HttpMethod.class); - if(httpMethod != null) { - return httpMethod.value().toLowerCase(); - } - } - return null; - }*/ - - private Operation parseMethod(Class cls, Method method, List globalParameters) { - Operation operation = new Operation(); - - ApiOperation apiOperation = ReflectionUtils.getAnnotation(method, ApiOperation.class); - ApiResponses responseAnnotation = ReflectionUtils.getAnnotation(method, ApiResponses.class); - - String operationId = method.getName(); - String responseContainer = null; - - Type responseType = null; - Map defaultResponseHeaders = new HashMap(); - - if (apiOperation != null) { - if (apiOperation.hidden()) { - return null; - } - if (!"".equals(apiOperation.nickname())) { - operationId = apiOperation.nickname(); - } - - defaultResponseHeaders = parseResponseHeaders(apiOperation.responseHeaders()); - - operation - .summary(apiOperation.value()) - .description(apiOperation.notes()); - - if (apiOperation.response() != null && !isVoid(apiOperation.response())) { - responseType = apiOperation.response(); - } - if (!"".equals(apiOperation.responseContainer())) { - responseContainer = apiOperation.responseContainer(); - } - if (apiOperation.authorizations() != null) { - List securities = new ArrayList(); - for (Authorization auth : apiOperation.authorizations()) { - if (auth.value() != null && !"".equals(auth.value())) { - SecurityRequirement security = new SecurityRequirement(); - security.setName(auth.value()); - AuthorizationScope[] scopes = auth.scopes(); - for (AuthorizationScope scope : scopes) { - if (scope.scope() != null && !"".equals(scope.scope())) { - security.addScope(scope.scope()); - } - } - securities.add(security); - } - } - if (securities.size() > 0) { - for (SecurityRequirement sec : securities) { - operation.security(sec); - } - } - } - if (apiOperation.consumes() != null && !apiOperation.consumes().isEmpty()) { - operation.consumes(apiOperation.consumes()); - } - if (apiOperation.produces() != null && !apiOperation.produces().isEmpty()) { - operation.produces(apiOperation.produces()); - } - } - - if (apiOperation != null && StringUtils.isNotEmpty(apiOperation.responseReference())) { - Response response = new Response().description(SUCCESSFUL_OPERATION); - response.schema(new RefProperty(apiOperation.responseReference())); - operation.addResponse(String.valueOf(apiOperation.code()), response); - } else if (responseType == null) { - // pick out response from method declaration - LOGGER.debug("picking up response class from method " + method); - responseType = method.getGenericReturnType(); - } - if (isValidResponse(responseType)) { - final Property property = ModelConverters.getInstance().readAsProperty(responseType); - if (property != null) { - final Property responseProperty = ContainerWrapper.wrapContainer(responseContainer, property); - final int responseCode = apiOperation == null ? 200 : apiOperation.code(); - operation.response(responseCode, - new Response().description(SUCCESSFUL_OPERATION).schema(responseProperty) - .headers(defaultResponseHeaders)); - appendModels(responseType); - } - } - - operation.operationId(operationId); - -// if (apiOperation != null && apiOperation.consumes() != null && apiOperation.consumes().isEmpty()) { -// final Consumes consumes = ReflectionUtils.getAnnotation(method, Consumes.class); -// if (consumes != null) { -// for (String mediaType : ReaderUtils.splitContentValues(consumes.value())) { -// operation.consumes(mediaType); -// } -// } -// } -// -// if (apiOperation != null && apiOperation.produces() != null && apiOperation.produces().isEmpty()) { -// final Produces produces = ReflectionUtils.getAnnotation(method, Produces.class); -// if (produces != null) { -// for (String mediaType : ReaderUtils.splitContentValues(produces.value())) { -// operation.produces(mediaType); -// } -// } -// } - - List apiResponses = new ArrayList(); - if (responseAnnotation != null) { - apiResponses.addAll(Arrays.asList(responseAnnotation.value())); - } - - Class[] exceptionTypes = method.getExceptionTypes(); - for (Class exceptionType : exceptionTypes) { - ApiResponses exceptionResponses = ReflectionUtils.getAnnotation(exceptionType, ApiResponses.class); - if (exceptionResponses != null) { - apiResponses.addAll(Arrays.asList(exceptionResponses.value())); - } - } - - for (ApiResponse apiResponse : apiResponses) { - Map responseHeaders = parseResponseHeaders(apiResponse.responseHeaders()); - - Response response = new Response() - .description(apiResponse.message()) - .headers(responseHeaders); - - if (apiResponse.code() == 0) { - operation.defaultResponse(response); - } else { - operation.response(apiResponse.code(), response); - } - - if (StringUtils.isNotEmpty(apiResponse.reference())) { - - response.schema(new RefProperty(apiResponse.reference())); - } else if (!isVoid(apiResponse.response())) { - responseType = apiResponse.response(); - final Property property = ModelConverters.getInstance().readAsProperty(responseType); - if (property != null) { - response.schema(ContainerWrapper.wrapContainer(apiResponse.responseContainer(), property)); - appendModels(responseType); - } - } - } - if (ReflectionUtils.getAnnotation(method, Deprecated.class) != null) { - operation.setDeprecated(true); - } - - // process parameters - for (Parameter globalParameter : globalParameters) { - operation.parameter(globalParameter); - } - - Type[] genericParameterTypes = method.getGenericParameterTypes(); - Annotation[][] paramAnnotations = method.getParameterAnnotations(); - LocalVariableTableParameterNameDiscoverer disco = new LocalVariableTableParameterNameDiscoverer(); - String[] parameterNames = disco.getParameterNames(method); - for (int i = 0; i < genericParameterTypes.length; i++) { - final Type type = TypeFactory.defaultInstance().constructType(genericParameterTypes[i], cls); - List parameters = getParameters(type, Arrays.asList(paramAnnotations[i])); - - for (Parameter parameter : parameters) { - if (parameter.getName().equals("")) { - parameter.setName(parameterNames[i]); - } - operation.parameter(parameter); - } - } - - if (operation.getResponses() == null) { - Response response = new Response().description(SUCCESSFUL_OPERATION); - operation.defaultResponse(response); - } - return operation; - } - - private List getParameters(Type type, List annotations) { - final Iterator chain = SwaggerExtensions.chain(); - if (!chain.hasNext()) { - return Collections.emptyList(); - } - LOGGER.debug("getParameters for " + type); - Set typesToSkip = new HashSet(); - final SwaggerExtension extension = chain.next(); - LOGGER.debug("trying extension " + extension); - - final List parameters = extension.extractParameters(annotations, type, typesToSkip, chain); - if (parameters.size() > 0) { - final List processed = new ArrayList(parameters.size()); - for (Parameter parameter : parameters) { - if (ParameterProcessor.applyAnnotations(swagger, parameter, type, annotations) != null) { - processed.add(parameter); - } - } - return processed; - } else { - LOGGER.debug("no parameter found, looking at body params"); - final List body = new ArrayList(); - if (!typesToSkip.contains(type)) { - Parameter param = ParameterProcessor.applyAnnotations(swagger, null, type, annotations); - if (param != null) { - body.add(param); - } - } - return body; - } - } - - public String extractOperationMethod(ApiOperation apiOperation, Method method, Iterator chain) { - if (apiOperation != null && apiOperation.httpMethod() != null && !"".equals(apiOperation.httpMethod())) { - return apiOperation.httpMethod().toLowerCase(); - //TODO add DE here! -// } else if (method.getAnnotation(javax.ws.rs.GET.class) != null) { -// return "get"; -// } else if (method.getAnnotation(javax.ws.rs.PUT.class) != null) { -// return "put"; -// } else if (method.getAnnotation(javax.ws.rs.POST.class) != null) { -// return "post"; -// } else if (method.getAnnotation(javax.ws.rs.DELETE.class) != null) { -// return "delete"; -// } else if (method.getAnnotation(javax.ws.rs.OPTIONS.class) != null) { -// return "options"; -// } else if (method.getAnnotation(javax.ws.rs.HEAD.class) != null) { -// return "head"; -// } else if (method.getAnnotation(PATCH.class) != null) { -// return "patch"; -// } else if (method.getAnnotation(HttpMethod.class) != null) { -// HttpMethod httpMethod = method.getAnnotation(HttpMethod.class); -// return httpMethod.value().toLowerCase(); - /*} else if(!StringUtils.isEmpty(getHttpMethodFromCustomAnnotations(method))) { - return getHttpMethodFromCustomAnnotations(method);*/ - } - - Uri uri = (Uri) method.getAnnotation(Uri.class); - if (uri != null) { - return uri.method().toString().toLowerCase(); - } - - if ((ReflectionUtils.getOverriddenMethod(method)) != null) { - return extractOperationMethod(apiOperation, ReflectionUtils.getOverriddenMethod(method), chain); - } else if (chain != null && chain.hasNext()) { - return chain.next().extractOperationMethod(apiOperation, method, chain); - } else { - return null; - } - } - - private void appendModels(Type type) { - final Map models = ModelConverters.getInstance().readAll(type); - for (Map.Entry entry : models.entrySet()) { - swagger.model(entry.getKey(), entry.getValue()); - } - } - - private boolean isIgnored(String path) { - for (String item : getIgnoredRoutes()) { - final int length = item.length(); - if (path.startsWith(item) && (path.length() == length || path.startsWith(PATH_DELIMITER, length))) { - return true; - } - } - return false; - } - - public String[] getIgnoredRoutes() { - return ignoredRoutes; - } - - public void setIgnoredRoutes(String[] ignoredRoutes) { - this.ignoredRoutes = ignoredRoutes; - } - - - enum ContainerWrapper { - LIST("list") { - @Override - protected Property doWrap(Property property) { - return new ArrayProperty(property); - } - }, - ARRAY("array") { - @Override - protected Property doWrap(Property property) { - return new ArrayProperty(property); - } - }, - MAP("map") { - @Override - protected Property doWrap(Property property) { - return new MapProperty(property); - } - }, - SET("set") { - @Override - protected Property doWrap(Property property) { - ArrayProperty arrayProperty = new ArrayProperty(property); - arrayProperty.setUniqueItems(true); - return arrayProperty; - } - }; - - private final String container; - - ContainerWrapper(String container) { - this.container = container; - } - - public static Property wrapContainer(String container, Property property, ContainerWrapper... allowed) { - final Set tmp = - allowed.length > 0 ? EnumSet.copyOf(Arrays.asList(allowed)) : EnumSet.allOf(ContainerWrapper.class); - for (ContainerWrapper wrapper : tmp) { - final Property prop = wrapper.wrap(container, property); - if (prop != null) { - return prop; - } - } - return property; - } - - public Property wrap(String container, Property property) { - if (this.container.equalsIgnoreCase(container)) { - return doWrap(property); - } - return null; - } - - protected abstract Property doWrap(Property property); - } -} diff --git a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/AbstractSwaggerExtension.java b/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/AbstractSwaggerExtension.java deleted file mode 100644 index 2811ead0..00000000 --- a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/AbstractSwaggerExtension.java +++ /dev/null @@ -1,54 +0,0 @@ -package eu.xenit.swagger.reader.ext; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; -import io.swagger.annotations.ApiOperation; -import io.swagger.models.parameters.Parameter; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -public abstract class AbstractSwaggerExtension implements SwaggerExtension { - - - public String extractOperationMethod(ApiOperation apiOperation, Method method, Iterator chain) { - if (chain.hasNext()) { - return chain.next().extractOperationMethod(apiOperation, method, chain); - } else { - return null; - } - } - - - public List extractParameters(List annotations, Type type, Set typesToSkip, - Iterator chain) { - if (chain.hasNext()) { - return chain.next().extractParameters(annotations, type, typesToSkip, chain); - } else { - return Collections.emptyList(); - } - } - - protected boolean shouldIgnoreClass(Class cls) { - return false; - } - - protected boolean shouldIgnoreType(Type type, Set typesToSkip) { - if (typesToSkip.contains(type)) { - return true; - } - if (shouldIgnoreClass(constructType(type).getRawClass())) { - typesToSkip.add(type); - return true; - } - return false; - } - - protected JavaType constructType(Type type) { - return TypeFactory.defaultInstance().constructType(type); - } -} diff --git a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/SwaggerExtension.java b/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/SwaggerExtension.java deleted file mode 100644 index ad15b5d7..00000000 --- a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/SwaggerExtension.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.xenit.swagger.reader.ext; - -import io.swagger.annotations.ApiOperation; -import io.swagger.models.parameters.Parameter; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -public interface SwaggerExtension { - - String extractOperationMethod(ApiOperation apiOperation, Method method, Iterator chain); - - List extractParameters(List annotations, Type type, Set typesToSkip, - Iterator chain); -} diff --git a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/SwaggerExtensions.java b/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/SwaggerExtensions.java deleted file mode 100644 index 0ff600d8..00000000 --- a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/ext/SwaggerExtensions.java +++ /dev/null @@ -1,40 +0,0 @@ -package eu.xenit.swagger.reader.ext; - -import eu.xenit.swagger.reader.DESwaggerExtension; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.ServiceLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SwaggerExtensions { - - static Logger LOGGER = LoggerFactory.getLogger(SwaggerExtensions.class); - - private static List extensions = null; - - static { - extensions = new ArrayList(); - ServiceLoader loader = ServiceLoader.load(SwaggerExtension.class); - Iterator itr = loader.iterator(); - while (itr.hasNext()) { - SwaggerExtension ext = itr.next(); - LOGGER.debug("adding extension " + ext); - extensions.add(ext); - } - extensions.add(new DESwaggerExtension()); - } - - public static List getExtensions() { - return extensions; - } - - public static void setExtensions(List ext) { - extensions = ext; - } - - public static Iterator chain() { - return extensions.iterator(); - } -} \ No newline at end of file diff --git a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/utils/ReaderUtils.java b/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/utils/ReaderUtils.java deleted file mode 100644 index 09cdca04..00000000 --- a/de-swagger-reader/src/main/java/eu/xenit/swagger/reader/utils/ReaderUtils.java +++ /dev/null @@ -1,135 +0,0 @@ -package eu.xenit.swagger.reader.utils; - -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; -import eu.xenit.swagger.reader.ext.SwaggerExtension; -import eu.xenit.swagger.reader.ext.SwaggerExtensions; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.Parameter; -import io.swagger.util.ParameterProcessor; -import io.swagger.util.ReflectionUtils; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -public class ReaderUtils { - - /** - * Collects constructor-level parameters from class. - * - * @param cls is a class for collecting - * @param swagger is the instance of the Swagger - * @return the collection of supported parameters - */ - public static List collectConstructorParameters(Class cls, Swagger swagger) { - if (cls.isLocalClass() || (cls.isMemberClass() && !Modifier.isStatic(cls.getModifiers()))) { - return Collections.emptyList(); - } - - List selected = Collections.emptyList(); - int maxParamsCount = 0; - - for (Constructor constructor : cls.getDeclaredConstructors()) { - if (!ReflectionUtils.isConstructorCompatible(constructor) - && !ReflectionUtils.isInject(Arrays.asList(constructor.getDeclaredAnnotations()))) { - continue; - } - - final Type[] genericParameterTypes = constructor.getGenericParameterTypes(); - final Annotation[][] annotations = constructor.getParameterAnnotations(); - - int paramsCount = 0; - final List parameters = new ArrayList(); - for (int i = 0; i < genericParameterTypes.length; i++) { - final List tmpAnnotations = Arrays.asList(annotations[i]); - if (isContext(tmpAnnotations)) { - paramsCount++; - } else { - final Type genericParameterType = genericParameterTypes[i]; - final List tmpParameters = collectParameters(genericParameterType, tmpAnnotations); - if (tmpParameters.size() >= 1) { - for (Parameter tmpParameter : tmpParameters) { - if (ParameterProcessor - .applyAnnotations(swagger, tmpParameter, genericParameterType, tmpAnnotations) - != null) { - parameters.add(tmpParameter); - } - } - paramsCount++; - } - } - } - - if (paramsCount >= maxParamsCount) { - maxParamsCount = paramsCount; - selected = parameters; - } - } - - return selected; - } - - /** - * Collects field-level parameters from class. - * - * @param cls is a class for collecting - * @param swagger is the instance of the Swagger - * @return the collection of supported parameters - */ - public static List collectFieldParameters(Class cls, Swagger swagger) { - final List parameters = new ArrayList(); - for (Field field : ReflectionUtils.getDeclaredFields(cls)) { - final List annotations = Arrays.asList(field.getAnnotations()); - final Type genericType = field.getGenericType(); - for (Parameter parameter : collectParameters(genericType, annotations)) { - if (ParameterProcessor.applyAnnotations(swagger, parameter, genericType, annotations) != null) { - parameters.add(parameter); - } - } - } - return parameters; - } - - /** - * Splits the provided array of strings into an array, using comma as the separator. Also removes leading and - * trailing whitespace and omits empty strings from the results. - * - * @param strings is the provided array of strings - * @return the resulted array of strings - */ - public static String[] splitContentValues(String[] strings) { - final Set result = new LinkedHashSet(); - - for (String string : strings) { - Iterables.addAll(result, Splitter.on(",").trimResults().omitEmptyStrings().split(string)); - } - - return result.toArray(new String[result.size()]); - } - - private static List collectParameters(Type type, List annotations) { - final Iterator chain = SwaggerExtensions.chain(); - return chain.hasNext() ? chain.next().extractParameters(annotations, type, new HashSet(), chain) : - Collections.emptyList(); - } - - private static boolean isContext(List annotations) { - // Remove jaxrs -// for (Annotation annotation : annotations) { -// if (annotation instanceof Context) { -// return true; -// } -// } - return false; - } -} diff --git a/de-swagger-reader/src/test/java/io/swagger/sample/reader/DEWebscript.java b/de-swagger-reader/src/test/java/io/swagger/sample/reader/DEWebscript.java deleted file mode 100644 index 67f49250..00000000 --- a/de-swagger-reader/src/test/java/io/swagger/sample/reader/DEWebscript.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.swagger.sample.reader; - -import com.github.dynamicextensionsalfresco.webscripts.annotations.RequestParam; -import com.github.dynamicextensionsalfresco.webscripts.annotations.Uri; -import com.github.dynamicextensionsalfresco.webscripts.annotations.UriVariable; -import com.github.dynamicextensionsalfresco.webscripts.annotations.WebScript; -import io.swagger.annotations.*; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; - -@SwaggerDefinition( - info = @Info( - description = "This is the swagger specification for a TEST moehahaha", - version = "1.33.7", - title = "Swagger Dynamic Extensions Test", - //termsOfService = "http://swagger.io/terms/", - contact = @Contact(name = "XeniT", email = "apix@xenit.eu", url = "http://www.xenit.eu") - //license = @License(name = "Apache 2.0", url = "http://www.apache.org/licenses/LICENSE-2.0.html") - ), - //consumes = {"application/json", "application/xml"}, - produces = {"application/json"}, - schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}, - tags = {@Tag(name = "core", description = "Core Operations")} -) -@WebScript(description = "Webscript description", baseUri = "/de") -public class DEWebscript { - - @Uri("/home") - @ApiOperation(value = "/home summary", tags = "core", notes = "/home description") - public SampleModel simpleGet(@RequestParam String requestParam, - @RequestParam(required = false) String requiredParam, - @UriVariable String pathParam, - SampleModel bodyParam) { - return new SampleModel(); - } - - @Uri("/ignoreParams") - public void ignoreParams(WebScriptRequest request, WebScriptResponse responose) { - - } -} diff --git a/de-swagger-reader/src/test/java/io/swagger/sample/reader/DEWebscriptSwaggerTest.java b/de-swagger-reader/src/test/java/io/swagger/sample/reader/DEWebscriptSwaggerTest.java deleted file mode 100644 index cca88d70..00000000 --- a/de-swagger-reader/src/test/java/io/swagger/sample/reader/DEWebscriptSwaggerTest.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.swagger.sample.reader; - - -import com.fasterxml.jackson.core.JsonProcessingException; -import eu.xenit.swagger.reader.Reader; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; -import io.swagger.util.Json; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -import static org.junit.Assert.*; - -/** - * Created by Michiel Huygen on 02/03/2016. - */ -public class DEWebscriptSwaggerTest { - - private final static Logger logger = LoggerFactory.getLogger(DEWebscriptSwaggerTest.class); - - private Reader reader; - - @Before - public void Setup() { - reader = new Reader(new Swagger()); - reader.read(DEWebscript.class); - } - - @After - public void TearDown() throws JsonProcessingException { - String json = Json.mapper().writeValueAsString(reader.getSwagger()); - logger.debug(json); - } - - @Test - public void TestParser() throws IOException { - - String json = Json.mapper().writeValueAsString(reader.getSwagger()); - logger.debug(json); - } - - @Test - public void TestDetectWebscriptAnnotation() throws JsonProcessingException { - assertFalse(reader.getSwagger().getPaths().isEmpty()); - } - - @Test - public void TestReadUriAnnotation() throws JsonProcessingException { - Path path = reader.getSwagger().getPath("/de/home"); // Testing basepath - assertNotNull(path); - - Operation get = path.getGet(); - - assertNotNull(get); - - assertEquals("core", get.getTags().get(0)); - assertEquals("/home summary", get.getSummary()); - assertEquals("/home description", get.getDescription()); - - logger.debug(path.toString()); - } - - @Test - public void TestReadParameters() throws JsonProcessingException { - - Path path = reader.getSwagger().getPath("/de/home"); - Operation get = path.getGet(); - logger.debug(get.getParameters().toString()); - - QueryParameter p1 = (QueryParameter) get.getParameters().get(0); - QueryParameter p2 = (QueryParameter) get.getParameters().get(1); - PathParameter p3 = (PathParameter) get.getParameters().get(2); - BodyParameter p4 = (BodyParameter) get.getParameters().get(3); - assertEquals("requestParam", p1.getName()); - assertEquals("query", p1.getIn()); - assertEquals(true, p1.getRequired()); - assertEquals("string", p1.getType()); - - assertEquals("requiredParam", p2.getName()); - assertEquals(false, p2.getRequired()); - assertEquals("string", p1.getType()); - - assertEquals("pathParam", p3.getName()); - assertEquals("path", p3.getIn()); - assertEquals("string", p1.getType()); - - assertEquals("body", p4.getName()); // Is this correct - assertEquals("body", p4.getIn()); - //assertEquals("body", p4.getSchema()); - - } - - @Test - public void TestIgnoreWebscriptRequestAndResponse() throws JsonProcessingException { - - Path path = reader.getSwagger().getPath("/de/ignoreParams"); - Operation get = path.getGet(); - assertEquals(0, get.getParameters().size()); - logger.debug(get.getParameters().toString()); - } - -} diff --git a/de-swagger-reader/src/test/java/io/swagger/sample/reader/ResourceListingIT.java b/de-swagger-reader/src/test/java/io/swagger/sample/reader/ResourceListingIT.java deleted file mode 100644 index b01d8ed7..00000000 --- a/de-swagger-reader/src/test/java/io/swagger/sample/reader/ResourceListingIT.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.swagger.sample.reader; - -import io.swagger.models.Info; -import io.swagger.models.Scheme; -import io.swagger.models.Swagger; -import io.swagger.models.Tag; -import io.swagger.util.Json; - -import org.junit.Assert; -import org.junit.Test; - -import java.io.IOException; -import java.net.URL; -import java.util.Arrays; -import java.util.Collections; - -public class ResourceListingIT { - - @Test - public void readerTest() throws IOException { - final Swagger swagger = Json.mapper() - .readValue(new URL("http://localhost:8002/api/swagger.json"), Swagger.class); - - Assert.assertNotNull(swagger); - - final Info info = swagger.getInfo(); - Assert.assertNotNull(info); - Assert.assertEquals(info.getDescription(), "This is a sample server"); - Assert.assertEquals(info.getVersion(), "1.0.0"); - Assert.assertEquals(info.getTitle(), "Swagger Sample Servlet"); - - Assert.assertEquals(info.getContact().getName(), "Sponge-Bob"); - Assert.assertEquals(info.getContact().getUrl(), "http://swagger.io"); - Assert.assertEquals(info.getContact().getEmail(), "apiteam@swagger.io"); - - Assert.assertEquals(info.getLicense().getName(), "Apache 2.0"); - Assert.assertEquals(info.getLicense().getUrl(), "http://www.apache.org/licenses/LICENSE-2.0.html"); - - Assert.assertEquals(swagger.getHost(), "localhost:8002"); - Assert.assertEquals(swagger.getSwagger(), "2.0"); - - Assert.assertEquals(swagger.getTags(), - Collections.singletonList(new Tag().name("users").description("Operations about user"))); - Assert.assertEquals(swagger.getSchemes(), Arrays.asList(Scheme.HTTP, Scheme.HTTPS)); - Assert.assertEquals(swagger.getConsumes(), Arrays.asList("application/json", "application/xml")); - Assert.assertEquals(swagger.getConsumes(), Arrays.asList("application/json", "application/xml")); - - Assert.assertNotNull(swagger.getPath("/sample/users/getUser")); - Assert.assertTrue(swagger.getDefinitions().containsKey("ApiResponse")); - Assert.assertTrue(swagger.getDefinitions().containsKey("SampleData")); - } -} diff --git a/de-swagger-reader/src/test/java/io/swagger/sample/reader/SampleModel.java b/de-swagger-reader/src/test/java/io/swagger/sample/reader/SampleModel.java deleted file mode 100644 index 6fc923ca..00000000 --- a/de-swagger-reader/src/test/java/io/swagger/sample/reader/SampleModel.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.swagger.sample.reader; - -/** - * Created by Michiel Huygen on 11/03/2016. - */ -public class SampleModel { - - public String field; -} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..142113c8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,13 @@ +# Documentation Alfred API + +## Generate website + +This directory generates the product documentation website at https://docs.xenit.eu/alfred-api/ + +This is done by manually executing: +```bash +./build-website.sh +``` + +This will generate a ZIP containing the documentation website, which still needs to be +uploaded and unzipped on our host. \ No newline at end of file diff --git a/docs/_hugo/config.yaml b/docs/_hugo/config.yaml new file mode 100644 index 00000000..f37ceb10 --- /dev/null +++ b/docs/_hugo/config.yaml @@ -0,0 +1,5 @@ +title: Alfred API +params: + metadata: + hugo-config: + product: api \ No newline at end of file diff --git a/docs/build-website.sh b/docs/build-website.sh new file mode 100755 index 00000000..f08cfb5e --- /dev/null +++ b/docs/build-website.sh @@ -0,0 +1,92 @@ +#!/bin/bash +set -e +scriptPath="$( dirname "${BASH_SOURCE[0]}" )" # cd to the directory containing the script +cd "$scriptPath" + +MARKDOWNTOPDF_VERSION=v1.1.4 +MARKDOWNTOWEBSITE_VERSION=1.0.5 + +MARKDOWNTOPDF_IMAGE="private.docker.xenit.eu/xenit-markdowntopdf:$MARKDOWNTOPDF_VERSION" +MARKDOWN_SPLITTER_IMAGE="private.docker.xenit.eu/customer/xenit/xenit-manuals-markdown-splitter:$MARKDOWNTOWEBSITE_VERSION" +MANUALS_HUGO_GENERATOR_IMAGE="private.docker.xenit.eu/customer/xenit/xenit-manuals-hugo-generator:$MARKDOWNTOWEBSITE_VERSION" + +WEIGHT=0 + +build_manual() { + echo "===== Building manual =====" + local productName="$1" + local versionName="$2" + shift 2; + + mkdir -p "build/normalized/$productName" + tar c --portability -C $versionName . | \ + docker run --rm -i $MARKDOWNTOPDF_IMAGE --tar \ + --template default -t markdown-simple_tables-multiline_tables-grid_tables --extract-media assets \ + --resource-path . \ + "$@" \ + -o normalized.md > "build/normalized/$productName/$versionName.tar" + sync +} + +split_manual() { + echo "===== Splitting manual =====" + local productName="$1" + local versionName="$2" + WEIGHT=$[$WEIGHT + 1] + mkdir -p "build/product/$productName" + tar tf "build/normalized/$productName/$versionName.tar" + cat "build/normalized/$productName/$versionName.tar" | docker run --rm -i $MARKDOWN_SPLITTER_IMAGE normalized.md "target-path=$versionName" "weight=$WEIGHT" > "build/normalized/$productName/$versionName-out.tar" + sync + tar xf "build/normalized/$productName/$versionName-out.tar" -C "build/product/$productName" +} + +build_and_split_manual() { + build_manual "$@" + split_manual "$1" "$2" +} + +build_product_website() { + echo "===== Building website =====" + local productName="$1" + mkdir -p "build/website/$productName" + cp -r "_hugo" "build/product/$productName/_hugo" + tar c --portability -C "build/product/$productName" . | \ + docker run --rm -i $MANUALS_HUGO_GENERATOR_IMAGE | \ + tar x -C "build/website/$productName" + sync +} + +build_javadoc() { + echo "===== Generating javadoc =====" + local productName="$1" + local alfredapidir=".." + + pushd "$alfredapidir" + ./gradlew clean :apix-interface:javadoc + popd + + local outputdir="build/website/$productName/" + mkdir -p "$outputdir" + cp -a "$alfredapidir/apix-interface/build/docs/javadoc" $outputdir +} + +build_swaggerdoc() { + echo "===== Copying swaggerdoc =====" + local productName="$1" + local outputdir="build/website/$productName/" + + mkdir -p $outputdir + cp -a "swagger-ui" $outputdir +} + +rm -rf build/ +build_and_split_manual alfred-api user "user-guide.md" +build_product_website alfred-api +build_javadoc alfred-api +build_swaggerdoc alfred-api + +find build/website -type f -name '*.html' -print0 | xargs -0 sed -i "/^<\!DOCTYPE html>$/a\ +\<\!-- alfred-docs@$(git describe --always --dirty) --\>" + +TODAY=$( date -I ) +tar czf "build/website-alfred-api_$TODAY.tar.gz" -C build/website . diff --git a/docs/swagger-ui/favicon-16x16.png b/docs/swagger-ui/favicon-16x16.png new file mode 100644 index 00000000..0f7e13b0 Binary files /dev/null and b/docs/swagger-ui/favicon-16x16.png differ diff --git a/docs/swagger-ui/favicon-32x32.png b/docs/swagger-ui/favicon-32x32.png new file mode 100644 index 00000000..b0a3352f Binary files /dev/null and b/docs/swagger-ui/favicon-32x32.png differ diff --git a/docs/swagger-ui/index.html b/docs/swagger-ui/index.html new file mode 100644 index 00000000..96857852 --- /dev/null +++ b/docs/swagger-ui/index.html @@ -0,0 +1,111 @@ + + + + + + + + Swagger UI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + + + \ No newline at end of file diff --git a/docs/swagger-ui/oauth2-redirect.html b/docs/swagger-ui/oauth2-redirect.html new file mode 100644 index 00000000..eb00dc68 --- /dev/null +++ b/docs/swagger-ui/oauth2-redirect.html @@ -0,0 +1,60 @@ + + + + + + diff --git a/docs/swagger-ui/swagger-ui-bundle.js b/docs/swagger-ui/swagger-ui-bundle.js new file mode 100644 index 00000000..385e8e51 --- /dev/null +++ b/docs/swagger-ui/swagger-ui-bundle.js @@ -0,0 +1,99 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/dist",t(t.s=1220)}([function(e,t,n){"use strict";e.exports=n(93)},function(e,t,n){e.exports=n(1001)()},function(e,t,n){"use strict";t.__esModule=!0,t.default=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){"use strict";t.__esModule=!0;var r=n(331),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default=function(){function e(e,t){for(var n=0;n>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?d(e)+t:t}function v(){return!0}function g(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function y(e,t){return b(e,t,0)}function _(e,t){return b(e,t,t)}function b(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}function x(e){this.next=e}function w(e,t,n,r){var i=0===e?t:1===e?n:[t,n];return r?r.value=i:r={value:i,done:!1},r}function k(){return{value:void 0,done:!0}}function E(e){return!!A(e)}function S(e){return e&&"function"==typeof e.next}function C(e){var t=A(e);return t&&t.call(e)}function A(e){var t=e&&(wn&&e[wn]||e[kn]);if("function"==typeof t)return t}function D(e){return e&&"number"==typeof e.length}function O(e){return null===e||void 0===e?B():o(e)?e.toSeq():z(e)}function M(e){return null===e||void 0===e?B().toKeyedSeq():o(e)?a(e)?e.toSeq():e.fromEntrySeq():L(e)}function T(e){return null===e||void 0===e?B():o(e)?a(e)?e.entrySeq():e.toIndexedSeq():q(e)}function P(e){return(null===e||void 0===e?B():o(e)?a(e)?e.entrySeq():e:q(e)).toSetSeq()}function I(e){this._array=e,this.size=e.length}function R(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function j(e){this._iterable=e,this.size=e.length||e.size}function F(e){this._iterator=e,this._iteratorCache=[]}function N(e){return!(!e||!e[Sn])}function B(){return Cn||(Cn=new I([]))}function L(e){var t=Array.isArray(e)?new I(e).fromEntrySeq():S(e)?new F(e).fromEntrySeq():E(e)?new j(e).fromEntrySeq():"object"==typeof e?new R(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function q(e){var t=U(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function z(e){var t=U(e)||"object"==typeof e&&new R(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}function U(e){return D(e)?new I(e):S(e)?new F(e):E(e)?new j(e):void 0}function W(e,t,n,r){var i=e._cache;if(i){for(var o=i.length-1,a=0;a<=o;a++){var s=i[n?o-a:a];if(!1===t(s[1],r?s[0]:a,e))return a+1}return a}return e.__iterateUncached(t,n)}function V(e,t,n,r){var i=e._cache;if(i){var o=i.length-1,a=0;return new x(function(){var e=i[n?o-a:a];return a++>o?k():w(t,r?e[0]:a-1,e[1])})}return e.__iteratorUncached(t,n)}function H(e,t){return t?G(t,e,"",{"":e}):J(e)}function G(e,t,n,r){return Array.isArray(t)?e.call(r,n,T(t).map(function(n,r){return G(e,n,r,t)})):K(t)?e.call(r,n,M(t).map(function(n,r){return G(e,n,r,t)})):t}function J(e){return Array.isArray(e)?T(e).map(J).toList():K(e)?M(e).map(J).toMap():e}function K(e){return e&&(e.constructor===Object||void 0===e.constructor)}function X(e,t){if(e===t||e!==e&&t!==t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if(e=e.valueOf(),t=t.valueOf(),e===t||e!==e&&t!==t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function Y(e,t){if(e===t)return!0;if(!o(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||a(e)!==a(t)||s(e)!==s(t)||l(e)!==l(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!u(e);if(l(e)){var r=e.entries();return t.every(function(e,t){var i=r.next().value;return i&&X(i[1],e)&&(n||X(i[0],t))})&&r.next().done}var i=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{i=!0;var c=e;e=t,t=c}var p=!0,f=t.__iterate(function(t,r){if(n?!e.has(t):i?!X(t,e.get(r,vn)):!X(e.get(r,vn),t))return p=!1,!1});return p&&e.size===f}function $(e,t){if(!(this instanceof $))return new $(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(An)return An;An=this}}function Z(e,t){if(!e)throw new Error(t)}function Q(e,t,n){if(!(this instanceof Q))return new Q(e,t,n);if(Z(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),t>>1&1073741824|3221225471&e}function oe(e){if(!1===e||null===e||void 0===e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null===e||void 0===e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!==e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)e/=4294967295,n^=e;return ie(n)}if("string"===t)return e.length>Fn?ae(e):se(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return ue(e);if("function"==typeof e.toString)return se(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function ae(e){var t=Ln[e];return void 0===t&&(t=se(e),Bn===Nn&&(Bn=0,Ln={}),Bn++,Ln[e]=t),t}function se(e){for(var t=0,n=0;n0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}function ce(e){Z(e!==1/0,"Cannot perform this action with an infinite size.")}function pe(e){return null===e||void 0===e?we():fe(e)&&!l(e)?e:we().withMutations(function(t){var r=n(e);ce(r.size),r.forEach(function(e,n){return t.set(n,e)})})}function fe(e){return!(!e||!e[qn])}function he(e,t){this.ownerID=e,this.entries=t}function de(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function me(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function ve(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function ge(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function ye(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&be(e._root)}function _e(e,t){return w(e,t[0],t[1])}function be(e,t){return{node:e,index:0,__prev:t}}function xe(e,t,n,r){var i=Object.create(zn);return i.size=e,i._root=t,i.__ownerID=n,i.__hash=r,i.__altered=!1,i}function we(){return Un||(Un=xe(0))}function ke(e,t,n){var r,i;if(e._root){var o=c(gn),a=c(yn);if(r=Ee(e._root,e.__ownerID,0,void 0,t,n,o,a),!a.value)return e;i=e.size+(o.value?n===vn?-1:1:0)}else{if(n===vn)return e;i=1,r=new he(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=i,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?xe(i,r):we()}function Ee(e,t,n,r,i,o,a,s){return e?e.update(t,n,r,i,o,a,s):o===vn?e:(p(s),p(a),new ge(t,r,[i,o]))}function Se(e){return e.constructor===ge||e.constructor===ve}function Ce(e,t,n,r,i){if(e.keyHash===r)return new ve(t,r,[e.entry,i]);var o,a=(0===n?e.keyHash:e.keyHash>>>n)&mn,s=(0===n?r:r>>>n)&mn;return new de(t,1<>>=1)a[s]=1&n?t[o++]:void 0;return a[r]=i,new me(e,o+1,a)}function Me(e,t,r){for(var i=[],a=0;a>1&1431655765,e=(858993459&e)+(e>>2&858993459),e=e+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function Fe(e,t,n,r){var i=r?e:h(e);return i[t]=n,i}function Ne(e,t,n,r){var i=e.length+1;if(r&&t+1===i)return e[t]=n,e;for(var o=new Array(i),a=0,s=0;s0&&io?0:o-n,l=a-n;return l>dn&&(l=dn),function(){if(i===l)return Xn;var e=t?--l:i++;return r&&r[e]}}function i(e,r,i){var s,u=e&&e.array,l=i>o?0:o-i>>r,c=1+(a-i>>r);return c>dn&&(c=dn),function(){for(;;){if(s){var e=s();if(e!==Xn)return e;s=null}if(l===c)return Xn;var o=t?--c:l++;s=n(u&&u[o],r-hn,i+(o<=e.size||t<0)return e.withMutations(function(e){t<0?Xe(e,t).set(0,n):Xe(e,0,t+1).set(t,n)});t+=e._origin;var r=e._tail,i=e._root,o=c(yn);return t>=$e(e._capacity)?r=Ge(r,e.__ownerID,0,t,n,o):i=Ge(i,e.__ownerID,e._level,t,n,o),o.value?e.__ownerID?(e._root=i,e._tail=r,e.__hash=void 0,e.__altered=!0,e):We(e._origin,e._capacity,e._level,i,r):e}function Ge(e,t,n,r,i,o){var a=r>>>n&mn,s=e&&a0){var l=e&&e.array[a],c=Ge(l,t,n-hn,r,i,o);return c===l?e:(u=Je(e,t),u.array[a]=c,u)}return s&&e.array[a]===i?e:(p(o),u=Je(e,t),void 0===i&&a===u.array.length-1?u.array.pop():u.array[a]=i,u)}function Je(e,t){return t&&e&&t===e.ownerID?e:new ze(e?e.array.slice():[],t)}function Ke(e,t){if(t>=$e(e._capacity))return e._tail;if(t<1<0;)n=n.array[t>>>r&mn],r-=hn;return n}}function Xe(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new f,i=e._origin,o=e._capacity,a=i+t,s=void 0===n?o:n<0?o+n:i+n;if(a===i&&s===o)return e;if(a>=s)return e.clear();for(var u=e._level,l=e._root,c=0;a+c<0;)l=new ze(l&&l.array.length?[void 0,l]:[],r),u+=hn,c+=1<=1<p?new ze([],r):d;if(d&&h>p&&ahn;g-=hn){var y=p>>>g&mn;v=v.array[y]=Je(v.array[y],r)}v.array[p>>>hn&mn]=d}if(s=h)a-=h,s-=h,u=hn,l=null,m=m&&m.removeBefore(r,0,a);else if(a>i||h>>u&mn;if(_!==h>>>u&mn)break;_&&(c+=(1<i&&(l=l.removeBefore(r,u,a-c)),l&&ha&&(a=l.size),o(u)||(l=l.map(function(e){return H(e)})),i.push(l)}return a>e.size&&(e=e.setSize(a)),Ie(e,t,i)}function $e(e){return e>>hn<=dn&&a.size>=2*o.size?(i=a.filter(function(e,t){return void 0!==e&&s!==t}),r=i.toKeyedSeq().map(function(e){return e[0]}).flip().toMap(),e.__ownerID&&(r.__ownerID=i.__ownerID=e.__ownerID)):(r=o.remove(t),i=s===a.size-1?a.pop():a.set(s,void 0))}else if(u){if(n===a.get(s)[1])return e;r=o,i=a.set(s,[t,n])}else r=o.set(t,a.size),i=a.set(a.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=i,e.__hash=void 0,e):et(r,i)}function rt(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function it(e){this._iter=e,this.size=e.size}function ot(e){this._iter=e,this.size=e.size}function at(e){this._iter=e,this.size=e.size}function st(e){var t=Dt(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=Ot,t.__iterateUncached=function(t,n){var r=this;return e.__iterate(function(e,n){return!1!==t(n,e,r)},n)},t.__iteratorUncached=function(t,n){if(t===xn){var r=e.__iterator(t,n);return new x(function(){var e=r.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e})}return e.__iterator(t===bn?_n:bn,n)},t}function ut(e,t,n){var r=Dt(e);return r.size=e.size,r.has=function(t){return e.has(t)},r.get=function(r,i){var o=e.get(r,vn);return o===vn?i:t.call(n,o,r,e)},r.__iterateUncached=function(r,i){var o=this;return e.__iterate(function(e,i,a){return!1!==r(t.call(n,e,i,a),i,o)},i)},r.__iteratorUncached=function(r,i){var o=e.__iterator(xn,i);return new x(function(){var i=o.next();if(i.done)return i;var a=i.value,s=a[0];return w(r,s,t.call(n,a[1],s,e),i)})},r}function lt(e,t){var n=Dt(e);return n._iter=e,n.size=e.size,n.reverse=function(){return e},e.flip&&(n.flip=function(){var t=st(e);return t.reverse=function(){return e.flip()},t}),n.get=function(n,r){return e.get(t?n:-1-n,r)},n.has=function(n){return e.has(t?n:-1-n)},n.includes=function(t){return e.includes(t)},n.cacheResult=Ot,n.__iterate=function(t,n){var r=this;return e.__iterate(function(e,n){return t(e,n,r)},!n)},n.__iterator=function(t,n){return e.__iterator(t,!n)},n}function ct(e,t,n,r){var i=Dt(e);return r&&(i.has=function(r){var i=e.get(r,vn);return i!==vn&&!!t.call(n,i,r,e)},i.get=function(r,i){var o=e.get(r,vn);return o!==vn&&t.call(n,o,r,e)?o:i}),i.__iterateUncached=function(i,o){var a=this,s=0;return e.__iterate(function(e,o,u){if(t.call(n,e,o,u))return s++,i(e,r?o:s-1,a)},o),s},i.__iteratorUncached=function(i,o){var a=e.__iterator(xn,o),s=0;return new x(function(){for(;;){var o=a.next();if(o.done)return o;var u=o.value,l=u[0],c=u[1];if(t.call(n,c,l,e))return w(i,r?l:s++,c,o)}})},i}function pt(e,t,n){var r=pe().asMutable();return e.__iterate(function(i,o){r.update(t.call(n,i,o,e),0,function(e){return e+1})}),r.asImmutable()}function ft(e,t,n){var r=a(e),i=(l(e)?Ze():pe()).asMutable();e.__iterate(function(o,a){i.update(t.call(n,o,a,e),function(e){return e=e||[],e.push(r?[a,o]:o),e})});var o=At(e);return i.map(function(t){return Et(e,o(t))})}function ht(e,t,n,r){var i=e.size;if(void 0!==t&&(t|=0),void 0!==n&&(n===1/0?n=i:n|=0),g(t,n,i))return e;var o=y(t,i),a=_(n,i);if(o!==o||a!==a)return ht(e.toSeq().cacheResult(),t,n,r);var s,u=a-o;u===u&&(s=u<0?0:u);var l=Dt(e);return l.size=0===s?s:e.size&&s||void 0,!r&&N(e)&&s>=0&&(l.get=function(t,n){return t=m(this,t),t>=0&&ts)return k();var e=i.next();return r||t===bn?e:t===_n?w(t,u-1,void 0,e):w(t,u-1,e.value[1],e)})},l}function dt(e,t,n){var r=Dt(e);return r.__iterateUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterate(r,i);var a=0;return e.__iterate(function(e,i,s){return t.call(n,e,i,s)&&++a&&r(e,i,o)}),a},r.__iteratorUncached=function(r,i){var o=this;if(i)return this.cacheResult().__iterator(r,i);var a=e.__iterator(xn,i),s=!0;return new x(function(){if(!s)return k();var e=a.next();if(e.done)return e;var i=e.value,u=i[0],l=i[1];return t.call(n,l,u,o)?r===xn?e:w(r,u,l,e):(s=!1,k())})},r}function mt(e,t,n,r){var i=Dt(e);return i.__iterateUncached=function(i,o){var a=this;if(o)return this.cacheResult().__iterate(i,o);var s=!0,u=0;return e.__iterate(function(e,o,l){if(!s||!(s=t.call(n,e,o,l)))return u++,i(e,r?o:u-1,a)}),u},i.__iteratorUncached=function(i,o){var a=this;if(o)return this.cacheResult().__iterator(i,o);var s=e.__iterator(xn,o),u=!0,l=0;return new x(function(){var e,o,c;do{if(e=s.next(),e.done)return r||i===bn?e:i===_n?w(i,l++,void 0,e):w(i,l++,e.value[1],e);var p=e.value;o=p[0],c=p[1],u&&(u=t.call(n,c,o,a))}while(u);return i===xn?e:w(i,o,c,e)})},i}function vt(e,t){var r=a(e),i=[e].concat(t).map(function(e){return o(e)?r&&(e=n(e)):e=r?L(e):q(Array.isArray(e)?e:[e]),e}).filter(function(e){return 0!==e.size});if(0===i.length)return e;if(1===i.length){var u=i[0];if(u===e||r&&a(u)||s(e)&&s(u))return u}var l=new I(i);return r?l=l.toKeyedSeq():s(e)||(l=l.toSetSeq()),l=l.flatten(!0),l.size=i.reduce(function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}},0),l}function gt(e,t,n){var r=Dt(e);return r.__iterateUncached=function(r,i){function a(e,l){var c=this;e.__iterate(function(e,i){return(!t||l0}function kt(e,n,r){var i=Dt(e);return i.size=new I(r).map(function(e){return e.size}).min(),i.__iterate=function(e,t){for(var n,r=this.__iterator(bn,t),i=0;!(n=r.next()).done&&!1!==e(n.value,i++,this););return i},i.__iteratorUncached=function(e,i){var o=r.map(function(e){return e=t(e),C(i?e.reverse():e)}),a=0,s=!1;return new x(function(){var t;return s||(t=o.map(function(e){return e.next()}),s=t.some(function(e){return e.done})),s?k():w(e,a++,n.apply(null,t.map(function(e){return e.value})))})},i}function Et(e,t){return N(e)?t:e.constructor(t)}function St(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function Ct(e){return ce(e.size),d(e)}function At(e){return a(e)?n:s(e)?r:i}function Dt(e){return Object.create((a(e)?M:s(e)?T:P).prototype)}function Ot(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):O.prototype.cacheResult.call(this)}function Mt(e,t){return e>t?1:et?-1:0}function on(e){if(e.size===1/0)return 0;var t=l(e),n=a(e),r=t?1:0;return an(e.__iterate(n?t?function(e,t){r=31*r+sn(oe(e),oe(t))|0}:function(e,t){r=r+sn(oe(e),oe(t))|0}:t?function(e){r=31*r+oe(e)|0}:function(e){r=r+oe(e)|0}),r)}function an(e,t){return t=Mn(t,3432918353),t=Mn(t<<15|t>>>-15,461845907),t=Mn(t<<13|t>>>-13,5),t=(t+3864292196|0)^e,t=Mn(t^t>>>16,2246822507),t=Mn(t^t>>>13,3266489909),t=ie(t^t>>>16)}function sn(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}var un=Array.prototype.slice;e(n,t),e(r,t),e(i,t),t.isIterable=o,t.isKeyed=a,t.isIndexed=s,t.isAssociative=u,t.isOrdered=l,t.Keyed=n,t.Indexed=r,t.Set=i;var ln="@@__IMMUTABLE_ITERABLE__@@",cn="@@__IMMUTABLE_KEYED__@@",pn="@@__IMMUTABLE_INDEXED__@@",fn="@@__IMMUTABLE_ORDERED__@@",hn=5,dn=1<r?k():w(e,i,n[t?r-i++:i++])})},e(R,M),R.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},R.prototype.has=function(e){return this._object.hasOwnProperty(e)},R.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,i=r.length-1,o=0;o<=i;o++){var a=r[t?i-o:o];if(!1===e(n[a],a,this))return o+1}return o},R.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,i=r.length-1,o=0;return new x(function(){var a=r[t?i-o:o];return o++>i?k():w(e,a,n[a])})},R.prototype[fn]=!0,e(j,T),j.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=this._iterable,r=C(n),i=0;if(S(r))for(var o;!(o=r.next()).done&&!1!==e(o.value,i++,this););return i},j.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=this._iterable,r=C(n);if(!S(r))return new x(k);var i=0;return new x(function(){var t=r.next();return t.done?t:w(e,i++,t.value)})},e(F,T),F.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n=this._iterator,r=this._iteratorCache,i=0;i=r.length){var t=n.next();if(t.done)return t;r[i]=t.value}return w(e,i,r[i++])})};var Cn;e($,T),$.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},$.prototype.get=function(e,t){return this.has(e)?this._value:t},$.prototype.includes=function(e){return X(this._value,e)},$.prototype.slice=function(e,t){var n=this.size;return g(e,t,n)?this:new $(this._value,_(t,n)-y(e,n))},$.prototype.reverse=function(){return this},$.prototype.indexOf=function(e){return X(this._value,e)?0:-1},$.prototype.lastIndexOf=function(e){return X(this._value,e)?this.size:-1},$.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?k():w(e,o++,a)})},Q.prototype.equals=function(e){return e instanceof Q?this._start===e._start&&this._end===e._end&&this._step===e._step:Y(this,e)};var Dn;e(ee,t),e(te,ee),e(ne,ee),e(re,ee),ee.Keyed=te,ee.Indexed=ne,ee.Set=re;var On,Mn="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){e|=0,t|=0;var n=65535&e,r=65535&t;return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0},Tn=Object.isExtensible,Pn=function(){try{return Object.defineProperty({},"@",{}),!0}catch(e){return!1}}(),In="function"==typeof WeakMap;In&&(On=new WeakMap);var Rn=0,jn="__immutablehash__";"function"==typeof Symbol&&(jn=Symbol(jn));var Fn=16,Nn=255,Bn=0,Ln={};e(pe,te),pe.of=function(){var e=un.call(arguments,0);return we().withMutations(function(t){for(var n=0;n=e.length)throw new Error("Missing value for key: "+e[n]);t.set(e[n],e[n+1])}})},pe.prototype.toString=function(){return this.__toString("Map {","}")},pe.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},pe.prototype.set=function(e,t){return ke(this,e,t)},pe.prototype.setIn=function(e,t){return this.updateIn(e,vn,function(){return t})},pe.prototype.remove=function(e){return ke(this,e,vn)},pe.prototype.deleteIn=function(e){return this.updateIn(e,function(){return vn})},pe.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},pe.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=Re(this,Tt(e),t,n);return r===vn?void 0:r},pe.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):we()},pe.prototype.merge=function(){return Me(this,void 0,arguments)},pe.prototype.mergeWith=function(e){return Me(this,e,un.call(arguments,1))},pe.prototype.mergeIn=function(e){var t=un.call(arguments,1);return this.updateIn(e,we(),function(e){return"function"==typeof e.merge?e.merge.apply(e,t):t[t.length-1]})},pe.prototype.mergeDeep=function(){return Me(this,Te,arguments)},pe.prototype.mergeDeepWith=function(e){var t=un.call(arguments,1);return Me(this,Pe(e),t)},pe.prototype.mergeDeepIn=function(e){var t=un.call(arguments,1);return this.updateIn(e,we(),function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,t):t[t.length-1]})},pe.prototype.sort=function(e){return Ze(bt(this,e))},pe.prototype.sortBy=function(e,t){return Ze(bt(this,t,e))},pe.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},pe.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new f)},pe.prototype.asImmutable=function(){return this.__ensureOwner()},pe.prototype.wasAltered=function(){return this.__altered},pe.prototype.__iterator=function(e,t){return new ye(this,e,t)},pe.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate(function(t){return r++,e(t[1],t[0],n)},t),r},pe.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?xe(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},pe.isMap=fe;var qn="@@__IMMUTABLE_MAP__@@",zn=pe.prototype;zn[qn]=!0,zn.delete=zn.remove,zn.removeIn=zn.deleteIn,he.prototype.get=function(e,t,n,r){for(var i=this.entries,o=0,a=i.length;o=Wn)return Ae(e,u,r,i);var d=e&&e===this.ownerID,m=d?u:h(u);return f?s?l===c-1?m.pop():m[l]=m.pop():m[l]=[r,i]:m.push([r,i]),d?(this.entries=m,this):new he(e,m)}},de.prototype.get=function(e,t,n,r){void 0===t&&(t=oe(n));var i=1<<((0===e?t:t>>>e)&mn),o=this.bitmap;return 0==(o&i)?r:this.nodes[je(o&i-1)].get(e+hn,t,n,r)},de.prototype.update=function(e,t,n,r,i,o,a){void 0===n&&(n=oe(r));var s=(0===t?n:n>>>t)&mn,u=1<=Vn)return Oe(e,f,l,s,d);if(c&&!d&&2===f.length&&Se(f[1^p]))return f[1^p];if(c&&d&&1===f.length&&Se(d))return d;var m=e&&e===this.ownerID,v=c?d?l:l^u:l|u,g=c?d?Fe(f,p,d,m):Be(f,p,m):Ne(f,p,d,m);return m?(this.bitmap=v,this.nodes=g,this):new de(e,v,g)},me.prototype.get=function(e,t,n,r){void 0===t&&(t=oe(n));var i=(0===e?t:t>>>e)&mn,o=this.nodes[i];return o?o.get(e+hn,t,n,r):r},me.prototype.update=function(e,t,n,r,i,o,a){void 0===n&&(n=oe(r));var s=(0===t?n:n>>>t)&mn,u=i===vn,l=this.nodes,c=l[s];if(u&&!c)return this;var p=Ee(c,e,t+hn,n,r,i,o,a);if(p===c)return this;var f=this.count;if(c){if(!p&&--f=0&&e>>t&mn;if(r>=this.array.length)return new ze([],e);var i,o=0===r;if(t>0){var a=this.array[r];if((i=a&&a.removeBefore(e,t-hn,n))===a&&o)return this}if(o&&!i)return this;var s=Je(this,e);if(!o)for(var u=0;u>>t&mn;if(r>=this.array.length)return this;var i;if(t>0){var o=this.array[r];if((i=o&&o.removeAfter(e,t-hn,n))===o&&r===this.array.length-1)return this}var a=Je(this,e);return a.array.splice(r+1),i&&(a.array[r]=i),a};var Kn,Xn={};e(Ze,pe),Ze.of=function(){return this(arguments)},Ze.prototype.toString=function(){return this.__toString("OrderedMap {","}")},Ze.prototype.get=function(e,t){var n=this._map.get(e);return void 0!==n?this._list.get(n)[1]:t},Ze.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):tt()},Ze.prototype.set=function(e,t){return nt(this,e,t)},Ze.prototype.remove=function(e){return nt(this,e,vn)},Ze.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Ze.prototype.__iterate=function(e,t){var n=this;return this._list.__iterate(function(t){return t&&e(t[1],t[0],n)},t)},Ze.prototype.__iterator=function(e,t){return this._list.fromEntrySeq().__iterator(e,t)},Ze.prototype.__ensureOwner=function(e){if(e===this.__ownerID)return this;var t=this._map.__ensureOwner(e),n=this._list.__ensureOwner(e);return e?et(t,n,e,this.__hash):(this.__ownerID=e,this._map=t,this._list=n,this)},Ze.isOrderedMap=Qe,Ze.prototype[fn]=!0,Ze.prototype.delete=Ze.prototype.remove;var Yn;e(rt,M),rt.prototype.get=function(e,t){return this._iter.get(e,t)},rt.prototype.has=function(e){return this._iter.has(e)},rt.prototype.valueSeq=function(){return this._iter.valueSeq()},rt.prototype.reverse=function(){var e=this,t=lt(this,!0);return this._useKeys||(t.valueSeq=function(){return e._iter.toSeq().reverse()}),t},rt.prototype.map=function(e,t){var n=this,r=ut(this,e,t);return this._useKeys||(r.valueSeq=function(){return n._iter.toSeq().map(e,t)}),r},rt.prototype.__iterate=function(e,t){var n,r=this;return this._iter.__iterate(this._useKeys?function(t,n){return e(t,n,r)}:(n=t?Ct(this):0,function(i){return e(i,t?--n:n++,r)}),t)},rt.prototype.__iterator=function(e,t){if(this._useKeys)return this._iter.__iterator(e,t);var n=this._iter.__iterator(bn,t),r=t?Ct(this):0;return new x(function(){var i=n.next();return i.done?i:w(e,t?--r:r++,i.value,i)})},rt.prototype[fn]=!0,e(it,T),it.prototype.includes=function(e){return this._iter.includes(e)},it.prototype.__iterate=function(e,t){var n=this,r=0;return this._iter.__iterate(function(t){return e(t,r++,n)},t)},it.prototype.__iterator=function(e,t){var n=this._iter.__iterator(bn,t),r=0;return new x(function(){var t=n.next();return t.done?t:w(e,r++,t.value,t)})},e(ot,P),ot.prototype.has=function(e){return this._iter.includes(e)},ot.prototype.__iterate=function(e,t){var n=this;return this._iter.__iterate(function(t){return e(t,t,n)},t)},ot.prototype.__iterator=function(e,t){var n=this._iter.__iterator(bn,t);return new x(function(){var t=n.next();return t.done?t:w(e,t.value,t.value,t)})},e(at,M),at.prototype.entrySeq=function(){return this._iter.toSeq()},at.prototype.__iterate=function(e,t){var n=this;return this._iter.__iterate(function(t){if(t){St(t);var r=o(t);return e(r?t.get(1):t[1],r?t.get(0):t[0],n)}},t)},at.prototype.__iterator=function(e,t){var n=this._iter.__iterator(bn,t);return new x(function(){for(;;){var t=n.next();if(t.done)return t;var r=t.value;if(r){St(r);var i=o(r);return w(e,i?r.get(0):r[0],i?r.get(1):r[1],t)}}})},it.prototype.cacheResult=rt.prototype.cacheResult=ot.prototype.cacheResult=at.prototype.cacheResult=Ot,e(Pt,te),Pt.prototype.toString=function(){return this.__toString(Rt(this)+" {","}")},Pt.prototype.has=function(e){return this._defaultValues.hasOwnProperty(e)},Pt.prototype.get=function(e,t){if(!this.has(e))return t;var n=this._defaultValues[e];return this._map?this._map.get(e,n):n},Pt.prototype.clear=function(){if(this.__ownerID)return this._map&&this._map.clear(),this;var e=this.constructor;return e._empty||(e._empty=It(this,we()))},Pt.prototype.set=function(e,t){if(!this.has(e))throw new Error('Cannot set unknown key "'+e+'" on '+Rt(this));if(this._map&&!this._map.has(e)){if(t===this._defaultValues[e])return this}var n=this._map&&this._map.set(e,t);return this.__ownerID||n===this._map?this:It(this,n)},Pt.prototype.remove=function(e){if(!this.has(e))return this;var t=this._map&&this._map.remove(e);return this.__ownerID||t===this._map?this:It(this,t)},Pt.prototype.wasAltered=function(){return this._map.wasAltered()},Pt.prototype.__iterator=function(e,t){var r=this;return n(this._defaultValues).map(function(e,t){return r.get(t)}).__iterator(e,t)},Pt.prototype.__iterate=function(e,t){var r=this;return n(this._defaultValues).map(function(e,t){return r.get(t)}).__iterate(e,t)},Pt.prototype.__ensureOwner=function(e){if(e===this.__ownerID)return this;var t=this._map&&this._map.__ensureOwner(e);return e?It(this,t,e):(this.__ownerID=e,this._map=t,this)};var $n=Pt.prototype;$n.delete=$n.remove,$n.deleteIn=$n.removeIn=zn.removeIn,$n.merge=zn.merge,$n.mergeWith=zn.mergeWith,$n.mergeIn=zn.mergeIn,$n.mergeDeep=zn.mergeDeep,$n.mergeDeepWith=zn.mergeDeepWith,$n.mergeDeepIn=zn.mergeDeepIn,$n.setIn=zn.setIn,$n.update=zn.update,$n.updateIn=zn.updateIn,$n.withMutations=zn.withMutations,$n.asMutable=zn.asMutable,$n.asImmutable=zn.asImmutable,e(Nt,re),Nt.of=function(){return this(arguments)},Nt.fromKeys=function(e){return this(n(e).keySeq())},Nt.prototype.toString=function(){return this.__toString("Set {","}")},Nt.prototype.has=function(e){return this._map.has(e)},Nt.prototype.add=function(e){return Lt(this,this._map.set(e,!0))},Nt.prototype.remove=function(e){return Lt(this,this._map.remove(e))},Nt.prototype.clear=function(){return Lt(this,this._map.clear())},Nt.prototype.union=function(){var e=un.call(arguments,0);return e=e.filter(function(e){return 0!==e.size}),0===e.length?this:0!==this.size||this.__ownerID||1!==e.length?this.withMutations(function(t){for(var n=0;n=0;n--)t={value:arguments[n],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):Kt(e,t)},Gt.prototype.pushAll=function(e){if(e=r(e),0===e.size)return this;ce(e.size);var t=this.size,n=this._head;return e.reverse().forEach(function(e){t++,n={value:e,next:n}}),this.__ownerID?(this.size=t,this._head=n,this.__hash=void 0,this.__altered=!0,this):Kt(t,n)},Gt.prototype.pop=function(){return this.slice(1)},Gt.prototype.unshift=function(){return this.push.apply(this,arguments)},Gt.prototype.unshiftAll=function(e){return this.pushAll(e)},Gt.prototype.shift=function(){return this.pop.apply(this,arguments)},Gt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Xt()},Gt.prototype.slice=function(e,t){if(g(e,t,this.size))return this;var n=y(e,this.size);if(_(t,this.size)!==this.size)return ne.prototype.slice.call(this,e,t);for(var r=this.size-n,i=this._head;n--;)i=i.next;return this.__ownerID?(this.size=r,this._head=i,this.__hash=void 0,this.__altered=!0,this):Kt(r,i)},Gt.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Kt(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Gt.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var n=0,r=this._head;r&&!1!==e(r.value,n++,this);)r=r.next;return n},Gt.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var n=0,r=this._head;return new x(function(){if(r){var t=r.value;return r=r.next,w(e,n++,t)}return k()})},Gt.isStack=Jt;var rr="@@__IMMUTABLE_STACK__@@",ir=Gt.prototype;ir[rr]=!0,ir.withMutations=zn.withMutations,ir.asMutable=zn.asMutable,ir.asImmutable=zn.asImmutable,ir.wasAltered=zn.wasAltered;var or;t.Iterator=x,Yt(t,{toArray:function(){ce(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate(function(t,n){e[n]=t}),e},toIndexedSeq:function(){return new it(this)},toJS:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJS?e.toJS():e}).__toJS()},toJSON:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e}).__toJS()},toKeyedSeq:function(){return new rt(this,!0)},toMap:function(){return pe(this.toKeyedSeq())},toObject:function(){ce(this.size);var e={};return this.__iterate(function(t,n){e[n]=t}),e},toOrderedMap:function(){return Ze(this.toKeyedSeq())},toOrderedSet:function(){return Ut(a(this)?this.valueSeq():this)},toSet:function(){return Nt(a(this)?this.valueSeq():this)},toSetSeq:function(){return new ot(this)},toSeq:function(){return s(this)?this.toIndexedSeq():a(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Gt(a(this)?this.valueSeq():this)},toList:function(){return Le(a(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){return Et(this,vt(this,un.call(arguments,0)))},includes:function(e){return this.some(function(t){return X(t,e)})},entries:function(){return this.__iterator(xn)},every:function(e,t){ce(this.size);var n=!0;return this.__iterate(function(r,i,o){if(!e.call(t,r,i,o))return n=!1,!1}),n},filter:function(e,t){return Et(this,ct(this,e,t,!0))},find:function(e,t,n){var r=this.findEntry(e,t);return r?r[1]:n},forEach:function(e,t){return ce(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){ce(this.size),e=void 0!==e?""+e:",";var t="",n=!0;return this.__iterate(function(r){n?n=!1:t+=e,t+=null!==r&&void 0!==r?r.toString():""}),t},keys:function(){return this.__iterator(_n)},map:function(e,t){return Et(this,ut(this,e,t))},reduce:function(e,t,n){ce(this.size);var r,i;return arguments.length<2?i=!0:r=t,this.__iterate(function(t,o,a){i?(i=!1,r=t):r=e.call(n,r,t,o,a)}),r},reduceRight:function(e,t,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return Et(this,lt(this,!0))},slice:function(e,t){return Et(this,ht(this,e,t,!0))},some:function(e,t){return!this.every(Qt(e),t)},sort:function(e){return Et(this,bt(this,e))},values:function(){return this.__iterator(bn)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(e,t){return d(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return pt(this,e,t)},equals:function(e){return Y(this,e)},entrySeq:function(){var e=this;if(e._cache)return new I(e._cache);var t=e.toSeq().map(Zt).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(Qt(e),t)},findEntry:function(e,t,n){var r=n;return this.__iterate(function(n,i,o){if(e.call(t,n,i,o))return r=[i,n],!1}),r},findKey:function(e,t){var n=this.findEntry(e,t);return n&&n[0]},findLast:function(e,t,n){return this.toKeyedSeq().reverse().find(e,t,n)},findLastEntry:function(e,t,n){return this.toKeyedSeq().reverse().findEntry(e,t,n)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(v)},flatMap:function(e,t){return Et(this,yt(this,e,t))},flatten:function(e){return Et(this,gt(this,e,!0))},fromEntrySeq:function(){return new at(this)},get:function(e,t){return this.find(function(t,n){return X(n,e)},void 0,t)},getIn:function(e,t){for(var n,r=this,i=Tt(e);!(n=i.next()).done;){var o=n.value;if((r=r&&r.get?r.get(o,vn):vn)===vn)return t}return r},groupBy:function(e,t){return ft(this,e,t)},has:function(e){return this.get(e,vn)!==vn},hasIn:function(e){return this.getIn(e,vn)!==vn},isSubset:function(e){return e="function"==typeof e.includes?e:t(e),this.every(function(t){return e.includes(t)})},isSuperset:function(e){return e="function"==typeof e.isSubset?e:t(e),e.isSubset(this)},keyOf:function(e){return this.findKey(function(t){return X(t,e)})},keySeq:function(){return this.toSeq().map($t).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return xt(this,e)},maxBy:function(e,t){return xt(this,t,e)},min:function(e){return xt(this,e?en(e):rn)},minBy:function(e,t){return xt(this,t?en(t):rn,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return Et(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return Et(this,mt(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(Qt(e),t)},sortBy:function(e,t){return Et(this,bt(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return Et(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return Et(this,dt(this,e,t))},takeUntil:function(e,t){return this.takeWhile(Qt(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=on(this))}});var ar=t.prototype;ar[ln]=!0,ar[En]=ar.values,ar.__toJS=ar.toArray,ar.__toStringMapper=tn,ar.inspect=ar.toSource=function(){return this.toString()},ar.chain=ar.flatMap,ar.contains=ar.includes,Yt(n,{flip:function(){return Et(this,st(this))},mapEntries:function(e,t){var n=this,r=0;return Et(this,this.toSeq().map(function(i,o){return e.call(t,[o,i],r++,n)}).fromEntrySeq())},mapKeys:function(e,t){var n=this;return Et(this,this.toSeq().flip().map(function(r,i){return e.call(t,r,i,n)}).flip())}});var sr=n.prototype;return sr[cn]=!0,sr[En]=ar.entries,sr.__toJS=ar.toObject,sr.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+tn(e)},Yt(r,{toKeyedSeq:function(){return new rt(this,!1)},filter:function(e,t){return Et(this,ct(this,e,t,!1))},findIndex:function(e,t){var n=this.findEntry(e,t);return n?n[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return Et(this,lt(this,!1))},slice:function(e,t){return Et(this,ht(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=y(e,e<0?this.count():this.size);var r=this.slice(0,e);return Et(this,1===n?r:r.concat(h(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var n=this.findLastEntry(e,t);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(e){return Et(this,gt(this,e,!1))},get:function(e,t){return e=m(this,e),e<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find(function(t,n){return n===e},void 0,t)},has:function(e){return(e=m(this,e))>=0&&(void 0!==this.size?this.size===1/0||e5e3)return e.textContent;return function(e){for(var n,r,i,o,a,s=e.textContent,u=0,l=s[0],c=1,p=e.innerHTML="",f=0;r=n,n=f<7&&"\\"==n?1:c;){if(c=l,l=s[++u],o=p.length>1,!c||f>8&&"\n"==c||[/\S/.test(c),1,1,!/[$\w]/.test(c),("/"==n||"\n"==n)&&o,'"'==n&&o,"'"==n&&o,s[u-4]+r+n=="--\x3e",r+n=="*/"][f])for(p&&(e.appendChild(a=t.createElement("span")).setAttribute("style",["color: #555; font-weight: bold;","","","color: #555;",""][f?f<3?2:f>6?4:f>3?3:+/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/.test(p):0]),a.appendChild(t.createTextNode(p))),i=f&&f<7?f:i,p="",f=11;![1,/[\/{}[(\-+*=<>:;|\\.,?!&@~]/.test(c),/[\])]/.test(c),/[$\w]/.test(c),"/"==c&&i<2&&"<"!=n,'"'==c,"'"==c,c+l+s[u+1]+s[u+2]=="\x3c!--",c+l=="/*",c+l=="//","#"==c][--f];);p+=c}}(e)}function b(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"key",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:B.default.Map();if(!B.default.Map.isMap(e)||!e.size)return B.default.List();if(Array.isArray(t)||(t=[t]),t.length<1)return e.merge(n);var r=B.default.List(),i=t[0],o=!0,a=!1,s=void 0;try{for(var u,l=(0,M.default)(e.entries());!(o=(u=l.next()).done);o=!0){var c=u.value,p=(0,D.default)(c,2),f=p[0],h=p[1],d=b(h,t.slice(1),n.set(i,f));r=B.default.List.isList(d)?r.concat(d):r.push(d)}}catch(e){a=!0,s=e}finally{try{!o&&l.return&&l.return()}finally{if(a)throw s}}return r}function x(e){return(0,W.default)((0,z.default)(e))}function w(e){return x(e.replace(/\.[^.\/]*$/,""))}function k(e){return"string"!=typeof e||""===e?"":(0,L.sanitizeUrl)(e)}function E(e){if(!B.default.OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;var t=e.find(function(e,t){return t.startsWith("2")&&(0,R.default)(e.get("content")||{}).length>0}),n=e.get("default")||B.default.OrderedMap(),r=(n.get("content")||B.default.OrderedMap()).keySeq().toJS(),i=r.length?n:null;return t||i}Object.defineProperty(t,"__esModule",{value:!0}),t.getExtensions=t.escapeDeepLinkPath=t.createDeepLinkPath=t.shallowEqualKeys=t.buildFormData=t.sorters=t.btoa=t.parseSearch=t.getSampleSchema=t.validateParam=t.validatePattern=t.validateMinLength=t.validateMaxLength=t.validateGuid=t.validateDateTime=t.validateString=t.validateBoolean=t.validateFile=t.validateInteger=t.validateNumber=t.validateMinimum=t.validateMaximum=t.propChecker=t.errorLog=t.memoize=t.isImmutable=void 0;var S=n(35),C=r(S),A=n(17),D=r(A),O=n(96),M=r(O),T=n(36),P=r(T),I=n(47),R=r(I),j=n(48),F=r(j);t.isJSONObject=i,t.objectify=o,t.arrayify=a,t.fromJSOrdered=s,t.bindToState=u,t.normalizeArray=l,t.isFn=c,t.isObject=p,t.isFunc=f,t.isArray=h,t.objMap=d,t.objReduce=m,t.systemThunkMiddleware=v,t.defaultStatusCode=g,t.getList=y,t.highlight=_,t.mapToList=b,t.pascalCase=x,t.pascalCaseFilename=w,t.sanitizeUrl=k,t.getAcceptControllingResponse=E;var N=n(8),B=r(N),L=n(503),q=n(944),z=r(q),U=n(426),W=r(U),V=n(423),H=r(V),G=n(225),J=r(G),K=n(960),X=r(K),Y=n(120),$=r(Y),Z=n(171),Q=n(53),ee=r(Q),te=n(700),ne=r(te),re="default",ie=t.isImmutable=function(e){return B.default.Iterable.isIterable(e)},oe=(t.memoize=H.default,t.errorLog=function(e){return function(){return function(t){return function(n){try{t(n)}catch(t){e().errActions.newThrownErr(t,n)}}}}},t.propChecker=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];return(0,R.default)(e).length!==(0,R.default)(t).length||((0,X.default)(e,function(e,n){if(r.includes(n))return!1;var i=t[n];return B.default.Iterable.isIterable(e)?!B.default.is(e,i):("object"!==(void 0===e?"undefined":(0,F.default)(e))||"object"!==(void 0===i?"undefined":(0,F.default)(i)))&&e!==i})||n.some(function(n){return!(0,$.default)(e[n],t[n])}))},t.validateMaximum=function(e,t){if(e>t)return"Value must be less than Maximum"}),ae=t.validateMinimum=function(e,t){if(et)return"Value must be less than MaxLength"},me=t.validateMinLength=function(e,t){if(e.length2&&void 0!==arguments[2]&&arguments[2],r=[],i=t&&"body"===e.get("in")?e.get("value_xml"):e.get("value"),o=e.get("required"),a=n?e.get("schema"):e;if(!a)return r;var s=a.get("maximum"),u=a.get("minimum"),l=a.get("type"),c=a.get("format"),p=a.get("maxLength"),f=a.get("minLength"),h=a.get("pattern");if(l&&(o||i)){var d="string"===l&&i,m="array"===l&&Array.isArray(i)&&i.length,v="array"===l&&B.default.List.isList(i)&&i.count(),g="file"===l&&i instanceof ee.default.File,y="boolean"===l&&(i||!1===i),_="number"===l&&(i||0===i),b="integer"===l&&(i||0===i);if(o&&!(d||m||v||g||y||_||b))return r.push("Required field is not provided"),r;if(h){var x=ve(i,h);x&&r.push(x)}if(p||0===p){var w=de(i,p);w&&r.push(w)}if(f){var k=me(i,f);k&&r.push(k)}if(s||0===s){var E=oe(i,s);E&&r.push(E)}if(u||0===u){var S=ae(i,u);S&&r.push(S)}if("string"===l){var C=void 0;if(!(C="date-time"===c?fe(i):"uuid"===c?he(i):pe(i)))return r;r.push(C)}else if("boolean"===l){var A=ce(i);if(!A)return r;r.push(A)}else if("number"===l){var D=se(i);if(!D)return r;r.push(D)}else if("integer"===l){var O=ue(i);if(!O)return r;r.push(O)}else if("array"===l){var M=void 0;if(!i.count())return r;M=a.getIn(["items","type"]),i.forEach(function(e,t){var n=void 0;"number"===M?n=se(e):"integer"===M?n=ue(e):"string"===M&&(n=pe(e)),n&&r.push({index:t,error:n})})}else if("file"===l){var T=le(i);if(!T)return r;r.push(T)}}return r},t.getSampleSchema=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(/xml/.test(t)){if(!e.xml||!e.xml.name){if(e.xml=e.xml||{},!e.$$ref)return e.type||e.items||e.properties||e.additionalProperties?'\n\x3c!-- XML example cannot be generated --\x3e':null;var r=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=r[1]}return(0,Z.memoizedCreateXMLExample)(e,n)}return(0,C.default)((0,Z.memoizedSampleFromSchema)(e,n),null,2)},t.parseSearch=function(){var e={},t=ee.default.location.search;if(!t)return{};if(""!=t){var n=t.substr(1).split("&");for(var r in n)r=n[r].split("="),e[decodeURIComponent(r[0])]=decodeURIComponent(r[1])}return e},t.btoa=function(t){var n=void 0;return n=t instanceof e?t:new e(t.toString(),"utf-8"),n.toString("base64")},t.sorters={operationsSorter:{alpha:function(e,t){return e.get("path").localeCompare(t.get("path"))},method:function(e,t){return e.get("method").localeCompare(t.get("method"))}},tagsSorter:{alpha:function(e,t){return e.localeCompare(t)}}},t.buildFormData=function(e){var t=[];for(var n in e){var r=e[n];void 0!==r&&""!==r&&t.push([n,"=",encodeURIComponent(r).replace(/%20/g,"+")].join(""))}return t.join("&")},t.shallowEqualKeys=function(e,t,n){return!!(0,J.default)(n,function(n){return(0,$.default)(e[n],t[n])})},t.createDeepLinkPath=function(e){return"string"==typeof e||e instanceof String?e.trim().replace(/\s/g,"_"):""});t.escapeDeepLinkPath=function(e){return(0,ne.default)(ge(e))},t.getExtensions=function(e){return e.filter(function(e,t){return/^x-/.test(t)})}}).call(t,n(41).Buffer)},function(e,t,n){"use strict";var r=n(32),i=r;e.exports=i},function(e,t,n){"use strict";function r(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,r=0;r6?s-6:0),l=6;l5?l-5:0),p=5;p5?a-5:0),u=5;u key("+c[p]+")"].concat(s));if(h instanceof Error)return h}}return i(t)}function u(e){return a(e,"List",b.List.isList)}function l(e,t,n,r){function o(){for(var i=arguments.length,o=Array(i),u=0;u5?s-5:0),l=5;l5?l-5:0),p=5;p>",w={listOf:u,mapOf:c,orderedMapOf:p,setOf:f,orderedSetOf:h,stackOf:d,iterableOf:m,recordOf:v,shape:y,contains:y,mapContains:_,list:o("List",b.List.isList),map:o("Map",b.Map.isMap),orderedMap:o("OrderedMap",b.OrderedMap.isOrderedMap),set:o("Set",b.Set.isSet),orderedSet:o("OrderedSet",b.OrderedSet.isOrderedSet),stack:o("Stack",b.Stack.isStack),seq:o("Seq",b.Seq.isSeq),record:o("Record",function(e){return e instanceof b.Record}),iterable:o("Iterable",b.Iterable.isIterable)};e.exports=w},function(e,t,n){var r=n(24),i=n(14),o=n(54),a=n(57),s=function(e,t,n){var u,l,c,p=e&s.F,f=e&s.G,h=e&s.S,d=e&s.P,m=e&s.B,v=e&s.W,g=f?i:i[t]||(i[t]={}),y=g.prototype,_=f?r:h?r[t]:(r[t]||{}).prototype;f&&(n=t);for(u in n)(l=!p&&_&&void 0!==_[u])&&u in g||(c=l?_[u]:n[u],g[u]=f&&"function"!=typeof _[u]?n[u]:m&&l?o(c,r):v&&_[u]==c?function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t.prototype=e.prototype,t}(c):d&&"function"==typeof c?o(Function.call,c):c,d&&((g.virtual||(g.virtual={}))[u]=c,e&s.R&&y&&!y[u]&&a(y,u,c)))};s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,e.exports=s},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){"use strict";var r=!("undefined"==typeof window||!window.document||!window.document.createElement),i={canUseDOM:r,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:r&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:r&&!!window.screen,isInWorker:!r};e.exports=i},function(e,t,n){"use strict";function r(e){return Object.prototype.toString.call(e)}function i(e){return"[object String]"===r(e)}function o(e,t){return!!e&&d.call(e,t)}function a(e){return[].slice.call(arguments,1).forEach(function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach(function(n){e[n]=t[n]})}}),e}function s(e){return e.indexOf("\\")<0?e:e.replace(m,"$1")}function u(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function l(e){if(e>65535){e-=65536;var t=55296+(e>>10),n=56320+(1023&e);return String.fromCharCode(t,n)}return String.fromCharCode(e)}function c(e,t){var n=0;return o(y,t)?y[t]:35===t.charCodeAt(0)&&g.test(t)&&(n="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10),u(n))?l(n):e}function p(e){return e.indexOf("&")<0?e:e.replace(v,c)}function f(e){return x[e]}function h(e){return _.test(e)?e.replace(b,f):e}var d=Object.prototype.hasOwnProperty,m=/\\([\\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g,v=/&([a-z#][a-z0-9]{1,31});/gi,g=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,y=n(486),_=/[&<>"]/,b=/[&<>"]/g,x={"&":"&","<":"<",">":">",'"':"""};t.assign=a,t.isString=i,t.has=o,t.unescapeMd=s,t.isValidEntityCode=u,t.fromCodePoint=l,t.replaceEntities=p,t.escapeHtml=h},function(e,t,n){"use strict";t.__esModule=!0;var r=n(562),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default=function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t"+i+""};e.exports=function(e,t){var n={};n[e]=t(s),r(r.P+r.F*i(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}),"String",n)}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){"use strict";function r(e){return function(){return e}}var i=function(){};i.thatReturns=r,i.thatReturnsFalse=r(!1),i.thatReturnsTrue=r(!0),i.thatReturnsNull=r(null),i.thatReturnsThis=function(){return this},i.thatReturnsArgument=function(e){return e},e.exports=i},function(e,t){function n(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function i(e){if(c===setTimeout)return setTimeout(e,0);if((c===n||!c)&&setTimeout)return c=setTimeout,setTimeout(e,0);try{return c(e,0)}catch(t){try{return c.call(null,e,0)}catch(t){return c.call(this,e,0)}}}function o(e){if(p===clearTimeout)return clearTimeout(e);if((p===r||!p)&&clearTimeout)return p=clearTimeout,clearTimeout(e);try{return p(e)}catch(t){try{return p.call(null,e)}catch(t){return p.call(this,e)}}}function a(){m&&h&&(m=!1,h.length?d=h.concat(d):v=-1,d.length&&s())}function s(){if(!m){var e=i(a);m=!0;for(var t=d.length;t;){for(h=d,d=[];++v1)for(var n=1;n=r())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+r().toString(16)+" bytes");return 0|e}function m(e){return+e!=e&&(e=0),o.alloc(+e)}function v(e,t){if(o.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return V(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return J(e).length;default:if(r)return V(e).length;t=(""+t).toLowerCase(),r=!0}}function g(e,t,n){var r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if(n>>>=0,t>>>=0,n<=t)return"";for(e||(e="utf8");;)switch(e){case"hex":return P(this,t,n);case"utf8":case"utf-8":return D(this,t,n);case"ascii":return M(this,t,n);case"latin1":case"binary":return T(this,t,n);case"base64":return A(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function y(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function _(e,t,n,r,i){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=i?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(i)return-1;n=e.length-1}else if(n<0){if(!i)return-1;n=0}if("string"==typeof t&&(t=o.from(t,r)),o.isBuffer(t))return 0===t.length?-1:b(e,t,n,r,i);if("number"==typeof t)return t&=255,o.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):b(e,[t],n,r,i);throw new TypeError("val must be string, number or Buffer")}function b(e,t,n,r,i){function o(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}var a=1,s=e.length,u=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,u/=2,n/=2}var l;if(i){var c=-1;for(l=n;ls&&(n=s-u),l=n;l>=0;l--){for(var p=!0,f=0;fi&&(r=i):r=i;var o=t.length;if(o%2!=0)throw new TypeError("Invalid hex string");r>o/2&&(r=o/2);for(var a=0;a239?4:o>223?3:o>191?2:1;if(i+s<=n){var u,l,c,p;switch(s){case 1:o<128&&(a=o);break;case 2:u=e[i+1],128==(192&u)&&(p=(31&o)<<6|63&u)>127&&(a=p);break;case 3:u=e[i+1],l=e[i+2],128==(192&u)&&128==(192&l)&&(p=(15&o)<<12|(63&u)<<6|63&l)>2047&&(p<55296||p>57343)&&(a=p);break;case 4:u=e[i+1],l=e[i+2],c=e[i+3],128==(192&u)&&128==(192&l)&&128==(192&c)&&(p=(15&o)<<18|(63&u)<<12|(63&l)<<6|63&c)>65535&&p<1114112&&(a=p)}}null===a?(a=65533,s=1):a>65535&&(a-=65536,r.push(a>>>10&1023|55296),a=56320|1023&a),r.push(a),i+=s}return O(r)}function O(e){var t=e.length;if(t<=Q)return String.fromCharCode.apply(String,e);for(var n="",r=0;rr)&&(n=r);for(var i="",o=t;on)throw new RangeError("Trying to access beyond buffer length")}function j(e,t,n,r,i,a){if(!o.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>i||te.length)throw new RangeError("Index out of range")}function F(e,t,n,r){t<0&&(t=65535+t+1);for(var i=0,o=Math.min(e.length-n,2);i>>8*(r?i:1-i)}function N(e,t,n,r){t<0&&(t=4294967295+t+1);for(var i=0,o=Math.min(e.length-n,4);i>>8*(r?i:3-i)&255}function B(e,t,n,r,i,o){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function L(e,t,n,r,i){return i||B(e,t,n,4,3.4028234663852886e38,-3.4028234663852886e38),$.write(e,t,n,r,23,4),n+4}function q(e,t,n,r,i){return i||B(e,t,n,8,1.7976931348623157e308,-1.7976931348623157e308),$.write(e,t,n,r,52,8),n+8}function z(e){if(e=U(e).replace(ee,""),e.length<2)return"";for(;e.length%4!=0;)e+="=";return e}function U(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function W(e){return e<16?"0"+e.toString(16):e.toString(16)}function V(e,t){t=t||1/0;for(var n,r=e.length,i=null,o=[],a=0;a55295&&n<57344){if(!i){if(n>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&o.push(239,191,189);continue}i=n;continue}if(n<56320){(t-=3)>-1&&o.push(239,191,189),i=n;continue}n=65536+(i-55296<<10|n-56320)}else i&&(t-=3)>-1&&o.push(239,191,189);if(i=null,n<128){if((t-=1)<0)break;o.push(n)}else if(n<2048){if((t-=2)<0)break;o.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;o.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return o}function H(e){for(var t=[],n=0;n>8,i=n%256,o.push(i),o.push(r);return o}function J(e){return Y.toByteArray(z(e))}function K(e,t,n,r){for(var i=0;i=t.length||i>=e.length);++i)t[i+n]=e[i];return i}function X(e){return e!==e}/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +var Y=n(569),$=n(768),Z=n(386);t.Buffer=o,t.SlowBuffer=m,t.INSPECT_MAX_BYTES=50,o.TYPED_ARRAY_SUPPORT=void 0!==e.TYPED_ARRAY_SUPPORT?e.TYPED_ARRAY_SUPPORT:function(){try{var e=new Uint8Array(1);return e.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===e.foo()&&"function"==typeof e.subarray&&0===e.subarray(1,1).byteLength}catch(e){return!1}}(),t.kMaxLength=r(),o.poolSize=8192,o._augment=function(e){return e.__proto__=o.prototype,e},o.from=function(e,t,n){return a(null,e,t,n)},o.TYPED_ARRAY_SUPPORT&&(o.prototype.__proto__=Uint8Array.prototype,o.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&o[Symbol.species]===o&&Object.defineProperty(o,Symbol.species,{value:null,configurable:!0})),o.alloc=function(e,t,n){return u(null,e,t,n)},o.allocUnsafe=function(e){return l(null,e)},o.allocUnsafeSlow=function(e){return l(null,e)},o.isBuffer=function(e){return!(null==e||!e._isBuffer)},o.compare=function(e,t){if(!o.isBuffer(e)||!o.isBuffer(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var n=e.length,r=t.length,i=0,a=Math.min(n,r);i0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},o.prototype.compare=function(e,t,n,r,i){if(!o.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===i&&(i=this.length),t<0||n>e.length||r<0||i>this.length)throw new RangeError("out of range index");if(r>=i&&t>=n)return 0;if(r>=i)return-1;if(t>=n)return 1;if(t>>>=0,n>>>=0,r>>>=0,i>>>=0,this===e)return 0;for(var a=i-r,s=n-t,u=Math.min(a,s),l=this.slice(r,i),c=e.slice(t,n),p=0;pi)&&(n=i),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var o=!1;;)switch(r){case"hex":return x(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":return k(this,e,t,n);case"latin1":case"binary":return E(this,e,t,n);case"base64":return S(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,e,t,n);default:if(o)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),o=!0}},o.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var Q=4096;o.prototype.slice=function(e,t){var n=this.length;e=~~e,t=void 0===t?n:~~t,e<0?(e+=n)<0&&(e=0):e>n&&(e=n),t<0?(t+=n)<0&&(t=0):t>n&&(t=n),t0&&(i*=256);)r+=this[e+--t]*i;return r},o.prototype.readUInt8=function(e,t){return t||R(e,1,this.length),this[e]},o.prototype.readUInt16LE=function(e,t){return t||R(e,2,this.length),this[e]|this[e+1]<<8},o.prototype.readUInt16BE=function(e,t){return t||R(e,2,this.length),this[e]<<8|this[e+1]},o.prototype.readUInt32LE=function(e,t){return t||R(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},o.prototype.readUInt32BE=function(e,t){return t||R(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},o.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||R(e,t,this.length);for(var r=this[e],i=1,o=0;++o=i&&(r-=Math.pow(2,8*t)),r},o.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||R(e,t,this.length);for(var r=t,i=1,o=this[e+--r];r>0&&(i*=256);)o+=this[e+--r]*i;return i*=128,o>=i&&(o-=Math.pow(2,8*t)),o},o.prototype.readInt8=function(e,t){return t||R(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},o.prototype.readInt16LE=function(e,t){t||R(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},o.prototype.readInt16BE=function(e,t){t||R(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},o.prototype.readInt32LE=function(e,t){return t||R(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},o.prototype.readInt32BE=function(e,t){return t||R(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},o.prototype.readFloatLE=function(e,t){return t||R(e,4,this.length),$.read(this,e,!0,23,4)},o.prototype.readFloatBE=function(e,t){return t||R(e,4,this.length),$.read(this,e,!1,23,4)},o.prototype.readDoubleLE=function(e,t){return t||R(e,8,this.length),$.read(this,e,!0,52,8)},o.prototype.readDoubleBE=function(e,t){return t||R(e,8,this.length),$.read(this,e,!1,52,8)},o.prototype.writeUIntLE=function(e,t,n,r){if(e=+e,t|=0,n|=0,!r){j(this,e,t,n,Math.pow(2,8*n)-1,0)}var i=1,o=0;for(this[t]=255&e;++o=0&&(o*=256);)this[t+i]=e/o&255;return t+n},o.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,1,255,0),o.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},o.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,65535,0),o.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):F(this,e,t,!0),t+2},o.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,65535,0),o.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):F(this,e,t,!1),t+2},o.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,4294967295,0),o.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):N(this,e,t,!0),t+4},o.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,4294967295,0),o.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},o.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var i=Math.pow(2,8*n-1);j(this,e,t,n,i-1,-i)}var o=0,a=1,s=0;for(this[t]=255&e;++o>0)-s&255;return t+n},o.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var i=Math.pow(2,8*n-1);j(this,e,t,n,i-1,-i)}var o=n-1,a=1,s=0;for(this[t+o]=255&e;--o>=0&&(a*=256);)e<0&&0===s&&0!==this[t+o+1]&&(s=1),this[t+o]=(e/a>>0)-s&255;return t+n},o.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,1,127,-128),o.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},o.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,32767,-32768),o.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):F(this,e,t,!0),t+2},o.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,32767,-32768),o.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):F(this,e,t,!1),t+2},o.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,2147483647,-2147483648),o.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):N(this,e,t,!0),t+4},o.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),o.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},o.prototype.writeFloatLE=function(e,t,n){return L(this,e,t,!0,n)},o.prototype.writeFloatBE=function(e,t,n){return L(this,e,t,!1,n)},o.prototype.writeDoubleLE=function(e,t,n){return q(this,e,t,!0,n)},o.prototype.writeDoubleBE=function(e,t,n){return q(this,e,t,!1,n)},o.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--i)e[i+t]=this[i+n];else if(a<1e3||!o.TYPED_ARRAY_SUPPORT)for(i=0;i>>=0,n=void 0===n?this.length:n>>>0,e||(e=0);var a;if("number"==typeof e)for(a=t;a0&&(a=this.buffer[u-1],e.call(r,a)<0);)if(u--,this.pointer-u>n/2-1){o=" ... ",u+=5;break}for(l="",i=this.pointer;in/2-1){l=" ... ",i-=5;break}return""+new Array(t).join(" ")+o+this.buffer.slice(u,i)+l+"\n"+new Array(t+this.pointer-u+o.length).join(" ")+"^"},t.prototype.toString=function(){var e,t;return e=this.get_snippet(),t=" on line "+(this.line+1)+", column "+(this.column+1),e?t:t+":\n"+e},t}(),this.YAMLError=function(e){function n(e){this.message=e,n.__super__.constructor.call(this),this.stack=this.toString()+"\n"+(new Error).stack.split("\n").slice(1).join("\n")}return t(n,e),n.prototype.toString=function(){return this.message},n}(Error),this.MarkedYAMLError=function(e){function n(e,t,r,i,o){this.context=e,this.context_mark=t,this.problem=r,this.problem_mark=i,this.note=o,n.__super__.constructor.call(this)}return t(n,e),n.prototype.toString=function(){var e;return e=[],null!=this.context&&e.push(this.context),null==this.context_mark||null!=this.problem&&null!=this.problem_mark&&this.context_mark.line===this.problem_mark.line&&this.context_mark.column===this.problem_mark.column||e.push(this.context_mark.toString()),null!=this.problem&&e.push(this.problem),null!=this.problem_mark&&e.push(this.problem_mark.toString()),null!=this.note&&e.push(this.note),e.join("\n")},n}(this.YAMLError)}).call(this)},function(e,t,n){e.exports={default:n(596),__esModule:!0}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(566),o=r(i),a=n(565),s=r(a),u="function"==typeof s.default&&"symbol"==typeof o.default?function(e){return typeof e}:function(e){return e&&"function"==typeof s.default&&e.constructor===s.default&&e!==s.default.prototype?"symbol":typeof e};t.default="function"==typeof s.default&&"symbol"===u(o.default)?function(e){return void 0===e?"undefined":u(e)}:function(e){return e&&"function"==typeof s.default&&e.constructor===s.default&&e!==s.default.prototype?"symbol":void 0===e?"undefined":u(e)}},function(e,t,n){e.exports=!n(55)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t,n){"use strict";function r(e,t,n){return n?[e,t]:e}Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,e.exports=t.default},function(e,t,n){"use strict";function r(e,t,n,r){this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=n;var i=this.constructor.Interface;for(var o in i)if(i.hasOwnProperty(o)){var s=i[o];s?this[o]=s(n):"target"===o?this.target=r:this[o]=n[o]}var u=null!=n.defaultPrevented?n.defaultPrevented:!1===n.returnValue;return this.isDefaultPrevented=u?a.thatReturnsTrue:a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse,this}var i=n(12),o=n(71),a=n(32),s=(n(10),["dispatchConfig","_targetInst","nativeEvent","isDefaultPrevented","isPropagationStopped","_dispatchListeners","_dispatchInstances"]),u={type:null,target:null,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};i(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=a.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=a.thatReturnsTrue)},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;for(var n=0;n1?t-1:0),i=1;i2?n-2:0),o=2;o=n?e:e.length+1===n?""+t+e:""+new Array(n-e.length+1).join(t)+e},this.to_hex=function(e){return"string"==typeof e&&(e=e.charCodeAt(0)),e.toString(16)}}).call(this)}).call(t,n(16))},function(e,t,n){var r=n(78);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t){var n=e.exports={version:"2.5.1"};"number"==typeof __e&&(__e=n)},function(e,t,n){var r=n(138),i=n(359);e.exports=n(106)?function(e,t,n){return r.f(e,t,i(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";var r=n(729),i=Math.max;e.exports=function(e){return i(0,r(e))}},function(e,t,n){function r(e){return null==e?void 0===e?u:s:(e=Object(e),l&&l in e?o(e):a(e))}var i=n(83),o=n(900),a=n(929),s="[object Null]",u="[object Undefined]",l=i?i.toStringTag:void 0;e.exports=r},function(e,t,n){function r(e,t){var n=o(e,t);return i(n)?n:void 0}var i=n(858),o=n(901);e.exports=r},function(e,t){function n(e){return null!=e&&"object"==typeof e}e.exports=n},function(e,t,n){"use strict"},function(e,t,n){"use strict";var r=n(11),i=(n(7),function(e){var t=this;if(t.instancePool.length){var n=t.instancePool.pop();return t.call(n,e),n}return new t(e)}),o=function(e,t){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,e,t),r}return new n(e,t)},a=function(e,t,n){var r=this;if(r.instancePool.length){var i=r.instancePool.pop();return r.call(i,e,t,n),i}return new r(e,t,n)},s=function(e,t,n,r){var i=this;if(i.instancePool.length){var o=i.instancePool.pop();return i.call(o,e,t,n,r),o}return new i(e,t,n,r)},u=function(e){var t=this;e instanceof t||r("25"),e.destructor(),t.instancePool.length`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>",u="]",l=new RegExp("^(?:<[A-Za-z][A-Za-z0-9-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>|]|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|[<][?].*?[?][>]|]*>|)","i"),c=/[\\&]/,p="[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]",f=new RegExp("\\\\"+p+"|"+a,"gi"),h=new RegExp('[&<>"]',"g"),d=new RegExp(a+'|[&<>"]',"gi"),m=function(e){return 92===e.charCodeAt(0)?e.charAt(1):o(e)},v=function(e){return c.test(e)?e.replace(f,m):e},g=function(e){try{return r(i(e))}catch(t){return e}},y=function(e){switch(e){case"&":return"&";case"<":return"<";case">":return">";case'"':return""";default:return e}},_=function(e,t){return h.test(e)?t?e.replace(d,y):e.replace(h,y):e};e.exports={unescapeString:v,normalizeURI:g,escapeXml:_,reHtmlTag:l,OPENTAG:s,CLOSETAG:u,ENTITY:a,ESCAPABLE:p}},function(e,t){e.exports={}},function(e,t,n){var r=n(183),i=n(180);e.exports=function(e){return r(i(e))}},function(e,t,n){var r=n(180);e.exports=function(e){return Object(r(e))}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){var r=n(31),i=n(65),o=n(108),a=n(204)("src"),s=Function.toString,u=(""+s).split("toString");n(64).inspectSource=function(e){return s.call(e)},(e.exports=function(e,t,n,s){var l="function"==typeof n;l&&(o(n,"name")||i(n,"name",t)),e[t]!==n&&(l&&(o(n,a)||i(n,a,e[t]?""+e[t]:u.join(String(t)))),e===r?e[t]=n:s?e[t]?e[t]=n:i(e,t,n):(delete e[t],i(e,t,n)))})(Function.prototype,"toString",function(){return"function"==typeof this&&this[a]||s.call(this)})},function(e,t,n){"use strict";var r=n(371)();e.exports=function(e){return e!==r&&null!==e}},function(e,t,n){"use strict";function r(e){return void 0===e||null===e}function i(e){return"object"==typeof e&&null!==e}function o(e){return Array.isArray(e)?e:r(e)?[]:[e]}function a(e,t){var n,r,i,o;if(t)for(o=Object.keys(t),n=0,r=o.length;n`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>",u="]",l=new RegExp("^(?:<[A-Za-z][A-Za-z0-9-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>|]|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|[<][?].*?[?][>]|]*>|)","i"),c=/[\\&]/,p="[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]",f=new RegExp("\\\\"+p+"|"+a,"gi"),h=new RegExp('[&<>"]',"g"),d=new RegExp(a+'|[&<>"]',"gi"),m=function(e){return 92===e.charCodeAt(0)?e.charAt(1):o(e)},v=function(e){return c.test(e)?e.replace(f,m):e},g=function(e){try{return r(i(e))}catch(t){return e}},y=function(e){switch(e){case"&":return"&";case"<":return"<";case">":return">";case'"':return""";default:return e}},_=function(e,t){return h.test(e)?t?e.replace(d,y):e.replace(h,y):e};e.exports={unescapeString:v,normalizeURI:g,escapeXml:_,reHtmlTag:l,OPENTAG:s,CLOSETAG:u,ENTITY:a,ESCAPABLE:p}},function(e,t,n){"use strict";var r=n(12),i=n(472),o=n(1104),a=n(1105),s=n(94),u=n(1106),l=n(1107),c=n(1108),p=n(1112),f=s.createElement,h=s.createFactory,d=s.cloneElement,m=r,v=function(e){return e},g={Children:{map:o.map,forEach:o.forEach,count:o.count,toArray:o.toArray,only:p},Component:i.Component,PureComponent:i.PureComponent,createElement:f,cloneElement:d,isValidElement:s.isValidElement,PropTypes:u,createClass:c,createFactory:h,createMixin:v,DOM:a,version:l,__spread:m};e.exports=g},function(e,t,n){"use strict";function r(e){return void 0!==e.ref}function i(e){return void 0!==e.key}var o=n(12),a=n(52),s=(n(10),n(476),Object.prototype.hasOwnProperty),u=n(474),l={key:!0,ref:!0,__self:!0,__source:!0},c=function(e,t,n,r,i,o,a){var s={$$typeof:u,type:e,key:t,ref:n,props:a,_owner:o};return s};c.createElement=function(e,t,n){var o,u={},p=null,f=null;if(null!=t){r(t)&&(f=t.ref),i(t)&&(p=""+t.key),void 0===t.__self?null:t.__self,void 0===t.__source?null:t.__source;for(o in t)s.call(t,o)&&!l.hasOwnProperty(o)&&(u[o]=t[o])}var h=arguments.length-2;if(1===h)u.children=n;else if(h>1){for(var d=Array(h),m=0;m1){for(var g=Array(v),y=0;y=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){var r=n(344),i=n(182);e.exports=Object.keys||function(e){return r(e,i)}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){var r=n(42).f,i=n(56),o=n(21)("toStringTag");e.exports=function(e,t,n){e&&!i(e=n?e:e.prototype,o)&&r(e,o,{configurable:!0,value:t})}},function(e,t,n){"use strict";var r=n(620)(!0);n(338)(String,"String",function(e){this._t=String(e),this._i=0},function(){var e,t=this._t,n=this._i;return n>=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){n(625);for(var r=n(24),i=n(57),o=n(75),a=n(21)("toStringTag"),s="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),u=0;u0?i(r(e),9007199254740991):0}},function(e,t,n){(function(e){function n(e){return Array.isArray?Array.isArray(e):"[object Array]"===v(e)}function r(e){return"boolean"==typeof e}function i(e){return null===e}function o(e){return null==e}function a(e){return"number"==typeof e}function s(e){return"string"==typeof e}function u(e){return"symbol"==typeof e}function l(e){return void 0===e}function c(e){return"[object RegExp]"===v(e)}function p(e){return"object"==typeof e&&null!==e}function f(e){return"[object Date]"===v(e)}function h(e){return"[object Error]"===v(e)||e instanceof Error}function d(e){return"function"==typeof e}function m(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e}function v(e){return Object.prototype.toString.call(e)}t.isArray=n,t.isBoolean=r,t.isNull=i,t.isNullOrUndefined=o,t.isNumber=a,t.isString=s,t.isSymbol=u,t.isUndefined=l,t.isRegExp=c,t.isObject=p,t.isDate=f,t.isError=h,t.isFunction=d,t.isPrimitive=m,t.isBuffer=e.isBuffer}).call(t,n(41).Buffer)},function(e,t,n){"use strict";function r(e){return"string"==typeof e&&i.test(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=/-webkit-|-moz-|-ms-/;e.exports=t.default},function(e,t){e.exports={Text:"text",Directive:"directive",Comment:"comment",Script:"script",Style:"style",Tag:"tag",CDATA:"cdata",Doctype:"doctype",isTag:function(e){return"tag"===e.type||"script"===e.type||"style"===e.type}}},function(e,t,n){var r=n(715),i=n(714);t.decode=function(e,t){return(!t||t<=0?i.XML:i.HTML)(e)},t.decodeStrict=function(e,t){return(!t||t<=0?i.XML:i.HTMLStrict)(e)},t.encode=function(e,t){return(!t||t<=0?r.XML:r.HTML)(e)},t.encodeXML=r.XML,t.encodeHTML4=t.encodeHTML5=t.encodeHTML=r.HTML,t.decodeXML=t.decodeXMLStrict=i.XML,t.decodeHTML4=t.decodeHTML5=t.decodeHTML=i.HTML,t.decodeHTML4Strict=t.decodeHTML5Strict=t.decodeHTMLStrict=i.HTMLStrict,t.escape=r.escape},function(e,t,n){"use strict";var r=n(80);e.exports=function(e){if(!r(e))throw new TypeError("Cannot use null or undefined");return e}},function(e,t,n){function r(t,n){return delete e.exports[t],e.exports[t]=n,n}var i=n(379),o=n(703);e.exports={Parser:i,Tokenizer:n(380),ElementType:n(113),DomHandler:o,get FeedHandler(){return r("FeedHandler",n(764))},get Stream(){return r("Stream",n(766))},get WritableStream(){return r("WritableStream",n(381))},get ProxyHandler(){return r("ProxyHandler",n(765))},get DomUtils(){return r("DomUtils",n(705))},get CollectingHandler(){return r("CollectingHandler",n(763))},DefaultHandler:o,get RssHandler(){return r("RssHandler",this.FeedHandler)},parseDOM:function(e,t){var n=new o(t);return new i(n,t).end(e),n.dom},parseFeed:function(t,n){var r=new e.exports.FeedHandler(n);return new i(r,n).end(t),r.dom},createDomStream:function(e,t,n){var r=new o(e,t,n);return new i(r,t)},EVENTS:{attribute:2,cdatastart:0,cdataend:0,text:1,processinginstruction:2,comment:1,commentend:0,closetag:1,opentag:2,opentagname:1,error:1,end:0}}},function(e,t,n){"use strict";function r(e,t){Error.call(this),this.name="YAMLException",this.reason=e,this.mark=t,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():""),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||""}r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r.prototype.toString=function(e){var t=this.name+": ";return t+=this.reason||"(unknown reason)",!e&&this.mark&&(t+=" "+this.mark.toString()),t},e.exports=r},function(e,t,n){"use strict";var r=n(82);e.exports=new r({include:[n(387)],implicit:[n(818),n(811)],explicit:[n(803),n(813),n(814),n(816)]})},function(e,t,n){function r(e){return"function"==typeof e?e:null==e?a:"object"==typeof e?s(e)?o(e[0],e[1]):i(e):u(e)}var i=n(862),o=n(863),a=n(227),s=n(19),u=n(957);e.exports=r},function(e,t){function n(e,t){return e===t||e!==e&&t!==t}e.exports=n},function(e,t){function n(e,t,n){if(t in e)return e[t];if(3===arguments.length)return n;throw new Error('"'+t+'" is a required argument.')}function r(e){var t=e.match(y);return t?{scheme:t[1],auth:t[2],host:t[3],port:t[4],path:t[5]}:null}function i(e){var t="";return e.scheme&&(t+=e.scheme+":"),t+="//",e.auth&&(t+=e.auth+"@"),e.host&&(t+=e.host),e.port&&(t+=":"+e.port),e.path&&(t+=e.path),t}function o(e){var n=e,o=r(e);if(o){if(!o.path)return e;n=o.path}for(var a,s=t.isAbsolute(n),u=n.split(/\/+/),l=0,c=u.length-1;c>=0;c--)a=u[c],"."===a?u.splice(c,1):".."===a?l++:l>0&&(""===a?(u.splice(c+1,l),l=0):(u.splice(c,2),l--));return n=u.join("/"),""===n&&(n=s?"/":"."),o?(o.path=n,i(o)):n}function a(e,t){""===e&&(e="."),""===t&&(t=".");var n=r(t),a=r(e);if(a&&(e=a.path||"/"),n&&!n.scheme)return a&&(n.scheme=a.scheme),i(n);if(n||t.match(_))return t;if(a&&!a.host&&!a.path)return a.host=t,i(a);var s="/"===t.charAt(0)?t:o(e.replace(/\/+$/,"")+"/"+t);return a?(a.path=s,i(a)):s}function s(e,t){""===e&&(e="."),e=e.replace(/\/$/,"");for(var n=0;0!==t.indexOf(e+"/");){var r=e.lastIndexOf("/");if(r<0)return t;if(e=e.slice(0,r),e.match(/^([^\/]+:\/)?\/*$/))return t;++n}return Array(n+1).join("../")+t.substr(e.length+1)}function u(e){return e}function l(e){return p(e)?"$"+e:e}function c(e){return p(e)?e.slice(1):e}function p(e){if(!e)return!1;var t=e.length;if(t<9)return!1;if(95!==e.charCodeAt(t-1)||95!==e.charCodeAt(t-2)||111!==e.charCodeAt(t-3)||116!==e.charCodeAt(t-4)||111!==e.charCodeAt(t-5)||114!==e.charCodeAt(t-6)||112!==e.charCodeAt(t-7)||95!==e.charCodeAt(t-8)||95!==e.charCodeAt(t-9))return!1;for(var n=t-10;n>=0;n--)if(36!==e.charCodeAt(n))return!1;return!0}function f(e,t,n){var r=d(e.source,t.source);return 0!==r?r:0!==(r=e.originalLine-t.originalLine)?r:0!==(r=e.originalColumn-t.originalColumn)||n?r:0!==(r=e.generatedColumn-t.generatedColumn)?r:(r=e.generatedLine-t.generatedLine,0!==r?r:d(e.name,t.name))}function h(e,t,n){var r=e.generatedLine-t.generatedLine;return 0!==r?r:0!==(r=e.generatedColumn-t.generatedColumn)||n?r:0!==(r=d(e.source,t.source))?r:0!==(r=e.originalLine-t.originalLine)?r:(r=e.originalColumn-t.originalColumn,0!==r?r:d(e.name,t.name))}function d(e,t){return e===t?0:null===e?1:null===t?-1:e>t?1:-1}function m(e,t){var n=e.generatedLine-t.generatedLine;return 0!==n?n:0!==(n=e.generatedColumn-t.generatedColumn)?n:0!==(n=d(e.source,t.source))?n:0!==(n=e.originalLine-t.originalLine)?n:(n=e.originalColumn-t.originalColumn,0!==n?n:d(e.name,t.name))}function v(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))}function g(e,t,n){if(t=t||"",e&&("/"!==e[e.length-1]&&"/"!==t[0]&&(e+="/"),t=e+t),n){var s=r(n);if(!s)throw new Error("sourceMapURL could not be parsed");if(s.path){var u=s.path.lastIndexOf("/");u>=0&&(s.path=s.path.substring(0,u+1))}t=a(i(s),t)}return o(t)}t.getArg=n;var y=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/,_=/^data:.+\,.+$/;t.urlParse=r,t.urlGenerate=i,t.normalize=o,t.join=a,t.isAbsolute=function(e){return"/"===e.charAt(0)||y.test(e)},t.relative=s;var b=function(){return!("__proto__"in Object.create(null))}();t.toSetString=b?u:l,t.fromSetString=b?u:c,t.compareByOriginalPositions=f,t.compareByGeneratedPositionsDeflated=h,t.compareByGeneratedPositionsInflated=m,t.parseSourceMapInput=v,t.computeSourceURL=g},function(e,t,n){"use strict";function r(e){return"button"===e||"input"===e||"select"===e||"textarea"===e}function i(e,t,n){switch(e){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":return!(!n.disabled||!r(t));default:return!1}}var o=n(11),a=n(244),s=n(245),u=n(249),l=n(460),c=n(461),p=(n(7),{}),f=null,h=function(e,t){e&&(s.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e))},d=function(e){return h(e,!0)},m=function(e){return h(e,!1)},v=function(e){return"."+e._rootNodeID},g={injection:{injectEventPluginOrder:a.injectEventPluginOrder,injectEventPluginsByName:a.injectEventPluginsByName},putListener:function(e,t,n){"function"!=typeof n&&o("94",t,typeof n);var r=v(e);(p[t]||(p[t]={}))[r]=n;var i=a.registrationNameModules[t];i&&i.didPutListener&&i.didPutListener(e,t,n)},getListener:function(e,t){var n=p[t];if(i(t,e._currentElement.type,e._currentElement.props))return null;var r=v(e);return n&&n[r]},deleteListener:function(e,t){var n=a.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t);var r=p[t];if(r){delete r[v(e)]}},deleteAllListeners:function(e){var t=v(e);for(var n in p)if(p.hasOwnProperty(n)&&p[n][t]){var r=a.registrationNameModules[n];r&&r.willDeleteListener&&r.willDeleteListener(e,n),delete p[n][t]}},extractEvents:function(e,t,n,r){for(var i,o=a.plugins,s=0;s0&&void 0!==arguments[0]?arguments[0]:{};return{type:v,payload:e}}Object.defineProperty(t,"__esModule",{value:!0}),t.CLEAR=t.NEW_AUTH_ERR=t.NEW_SPEC_ERR_BATCH=t.NEW_SPEC_ERR=t.NEW_THROWN_ERR_BATCH=t.NEW_THROWN_ERR=void 0,t.newThrownErr=r,t.newThrownErrBatch=i,t.newSpecErr=o,t.newSpecErrBatch=a,t.newAuthErr=s,t.clear=u;var l=n(266),c=function(e){return e&&e.__esModule?e:{default:e}}(l),p=t.NEW_THROWN_ERR="err_new_thrown_err",f=t.NEW_THROWN_ERR_BATCH="err_new_thrown_err_batch",h=t.NEW_SPEC_ERR="err_new_spec_err",d=t.NEW_SPEC_ERR_BATCH="err_new_spec_err_batch",m=t.NEW_AUTH_ERR="err_new_auth_err",v=t.CLEAR="err_clear"},function(e,t,n){var r=n(54),i=n(337),o=n(335),a=n(38),s=n(133),u=n(195),l={},c={},t=e.exports=function(e,t,n,p,f){var h,d,m,v,g=f?function(){return e}:u(e),y=r(n,p,t?2:1),_=0;if("function"!=typeof g)throw TypeError(e+" is not iterable!");if(o(g)){for(h=s(e.length);h>_;_++)if((v=t?y(a(d=e[_])[0],d[1]):y(e[_]))===l||v===c)return v}else for(m=g.call(e);!(d=m.next()).done;)if((v=i(m,y,d.value,t))===l||v===c)return v};t.BREAK=l,t.RETURN=c},function(e,t){e.exports=!0},function(e,t,n){var r=n(134)("meta"),i=n(30),o=n(56),a=n(42).f,s=0,u=Object.isExtensible||function(){return!0},l=!n(55)(function(){return u(Object.preventExtensions({}))}),c=function(e){a(e,r,{value:{i:"O"+ ++s,w:{}}})},p=function(e,t){if(!i(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!o(e,r)){if(!u(e))return"F";if(!t)return"E";c(e)}return e[r].i},f=function(e,t){if(!o(e,r)){if(!u(e))return!0;if(!t)return!1;c(e)}return e[r].w},h=function(e){return l&&d.NEED&&u(e)&&!o(e,r)&&c(e),e},d=e.exports={KEY:r,NEED:!1,fastKey:p,getWeak:f,onFreeze:h}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){var r=n(191),i=Math.min;e.exports=function(e){return e>0?i(r(e),9007199254740991):0}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){var r=n(135);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}}},function(e,t,n){"use strict";var r=n(65),i=n(79),o=n(107),a=n(58),s=n(18);e.exports=function(e,t,n){var u=s(e),l=n(a,u,""[e]),c=l[0],p=l[1];o(function(){var t={};return t[u]=function(){return 7},7!=""[e](t)})&&(i(String.prototype,e,c),r(RegExp.prototype,u,2==t?function(e,t){return p.call(e,this,t)}:function(e){return p.call(e,this)}))}},function(e,t,n){var r=n(63),i=n(645),o=n(664),a=Object.defineProperty;t.f=n(106)?Object.defineProperty:function(e,t,n){if(r(e),t=o(t,!0),r(n),i)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){var r=n(647),i=n(58);e.exports=function(e){return r(i(e))}},function(e,t,n){"use strict";var r,i=n(372),o=n(375),a=n(733),s=n(738);r=e.exports=function(e,t){var n,r,a,u,l;return arguments.length<2||"string"!=typeof e?(u=t,t=e,e=null):u=arguments[2],null==e?(n=a=!0,r=!1):(n=s.call(e,"c"),r=s.call(e,"e"),a=s.call(e,"w")),l={value:t,configurable:n,enumerable:r,writable:a},u?i(o(u),l):l},r.gs=function(e,t,n){var r,u,l,c;return"string"!=typeof e?(l=n,n=t,t=e,e=null):l=arguments[3],null==t?t=void 0:a(t)?null==n?n=void 0:a(n)||(l=n,n=void 0):(l=t,t=n=void 0),null==e?(r=!0,u=!1):(r=s.call(e,"c"),u=s.call(e,"e")),c={get:t,set:n,configurable:r,enumerable:u},l?i(o(l),c):c}},function(e,t,n){"use strict";e.exports=n(730)("forEach")},function(e,t){function n(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function r(e){return"function"==typeof e}function i(e){return"number"==typeof e}function o(e){return"object"==typeof e&&null!==e}function a(e){return void 0===e}e.exports=n,n.EventEmitter=n,n.prototype._events=void 0,n.prototype._maxListeners=void 0,n.defaultMaxListeners=10,n.prototype.setMaxListeners=function(e){if(!i(e)||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},n.prototype.emit=function(e){var t,n,i,s,u,l;if(this._events||(this._events={}),"error"===e&&(!this._events.error||o(this._events.error)&&!this._events.error.length)){if((t=arguments[1])instanceof Error)throw t;var c=new Error('Uncaught, unspecified "error" event. ('+t+")");throw c.context=t,c}if(n=this._events[e],a(n))return!1;if(r(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:s=Array.prototype.slice.call(arguments,1),n.apply(this,s)}else if(o(n))for(s=Array.prototype.slice.call(arguments,1),l=n.slice(),i=l.length,u=0;u0&&this._events[e].length>i&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){function n(){this.removeListener(e,n),i||(i=!0,t.apply(this,arguments))}if(!r(t))throw TypeError("listener must be a function");var i=!1;return n.listener=t,this.on(e,n),this},n.prototype.removeListener=function(e,t){var n,i,a,s;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],a=n.length,i=-1,n===t||r(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(o(n)){for(s=a;s-- >0;)if(n[s]===t||n[s].listener&&n[s].listener===t){i=s;break}if(i<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(i,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],r(n))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){return this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(r(t))return 1;if(t)return t.length}return 0},n.listenerCount=function(e,t){return e.listenerCount(t)}},function(e,t,n){"use strict";var r={};e.exports=r},function(e,t,n){"use strict";var r=n(82);e.exports=r.DEFAULT=new r({include:[n(118)],explicit:[n(809),n(808),n(807)]})},function(e,t,n){function r(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e]/;e.exports=i},function(e,t,n){"use strict";var r,i=n(25),o=n(243),a=/^[ \r\n\t\f]/,s=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,u=n(251),l=u(function(e,t){if(e.namespaceURI!==o.svg||"innerHTML"in e)e.innerHTML=t;else{r=r||document.createElement("div"),r.innerHTML=""+t+"";for(var n=r.firstChild;n.firstChild;)e.appendChild(n.firstChild)}});if(i.canUseDOM){var c=document.createElement("div");c.innerHTML=" ",""===c.innerHTML&&(l=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||"<"===t[0]&&s.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t}),c=null}e.exports=l},function(e,t,n){"use strict";function r(e){var t={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]="number"==typeof e[n]?e[n]:e[n].val);return t}t.__esModule=!0,t.default=r,e.exports=t.default},function(e,t,n){"use strict";e.exports=function(e,t){var n,r,i,o=-1,a=e.posMax,s=e.pos,u=e.isInLabel;if(e.isInLabel)return-1;if(e.labelUnmatchedScopes)return e.labelUnmatchedScopes--,-1;for(e.pos=t+1,e.isInLabel=!0,n=1;e.pos1&&void 0!==arguments[1])||arguments[1];return e=(0,s.normalizeArray)(e),{type:p,payload:{thing:e,shown:t}}}function a(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=(0,s.normalizeArray)(e),{type:c,payload:{thing:e,mode:t}}}Object.defineProperty(t,"__esModule",{value:!0}),t.SHOW=t.UPDATE_MODE=t.UPDATE_FILTER=t.UPDATE_LAYOUT=void 0,t.updateLayout=r,t.updateFilter=i,t.show=o,t.changeMode=a;var s=n(9),u=t.UPDATE_LAYOUT="layout_update_layout",l=t.UPDATE_FILTER="layout_update_filter",c=t.UPDATE_MODE="layout_update_mode",p=t.SHOW="layout_show"},function(e,t,n){"use strict";function r(e,t){return{type:u,payload:{selectedServerUrl:e,namespace:t}}}function i(e){var t=e.value,n=e.pathMethod;return{type:l,payload:{value:t,pathMethod:n}}}function o(e){var t=e.value,n=e.pathMethod;return{type:c,payload:{value:t,pathMethod:n}}}function a(e){var t=e.value,n=e.path,r=e.method;return{type:p,payload:{value:t,path:n,method:r}}}function s(e){var t=e.server,n=e.namespace,r=e.key,i=e.val;return{type:f,payload:{server:t,namespace:n,key:r,val:i}}}Object.defineProperty(t,"__esModule",{value:!0}),t.setSelectedServer=r,t.setRequestBodyValue=i,t.setRequestContentType=o,t.setResponseContentType=a,t.setServerVariableValue=s;var u=t.UPDATE_SELECTED_SERVER="oas3_set_servers",l=t.UPDATE_REQUEST_BODY_VALUE="oas3_set_request_body_value",c=t.UPDATE_REQUEST_CONTENT_TYPE="oas3_set_request_content_type",p=t.UPDATE_RESPONSE_CONTENT_TYPE="oas3_set_response_content_type",f=t.UPDATE_SERVER_VARIABLE_VALUE="oas3_set_server_variable_value"},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function i(e,t){var n=h(e,t);if(n)return(0,s.default)(n,{declaration:!0,indent:"\t"})}Object.defineProperty(t,"__esModule",{value:!0}),t.memoizedSampleFromSchema=t.memoizedCreateXMLExample=t.sampleXmlFromSchema=t.inferSchema=t.sampleFromSchema=void 0,t.createXMLExample=i;var o=n(9),a=n(1204),s=r(a),u=n(973),l=r(u),c={string:function(){return"string"},string_email:function(){return"user@example.com"},"string_date-time":function(){return(new Date).toISOString()},number:function(){return 0},number_float:function(){return 0},integer:function(){return 0},boolean:function(e){return"boolean"!=typeof e.default||e.default}},p=function(e){e=(0,o.objectify)(e);var t=e,n=t.type,r=t.format,i=c[n+"_"+r]||c[n];return(0,o.isFunc)(i)?i(e):"Unknown Type: "+e.type},f=t.sampleFromSchema=function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=(0,o.objectify)(t),i=r.type,a=r.example,s=r.properties,u=r.additionalProperties,l=r.items,c=n.includeReadOnly,f=n.includeWriteOnly;if(void 0!==a)return a;if(!i)if(s)i="object";else{if(!l)return;i="array"}if("object"===i){var h=(0,o.objectify)(s),d={};for(var m in h)h[m].readOnly&&!c||h[m].writeOnly&&!f||(d[m]=e(h[m],n));if(!0===u)d.additionalProp1={};else if(u)for(var v=(0,o.objectify)(u),g=e(v,n),y=1;y<4;y++)d["additionalProp"+y]=g;return d}return"array"===i?[e(l,n)]:t.enum?t.default?t.default:(0,o.normalizeArray)(t.enum)[0]:"file"!==i?p(t):void 0},h=(t.inferSchema=function(e){return e.schema&&(e=e.schema),e.properties&&(e.type="object"),e},t.sampleXmlFromSchema=function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=(0,o.objectify)(t),i=r.type,a=r.properties,s=r.additionalProperties,u=r.items,l=r.example,c=n.includeReadOnly,f=n.includeWriteOnly,h=r.default,d={},m={},v=t.xml,g=v.name,y=v.prefix,_=v.namespace,b=r.enum,x=void 0,w=void 0;if(!i)if(a||s)i="object";else{if(!u)return;i="array"}if(g=g||"notagname",x=(y?y+":":"")+g,_){m[y?"xmlns:"+y:"xmlns"]=_}if("array"===i&&u){if(u.xml=u.xml||v||{},u.xml.name=u.xml.name||v.name,v.wrapped)return d[x]=[],Array.isArray(l)?l.forEach(function(t){u.example=t,d[x].push(e(u,n))}):Array.isArray(h)?h.forEach(function(t){u.default=t,d[x].push(e(u,n))}):d[x]=[e(u,n)],m&&d[x].push({_attr:m}),d;var k=[];return Array.isArray(l)?(l.forEach(function(t){u.example=t,k.push(e(u,n))}),k):Array.isArray(h)?(h.forEach(function(t){u.default=t,k.push(e(u,n))}),k):e(u,n)}if("object"===i){var E=(0,o.objectify)(a);d[x]=[],l=l||{};for(var S in E)if((!E[S].readOnly||c)&&(!E[S].writeOnly||f))if(E[S].xml=E[S].xml||{},E[S].xml.attribute){var C=Array.isArray(E[S].enum)&&E[S].enum[0],A=E[S].example,D=E[S].default;m[E[S].xml.name||S]=void 0!==A&&A||void 0!==l[S]&&l[S]||void 0!==D&&D||C||p(E[S])}else{E[S].xml.name=E[S].xml.name||S,E[S].example=void 0!==E[S].example?E[S].example:l[S];var O=e(E[S]);Array.isArray(O)?d[x]=d[x].concat(O):d[x].push(O)}return!0===s?d[x].push({additionalProp:"Anything can be here"}):s&&d[x].push({additionalProp:p(s)}),m&&d[x].push({_attr:m}),d}return w=void 0!==l?l:void 0!==h?h:Array.isArray(b)?b[0]:p(t),d[x]=m?[{_attr:m},w]:w,d});t.memoizedCreateXMLExample=(0,l.default)(i),t.memoizedSampleFromSchema=(0,l.default)(f)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function i(e){return e instanceof Error?{type:P,error:!0,payload:e}:"string"==typeof e?{type:P,payload:e.replace(/\t/g," ")||""}:{type:P,payload:""}}function o(e){return{type:H,payload:e}}function a(e){return{type:I,payload:e}}function s(e){if(!e||"object"!==(void 0===e?"undefined":(0,E.default)(e)))throw new Error("updateJson must only accept a simple JSON object");return{type:R,payload:e}}function u(e,t,n,r,i){return{type:j,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:i}}}function l(e){return{type:W,payload:{pathMethod:e}}}function c(e,t){return{type:V,payload:{path:e,value:t,key:"consumes_value"}}}function p(e,t){return{type:V,payload:{path:e,value:t,key:"produces_value"}}}function f(e,t){return{type:z,payload:{path:e,method:t}}}function h(e,t){return{type:U,payload:{path:e,method:t}}}function d(e,t,n){return{type:G,payload:{scheme:e,path:t,method:n}}}Object.defineProperty(t,"__esModule",{value:!0}),t.execute=t.executeRequest=t.logRequest=t.setMutatedRequest=t.setRequest=t.setResponse=t.validateParams=t.formatIntoYaml=t.resolveSpec=t.parseToJson=t.SET_SCHEME=t.UPDATE_RESOLVED=t.UPDATE_OPERATION_VALUE=t.CLEAR_VALIDATE_PARAMS=t.CLEAR_REQUEST=t.CLEAR_RESPONSE=t.LOG_REQUEST=t.SET_MUTATED_REQUEST=t.SET_REQUEST=t.SET_RESPONSE=t.VALIDATE_PARAMS=t.UPDATE_PARAM=t.UPDATE_JSON=t.UPDATE_URL=t.UPDATE_SPEC=void 0;var m=n(20),v=r(m),g=n(97),y=r(g),_=n(36),b=r(_),x=n(47),w=r(x),k=n(48),E=r(k);t.updateSpec=i,t.updateResolved=o,t.updateUrl=a,t.updateJsonSpec=s,t.changeParam=u,t.clearValidateParams=l,t.changeConsumesValue=c,t.changeProducesValue=p,t.clearResponse=f,t.clearRequest=h,t.setScheme=d;var S=n(213),C=r(S),A=n(1194),D=r(A),O=n(266),M=r(O),T=n(9),P=t.UPDATE_SPEC="spec_update_spec",I=t.UPDATE_URL="spec_update_url",R=t.UPDATE_JSON="spec_update_json",j=t.UPDATE_PARAM="spec_update_param",F=t.VALIDATE_PARAMS="spec_validate_param",N=t.SET_RESPONSE="spec_set_response",B=t.SET_REQUEST="spec_set_request",L=t.SET_MUTATED_REQUEST="spec_set_mutated_request",q=t.LOG_REQUEST="spec_log_request",z=t.CLEAR_RESPONSE="spec_clear_response",U=t.CLEAR_REQUEST="spec_clear_request",W=t.CLEAR_VALIDATE_PARAMS="spec_clear_validate_param",V=t.UPDATE_OPERATION_VALUE="spec_update_operation_value",H=t.UPDATE_RESOLVED="spec_update_resolved",G=t.SET_SCHEME="set_scheme",J=(t.parseToJson=function(e){return function(t){var n=t.specActions,r=t.specSelectors,i=t.errActions,o=r.specStr,a=null;try{e=e||o(),i.clear({source:"parser"}),a=C.default.safeLoad(e)}catch(e){return console.error(e),i.newSpecErr({source:"parser",level:"error",message:e.reason,line:e.mark&&e.mark.line?e.mark.line+1:void 0})}return n.updateJsonSpec(a)}},t.resolveSpec=function(e,t){return function(n){var r=n.specActions,i=n.specSelectors,o=n.errActions,a=n.fn,s=a.fetch,u=a.resolve,l=a.AST,c=n.getConfigs,p=c(),f=p.modelPropertyMacro,h=p.parameterMacro,d=p.requestInterceptor,m=p.responseInterceptor;void 0===e&&(e=i.specJson()),void 0===t&&(t=i.url());var v=l.getLineNumberForPath,g=i.specStr();return u({fetch:s,spec:e,baseDoc:t,modelPropertyMacro:f,parameterMacro:h,requestInterceptor:d,responseInterceptor:m}).then(function(e){var t=e.spec,n=e.errors;if(o.clear({type:"thrown"}),n.length>0){var i=n.map(function(e){return console.error(e),e.line=e.fullPath?v(g,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",Object.defineProperty(e,"message",{enumerable:!0,value:e.message}),e});o.newThrownErrBatch(i)}return r.updateResolved(t)})}},t.formatIntoYaml=function(){return function(e){var t=e.specActions,n=e.specSelectors,r=n.specStr,i=t.updateSpec;try{var o=C.default.safeDump(C.default.safeLoad(r()),{indent:2});i(o)}catch(e){i(e)}}},t.validateParams=function(e,t){return{type:F,payload:{pathMethod:e,isOAS3:t}}},t.setResponse=function(e,t,n){return{payload:{path:e,method:t,res:n},type:N}},t.setRequest=function(e,t,n){return{payload:{path:e,method:t,req:n},type:B}},t.setMutatedRequest=function(e,t,n){return{payload:{path:e,method:t,req:n},type:L}},t.logRequest=function(e){return{payload:e,type:q}},t.executeRequest=function(e){return function(t){var n=t.fn,r=t.specActions,i=t.specSelectors,o=t.getConfigs,a=t.oas3Selectors,s=e.pathName,u=e.method,l=e.operation,c=o(),p=c.requestInterceptor,f=c.responseInterceptor,h=l.toJS();if(e.contextUrl=(0,D.default)(i.url()).toString(),h&&h.operationId?e.operationId=h.operationId:h&&s&&u&&(e.operationId=n.opId(h,s,u)),i.isOAS3()){var d=s+":"+u;e.server=a.selectedServer(d)||a.selectedServer();var m=a.serverVariables({server:e.server,namespace:d}).toJS(),v=a.serverVariables({server:e.server}).toJS();e.serverVariables=(0,w.default)(m).length?m:v,e.requestContentType=a.requestContentType(s,u),e.responseContentType=a.responseContentType(s,u)||"*/*";var g=a.requestBodyValue(s,u);(0,T.isJSONObject)(g)?e.requestBody=JSON.parse(g):e.requestBody=g}var y=(0,b.default)({},e);y=n.buildRequest(y),r.setRequest(e.pathName,e.method,y);var _=function(t){var n=p.apply(this,[t]),i=(0,b.default)({},n);return r.setMutatedRequest(e.pathName,e.method,i),n};e.requestInterceptor=_,e.responseInterceptor=f;var x=Date.now();return n.execute(e).then(function(t){t.duration=Date.now()-x,r.setResponse(e.pathName,e.method,t)}).catch(function(t){return r.setResponse(e.pathName,e.method,{error:!0,err:(0,M.default)(t)})})}},function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.path,n=e.method,r=(0,y.default)(e,["path","method"]);return function(e){var i=e.fn.fetch,o=e.specSelectors,a=e.specActions,s=o.spec().toJS(),u=o.operationScheme(t,n),l=o.contentTypeValues([t,n]).toJS(),c=l.requestContentType,p=l.responseContentType,f=/xml/i.test(c),h=o.parameterValues([t,n],f).toJS();return a.executeRequest((0,v.default)({fetch:i,spec:s,pathName:t,method:n,parameters:h,requestContentType:c,scheme:u,responseContentType:p},r))}});t.execute=J},function(e,t,n){"use strict";var r=n(9),i=n(1215);i.keys().forEach(function(t){if("./index.js"!==t){var n=i(t);e.exports[(0,r.pascalCaseFilename)(t)]=n.default?n.default:n}})},function(e,t,n){e.exports={default:n(598),__esModule:!0}},function(e,t,n){"use strict";function r(e){switch(e._type){case"document":case"block_quote":case"list":case"item":case"paragraph":case"heading":case"emph":case"strong":case"link":case"image":case"custom_inline":case"custom_block":return!0;default:return!1}}var i=function(e,t){this.current=e,this.entering=!0===t},o=function(){var e=this.current,t=this.entering;if(null===e)return null;var n=r(e);return t&&n?e._firstChild?(this.current=e._firstChild,this.entering=!0):this.entering=!1:e===this.root?this.current=null:null===e._next?(this.current=e._parent,this.entering=!1):(this.current=e._next,this.entering=!0),{entering:t,node:e}},a=function(e){return{current:e,root:e,entering:!0,next:o,resumeAt:i}},s=function(e,t){this._type=e,this._parent=null,this._firstChild=null,this._lastChild=null,this._prev=null,this._next=null,this._sourcepos=t,this._lastLineBlank=!1,this._open=!0,this._string_content=null,this._literal=null,this._listData={},this._info=null,this._destination=null,this._title=null,this._isFenced=!1,this._fenceChar=null,this._fenceLength=0,this._fenceOffset=null,this._level=null,this._onEnter=null,this._onExit=null},u=s.prototype;Object.defineProperty(u,"isContainer",{get:function(){return r(this)}}),Object.defineProperty(u,"type",{get:function(){return this._type}}),Object.defineProperty(u,"firstChild",{get:function(){return this._firstChild}}),Object.defineProperty(u,"lastChild",{get:function(){return this._lastChild}}),Object.defineProperty(u,"next",{get:function(){return this._next}}),Object.defineProperty(u,"prev",{get:function(){return this._prev}}),Object.defineProperty(u,"parent",{get:function(){return this._parent}}),Object.defineProperty(u,"sourcepos",{get:function(){return this._sourcepos}}),Object.defineProperty(u,"literal",{get:function(){return this._literal},set:function(e){this._literal=e}}),Object.defineProperty(u,"destination",{get:function(){return this._destination},set:function(e){this._destination=e}}),Object.defineProperty(u,"title",{get:function(){return this._title},set:function(e){this._title=e}}),Object.defineProperty(u,"info",{get:function(){return this._info},set:function(e){this._info=e}}),Object.defineProperty(u,"level",{get:function(){return this._level},set:function(e){this._level=e}}),Object.defineProperty(u,"listType",{get:function(){return this._listData.type},set:function(e){this._listData.type=e}}),Object.defineProperty(u,"listTight",{get:function(){return this._listData.tight},set:function(e){this._listData.tight=e}}),Object.defineProperty(u,"listStart",{get:function(){return this._listData.start},set:function(e){this._listData.start=e}}),Object.defineProperty(u,"listDelimiter",{get:function(){return this._listData.delimiter},set:function(e){this._listData.delimiter=e}}),Object.defineProperty(u,"onEnter",{get:function(){return this._onEnter},set:function(e){this._onEnter=e}}),Object.defineProperty(u,"onExit",{get:function(){return this._onExit},set:function(e){this._onExit=e}}),s.prototype.appendChild=function(e){e.unlink(),e._parent=this,this._lastChild?(this._lastChild._next=e,e._prev=this._lastChild,this._lastChild=e):(this._firstChild=e,this._lastChild=e)},s.prototype.prependChild=function(e){e.unlink(),e._parent=this,this._firstChild?(this._firstChild._prev=e,e._next=this._firstChild,this._firstChild=e):(this._firstChild=e,this._lastChild=e)},s.prototype.unlink=function(){this._prev?this._prev._next=this._next:this._parent&&(this._parent._firstChild=this._next),this._next?this._next._prev=this._prev:this._parent&&(this._parent._lastChild=this._prev),this._parent=null,this._next=null,this._prev=null},s.prototype.insertAfter=function(e){e.unlink(),e._next=this._next,e._next&&(e._next._prev=e),e._prev=this,this._next=e,e._parent=this._parent,e._next||(e._parent._lastChild=e)},s.prototype.insertBefore=function(e){e.unlink(),e._prev=this._prev,e._prev&&(e._prev._next=e),e._next=this,this._prev=e,e._parent=this._parent,e._prev||(e._parent._firstChild=e)},s.prototype.walker=function(){return new a(this)},e.exports=s},function(e,t){var n=Object.prototype.toString;e.exports=function(e){switch(n.call(e)){case"[object Date]":return"date";case"[object RegExp]":return"regexp";case"[object Arguments]":return"arguments";case"[object Array]":return"array";case"[object Error]":return"error"}return null===e?"null":void 0===e?"undefined":e!==e?"nan":e&&1===e.nodeType?"element":typeof(e=e.valueOf?e.valueOf():Object.prototype.valueOf.apply(e))}},function(e,t){e.exports=function(e,t,n,r){if(!(e instanceof t)||void 0!==r&&r in e)throw TypeError(n+": incorrect invocation!");return e}},function(e,t,n){var r=n(54),i=n(183),o=n(77),a=n(133),s=n(605);e.exports=function(e,t){var n=1==e,u=2==e,l=3==e,c=4==e,p=6==e,f=5==e||p,h=t||s;return function(t,s,d){for(var m,v,g=o(t),y=i(g),_=r(s,d,3),b=a(y.length),x=0,w=n?h(t,b):u?h(t,0):void 0;b>x;x++)if((f||x in y)&&(m=y[x],v=_(m,x,g),e))if(n)w[x]=v;else if(v)switch(e){case 3:return!0;case 5:return m;case 6:return x;case 2:w.push(m)}else if(c)return!1;return p?-1:l||c?c:w}}},function(e,t,n){var r=n(99),i=n(21)("toStringTag"),o="Arguments"==r(function(){return arguments}()),a=function(e,t){try{return e[t]}catch(e){}};e.exports=function(e){var t,n,s;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=a(t=Object(e),i))?n:o?r(t):"Object"==(s=r(t))&&"function"==typeof t.callee?"Arguments":s}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){var r=n(30),i=n(24).document,o=r(i)&&r(i.createElement);e.exports=function(e){return o?i.createElement(e):{}}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,n){var r=n(99);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==r(e)?e.split(""):Object(e)}},function(e,t,n){"use strict";function r(e){var t,n;this.promise=new e(function(e,r){if(void 0!==t||void 0!==n)throw TypeError("Bad Promise constructor");t=e,n=r}),this.resolve=i(t),this.reject=i(n)}var i=n(98);e.exports.f=function(e){return new r(e)}},function(e,t,n){var r=n(38),i=n(614),o=n(182),a=n(189)("IE_PROTO"),s=function(){},u=function(){var e,t=n(181)("iframe"),r=o.length;for(t.style.display="none",n(333).appendChild(t),t.src="javascript:",e=t.contentWindow.document,e.open(),e.write("