Merge conficts on EPersonServiceImpl

This commit is contained in:
frabacche
2023-12-19 09:37:05 +01:00
78 changed files with 1421 additions and 1016 deletions

View File

@@ -45,7 +45,7 @@ jobs:
steps: steps:
# https://github.com/actions/checkout # https://github.com/actions/checkout
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v3 uses: actions/checkout@v4
# https://github.com/actions/setup-java # https://github.com/actions/setup-java
- name: Install JDK ${{ matrix.java }} - name: Install JDK ${{ matrix.java }}
@@ -53,16 +53,7 @@ jobs:
with: with:
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
distribution: 'temurin' distribution: 'temurin'
cache: 'maven'
# https://github.com/actions/cache
- name: Cache Maven dependencies
uses: actions/cache@v3
with:
# Cache entire ~/.m2/repository
path: ~/.m2/repository
# Cache key is hash of all pom.xml files. Therefore any changes to POMs will invalidate cache
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-maven-
# Run parallel Maven builds based on the above 'strategy.matrix' # Run parallel Maven builds based on the above 'strategy.matrix'
- name: Run Maven ${{ matrix.type }} - name: Run Maven ${{ matrix.type }}
@@ -96,7 +87,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
# Download artifacts from previous 'tests' job # Download artifacts from previous 'tests' job
- name: Download coverage artifacts - name: Download coverage artifacts
@@ -108,10 +99,13 @@ jobs:
# Retry action: https://github.com/marketplace/actions/retry-action # Retry action: https://github.com/marketplace/actions/retry-action
# Codecov action: https://github.com/codecov/codecov-action # Codecov action: https://github.com/codecov/codecov-action
- name: Upload coverage to Codecov.io - name: Upload coverage to Codecov.io
uses: Wandalen/wretry.action@v1.0.36 uses: Wandalen/wretry.action@v1.3.0
with: with:
action: codecov/codecov-action@v3 action: codecov/codecov-action@v3
# Try upload 5 times max # Ensure codecov-action throws an error when it fails to upload
with: |
fail_ci_if_error: true
# Try re-running action 5 times max
attempt_limit: 5 attempt_limit: 5
# Run again in 30 seconds # Run again in 30 seconds
attempt_delay: 30000 attempt_delay: 30000

View File

@@ -35,7 +35,7 @@ jobs:
steps: steps:
# https://github.com/actions/checkout # https://github.com/actions/checkout
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
# https://github.com/actions/setup-java # https://github.com/actions/setup-java
- name: Install JDK - name: Install JDK

View File

@@ -3,6 +3,7 @@ name: Docker images
# Run this Build for all pushes to 'main' or maintenance branches, or tagged releases. # Run this Build for all pushes to 'main' or maintenance branches, or tagged releases.
# Also run for PRs to ensure PR doesn't break Docker build process # Also run for PRs to ensure PR doesn't break Docker build process
# NOTE: uses "reusable-docker-build.yml" to actually build each of the Docker images.
on: on:
push: push:
branches: branches:
@@ -15,83 +16,22 @@ on:
permissions: permissions:
contents: read # to fetch code (actions/checkout) contents: read # to fetch code (actions/checkout)
# Define shared environment variables for all jobs below
env:
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
# For a new commit on default branch (main), use the literal tag 'latest' on Docker image.
# For a new commit on other branches, use the branch name as the tag for Docker image.
# For a new tag, copy that tag name as the tag for Docker image.
IMAGE_TAGS: |
type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=tag
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We manage the 'latest' tag ourselves to the 'main' branch (see settings above)
TAGS_FLAVOR: |
latest=false
# Architectures / Platforms for which we will build Docker images
# If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work.
# If this is NOT a PR (e.g. a tag or merge commit), also build for ARM64. NOTE: The ARM64 build takes MUCH
# longer (around 45mins or so) which is why we only run it when pushing a new Docker image.
PLATFORMS: linux/amd64${{ github.event_name != 'pull_request' && ', linux/arm64' || '' }}
jobs: jobs:
#################################################### ####################################################
# Build/Push the 'dspace/dspace-dependencies' image. # Build/Push the 'dspace/dspace-dependencies' image.
# This image is used by all other jobs. # This image is used by all other DSpace build jobs.
#################################################### ####################################################
dspace-dependencies: dspace-dependencies:
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace'
if: github.repository == 'dspace/dspace' if: github.repository == 'dspace/dspace'
runs-on: ubuntu-latest uses: ./.github/workflows/reusable-docker-build.yml
with:
steps: build_id: dspace-dependencies
# https://github.com/actions/checkout image_name: dspace/dspace-dependencies
- name: Checkout codebase dockerfile_path: ./Dockerfile.dependencies
uses: actions/checkout@v3 secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
# https://github.com/docker/setup-buildx-action DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# https://github.com/docker/metadata-action
# Get Metadata for docker_build_deps step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-dependencies' image
id: meta_build_deps
uses: docker/metadata-action@v4
with:
images: dspace/dspace-dependencies
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
# https://github.com/docker/build-push-action
- name: Build and push 'dspace-dependencies' image
id: docker_build_deps
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.dependencies
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build_deps.outputs.tags }}
labels: ${{ steps.meta_build_deps.outputs.labels }}
####################################### #######################################
# Build/Push the 'dspace/dspace' image # Build/Push the 'dspace/dspace' image
@@ -101,52 +41,18 @@ jobs:
if: github.repository == 'dspace/dspace' if: github.repository == 'dspace/dspace'
# Must run after 'dspace-dependencies' job above # Must run after 'dspace-dependencies' job above
needs: dspace-dependencies needs: dspace-dependencies
runs-on: ubuntu-latest uses: ./.github/workflows/reusable-docker-build.yml
with:
steps: build_id: dspace
# https://github.com/actions/checkout image_name: dspace/dspace
- name: Checkout codebase dockerfile_path: ./Dockerfile
uses: actions/checkout@v3 secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
# https://github.com/docker/setup-buildx-action DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Setup Docker Buildx # Enable redeploy of sandbox & demo if the branch for this image matches the deployment branch of
uses: docker/setup-buildx-action@v2 # these sites as specified in reusable-docker-build.xml
REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }}
# https://github.com/docker/setup-qemu-action REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }}
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# Get Metadata for docker_build step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace' image
id: meta_build
uses: docker/metadata-action@v4
with:
images: dspace/dspace
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
- name: Build and push 'dspace' image
id: docker_build
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build.outputs.tags }}
labels: ${{ steps.meta_build.outputs.labels }}
############################################################# #############################################################
# Build/Push the 'dspace/dspace' image ('-test' tag) # Build/Push the 'dspace/dspace' image ('-test' tag)
@@ -156,55 +62,17 @@ jobs:
if: github.repository == 'dspace/dspace' if: github.repository == 'dspace/dspace'
# Must run after 'dspace-dependencies' job above # Must run after 'dspace-dependencies' job above
needs: dspace-dependencies needs: dspace-dependencies
runs-on: ubuntu-latest uses: ./.github/workflows/reusable-docker-build.yml
with:
steps: build_id: dspace-test
# https://github.com/actions/checkout image_name: dspace/dspace
- name: Checkout codebase dockerfile_path: ./Dockerfile.test
uses: actions/checkout@v3 # As this is a test/development image, its tags are all suffixed with "-test". Otherwise, it uses the same
# tagging logic as the primary 'dspace/dspace' image above.
# https://github.com/docker/setup-buildx-action tags_flavor: suffix=-test
- name: Setup Docker Buildx secrets:
uses: docker/setup-buildx-action@v2 DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# Get Metadata for docker_build_test step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-test' image
id: meta_build_test
uses: docker/metadata-action@v4
with:
images: dspace/dspace
tags: ${{ env.IMAGE_TAGS }}
# As this is a test/development image, its tags are all suffixed with "-test". Otherwise, it uses the same
# tagging logic as the primary 'dspace/dspace' image above.
flavor: ${{ env.TAGS_FLAVOR }}
suffix=-test
- name: Build and push 'dspace-test' image
id: docker_build_test
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.test
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build_test.outputs.tags }}
labels: ${{ steps.meta_build_test.outputs.labels }}
########################################### ###########################################
# Build/Push the 'dspace/dspace-cli' image # Build/Push the 'dspace/dspace-cli' image
@@ -214,52 +82,14 @@ jobs:
if: github.repository == 'dspace/dspace' if: github.repository == 'dspace/dspace'
# Must run after 'dspace-dependencies' job above # Must run after 'dspace-dependencies' job above
needs: dspace-dependencies needs: dspace-dependencies
runs-on: ubuntu-latest uses: ./.github/workflows/reusable-docker-build.yml
with:
steps: build_id: dspace-cli
# https://github.com/actions/checkout image_name: dspace/dspace-cli
- name: Checkout codebase dockerfile_path: ./Dockerfile.cli
uses: actions/checkout@v3 secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
# https://github.com/docker/setup-buildx-action DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# Get Metadata for docker_build_test step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-cli' image
id: meta_build_cli
uses: docker/metadata-action@v4
with:
images: dspace/dspace-cli
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
- name: Build and push 'dspace-cli' image
id: docker_build_cli
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.cli
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build_cli.outputs.tags }}
labels: ${{ steps.meta_build_cli.outputs.labels }}
########################################### ###########################################
# Build/Push the 'dspace/dspace-solr' image # Build/Push the 'dspace/dspace-solr' image
@@ -267,52 +97,18 @@ jobs:
dspace-solr: dspace-solr:
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace'
if: github.repository == 'dspace/dspace' if: github.repository == 'dspace/dspace'
runs-on: ubuntu-latest uses: ./.github/workflows/reusable-docker-build.yml
with:
steps: build_id: dspace-solr
# https://github.com/actions/checkout image_name: dspace/dspace-solr
- name: Checkout codebase dockerfile_path: ./dspace/src/main/docker/dspace-solr/Dockerfile
uses: actions/checkout@v3 secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
# https://github.com/docker/setup-buildx-action DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Setup Docker Buildx # Enable redeploy of sandbox & demo SOLR instance whenever dspace-solr image changes for deployed branch.
uses: docker/setup-buildx-action@v2 # These URLs MUST use different secrets than 'dspace/dspace' image build above as they are deployed separately.
REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_SOLR_URL }}
# https://github.com/docker/setup-qemu-action REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_SOLR_URL }}
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# Get Metadata for docker_build_solr step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-solr' image
id: meta_build_solr
uses: docker/metadata-action@v4
with:
images: dspace/dspace-solr
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
- name: Build and push 'dspace-solr' image
id: docker_build_solr
uses: docker/build-push-action@v4
with:
context: .
file: ./dspace/src/main/docker/dspace-solr/Dockerfile
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build_solr.outputs.tags }}
labels: ${{ steps.meta_build_solr.outputs.labels }}
########################################################### ###########################################################
# Build/Push the 'dspace/dspace-postgres-pgcrypto' image # Build/Push the 'dspace/dspace-postgres-pgcrypto' image
@@ -320,53 +116,16 @@ jobs:
dspace-postgres-pgcrypto: dspace-postgres-pgcrypto:
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace'
if: github.repository == 'dspace/dspace' if: github.repository == 'dspace/dspace'
runs-on: ubuntu-latest uses: ./.github/workflows/reusable-docker-build.yml
with:
steps: build_id: dspace-postgres-pgcrypto
# https://github.com/actions/checkout image_name: dspace/dspace-postgres-pgcrypto
- name: Checkout codebase # Must build out of subdirectory to have access to install script for pgcrypto.
uses: actions/checkout@v3 # NOTE: this context will build the image based on the Dockerfile in the specified directory
dockerfile_context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/
# https://github.com/docker/setup-buildx-action secrets:
- name: Setup Docker Buildx DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
uses: docker/setup-buildx-action@v2 DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# Get Metadata for docker_build_postgres step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-postgres-pgcrypto' image
id: meta_build_postgres
uses: docker/metadata-action@v4
with:
images: dspace/dspace-postgres-pgcrypto
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
- name: Build and push 'dspace-postgres-pgcrypto' image
id: docker_build_postgres
uses: docker/build-push-action@v4
with:
# Must build out of subdirectory to have access to install script for pgcrypto
context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/
dockerfile: Dockerfile
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build_postgres.outputs.tags }}
labels: ${{ steps.meta_build_postgres.outputs.labels }}
######################################################################## ########################################################################
# Build/Push the 'dspace/dspace-postgres-pgcrypto' image (-loadsql tag) # Build/Push the 'dspace/dspace-postgres-pgcrypto' image (-loadsql tag)
@@ -374,53 +133,16 @@ jobs:
dspace-postgres-pgcrypto-loadsql: dspace-postgres-pgcrypto-loadsql:
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace' # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace'
if: github.repository == 'dspace/dspace' if: github.repository == 'dspace/dspace'
runs-on: ubuntu-latest uses: ./.github/workflows/reusable-docker-build.yml
with:
steps: build_id: dspace-postgres-pgcrypto-loadsql
# https://github.com/actions/checkout image_name: dspace/dspace-postgres-pgcrypto
- name: Checkout codebase # Must build out of subdirectory to have access to install script for pgcrypto.
uses: actions/checkout@v3 # NOTE: this context will build the image based on the Dockerfile in the specified directory
dockerfile_context: ./dspace/src/main/docker/dspace-postgres-pgcrypto-curl/
# https://github.com/docker/setup-buildx-action # Suffix all tags with "-loadsql". Otherwise, it uses the same
- name: Setup Docker Buildx # tagging logic as the primary 'dspace/dspace-postgres-pgcrypto' image above.
uses: docker/setup-buildx-action@v2 tags_flavor: suffix=-loadsql
secrets:
# https://github.com/docker/setup-qemu-action DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
- name: Set up QEMU emulation to build for multiple architectures DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# Get Metadata for docker_build_postgres_loadsql step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-postgres-pgcrypto-loadsql' image
id: meta_build_postgres_loadsql
uses: docker/metadata-action@v4
with:
images: dspace/dspace-postgres-pgcrypto
tags: ${{ env.IMAGE_TAGS }}
# Suffix all tags with "-loadsql". Otherwise, it uses the same
# tagging logic as the primary 'dspace/dspace-postgres-pgcrypto' image above.
flavor: ${{ env.TAGS_FLAVOR }}
suffix=-loadsql
- name: Build and push 'dspace-postgres-pgcrypto-loadsql' image
id: docker_build_postgres_loadsql
uses: docker/build-push-action@v4
with:
# Must build out of subdirectory to have access to install script for pgcrypto
context: ./dspace/src/main/docker/dspace-postgres-pgcrypto-curl/
dockerfile: Dockerfile
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build_postgres_loadsql.outputs.tags }}
labels: ${{ steps.meta_build_postgres_loadsql.outputs.labels }}

View File

@@ -23,11 +23,11 @@ jobs:
if: github.event.pull_request.merged if: github.event.pull_request.merged
steps: steps:
# Checkout code # Checkout code
- uses: actions/checkout@v3 - uses: actions/checkout@v4
# Port PR to other branch (ONLY if labeled with "port to") # Port PR to other branch (ONLY if labeled with "port to")
# See https://github.com/korthout/backport-action # See https://github.com/korthout/backport-action
- name: Create backport pull requests - name: Create backport pull requests
uses: korthout/backport-action@v1 uses: korthout/backport-action@v2
with: with:
# Trigger based on a "port to [branch]" label on PR # Trigger based on a "port to [branch]" label on PR
# (This label must specify the branch name to port to) # (This label must specify the branch name to port to)

View File

@@ -21,4 +21,4 @@ jobs:
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards # Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
# See https://github.com/toshimaru/auto-author-assign # See https://github.com/toshimaru/auto-author-assign
- name: Assign PR to creator - name: Assign PR to creator
uses: toshimaru/auto-author-assign@v1.6.2 uses: toshimaru/auto-author-assign@v2.0.1

View File

@@ -0,0 +1,217 @@
#
# DSpace's reusable Docker build/push workflow.
#
# This is used by docker.yml for all Docker image builds
name: Reusable DSpace Docker Build
on:
workflow_call:
# Possible Inputs to this reusable job
inputs:
# Build name/id for this Docker build. Used for digest storage to avoid digest overlap between builds.
build_id:
required: true
type: string
# Requires the image name to build (e.g dspace/dspace-test)
image_name:
required: true
type: string
# Optionally the path to the Dockerfile to use for the build. (Default is [dockerfile_context]/Dockerfile)
dockerfile_path:
required: false
type: string
# Optionally the context directory to build the Dockerfile within. Defaults to "." (current directory)
dockerfile_context:
required: false
type: string
# If Docker image should have additional tag flavor details (e.g. a suffix), it may be passed in.
tags_flavor:
required: false
type: string
secrets:
# Requires that Docker login info be passed in as secrets.
DOCKER_USERNAME:
required: true
DOCKER_ACCESS_TOKEN:
required: true
# These URL secrets are optional. When specified & branch checks match, the redeployment code below will trigger.
# Therefore builds which need to trigger redeployment MUST specify these URLs. All others should leave them empty.
REDEPLOY_SANDBOX_URL:
required: false
REDEPLOY_DEMO_URL:
required: false
# Define shared default settings as environment variables
env:
IMAGE_NAME: ${{ inputs.image_name }}
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
# For a new commit on default branch (main), use the literal tag 'latest' on Docker image.
# For a new commit on other branches, use the branch name as the tag for Docker image.
# For a new tag, copy that tag name as the tag for Docker image.
IMAGE_TAGS: |
type=raw,value=latest,enable=${{ github.ref_name == github.event.repository.default_branch }}
type=ref,event=branch,enable=${{ github.ref_name != github.event.repository.default_branch }}
type=ref,event=tag
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We manage the 'latest' tag ourselves to the 'main' branch (see settings above)
TAGS_FLAVOR: |
latest=false
${{ inputs.tags_flavor }}
# When these URL variables are specified & required branch matches, then the sandbox or demo site will be redeployed.
# See "Redeploy" steps below for more details.
REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }}
REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }}
# Current DSpace maintenance branch (and architecture) which is deployed to demo.dspace.org / sandbox.dspace.org
# (NOTE: No deployment branch specified for sandbox.dspace.org as it uses the default_branch)
DEPLOY_DEMO_BRANCH: 'dspace-7_x'
DEPLOY_ARCH: 'linux/amd64'
jobs:
docker-build:
strategy:
matrix:
# Architectures / Platforms for which we will build Docker images
arch: [ 'linux/amd64', 'linux/arm64' ]
os: [ ubuntu-latest ]
isPr:
- ${{ github.event_name == 'pull_request' }}
# If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work.
# The below exclude therefore ensures we do NOT build ARM64 for PRs.
exclude:
- isPr: true
os: ubuntu-latest
arch: linux/arm64
runs-on: ${{ matrix.os }}
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v4
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v3
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: ${{ ! matrix.isPr }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
# https://github.com/docker/metadata-action
# Get Metadata for docker_build_deps step below
- name: Sync metadata (tags, labels) from GitHub to Docker for image
id: meta_build
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
# https://github.com/docker/build-push-action
- name: Build and push image
id: docker_build
uses: docker/build-push-action@v5
with:
context: ${{ inputs.dockerfile_context || '.' }}
file: ${{ inputs.dockerfile_path }}
platforms: ${{ matrix.arch }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ ! matrix.isPr }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build.outputs.tags }}
labels: ${{ steps.meta_build.outputs.labels }}
# Export the digest of Docker build locally (for non PRs only)
- name: Export Docker build digest
if: ${{ ! matrix.isPr }}
run: |
mkdir -p /tmp/digests
digest="${{ steps.docker_build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
# Upload digest to an artifact, so that it can be used in manifest below
- name: Upload Docker build digest to artifact
if: ${{ ! matrix.isPr }}
uses: actions/upload-artifact@v3
with:
name: digests-${{ inputs.build_id }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
# If this build is NOT a PR and passed in a REDEPLOY_SANDBOX_URL secret,
# Then redeploy https://sandbox.dspace.org if this build is for our deployment architecture and 'main' branch.
- name: Redeploy sandbox.dspace.org (based on main branch)
if: |
!matrix.isPR &&
env.REDEPLOY_SANDBOX_URL != '' &&
matrix.arch == env.DEPLOY_ARCH &&
github.ref_name == github.event.repository.default_branch
run: |
curl -X POST $REDEPLOY_SANDBOX_URL
# If this build is NOT a PR and passed in a REDEPLOY_DEMO_URL secret,
# Then redeploy https://demo.dspace.org if this build is for our deployment architecture and demo branch.
- name: Redeploy demo.dspace.org (based on maintenace branch)
if: |
!matrix.isPR &&
env.REDEPLOY_DEMO_URL != '' &&
matrix.arch == env.DEPLOY_ARCH &&
github.ref_name == env.DEPLOY_DEMO_BRANCH
run: |
curl -X POST $REDEPLOY_DEMO_URL
# Merge Docker digests (from various architectures) into a manifest.
# This runs after all Docker builds complete above, and it tells hub.docker.com
# that these builds should be all included in the manifest for this tag.
# (e.g. AMD64 and ARM64 should be listed as options under the same tagged Docker image)
docker-build_manifest:
if: ${{ github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
needs:
- docker-build
steps:
- name: Download Docker build digests
uses: actions/download-artifact@v3
with:
name: digests-${{ inputs.build_id }}
path: /tmp/digests
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Add Docker metadata for image
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Create manifest list from digests and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}

View File

@@ -21,7 +21,10 @@ USER dspace
ADD --chown=dspace . /app/ ADD --chown=dspace . /app/
# Build DSpace (note: this build doesn't include the optional, deprecated "dspace-rest" webapp) # Build DSpace (note: this build doesn't include the optional, deprecated "dspace-rest" webapp)
# Copy the dspace-installer directory to /install. Clean up the build to keep the docker image small # Copy the dspace-installer directory to /install. Clean up the build to keep the docker image small
RUN mvn --no-transfer-progress package && \ # Maven flags here ensure that we skip building test environment and skip all code verification checks.
# These flags speed up this compilation as much as reasonably possible.
ENV MAVEN_FLAGS="-P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true"
RUN mvn --no-transfer-progress package ${MAVEN_FLAGS} && \
mv /app/dspace/target/${TARGET_DIR}/* /install && \ mv /app/dspace/target/${TARGET_DIR}/* /install && \
mvn clean mvn clean

View File

@@ -15,11 +15,6 @@ RUN useradd dspace \
&& mkdir -p /home/dspace \ && mkdir -p /home/dspace \
&& chown -Rv dspace: /home/dspace && chown -Rv dspace: /home/dspace
RUN chown -Rv dspace: /app RUN chown -Rv dspace: /app
# Need git to support buildnumber-maven-plugin, which lets us know what version of DSpace is being run.
RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& apt-get purge -y --auto-remove \
&& rm -rf /var/lib/apt/lists/*
# Switch to dspace user & run below commands as that user # Switch to dspace user & run below commands as that user
USER dspace USER dspace
@@ -28,7 +23,10 @@ USER dspace
ADD --chown=dspace . /app/ ADD --chown=dspace . /app/
# Trigger the installation of all maven dependencies (hide download progress messages) # Trigger the installation of all maven dependencies (hide download progress messages)
RUN mvn --no-transfer-progress package # Maven flags here ensure that we skip final assembly, skip building test environment and skip all code verification checks.
# These flags speed up this installation as much as reasonably possible.
ENV MAVEN_FLAGS="-P-assembly -P-test-environment -Denforcer.skip=true -Dcheckstyle.skip=true -Dlicense.skip=true -Dxml.skip=true"
RUN mvn --no-transfer-progress install ${MAVEN_FLAGS}
# Clear the contents of the /app directory (including all maven builds), so no artifacts remain. # Clear the contents of the /app directory (including all maven builds), so no artifacts remain.
# This ensures when dspace:dspace is built, it will use the Maven local cache (~/.m2) for dependencies # This ensures when dspace:dspace is built, it will use the Maven local cache (~/.m2) for dependencies

View File

@@ -121,6 +121,8 @@ services:
cp -r /opt/solr/server/solr/configsets/search/* search cp -r /opt/solr/server/solr/configsets/search/* search
precreate-core statistics /opt/solr/server/solr/configsets/statistics precreate-core statistics /opt/solr/server/solr/configsets/statistics
cp -r /opt/solr/server/solr/configsets/statistics/* statistics cp -r /opt/solr/server/solr/configsets/statistics/* statistics
precreate-core qaevent /opt/solr/server/solr/configsets/qaevent
cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent
exec solr -f exec solr -f
volumes: volumes:
assetstore: assetstore:

View File

@@ -77,7 +77,7 @@ import org.dspace.orcid.service.OrcidQueueService;
import org.dspace.orcid.service.OrcidSynchronizationService; import org.dspace.orcid.service.OrcidSynchronizationService;
import org.dspace.orcid.service.OrcidTokenService; import org.dspace.orcid.service.OrcidTokenService;
import org.dspace.profile.service.ResearcherProfileService; import org.dspace.profile.service.ResearcherProfileService;
import org.dspace.qaevent.dao.QAEventsDao; import org.dspace.qaevent.dao.QAEventsDAO;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.versioning.service.VersioningService; import org.dspace.versioning.service.VersioningService;
import org.dspace.workflow.WorkflowItemService; import org.dspace.workflow.WorkflowItemService;
@@ -172,7 +172,7 @@ public class ItemServiceImpl extends DSpaceObjectServiceImpl<Item> implements It
protected SubscribeService subscribeService; protected SubscribeService subscribeService;
@Autowired @Autowired
private QAEventsDao qaEventsDao; private QAEventsDAO qaEventsDao;
protected ItemServiceImpl() { protected ItemServiceImpl() {
super(); super();

View File

@@ -34,9 +34,17 @@ public class QAEvent {
private String source; private String source;
private String eventId; private String eventId;
/**
* contains the targeted dspace object,
* ie: oai:www.openstarts.units.it:123456789/1120 contains the handle
* of the DSpace pbject in its final part 123456789/1120
* */
private String originalId; private String originalId;
/**
* evaluated with the targeted dspace object id
*
* */
private String target; private String target;
private String related; private String related;

View File

@@ -48,7 +48,7 @@ import org.dspace.eperson.service.GroupService;
import org.dspace.eperson.service.SubscribeService; import org.dspace.eperson.service.SubscribeService;
import org.dspace.event.Event; import org.dspace.event.Event;
import org.dspace.orcid.service.OrcidTokenService; import org.dspace.orcid.service.OrcidTokenService;
import org.dspace.qaevent.dao.QAEventsDao; import org.dspace.qaevent.dao.QAEventsDAO;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.util.UUIDUtils; import org.dspace.util.UUIDUtils;
import org.dspace.versioning.Version; import org.dspace.versioning.Version;
@@ -109,7 +109,7 @@ public class EPersonServiceImpl extends DSpaceObjectServiceImpl<EPerson> impleme
@Autowired @Autowired
protected OrcidTokenService orcidTokenService; protected OrcidTokenService orcidTokenService;
@Autowired @Autowired
protected QAEventsDao qaEventsDao; protected QAEventsDAO qaEventsDao;
protected EPersonServiceImpl() { protected EPersonServiceImpl() {
super(); super();

View File

@@ -40,20 +40,20 @@ import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
/** /**
* based on OrcidRestConnector it's a rest connector for OpenAIRE API providing * based on OrcidRestConnector it's a rest connector for Openaire API providing
* ways to perform searches and token grabbing * ways to perform searches and token grabbing
* *
* @author paulo-graca * @author paulo-graca
* *
*/ */
public class OpenAIRERestConnector { public class OpenaireRestConnector {
/** /**
* log4j logger * log4j logger
*/ */
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenAIRERestConnector.class); private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenaireRestConnector.class);
/** /**
* OpenAIRE API Url * Openaire API Url
* and can be configured with: openaire.api.url * and can be configured with: openaire.api.url
*/ */
private String url = "https://api.openaire.eu"; private String url = "https://api.openaire.eu";
@@ -65,30 +65,30 @@ public class OpenAIRERestConnector {
boolean tokenEnabled = false; boolean tokenEnabled = false;
/** /**
* OpenAIRE Authorization and Authentication Token Service URL * Openaire Authorization and Authentication Token Service URL
* and can be configured with: openaire.token.url * and can be configured with: openaire.token.url
*/ */
private String tokenServiceUrl; private String tokenServiceUrl;
/** /**
* OpenAIRE clientId * Openaire clientId
* and can be configured with: openaire.token.clientId * and can be configured with: openaire.token.clientId
*/ */
private String clientId; private String clientId;
/** /**
* OpenAIRERest access token * OpenaireRest access token
*/ */
private OpenAIRERestToken accessToken; private OpenaireRestToken accessToken;
/** /**
* OpenAIRE clientSecret * Openaire clientSecret
* and can be configured with: openaire.token.clientSecret * and can be configured with: openaire.token.clientSecret
*/ */
private String clientSecret; private String clientSecret;
public OpenAIRERestConnector(String url) { public OpenaireRestConnector(String url) {
this.url = url; this.url = url;
} }
@@ -99,7 +99,7 @@ public class OpenAIRERestConnector {
* *
* @throws IOException * @throws IOException
*/ */
public OpenAIRERestToken grabNewAccessToken() throws IOException { public OpenaireRestToken grabNewAccessToken() throws IOException {
if (StringUtils.isBlank(tokenServiceUrl) || StringUtils.isBlank(clientId) if (StringUtils.isBlank(tokenServiceUrl) || StringUtils.isBlank(clientId)
|| StringUtils.isBlank(clientSecret)) { || StringUtils.isBlank(clientSecret)) {
@@ -145,13 +145,13 @@ public class OpenAIRERestConnector {
throw new IOException("Unable to grab the access token using provided service url, client id and secret"); throw new IOException("Unable to grab the access token using provided service url, client id and secret");
} }
return new OpenAIRERestToken(responseObject.get("access_token").toString(), return new OpenaireRestToken(responseObject.get("access_token").toString(),
Long.valueOf(responseObject.get("expires_in").toString())); Long.valueOf(responseObject.get("expires_in").toString()));
} }
/** /**
* Perform a GET request to the OpenAIRE API * Perform a GET request to the Openaire API
* *
* @param file * @param file
* @param accessToken * @param accessToken
@@ -218,12 +218,12 @@ public class OpenAIRERestConnector {
} }
/** /**
* Perform an OpenAIRE Project Search By Keywords * Perform an Openaire Project Search By Keywords
* *
* @param page * @param page
* @param size * @param size
* @param keywords * @param keywords
* @return OpenAIRE Response * @return Openaire Response
*/ */
public Response searchProjectByKeywords(int page, int size, String... keywords) { public Response searchProjectByKeywords(int page, int size, String... keywords) {
String path = "search/projects?keywords=" + String.join("+", keywords); String path = "search/projects?keywords=" + String.join("+", keywords);
@@ -231,13 +231,13 @@ public class OpenAIRERestConnector {
} }
/** /**
* Perform an OpenAIRE Project Search By ID and by Funder * Perform an Openaire Project Search By ID and by Funder
* *
* @param projectID * @param projectID
* @param projectFunder * @param projectFunder
* @param page * @param page
* @param size * @param size
* @return OpenAIRE Response * @return Openaire Response
*/ */
public Response searchProjectByIDAndFunder(String projectID, String projectFunder, int page, int size) { public Response searchProjectByIDAndFunder(String projectID, String projectFunder, int page, int size) {
String path = "search/projects?grantID=" + projectID + "&funder=" + projectFunder; String path = "search/projects?grantID=" + projectID + "&funder=" + projectFunder;
@@ -245,12 +245,12 @@ public class OpenAIRERestConnector {
} }
/** /**
* Perform an OpenAIRE Search request * Perform an Openaire Search request
* *
* @param path * @param path
* @param page * @param page
* @param size * @param size
* @return OpenAIRE Response * @return Openaire Response
*/ */
public Response search(String path, int page, int size) { public Response search(String path, int page, int size) {
String[] queryStringPagination = { "page=" + page, "size=" + size }; String[] queryStringPagination = { "page=" + page, "size=" + size };

View File

@@ -8,13 +8,13 @@
package org.dspace.external; package org.dspace.external;
/** /**
* OpenAIRE rest API token to be used when grabbing an accessToken.<br/> * Openaire rest API token to be used when grabbing an accessToken.<br/>
* Based on https://develop.openaire.eu/basic.html * Based on https://develop.openaire.eu/basic.html
* *
* @author paulo-graca * @author paulo-graca
* *
*/ */
public class OpenAIRERestToken { public class OpenaireRestToken {
/** /**
* Stored access token * Stored access token
@@ -32,7 +32,7 @@ public class OpenAIRERestToken {
* @param accessToken * @param accessToken
* @param expiresIn * @param expiresIn
*/ */
public OpenAIRERestToken(String accessToken, Long expiresIn) { public OpenaireRestToken(String accessToken, Long expiresIn) {
this.accessToken = accessToken; this.accessToken = accessToken;
this.setExpirationDate(expiresIn); this.setExpirationDate(expiresIn);
} }

View File

@@ -31,7 +31,7 @@ import eu.openaire.oaf.model.base.Project;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.dspace.content.dto.MetadataValueDTO; import org.dspace.content.dto.MetadataValueDTO;
import org.dspace.external.OpenAIRERestConnector; import org.dspace.external.OpenaireRestConnector;
import org.dspace.external.model.ExternalDataObject; import org.dspace.external.model.ExternalDataObject;
import org.dspace.external.provider.AbstractExternalDataProvider; import org.dspace.external.provider.AbstractExternalDataProvider;
import org.dspace.importer.external.metadatamapping.MetadataFieldConfig; import org.dspace.importer.external.metadatamapping.MetadataFieldConfig;
@@ -39,13 +39,13 @@ import org.springframework.beans.factory.annotation.Autowired;
/** /**
* This class is the implementation of the ExternalDataProvider interface that * This class is the implementation of the ExternalDataProvider interface that
* will deal with the OpenAIRE External Data lookup * will deal with the Openaire External Data lookup
* *
* @author paulo-graca * @author paulo-graca
*/ */
public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider { public class OpenaireFundingDataProvider extends AbstractExternalDataProvider {
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenAIREFundingDataProvider.class); private static Logger log = org.apache.logging.log4j.LogManager.getLogger(OpenaireFundingDataProvider.class);
/** /**
* GrantAgreement prefix * GrantAgreement prefix
@@ -75,7 +75,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
/** /**
* Connector to handle token and requests * Connector to handle token and requests
*/ */
protected OpenAIRERestConnector connector; protected OpenaireRestConnector connector;
protected Map<String, MetadataFieldConfig> metadataFields; protected Map<String, MetadataFieldConfig> metadataFields;
@@ -93,7 +93,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
// characters that must be escaped for the <:entry-id> // characters that must be escaped for the <:entry-id>
String decodedId = new String(Base64.getDecoder().decode(id)); String decodedId = new String(Base64.getDecoder().decode(id));
if (!isValidProjectURI(decodedId)) { if (!isValidProjectURI(decodedId)) {
log.error("Invalid ID for OpenAIREFunding - " + id); log.error("Invalid ID for OpenaireFunding - " + id);
return Optional.empty(); return Optional.empty();
} }
Response response = searchByProjectURI(decodedId); Response response = searchByProjectURI(decodedId);
@@ -101,7 +101,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
try { try {
if (response.getHeader() != null && Integer.parseInt(response.getHeader().getTotal()) > 0) { if (response.getHeader() != null && Integer.parseInt(response.getHeader().getTotal()) > 0) {
Project project = response.getResults().getResult().get(0).getMetadata().getEntity().getProject(); Project project = response.getResults().getResult().get(0).getMetadata().getEntity().getProject();
ExternalDataObject externalDataObject = new OpenAIREFundingDataProvider ExternalDataObject externalDataObject = new OpenaireFundingDataProvider
.ExternalDataObjectBuilder(project) .ExternalDataObjectBuilder(project)
.setId(generateProjectURI(project)) .setId(generateProjectURI(project))
.setSource(sourceIdentifier) .setSource(sourceIdentifier)
@@ -123,7 +123,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
limit = LIMIT_DEFAULT; limit = LIMIT_DEFAULT;
} }
// OpenAIRE uses pages and first page starts with 1 // Openaire uses pages and first page starts with 1
int page = (start / limit) + 1; int page = (start / limit) + 1;
// escaping query // escaping query
@@ -148,7 +148,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
if (projects.size() > 0) { if (projects.size() > 0) {
return projects.stream() return projects.stream()
.map(project -> new OpenAIREFundingDataProvider .map(project -> new OpenaireFundingDataProvider
.ExternalDataObjectBuilder(project) .ExternalDataObjectBuilder(project)
.setId(generateProjectURI(project)) .setId(generateProjectURI(project))
.setSource(sourceIdentifier) .setSource(sourceIdentifier)
@@ -176,24 +176,24 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
* Generic setter for the sourceIdentifier * Generic setter for the sourceIdentifier
* *
* @param sourceIdentifier The sourceIdentifier to be set on this * @param sourceIdentifier The sourceIdentifier to be set on this
* OpenAIREFunderDataProvider * OpenaireFunderDataProvider
*/ */
@Autowired(required = true) @Autowired(required = true)
public void setSourceIdentifier(String sourceIdentifier) { public void setSourceIdentifier(String sourceIdentifier) {
this.sourceIdentifier = sourceIdentifier; this.sourceIdentifier = sourceIdentifier;
} }
public OpenAIRERestConnector getConnector() { public OpenaireRestConnector getConnector() {
return connector; return connector;
} }
/** /**
* Generic setter for OpenAIRERestConnector * Generic setter for OpenaireRestConnector
* *
* @param connector * @param connector
*/ */
@Autowired(required = true) @Autowired(required = true)
public void setConnector(OpenAIRERestConnector connector) { public void setConnector(OpenaireRestConnector connector) {
this.connector = connector; this.connector = connector;
} }
@@ -219,7 +219,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
} }
/** /**
* This method returns an URI based on OpenAIRE 3.0 guidelines * This method returns an URI based on Openaire 3.0 guidelines
* https://guidelines.openaire.eu/en/latest/literature/field_projectid.html that * https://guidelines.openaire.eu/en/latest/literature/field_projectid.html that
* can be used as an ID if is there any missing part, that part it will be * can be used as an ID if is there any missing part, that part it will be
* replaced by the character '+' * replaced by the character '+'
@@ -281,7 +281,7 @@ public class OpenAIREFundingDataProvider extends AbstractExternalDataProvider {
} }
/** /**
* OpenAIRE Funding External Data Builder Class * Openaire Funding External Data Builder Class
* *
* @author pgraca * @author pgraca
*/ */

View File

@@ -162,7 +162,9 @@ public class CrossRefImportMetadataSourceServiceImpl extends AbstractImportMetad
Iterator<JsonNode> nodes = jsonNode.at("/message/items").iterator(); Iterator<JsonNode> nodes = jsonNode.at("/message/items").iterator();
while (nodes.hasNext()) { while (nodes.hasNext()) {
JsonNode node = nodes.next(); JsonNode node = nodes.next();
results.add(transformSourceRecords(node.toString())); if (!node.isMissingNode()) {
results.add(transformSourceRecords(node.toString()));
}
} }
return results; return results;
} }
@@ -196,7 +198,9 @@ public class CrossRefImportMetadataSourceServiceImpl extends AbstractImportMetad
String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params); String responseString = liveImportClient.executeHttpGetRequest(1000, uriBuilder.toString(), params);
JsonNode jsonNode = convertStringJsonToJsonNode(responseString); JsonNode jsonNode = convertStringJsonToJsonNode(responseString);
JsonNode messageNode = jsonNode.at("/message"); JsonNode messageNode = jsonNode.at("/message");
results.add(transformSourceRecords(messageNode.toString())); if (!messageNode.isMissingNode()) {
results.add(transformSourceRecords(messageNode.toString()));
}
return results; return results;
} }
} }
@@ -250,7 +254,9 @@ public class CrossRefImportMetadataSourceServiceImpl extends AbstractImportMetad
Iterator<JsonNode> nodes = jsonNode.at("/message/items").iterator(); Iterator<JsonNode> nodes = jsonNode.at("/message/items").iterator();
while (nodes.hasNext()) { while (nodes.hasNext()) {
JsonNode node = nodes.next(); JsonNode node = nodes.next();
results.add(transformSourceRecords(node.toString())); if (!node.isMissingNode()) {
results.add(transformSourceRecords(node.toString()));
}
} }
return results; return results;
} }
@@ -333,4 +339,4 @@ public class CrossRefImportMetadataSourceServiceImpl extends AbstractImportMetad
this.url = url; this.url = url;
} }
} }

View File

@@ -16,7 +16,7 @@ import org.dspace.qaevent.service.QAEventService;
import org.dspace.utils.DSpace; import org.dspace.utils.DSpace;
/** /**
* Consumer to delete qaevents once the target item is deleted * Consumer to delete qaevents from solr due to the target item deletion
* *
* @author Andrea Bollini (andrea.bollini at 4science.it) * @author Andrea Bollini (andrea.bollini at 4science.it)
* *

View File

@@ -10,7 +10,7 @@ package org.dspace.qaevent;
import java.util.Date; import java.util.Date;
/** /**
* This model class represent the source/provider of the QA events (as OpenAIRE). * This model class represent the source/provider of the QA events (as Openaire).
* *
* @author Luca Giamminonni (luca.giamminonni at 4Science) * @author Luca Giamminonni (luca.giamminonni at 4Science)
* *

View File

@@ -22,7 +22,7 @@ import org.dspace.eperson.EPerson;
* @author Andrea Bollini (andrea.bollini at 4science.it) * @author Andrea Bollini (andrea.bollini at 4science.it)
* *
*/ */
public interface QAEventsDao extends GenericDAO<QAEventProcessed> { public interface QAEventsDAO extends GenericDAO<QAEventProcessed> {
/** /**
* Returns all the stored QAEventProcessed entities. * Returns all the stored QAEventProcessed entities.

View File

@@ -17,16 +17,16 @@ import org.dspace.content.QAEventProcessed;
import org.dspace.core.AbstractHibernateDAO; import org.dspace.core.AbstractHibernateDAO;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.qaevent.dao.QAEventsDao; import org.dspace.qaevent.dao.QAEventsDAO;
/** /**
* Implementation of {@link QAEventsDao} that store processed events using an * Implementation of {@link QAEventsDAO} that store processed events using an
* SQL DBMS. * SQL DBMS.
* *
* @author Andrea Bollini (andrea.bollini at 4science.it) * @author Andrea Bollini (andrea.bollini at 4science.it)
* *
*/ */
public class QAEventsDaoImpl extends AbstractHibernateDAO<QAEventProcessed> implements QAEventsDao { public class QAEventsDAOImpl extends AbstractHibernateDAO<QAEventProcessed> implements QAEventsDAO {
@Override @Override
public List<QAEventProcessed> findAll(Context context) throws SQLException { public List<QAEventProcessed> findAll(Context context) throws SQLException {
@@ -60,7 +60,7 @@ public class QAEventsDaoImpl extends AbstractHibernateDAO<QAEventProcessed> impl
public List<QAEventProcessed> searchByEventId(Context context, String eventId, Integer start, Integer size) public List<QAEventProcessed> searchByEventId(Context context, String eventId, Integer start, Integer size)
throws SQLException { throws SQLException {
Query query = createQuery(context, Query query = createQuery(context,
"SELECT * " + "FROM QAEventProcessed qaevent WHERE qaevent.qaevent_id = :event_id "); "SELECT * FROM QAEventProcessed qaevent WHERE qaevent.qaevent_id = :event_id ");
query.setFirstResult(start); query.setFirstResult(start);
query.setMaxResults(size); query.setMaxResults(size);
query.setParameter("event_id", eventId); query.setParameter("event_id", eventId);

View File

@@ -32,7 +32,7 @@ import org.dspace.content.QAEvent;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.qaevent.service.BrokerClientFactory; import org.dspace.qaevent.service.OpenaireClientFactory;
import org.dspace.qaevent.service.QAEventService; import org.dspace.qaevent.service.QAEventService;
import org.dspace.scripts.DSpaceRunnable; import org.dspace.scripts.DSpaceRunnable;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
@@ -42,7 +42,9 @@ import org.dspace.utils.DSpace;
/** /**
* Implementation of {@link DSpaceRunnable} to perfom a QAEvents import from a * Implementation of {@link DSpaceRunnable} to perfom a QAEvents import from a
* json file. The JSON file contains an array of JSON Events, where each event * json file. The JSON file contains an array of JSON Events, where each event
* has the following structure * has the following structure. The message attribute follows the structure
* documented at
* @see <a href="https://graph.openaire.eu/docs/category/entities" target="_blank"> see </a>
* *
* <code> <br/> * <code> <br/>
* { <br/> * { <br/>
@@ -103,7 +105,7 @@ public class OpenaireEventsImport
qaEventService = new DSpace().getSingletonService(QAEventService.class); qaEventService = new DSpace().getSingletonService(QAEventService.class);
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
brokerClient = BrokerClientFactory.getInstance().getBrokerClient(); brokerClient = OpenaireClientFactory.getInstance().getBrokerClient();
topicsToImport = configurationService.getArrayProperty("qaevents.openaire.import.topic"); topicsToImport = configurationService.getArrayProperty("qaevents.openaire.import.topic");
openaireBrokerURL = getOpenaireBrokerUri(); openaireBrokerURL = getOpenaireBrokerUri();

View File

@@ -54,12 +54,12 @@ public class OpenaireEventsImportScriptConfiguration<T extends OpenaireEventsImp
if (options == null) { if (options == null) {
Options options = new Options(); Options options = new Options();
options.addOption("f", "file", true, "Import data from OpenAIRE quality assurance broker JSON file." options.addOption("f", "file", true, "Import data from Openaire quality assurance broker JSON file."
+ " This parameter is mutually exclusive to the email parameter."); + " This parameter is mutually exclusive to the email parameter.");
options.getOption("f").setType(InputStream.class); options.getOption("f").setType(InputStream.class);
options.getOption("f").setRequired(false); options.getOption("f").setRequired(false);
options.addOption("e", "email", true, "Email related to the subscriptions to import data from OpenAIRE " options.addOption("e", "email", true, "Email related to the subscriptions to import data from Openaire "
+ "broker. This parameter is mutually exclusive to the file parameter."); + "broker. This parameter is mutually exclusive to the file parameter.");
options.getOption("e").setType(String.class); options.getOption("e").setType(String.class);
options.getOption("e").setRequired(false); options.getOption("e").setRequired(false);

View File

@@ -16,7 +16,7 @@ import org.dspace.utils.DSpace;
* @author Luca Giamminonni (luca.giamminonni at 4science.it) * @author Luca Giamminonni (luca.giamminonni at 4science.it)
* *
*/ */
public interface BrokerClientFactory { public interface OpenaireClientFactory {
/** /**
* Returns an instance of the {@link BrokerClient}. * Returns an instance of the {@link BrokerClient}.
@@ -25,7 +25,7 @@ public interface BrokerClientFactory {
*/ */
public BrokerClient getBrokerClient(); public BrokerClient getBrokerClient();
public static BrokerClientFactory getInstance() { public static OpenaireClientFactory getInstance() {
return new DSpace().getServiceManager().getServiceByName("brokerClientFactory", BrokerClientFactory.class); return new DSpace().getServiceManager().getServiceByName("openaireClientFactory", OpenaireClientFactory.class);
} }
} }

View File

@@ -27,11 +27,9 @@ public interface QAEventService {
* Find all the event's topics. * Find all the event's topics.
* *
* @param offset the offset to apply * @param offset the offset to apply
* @param pageSize the page size
* @return the topics list * @return the topics list
*/ */
public List<QATopic> findAllTopics(long offset, long pageSize); public List<QATopic> findAllTopics(long offset, long count, String orderField, boolean ascending);
/** /**
* Find all the event's topics related to the given source. * Find all the event's topics related to the given source.
* *
@@ -40,7 +38,8 @@ public interface QAEventService {
* @param count the page size * @param count the page size
* @return the topics list * @return the topics list
*/ */
public List<QATopic> findAllTopicsBySource(String source, long offset, long count); public List<QATopic> findAllTopicsBySource(String source, long offset, long count,
String orderField, boolean ascending);
/** /**
* Count all the event's topics. * Count all the event's topics.

View File

@@ -11,7 +11,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* Implementation of {@link QAMessageDTO} that model message coming from OPENAIRE. * Implementation of {@link QAMessageDTO} that model message coming from OPENAIRE.
* * @see <a href="https://graph.openaire.eu/docs/category/entities" target="_blank"> see </a>
* @author Luca Giamminonni (luca.giamminonni at 4science.it) * @author Luca Giamminonni (luca.giamminonni at 4science.it)
* *
*/ */

View File

@@ -8,17 +8,17 @@
package org.dspace.qaevent.service.impl; package org.dspace.qaevent.service.impl;
import eu.dnetlib.broker.BrokerClient; import eu.dnetlib.broker.BrokerClient;
import org.dspace.qaevent.service.BrokerClientFactory; import org.dspace.qaevent.service.OpenaireClientFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
/** /**
* Implementation of {@link BrokerClientFactory} that returns the instance of * Implementation of {@link OpenaireClientFactory} that returns the instance of
* {@link BrokerClient} managed by the Spring context. * {@link BrokerClient} managed by the Spring context.
* *
* @author Luca Giamminonni (luca.giamminonni at 4science.it) * @author Luca Giamminonni (luca.giamminonni at 4science.it)
* *
*/ */
public class BrokerClientFactoryImpl implements BrokerClientFactory { public class OpenaireClientFactoryImpl implements OpenaireClientFactory {
@Autowired @Autowired
private BrokerClient brokerClient; private BrokerClient brokerClient;

View File

@@ -36,6 +36,7 @@ import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.FacetParams;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.content.QAEvent; import org.dspace.content.QAEvent;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
@@ -43,8 +44,8 @@ import org.dspace.core.Context;
import org.dspace.handle.service.HandleService; import org.dspace.handle.service.HandleService;
import org.dspace.qaevent.QASource; import org.dspace.qaevent.QASource;
import org.dspace.qaevent.QATopic; import org.dspace.qaevent.QATopic;
import org.dspace.qaevent.dao.QAEventsDao; import org.dspace.qaevent.dao.QAEventsDAO;
import org.dspace.qaevent.dao.impl.QAEventsDaoImpl; import org.dspace.qaevent.dao.impl.QAEventsDAOImpl;
import org.dspace.qaevent.service.QAEventService; import org.dspace.qaevent.service.QAEventService;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.services.factory.DSpaceServicesFactory;
@@ -54,7 +55,7 @@ import org.springframework.beans.factory.annotation.Autowired;
* Implementation of {@link QAEventService} that use Solr to store events. When * Implementation of {@link QAEventService} that use Solr to store events. When
* the user performs an action on the event (such as accepting the suggestion or * the user performs an action on the event (such as accepting the suggestion or
* rejecting it) then the event is removed from solr and saved in the database * rejecting it) then the event is removed from solr and saved in the database
* (see {@link QAEventsDao}) so that it is no longer proposed. * (see {@link QAEventsDAO}) so that it is no longer proposed.
* *
* @author Andrea Bollini (andrea.bollini at 4science.it) * @author Andrea Bollini (andrea.bollini at 4science.it)
* *
@@ -71,7 +72,7 @@ public class QAEventServiceImpl implements QAEventService {
private HandleService handleService; private HandleService handleService;
@Autowired @Autowired
private QAEventsDaoImpl qaEventsDao; private QAEventsDAOImpl qaEventsDao;
private ObjectMapper jsonMapper; private ObjectMapper jsonMapper;
@@ -188,12 +189,13 @@ public class QAEventServiceImpl implements QAEventService {
} }
@Override @Override
public List<QATopic> findAllTopics(long offset, long count) { public List<QATopic> findAllTopics(long offset, long count, String orderField, boolean ascending) {
return findAllTopicsBySource(null, offset, count); return findAllTopicsBySource(null, offset, count, orderField, ascending);
} }
@Override @Override
public List<QATopic> findAllTopicsBySource(String source, long offset, long count) { public List<QATopic> findAllTopicsBySource(String source, long offset, long count,
String orderField, boolean ascending) {
if (source != null && isNotSupportedSource(source)) { if (source != null && isNotSupportedSource(source)) {
return null; return null;
@@ -201,6 +203,8 @@ public class QAEventServiceImpl implements QAEventService {
SolrQuery solrQuery = new SolrQuery(); SolrQuery solrQuery = new SolrQuery();
solrQuery.setRows(0); solrQuery.setRows(0);
solrQuery.setSort(orderField, ascending ? ORDER.asc : ORDER.desc);
solrQuery.setFacetSort(FacetParams.FACET_SORT_INDEX);
solrQuery.setQuery("*:*"); solrQuery.setQuery("*:*");
solrQuery.setFacet(true); solrQuery.setFacet(true);
solrQuery.setFacetMinCount(1); solrQuery.setFacetMinCount(1);

View File

@@ -56,8 +56,16 @@ public class ContentGenerator implements SubscriptionGenerator<IndexableObject>
Locale supportedLocale = I18nUtil.getEPersonLocale(ePerson); Locale supportedLocale = I18nUtil.getEPersonLocale(ePerson);
Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "subscriptions_content")); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "subscriptions_content"));
email.addRecipient(ePerson.getEmail()); email.addRecipient(ePerson.getEmail());
email.addArgument(generateBodyMail(context, indexableComm));
email.addArgument(generateBodyMail(context, indexableColl)); String bodyCommunities = generateBodyMail(context, indexableComm);
String bodyCollections = generateBodyMail(context, indexableColl);
if (bodyCommunities.equals(EMPTY) && bodyCollections.equals(EMPTY)) {
log.debug("subscription(s) of eperson {} do(es) not match any new items: nothing to send" +
" - exit silently", ePerson::getID);
return;
}
email.addArgument(bodyCommunities);
email.addArgument(bodyCollections);
email.send(); email.send();
} }
} catch (Exception e) { } catch (Exception e) {
@@ -67,21 +75,19 @@ public class ContentGenerator implements SubscriptionGenerator<IndexableObject>
} }
private String generateBodyMail(Context context, List<IndexableObject> indexableObjects) { private String generateBodyMail(Context context, List<IndexableObject> indexableObjects) {
if (indexableObjects == null || indexableObjects.isEmpty()) {
return EMPTY;
}
try { try {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("\n".getBytes(UTF_8)); out.write("\n".getBytes(UTF_8));
if (indexableObjects.size() > 0) { for (IndexableObject indexableObject : indexableObjects) {
for (IndexableObject indexableObject : indexableObjects) { out.write("\n".getBytes(UTF_8));
out.write("\n".getBytes(UTF_8)); Item item = (Item) indexableObject.getIndexedObject();
Item item = (Item) indexableObject.getIndexedObject(); String entityType = itemService.getEntityTypeLabel(item);
String entityType = itemService.getEntityTypeLabel(item); Optional.ofNullable(entityType2Disseminator.get(entityType))
Optional.ofNullable(entityType2Disseminator.get(entityType)) .orElseGet(() -> entityType2Disseminator.get("Item"))
.orElseGet(() -> entityType2Disseminator.get("Item")) .disseminate(context, item, out);
.disseminate(context, item, out);
}
return out.toString();
} else {
out.write("No items".getBytes(UTF_8));
} }
return out.toString(); return out.toString();
} catch (Exception e) { } catch (Exception e) {

View File

@@ -1,19 +0,0 @@
--
-- The contents of this file are subject to the license and copyright
-- detailed in the LICENSE and NOTICE files at the root of the source
-- tree and available online at
--
-- http://www.dspace.org/license/
--
CREATE TABLE qaevent_processed (
qaevent_id VARCHAR(255) NOT NULL,
qaevent_timestamp TIMESTAMP NULL,
eperson_uuid UUID NULL,
item_uuid UUID NULL,
CONSTRAINT qaevent_pk PRIMARY KEY (qaevent_id),
CONSTRAINT eperson_uuid_fkey FOREIGN KEY (eperson_uuid) REFERENCES eperson (uuid),
CONSTRAINT item_uuid_fkey FOREIGN KEY (item_uuid) REFERENCES item (uuid)
);
CREATE INDEX item_uuid_idx ON qaevent_processed(item_uuid);

View File

@@ -7,7 +7,7 @@
http://www.springframework.org/schema/util/spring-util.xsd" http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true"> default-lazy-init="true">
<bean id="mockOpenAIRERestConnector" class="org.dspace.external.MockOpenAIRERestConnector"> <bean id="mockOpenaireRestConnector" class="org.dspace.external.MockOpenaireRestConnector">
<constructor-arg value="${openaire.api.url:https://api.openaire.eu}"/> <constructor-arg value="${openaire.api.url:https://api.openaire.eu}"/>
<property name="tokenEnabled" value="${openaire.token.enabled:false}"/> <property name="tokenEnabled" value="${openaire.token.enabled:false}"/>
<property name="tokenServiceUrl" value="${openaire.token.url:https://aai.openaire.eu/oidc/token}"/> <property name="tokenServiceUrl" value="${openaire.token.url:https://aai.openaire.eu/oidc/token}"/>
@@ -15,10 +15,10 @@
<property name="clientSecret" value="${openaire.token.clientSecret}"/> <property name="clientSecret" value="${openaire.token.clientSecret}"/>
</bean> </bean>
<bean <bean
class="org.dspace.external.provider.impl.OpenAIREFundingDataProvider" class="org.dspace.external.provider.impl.OpenaireFundingDataProvider"
init-method="init"> init-method="init">
<property name="sourceIdentifier" value="openAIREFunding" /> <property name="sourceIdentifier" value="openaireFunding" />
<property name="connector" ref="mockOpenAIRERestConnector" /> <property name="connector" ref="mockOpenaireRestConnector" />
<property name="metadataFields" ref="mapOfmetadata"/> <property name="metadataFields" ref="mapOfmetadata"/>
<property name="supportedEntityTypes"> <property name="supportedEntityTypes">
<list> <list>

View File

@@ -286,7 +286,7 @@
</property> </property>
</bean> </bean>
<!-- An example of an OpenAIRE compliance filter based on the same rules in xoai.xml <!-- An example of an Openaire compliance filter based on the same rules in xoai.xml
some sub-statements are defined within this bean, and some are referenced from earlier definitions some sub-statements are defined within this bean, and some are referenced from earlier definitions
--> -->
<bean id="openaire_filter" class="org.dspace.content.logic.DefaultFilter"> <bean id="openaire_filter" class="org.dspace.content.logic.DefaultFilter">
@@ -329,7 +329,7 @@
</list> </list>
</property> </property>
</bean> </bean>
<!-- AND the dc.relation is a valid OpenAIRE identifier <!-- AND the dc.relation is a valid Openaire identifier
(starts with "info:eu-repo/grantAgreement/") --> (starts with "info:eu-repo/grantAgreement/") -->
<bean id="has-openaire-relation_condition" <bean id="has-openaire-relation_condition"
class="org.dspace.content.logic.condition.MetadataValueMatchCondition"> class="org.dspace.content.logic.condition.MetadataValueMatchCondition">

View File

@@ -66,7 +66,7 @@
</bean> </bean>
<bean id="import-openaire-events" class="org.dspace.qaevent.script.OpenaireEventsImportCliScriptConfiguration" primary="true"> <bean id="import-openaire-events" class="org.dspace.qaevent.script.OpenaireEventsImportCliScriptConfiguration" primary="true">
<property name="description" value="Import new openAIRE quality assurance broker events"/> <property name="description" value="Import new openaire quality assurance broker events"/>
<property name="dspaceRunnableClass" value="org.dspace.qaevent.script.OpenaireEventsImportCli"/> <property name="dspaceRunnableClass" value="org.dspace.qaevent.script.OpenaireEventsImportCli"/>
</bean> </bean>

View File

@@ -18,7 +18,11 @@ import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat; import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle; import org.dspace.content.Bundle;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.DSpaceObjectService; import org.dspace.content.service.DSpaceObjectService;
import org.dspace.content.service.MetadataValueService;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
@@ -55,6 +59,13 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder<Bitstream> {
return builder.createInRequestedBundle(context, item, is, bundleName); return builder.createInRequestedBundle(context, item, is, bundleName);
} }
public static BitstreamBuilder createBitstream(Context context, Item item, InputStream is,
String bundleName, boolean iiifEnabled)
throws SQLException, AuthorizeException, IOException {
BitstreamBuilder builder = new BitstreamBuilder(context);
return builder.createInRequestedBundleWithIiifDisabled(context, item, is, bundleName, iiifEnabled);
}
private BitstreamBuilder create(Context context, Item item, InputStream is) private BitstreamBuilder create(Context context, Item item, InputStream is)
throws SQLException, AuthorizeException, IOException { throws SQLException, AuthorizeException, IOException {
this.context = context; this.context = context;
@@ -88,6 +99,41 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder<Bitstream> {
return this; return this;
} }
private BitstreamBuilder createInRequestedBundleWithIiifDisabled(Context context, Item item, InputStream is,
String bundleName, boolean iiifEnabled)
throws SQLException, AuthorizeException, IOException {
this.context = context;
this.item = item;
Bundle bundle = getBundleByNameAndIiiEnabled(item, bundleName, iiifEnabled);
bitstream = bitstreamService.create(context, bundle, is);
return this;
}
private Bundle getBundleByNameAndIiiEnabled(Item item, String bundleName, boolean iiifEnabled)
throws SQLException, AuthorizeException {
List<Bundle> bundles = itemService.getBundles(item, bundleName);
Bundle targetBundle = null;
if (bundles.size() < 1) {
// not found, create a new one
targetBundle = bundleService.create(context, item, bundleName);
MetadataValueService metadataValueService = ContentServiceFactory.getInstance().getMetadataValueService();
MetadataField iiifEnabledField = metadataFieldService.
findByString(context, "dspace.iiif.enabled", '.');
MetadataValue metadataValue = metadataValueService.create(context, targetBundle, iiifEnabledField);
metadataValue.setValue(String.valueOf(iiifEnabled));
} else {
// put bitstreams into first bundle
targetBundle = bundles.iterator().next();
}
return targetBundle;
}
private Bundle getBundleByName(Item item, String bundleName) throws SQLException, AuthorizeException { private Bundle getBundleByName(Item item, String bundleName) throws SQLException, AuthorizeException {
List<Bundle> bundles = itemService.getBundles(item, bundleName); List<Bundle> bundles = itemService.getBundles(item, bundleName);
Bundle targetBundle = null; Bundle targetBundle = null;
@@ -137,6 +183,11 @@ public class BitstreamBuilder extends AbstractDSpaceObjectBuilder<Bitstream> {
} }
public BitstreamBuilder withIIIFDisabled() throws SQLException {
bitstreamService.addMetadata(context, bitstream, "dspace", "iiif", "enabled", null, "false");
return this;
}
public BitstreamBuilder withIIIFLabel(String label) throws SQLException { public BitstreamBuilder withIIIFLabel(String label) throws SQLException {
bitstreamService.addMetadata(context, bitstream, "iiif", "label", null, null, label); bitstreamService.addMetadata(context, bitstream, "iiif", "label", null, null, label);
return this; return this;

View File

@@ -25,9 +25,21 @@ public class QAEventBuilder extends AbstractBuilder<QAEvent, QAEventService> {
private Item item; private Item item;
private QAEvent target; private QAEvent target;
private String source = QAEvent.OPENAIRE_SOURCE; private String source = QAEvent.OPENAIRE_SOURCE;
/**
* the title of the DSpace object
* */
private String title; private String title;
/**
* the name of the Quality Assurance Event Topic
* */
private String topic; private String topic;
/**
* thr original QA Event imported
* */
private String message; private String message;
/**
* uuid of the targeted DSpace object
* */
private String relatedItem; private String relatedItem;
private double trust = 0.5; private double trust = 0.5;
private Date lastUpdate = new Date(); private Date lastUpdate = new Date();

View File

@@ -1200,4 +1200,71 @@ public class CollectionTest extends AbstractDSpaceObjectTest {
equalTo(owningCommunity)); equalTo(owningCommunity));
} }
/**
* Test of retrieveCollectionWithSubmitByEntityType method getting the closest
* collection of non-item type starting from an item
*/
@Test
public void testRetrieveCollectionWithSubmitByEntityType() throws SQLException, AuthorizeException {
context.setDispatcher("default");
context.turnOffAuthorisationSystem();
Community com = communityService.create(null, context);
Group submitters = groupService.create(context);
Collection collection = collectionService.create(context, com);
collectionService.addMetadata(context, collection, "dspace", "entity", "type",
null, "Publication");
com.addCollection(collection);
WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false);
Item item = installItemService.installItem(context, workspaceItem);
EPerson epersonA = ePersonService.create(context);
Collection collectionPerson = collectionService.create(context, com);
collectionService.addMetadata(context, collectionPerson, "dspace", "entity", "type",
null, "Person");
collectionPerson.setSubmitters(submitters);
groupService.addMember(context, submitters, epersonA);
context.setCurrentUser(epersonA);
context.commit();
context.restoreAuthSystemState();
Collection resultCollection = collectionService.retrieveCollectionWithSubmitByEntityType
(context, item, "Person");
assertThat("testRetrieveCollectionWithSubmitByEntityType 0", resultCollection, notNullValue());
assertThat("testRetrieveCollectionWithSubmitByEntityType 1", resultCollection, equalTo(collectionPerson));
context.setDispatcher("exclude-discovery");
}
/**
* Test of rretrieveCollectionWithSubmitByCommunityAndEntityType method getting the closest
* collection of non-community type starting from an community
*/
@Test
public void testRetrieveCollectionWithSubmitByCommunityAndEntityType() throws SQLException, AuthorizeException {
context.setDispatcher("default");
context.turnOffAuthorisationSystem();
Community com = communityService.create(null, context);
Group submitters = groupService.create(context);
Collection collection = collectionService.create(context, com);
collectionService.addMetadata(context, collection, "dspace", "entity", "type",
null, "Publication");
com.addCollection(collection);
WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, false);
Item item = installItemService.installItem(context, workspaceItem);
EPerson epersonA = ePersonService.create(context);
Collection collectionPerson = collectionService.create(context, com);
collectionService.addMetadata(context, collectionPerson, "dspace", "entity", "type",
null, "Person");
collectionPerson.setSubmitters(submitters);
groupService.addMember(context, submitters, epersonA);
context.setCurrentUser(epersonA);
context.commit();
context.restoreAuthSystemState();
Collection resultCollection = collectionService.retrieveCollectionWithSubmitByCommunityAndEntityType
(context, com, "Person");
assertThat("testRetrieveCollectionWithSubmitByEntityType 0", resultCollection, notNullValue());
assertThat("testRetrieveCollectionWithSubmitByEntityType 1", resultCollection, equalTo(collectionPerson));
context.setDispatcher("exclude-discovery");
}
} }

View File

@@ -14,15 +14,15 @@ import eu.openaire.jaxb.helper.OpenAIREHandler;
import eu.openaire.jaxb.model.Response; import eu.openaire.jaxb.model.Response;
/** /**
* Mock the OpenAIRE rest connector for unit testing<br> * Mock the Openaire rest connector for unit testing<br>
* will be resolved against static test xml files * will be resolved against static test xml files
* *
* @author pgraca * @author pgraca
* *
*/ */
public class MockOpenAIRERestConnector extends OpenAIRERestConnector { public class MockOpenaireRestConnector extends OpenaireRestConnector {
public MockOpenAIRERestConnector(String url) { public MockOpenaireRestConnector(String url) {
super(url); super(url);
} }

View File

@@ -23,15 +23,15 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
/** /**
* Unit tests for OpenAIREFundingDataProvider * Unit tests for OpenaireFundingDataProvider
* *
* @author pgraca * @author pgraca
* *
*/ */
public class OpenAIREFundingDataProviderTest extends AbstractDSpaceTest { public class OpenaireFundingDataProviderTest extends AbstractDSpaceTest {
ExternalDataService externalDataService; ExternalDataService externalDataService;
ExternalDataProvider openAIREFundingDataProvider; ExternalDataProvider openaireFundingDataProvider;
/** /**
* This method will be run before every test as per @Before. It will initialize * This method will be run before every test as per @Before. It will initialize
@@ -44,38 +44,38 @@ public class OpenAIREFundingDataProviderTest extends AbstractDSpaceTest {
public void init() { public void init() {
// Set up External Service Factory and set data providers // Set up External Service Factory and set data providers
externalDataService = ExternalServiceFactory.getInstance().getExternalDataService(); externalDataService = ExternalServiceFactory.getInstance().getExternalDataService();
openAIREFundingDataProvider = externalDataService.getExternalDataProvider("openAIREFunding"); openaireFundingDataProvider = externalDataService.getExternalDataProvider("openaireFunding");
} }
@Test @Test
public void testNumberOfResultsWSingleKeyword() { public void testNumberOfResultsWSingleKeyword() {
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider); assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
assertEquals("openAIREFunding.numberOfResults.query:mock", 77, assertEquals("openaireFunding.numberOfResults.query:mock", 77,
openAIREFundingDataProvider.getNumberOfResults("mock")); openaireFundingDataProvider.getNumberOfResults("mock"));
} }
@Test @Test
public void testNumberOfResultsWKeywords() { public void testNumberOfResultsWKeywords() {
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider); assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
assertEquals("openAIREFunding.numberOfResults.query:mock+test", 77, assertEquals("openaireFunding.numberOfResults.query:mock+test", 77,
openAIREFundingDataProvider.getNumberOfResults("mock+test")); openaireFundingDataProvider.getNumberOfResults("mock+test"));
} }
@Test @Test
public void testQueryResultsWSingleKeyword() { public void testQueryResultsWSingleKeyword() {
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider); assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
List<ExternalDataObject> results = openAIREFundingDataProvider.searchExternalDataObjects("mock", 0, 10); List<ExternalDataObject> results = openaireFundingDataProvider.searchExternalDataObjects("mock", 0, 10);
assertEquals("openAIREFunding.searchExternalDataObjects.size", 10, results.size()); assertEquals("openaireFunding.searchExternalDataObjects.size", 10, results.size());
} }
@Test @Test
public void testQueryResultsWKeywords() { public void testQueryResultsWKeywords() {
String value = "Mushroom Robo-Pic - Development of an autonomous robotic mushroom picking system"; String value = "Mushroom Robo-Pic - Development of an autonomous robotic mushroom picking system";
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider); assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
List<ExternalDataObject> results = openAIREFundingDataProvider.searchExternalDataObjects("mock+test", 0, 10); List<ExternalDataObject> results = openaireFundingDataProvider.searchExternalDataObjects("mock+test", 0, 10);
assertEquals("openAIREFunding.searchExternalDataObjects.size", 10, results.size()); assertEquals("openaireFunding.searchExternalDataObjects.size", 10, results.size());
assertTrue("openAIREFunding.searchExternalDataObjects.first.value", value.equals(results.get(0).getValue())); assertTrue("openaireFunding.searchExternalDataObjects.first.value", value.equals(results.get(0).getValue()));
} }
@Test @Test
@@ -84,22 +84,22 @@ public class OpenAIREFundingDataProviderTest extends AbstractDSpaceTest {
String value = "Portuguese Wild Mushrooms: Chemical characterization and functional study" String value = "Portuguese Wild Mushrooms: Chemical characterization and functional study"
+ " of antiproliferative and proapoptotic properties in cancer cell lines"; + " of antiproliferative and proapoptotic properties in cancer cell lines";
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider); assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
Optional<ExternalDataObject> result = openAIREFundingDataProvider.getExternalDataObject(id); Optional<ExternalDataObject> result = openaireFundingDataProvider.getExternalDataObject(id);
assertTrue("openAIREFunding.getExternalDataObject.exists", result.isPresent()); assertTrue("openaireFunding.getExternalDataObject.exists", result.isPresent());
assertTrue("openAIREFunding.getExternalDataObject.value", value.equals(result.get().getValue())); assertTrue("openaireFunding.getExternalDataObject.value", value.equals(result.get().getValue()));
} }
@Test @Test
public void testGetDataObjectWInvalidId() { public void testGetDataObjectWInvalidId() {
String id = "WRONGID"; String id = "WRONGID";
assertNotNull("openAIREFundingDataProvider is not null", openAIREFundingDataProvider); assertNotNull("openaireFundingDataProvider is not null", openaireFundingDataProvider);
Optional<ExternalDataObject> result = openAIREFundingDataProvider.getExternalDataObject(id); Optional<ExternalDataObject> result = openaireFundingDataProvider.getExternalDataObject(id);
assertTrue("openAIREFunding.getExternalDataObject.notExists:WRONGID", result.isEmpty()); assertTrue("openaireFunding.getExternalDataObject.notExists:WRONGID", result.isEmpty());
} }
} }

View File

@@ -45,9 +45,9 @@ import org.dspace.content.Collection;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.matcher.QASourceMatcher; import org.dspace.matcher.QASourceMatcher;
import org.dspace.matcher.QATopicMatcher; import org.dspace.matcher.QATopicMatcher;
import org.dspace.qaevent.service.BrokerClientFactory; import org.dspace.qaevent.service.OpenaireClientFactory;
import org.dspace.qaevent.service.QAEventService; import org.dspace.qaevent.service.QAEventService;
import org.dspace.qaevent.service.impl.BrokerClientFactoryImpl; import org.dspace.qaevent.service.impl.OpenaireClientFactoryImpl;
import org.dspace.utils.DSpace; import org.dspace.utils.DSpace;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@@ -63,11 +63,13 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
private static final String BASE_JSON_DIR_PATH = "org/dspace/app/openaire-events/"; private static final String BASE_JSON_DIR_PATH = "org/dspace/app/openaire-events/";
private static final String ORDER_FIELD = "topic";
private QAEventService qaEventService = new DSpace().getSingletonService(QAEventService.class); private QAEventService qaEventService = new DSpace().getSingletonService(QAEventService.class);
private Collection collection; private Collection collection;
private BrokerClient brokerClient = BrokerClientFactory.getInstance().getBrokerClient(); private BrokerClient brokerClient = OpenaireClientFactory.getInstance().getBrokerClient();
private BrokerClient mockBrokerClient = mock(BrokerClient.class); private BrokerClient mockBrokerClient = mock(BrokerClient.class);
@@ -86,12 +88,12 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
context.restoreAuthSystemState(); context.restoreAuthSystemState();
((BrokerClientFactoryImpl) BrokerClientFactory.getInstance()).setBrokerClient(mockBrokerClient); ((OpenaireClientFactoryImpl) OpenaireClientFactory.getInstance()).setBrokerClient(mockBrokerClient);
} }
@After @After
public void after() { public void after() {
((BrokerClientFactoryImpl) BrokerClientFactory.getInstance()).setBrokerClient(brokerClient); ((OpenaireClientFactoryImpl) OpenaireClientFactory.getInstance()).setBrokerClient(brokerClient);
} }
@Test @Test
@@ -157,7 +159,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 5L))); assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 5L)));
assertThat(qaEventService.findAllTopics(0, 20), containsInAnyOrder( assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L), QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L),
QATopicMatcher.with("ENRICH/MORE/PID", 1L), QATopicMatcher.with("ENRICH/MORE/PID", 1L),
QATopicMatcher.with("ENRICH/MISSING/PID", 1L), QATopicMatcher.with("ENRICH/MISSING/PID", 1L),
@@ -213,7 +215,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 3L))); assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 3L)));
assertThat(qaEventService.findAllTopics(0, 20), containsInAnyOrder( assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L), QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L),
QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L), QATopicMatcher.with("ENRICH/MISSING/PROJECT", 1L),
QATopicMatcher.with("ENRICH/MORE/PID", 1L) QATopicMatcher.with("ENRICH/MORE/PID", 1L)
@@ -253,7 +255,8 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 1L))); assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 1L)));
assertThat(qaEventService.findAllTopics(0, 20), contains(QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L))); assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false),
contains(QATopicMatcher.with("ENRICH/MISSING/ABSTRACT", 1L)));
String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}"; String abstractMessage = "{\"abstracts[0]\":\"Missing Abstract\"}";
@@ -280,7 +283,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 0L))); assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
assertThat(qaEventService.findAllTopics(0, 20), empty()); assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), empty());
verifyNoInteractions(mockBrokerClient); verifyNoInteractions(mockBrokerClient);
} }
@@ -327,7 +330,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 6L))); assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
assertThat(qaEventService.findAllTopics(0, 20), containsInAnyOrder( assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L), QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L),
QATopicMatcher.with("ENRICH/MORE/PID", 1L), QATopicMatcher.with("ENRICH/MORE/PID", 1L),
QATopicMatcher.with("ENRICH/MISSING/PID", 1L), QATopicMatcher.with("ENRICH/MISSING/PID", 1L),
@@ -381,7 +384,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 0L))); assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 0L)));
assertThat(qaEventService.findAllTopics(0, 20), empty()); assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), empty());
verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com"); verify(mockBrokerClient).listSubscriptions(openaireURL, "user@test.com");
@@ -432,7 +435,7 @@ public class OpenaireEventsImportIT extends AbstractIntegrationTestWithDatabase
assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 6L))); assertThat(qaEventService.findAllSources(0, 20), contains(QASourceMatcher.with(OPENAIRE_SOURCE, 6L)));
assertThat(qaEventService.findAllTopics(0, 20), containsInAnyOrder( assertThat(qaEventService.findAllTopics(0, 20, ORDER_FIELD, false), containsInAnyOrder(
QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L), QATopicMatcher.with("ENRICH/MORE/PROJECT", 1L),
QATopicMatcher.with("ENRICH/MISSING/PID", 1L), QATopicMatcher.with("ENRICH/MISSING/PID", 1L),
QATopicMatcher.with("ENRICH/MORE/PID", 1L), QATopicMatcher.with("ENRICH/MORE/PID", 1L),

View File

@@ -1093,6 +1093,17 @@ public class RestResourceController implements InitializingBean {
return uriComponentsBuilder.encode().build().toString(); return uriComponentsBuilder.encode().build().toString();
} }
/**
* Method to delete an entity by ID
* Note that the regular expression in the request mapping accept a number as identifier;
*
* @param request
* @param apiCategory
* @param model
* @param id
* @return
* @throws HttpRequestMethodNotSupportedException
*/
@RequestMapping(method = RequestMethod.DELETE, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT) @RequestMapping(method = RequestMethod.DELETE, value = REGEX_REQUESTMAPPING_IDENTIFIER_AS_DIGIT)
public ResponseEntity<RepresentationModel<?>> delete(HttpServletRequest request, @PathVariable String apiCategory, public ResponseEntity<RepresentationModel<?>> delete(HttpServletRequest request, @PathVariable String apiCategory,
@PathVariable String model, @PathVariable Integer id) @PathVariable String model, @PathVariable Integer id)

View File

@@ -0,0 +1,46 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization.impl;
import java.sql.SQLException;
import org.dspace.app.rest.authorization.AuthorizationFeature;
import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation;
import org.dspace.app.rest.model.BaseObjectRest;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.core.Context;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* The QA Event feature. It can be used to verify if Quality Assurance can be seen.
*
* Authorization is granted if the current user has READ permissions on the given bitstream.
*/
@Component
@AuthorizationFeatureDocumentation(name = QAAuthorizationFeature.NAME,
description = "It can be used to verify if the user can manage Quality Assurance events")
public class QAAuthorizationFeature implements AuthorizationFeature {
public final static String NAME = "canSeeQA";
@Autowired
private ConfigurationService configurationService;
@Override
public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException {
return configurationService.getBooleanProperty("qaevents.enabled", false);
}
@Override
public String[] getSupportedTypes() {
return new String[]{
SiteRest.CATEGORY + "." + SiteRest.NAME
};
}
}

View File

@@ -23,7 +23,7 @@ import org.dspace.content.QAEvent;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.qaevent.dao.QAEventsDao; import org.dspace.qaevent.dao.QAEventsDAO;
import org.dspace.qaevent.service.QAEventService; import org.dspace.qaevent.service.QAEventService;
import org.dspace.util.UUIDUtils; import org.dspace.util.UUIDUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -48,7 +48,7 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
private QAEventService qaEventService; private QAEventService qaEventService;
@Autowired @Autowired
private QAEventsDao qaEventDao; private QAEventsDAO qaEventDao;
@Autowired @Autowired
private ItemService itemService; private ItemService itemService;
@@ -92,6 +92,7 @@ public class QAEventRestRepository extends DSpaceRestRepository<QAEventRest, Str
} }
@Override @Override
@PreAuthorize("hasAuthority('ADMIN')")
protected void delete(Context context, String eventId) throws AuthorizeException { protected void delete(Context context, String eventId) throws AuthorizeException {
Item item = findTargetItem(context, eventId); Item item = findTargetItem(context, eventId);
EPerson eperson = context.getCurrentUser(); EPerson eperson = context.getCurrentUser();

View File

@@ -18,6 +18,7 @@ import org.dspace.qaevent.service.QAEventService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -30,6 +31,8 @@ import org.springframework.stereotype.Component;
@Component(QATopicRest.CATEGORY + "." + QATopicRest.NAME) @Component(QATopicRest.CATEGORY + "." + QATopicRest.NAME)
public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, String> { public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, String> {
final static String ORDER_FIELD = "topic";
@Autowired @Autowired
private QAEventService qaEventService; private QAEventService qaEventService;
@@ -46,7 +49,13 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
@Override @Override
@PreAuthorize("hasAuthority('ADMIN')") @PreAuthorize("hasAuthority('ADMIN')")
public Page<QATopicRest> findAll(Context context, Pageable pageable) { public Page<QATopicRest> findAll(Context context, Pageable pageable) {
List<QATopic> topics = qaEventService.findAllTopics(pageable.getOffset(), pageable.getPageSize()); boolean ascending = false;
if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
ascending = pageable.getSort()
.getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
}
List<QATopic> topics = qaEventService.findAllTopics(pageable.getOffset(), pageable.getPageSize(),
ORDER_FIELD, ascending);
long count = qaEventService.countTopics(); long count = qaEventService.countTopics();
if (topics == null) { if (topics == null) {
return null; return null;
@@ -58,8 +67,12 @@ public class QATopicRestRepository extends DSpaceRestRepository<QATopicRest, Str
@PreAuthorize("hasAuthority('ADMIN')") @PreAuthorize("hasAuthority('ADMIN')")
public Page<QATopicRest> findBySource(Context context, public Page<QATopicRest> findBySource(Context context,
@Parameter(value = "source", required = true) String source, Pageable pageable) { @Parameter(value = "source", required = true) String source, Pageable pageable) {
boolean ascending = false;
if (pageable.getSort() != null && pageable.getSort().getOrderFor(ORDER_FIELD) != null) {
ascending = pageable.getSort().getOrderFor(ORDER_FIELD).getDirection() == Direction.ASC;
}
List<QATopic> topics = qaEventService.findAllTopicsBySource(source, List<QATopic> topics = qaEventService.findAllTopicsBySource(source,
pageable.getOffset(), pageable.getPageSize()); pageable.getOffset(), pageable.getPageSize(), ORDER_FIELD, ascending);
long count = qaEventService.countTopicsBySource(source); long count = qaEventService.countTopicsBySource(source);
if (topics == null) { if (topics == null) {
return null; return null;

View File

@@ -135,28 +135,28 @@
<heading>fake.workflow.readonly</heading> <processing-class>org.dspace.submit.step.SampleStep</processing-class> <heading>fake.workflow.readonly</heading> <processing-class>org.dspace.submit.step.SampleStep</processing-class>
<type>sample</type> <scope visibility="read-only">workflow</scope> </step-definition> --> <type>sample</type> <scope visibility="read-only">workflow</scope> </step-definition> -->
<!-- OpenAIRE submission steps/forms --> <!-- Openaire submission steps/forms -->
<step-definition id="openAIREProjectForm" mandatory="true"> <step-definition id="openaireProjectForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREPersonForm" mandatory="true"> <step-definition id="openairePersonForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREOrganizationForm" mandatory="true"> <step-definition id="openaireOrganizationForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREPublicationPageoneForm" mandatory="true"> <step-definition id="openairePublicationPageoneForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREPublicationPagetwoForm" mandatory="true"> <step-definition id="openairePublicationPagetwoForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
@@ -259,13 +259,13 @@
<step id="license"/> <step id="license"/>
</submission-process> </submission-process>
<!-- OpenAIRE submission processes --> <!-- Openaire submission processes -->
<submission-process name="openAIREPublicationSubmission"> <submission-process name="openairePublicationSubmission">
<step id="collection"/> <step id="collection"/>
<!--Step will be to Describe the item. --> <!--Step will be to Describe the item. -->
<step id="openAIREPublicationPageoneForm"/> <step id="openairePublicationPageoneForm"/>
<step id="openAIREPublicationPagetwoForm"/> <step id="openairePublicationPagetwoForm"/>
<!--Step will be to Upload the item --> <!--Step will be to Upload the item -->
<!-- step id="upload-with-embargo"/--> <!-- step id="upload-with-embargo"/-->
@@ -274,17 +274,17 @@
<!--Step will be to Sign off on the License --> <!--Step will be to Sign off on the License -->
<step id="license"/> <step id="license"/>
</submission-process> </submission-process>
<submission-process name="openAIREPersonSubmission"> <submission-process name="openairePersonSubmission">
<step id="collection"/> <step id="collection"/>
<step id="openAIREPersonForm"/> <step id="openairePersonForm"/>
</submission-process> </submission-process>
<submission-process name="openAIREProjectSubmission"> <submission-process name="openaireProjectSubmission">
<step id="collection"/> <step id="collection"/>
<step id="openAIREProjectForm"/> <step id="openaireProjectForm"/>
</submission-process> </submission-process>
<submission-process name="openAIREOrganizationSubmission"> <submission-process name="openaireOrganizationSubmission">
<step id="collection"/> <step id="collection"/>
<step id="openAIREOrganizationForm"/> <step id="openaireOrganizationForm"/>
</submission-process> </submission-process>
</submission-definitions> </submission-definitions>

View File

@@ -4,8 +4,8 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-lazy-init="true"> default-lazy-init="true">
<bean id="openAIRERestConnector" <bean id="openaireRestConnector"
class="org.dspace.external.OpenAIRERestConnector"> class="org.dspace.external.OpenaireRestConnector">
<constructor-arg <constructor-arg
value="${openaire.api.url:https://api.openaire.eu}" /> value="${openaire.api.url:https://api.openaire.eu}" />
<property name="tokenEnabled" <property name="tokenEnabled"
@@ -18,10 +18,10 @@
value="${openaire.token.clientSecret}" /> value="${openaire.token.clientSecret}" />
</bean> </bean>
<bean <bean
class="org.dspace.external.provider.impl.MockOpenAIREFundingDataProvider" class="org.dspace.external.provider.impl.MockOpenaireFundingDataProvider"
init-method="init"> init-method="init">
<property name="sourceIdentifier" value="openAIREFunding" /> <property name="sourceIdentifier" value="openaireFunding" />
<property name="connector" ref="openAIRERestConnector" /> <property name="connector" ref="openaireRestConnector" />
<property name="supportedEntityTypes"> <property name="supportedEntityTypes">
<list> <list>
<value>Project</value> <value>Project</value>

View File

@@ -86,8 +86,8 @@
<!-- search for an entity that can be a Person or an OrgUnit --> <!-- search for an entity that can be a Person or an OrgUnit -->
<entry key="personOrOrgunit" value-ref="personOrOrgunit"/> <entry key="personOrOrgunit" value-ref="personOrOrgunit"/>
<!-- OpenAIRE4 guidelines - search for an OrgUnit that have a specific dc.type=FundingOrganization --> <!-- Openaire4 guidelines - search for an OrgUnit that have a specific dc.type=FundingOrganization -->
<entry key="openAIREFundingAgency" value-ref="openAIREFundingAgency"/> <entry key="openaireFundingAgency" value-ref="openaireFundingAgency"/>
<entry key="eperson_claims" value-ref="eperson_claims"/> <entry key="eperson_claims" value-ref="eperson_claims"/>

View File

@@ -48,6 +48,24 @@ public class CrossRefImportMetadataSourceServiceIT extends AbstractLiveImportInt
@Autowired @Autowired
private CrossRefImportMetadataSourceServiceImpl crossRefServiceImpl; private CrossRefImportMetadataSourceServiceImpl crossRefServiceImpl;
@Test
public void crossRefImportMetadataGetNoRecordsTest() throws Exception {
context.turnOffAuthorisationSystem();
CloseableHttpClient originalHttpClient = liveImportClientImpl.getHttpClient();
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
try {
liveImportClientImpl.setHttpClient(httpClient);
CloseableHttpResponse response = mockResponse("" , 404, "Not Found");
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
context.restoreAuthSystemState();
Collection<ImportRecord> recordsImported = crossRefServiceImpl.getRecords("test query", 0, 2);
assertEquals(0, recordsImported.size());
} finally {
liveImportClientImpl.setHttpClient(originalHttpClient);
}
}
@Test @Test
public void crossRefImportMetadataGetRecordsTest() throws Exception { public void crossRefImportMetadataGetRecordsTest() throws Exception {
context.turnOffAuthorisationSystem(); context.turnOffAuthorisationSystem();

View File

@@ -1057,8 +1057,8 @@ public class DiscoveryVersioningIT extends AbstractControllerIntegrationTest {
} }
@Test @Test
public void test_discoveryXml_openAIREFundingAgency_expectLatestVersionsOnly() throws Exception { public void test_discoveryXml_openaireFundingAgency_expectLatestVersionsOnly() throws Exception {
final String configuration = "openAIREFundingAgency"; final String configuration = "openaireFundingAgency";
Collection collection = createCollection("OrgUnit"); Collection collection = createCollection("OrgUnit");

View File

@@ -52,7 +52,7 @@ public class ExternalSourcesRestControllerIT extends AbstractControllerIntegrati
ExternalSourceMatcher.matchExternalSource( ExternalSourceMatcher.matchExternalSource(
"pubmed", "pubmed", false), "pubmed", "pubmed", false),
ExternalSourceMatcher.matchExternalSource( ExternalSourceMatcher.matchExternalSource(
"openAIREFunding", "openAIREFunding", false) "openaireFunding", "openaireFunding", false)
))) )))
.andExpect(jsonPath("$.page.totalElements", Matchers.is(11))); .andExpect(jsonPath("$.page.totalElements", Matchers.is(11)));
} }

View File

@@ -19,7 +19,7 @@ import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.Test; import org.junit.Test;
public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrationTest { public class OpenaireFundingExternalSourcesIT extends AbstractControllerIntegrationTest {
/** /**
* Test openaire funding external source * Test openaire funding external source
@@ -27,10 +27,10 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void findOneOpenAIREFundingExternalSourceTest() throws Exception { public void findOneOpenaireFundingExternalSourceTest() throws Exception {
getClient().perform(get("/api/integration/externalsources")).andExpect(status().isOk()) getClient().perform(get("/api/integration/externalsources")).andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.externalsources", Matchers.hasItem( .andExpect(jsonPath("$._embedded.externalsources", Matchers.hasItem(
ExternalSourceMatcher.matchExternalSource("openAIREFunding", "openAIREFunding", false)))); ExternalSourceMatcher.matchExternalSource("openaireFunding", "openaireFunding", false))));
} }
/** /**
@@ -39,9 +39,9 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void findOneOpenAIREFundingExternalSourceEntriesEmptyWithQueryTest() throws Exception { public void findOneOpenaireFundingExternalSourceEntriesEmptyWithQueryTest() throws Exception {
getClient().perform(get("/api/integration/externalsources/openAIREFunding/entries").param("query", "empty")) getClient().perform(get("/api/integration/externalsources/openaireFunding/entries").param("query", "empty"))
.andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0))); .andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0)));
} }
@@ -52,11 +52,11 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void findOneOpenAIREFundingExternalSourceEntriesWithQueryMultipleKeywordsTest() throws Exception { public void findOneOpenaireFundingExternalSourceEntriesWithQueryMultipleKeywordsTest() throws Exception {
getClient() getClient()
.perform( .perform(
get("/api/integration/externalsources/openAIREFunding/entries").param("query", "empty+results")) get("/api/integration/externalsources/openaireFunding/entries").param("query", "empty+results"))
.andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0))); .andExpect(status().isOk()).andExpect(jsonPath("$.page.number", is(0)));
} }
@@ -66,14 +66,14 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void findOneOpenAIREFundingExternalSourceEntriesWithQueryTest() throws Exception { public void findOneOpenaireFundingExternalSourceEntriesWithQueryTest() throws Exception {
getClient().perform(get("/api/integration/externalsources/openAIREFunding/entries").param("query", "mushroom")) getClient().perform(get("/api/integration/externalsources/openaireFunding/entries").param("query", "mushroom"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.externalSourceEntries", .andExpect(jsonPath("$._embedded.externalSourceEntries",
Matchers.hasItem(ExternalSourceEntryMatcher.matchExternalSourceEntry( Matchers.hasItem(ExternalSourceEntryMatcher.matchExternalSourceEntry(
"aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L05XTy8rLzIzMDAxNDc3MjgvTkw=", "aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L05XTy8rLzIzMDAxNDc3MjgvTkw=",
"Master switches of initiation of mushroom formation", "Master switches of initiation of mushroom formation",
"Master switches of initiation of mushroom formation", "openAIREFunding")))); "Master switches of initiation of mushroom formation", "openaireFunding"))));
} }
@@ -83,19 +83,19 @@ public class OpenAIREFundingExternalSourcesIT extends AbstractControllerIntegrat
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void findOneOpenAIREFundingExternalSourceEntryValueTest() throws Exception { public void findOneOpenaireFundingExternalSourceEntryValueTest() throws Exception {
// "info:eu-repo/grantAgreement/mock/mock/mock/mock" base64 encoded // "info:eu-repo/grantAgreement/mock/mock/mock/mock" base64 encoded
String projectID = "aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L0ZDVC81ODc2LVBQQ0RUSS8xMTAwNjIvUFQ="; String projectID = "aW5mbzpldS1yZXBvL2dyYW50QWdyZWVtZW50L0ZDVC81ODc2LVBQQ0RUSS8xMTAwNjIvUFQ=";
String projectName = "Portuguese Wild Mushrooms: Chemical characterization and functional study" String projectName = "Portuguese Wild Mushrooms: Chemical characterization and functional study"
+ " of antiproliferative and proapoptotic properties in cancer cell lines"; + " of antiproliferative and proapoptotic properties in cancer cell lines";
getClient().perform(get("/api/integration/externalsources/openAIREFunding/entryValues/" + projectID)) getClient().perform(get("/api/integration/externalsources/openaireFunding/entryValues/" + projectID))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$", .andExpect(jsonPath("$",
Matchers.allOf(hasJsonPath("$.id", is(projectID)), hasJsonPath("$.display", is(projectName)), Matchers.allOf(hasJsonPath("$.id", is(projectID)), hasJsonPath("$.display", is(projectName)),
hasJsonPath("$.value", is(projectName)), hasJsonPath("$.value", is(projectName)),
hasJsonPath("$.externalSource", is("openAIREFunding")), hasJsonPath("$.externalSource", is("openaireFunding")),
hasJsonPath("$.type", is("externalSourceEntry"))))); hasJsonPath("$.type", is("externalSourceEntry")))));
} }

View File

@@ -44,7 +44,7 @@ import org.dspace.content.EntityType;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.content.QAEvent; import org.dspace.content.QAEvent;
import org.dspace.content.QAEventProcessed; import org.dspace.content.QAEventProcessed;
import org.dspace.qaevent.dao.QAEventsDao; import org.dspace.qaevent.dao.QAEventsDAO;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -58,7 +58,7 @@ import org.springframework.beans.factory.annotation.Autowired;
public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest { public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
@Autowired @Autowired
private QAEventsDao qaEventsDao; private QAEventsDAO qaEventsDao;
@Test @Test
public void findAllNotImplementedTest() throws Exception { public void findAllNotImplementedTest() throws Exception {
@@ -759,6 +759,11 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}") .withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}")
.build(); .build();
QAEvent event2 = QAEventBuilder.createTarget(context, col1, "Science and Freedom")
.withTopic("ENRICH/MISSING/PID")
.withMessage("{\"pids[0].type\":\"doi\",\"pids[0].value\":\"10.2307/2144300\"}")
.build();
context.restoreAuthSystemState(); context.restoreAuthSystemState();
String authToken = getAuthToken(admin.getEmail(), password); String authToken = getAuthToken(admin.getEmail(), password);
@@ -786,5 +791,11 @@ public class QAEventRestRepositoryIT extends AbstractControllerIntegrationTest {
assertThat(processedEvent.getEventTimestamp(), notNullValue()); assertThat(processedEvent.getEventTimestamp(), notNullValue());
assertThat(processedEvent.getEperson().getID(), is(admin.getID())); assertThat(processedEvent.getEperson().getID(), is(admin.getID()));
getClient(authToken).perform(delete("/api/integration/qualityassuranceevents/" + event.getEventId()))
.andExpect(status().isInternalServerError());
authToken = getAuthToken(eperson.getEmail(), password);
getClient(authToken).perform(delete("/api/integration/qualityassuranceevents/" + event2.getEventId()))
.andExpect(status().isForbidden());
} }
} }

View File

@@ -216,7 +216,12 @@ public class SitemapRestControllerIT extends AbstractControllerIntegrationTest {
//** THEN ** //** THEN **
.andExpect(status().isOk()) .andExpect(status().isOk())
//We expect the content type to match //We expect the content type to match
.andExpect(content().contentType("application/xml;charset=UTF-8")) .andExpect(res -> {
String actual = res.getResponse().getContentType();
assertTrue("Content Type",
"text/xml;charset=UTF-8".equals(actual) ||
"application/xml;charset=UTF-8".equals(actual));
})
.andReturn(); .andReturn();
String response = result.getResponse().getContentAsString(); String response = result.getResponse().getContentAsString();
@@ -232,7 +237,12 @@ public class SitemapRestControllerIT extends AbstractControllerIntegrationTest {
//** THEN ** //** THEN **
.andExpect(status().isOk()) .andExpect(status().isOk())
//We expect the content type to match //We expect the content type to match
.andExpect(content().contentType("application/xml;charset=UTF-8")) .andExpect(res -> {
String actual = res.getResponse().getContentType();
assertTrue("Content Type",
"text/xml;charset=UTF-8".equals(actual) ||
"application/xml;charset=UTF-8".equals(actual));
})
.andReturn(); .andReturn();
String response = result.getResponse().getContentAsString(); String response = result.getResponse().getContentAsString();

View File

@@ -0,0 +1,84 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.authorization;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.dspace.app.rest.authorization.impl.QAAuthorizationFeature;
import org.dspace.app.rest.converter.SiteConverter;
import org.dspace.app.rest.matcher.AuthorizationMatcher;
import org.dspace.app.rest.model.SiteRest;
import org.dspace.app.rest.projection.DefaultProjection;
import org.dspace.app.rest.test.AbstractControllerIntegrationTest;
import org.dspace.content.Site;
import org.dspace.content.service.SiteService;
import org.dspace.services.ConfigurationService;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Test suite for the Quality Assurance Authorization feature
*
* @author Francesco Bacchelli (francesco.bacchelli at 4science.it)
*
*/
public class QAAuthorizationFeatureIT extends AbstractControllerIntegrationTest {
@Autowired
private AuthorizationFeatureService authorizationFeatureService;
@Autowired
private SiteService siteService;
@Autowired
private SiteConverter siteConverter;
@Autowired
private ConfigurationService configurationService;
private AuthorizationFeature qaAuthorizationFeature;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
context.turnOffAuthorisationSystem();
qaAuthorizationFeature = authorizationFeatureService.find(QAAuthorizationFeature.NAME);
context.restoreAuthSystemState();
}
@Test
public void testQAAuthorizationSuccess() throws Exception {
configurationService.setProperty("qaevents.enabled", true);
Site site = siteService.findSite(context);
SiteRest siteRest = siteConverter.convert(site, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
Authorization authAdminSite = new Authorization(admin, qaAuthorizationFeature, siteRest);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
.andExpect(jsonPath("$", Matchers.is(
AuthorizationMatcher.matchAuthorization(authAdminSite))));
}
@Test
public void testQAAuthorizationFail() throws Exception {
configurationService.setProperty("qaevents.enabled", false);
Site site = siteService.findSite(context);
SiteRest siteRest = siteConverter.convert(site, DefaultProjection.DEFAULT);
String tokenAdmin = getAuthToken(admin.getEmail(), password);
Authorization authAdminSite = new Authorization(admin, qaAuthorizationFeature, siteRest);
getClient(tokenAdmin).perform(get("/api/authz/authorizations/" + authAdminSite.getID()))
.andExpect(status().isNotFound());
}
}

View File

@@ -221,6 +221,93 @@ public class IIIFControllerIT extends AbstractControllerIntegrationTest {
.andExpect(jsonPath("$.service").exists()); .andExpect(jsonPath("$.service").exists());
} }
@Test
public void findOneWithExcludedBitstreamIT() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
.build();
Item publicItem1 = ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.enableIIIF()
.build();
String bitstreamContent = "ThisIsSomeText";
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
BitstreamBuilder
.createBitstream(context, publicItem1, is)
.withName("Bitstream1.jpg")
.withMimeType("image/jpeg")
.withIIIFLabel("Custom Label")
.build();
}
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
BitstreamBuilder
.createBitstream(context, publicItem1, is)
.withName("Bitstream2.jpg")
.withMimeType("image/jpeg")
.withIIIFDisabled()
.build();
}
context.restoreAuthSystemState();
// Expect canvas label, width and height to match bitstream description.
getClient().perform(get("/iiif/" + publicItem1.getID() + "/manifest"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sequences[0].canvases", Matchers.hasSize(1)))
.andExpect(jsonPath("$.@context", is("http://iiif.io/api/presentation/2/context.json")))
.andExpect(jsonPath("$.sequences[0].canvases[0].@id",
Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c0")))
.andExpect(jsonPath("$.sequences[0].canvases[0].label", is("Custom Label")));
}
@Test
public void findOneWithExcludedBitstreamBundleIT() throws Exception {
context.turnOffAuthorisationSystem();
parentCommunity = CommunityBuilder.createCommunity(context)
.withName("Parent Community")
.build();
Collection col1 = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection 1")
.build();
Item publicItem1 = ItemBuilder.createItem(context, col1)
.withTitle("Public item 1")
.withIssueDate("2017-10-17")
.withAuthor("Smith, Donald").withAuthor("Doe, John")
.enableIIIF()
.build();
String bitstreamContent = "ThisIsSomeText";
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
BitstreamBuilder
.createBitstream(context, publicItem1, is)
.withName("Bitstream1.jpg")
.withMimeType("image/jpeg")
.withIIIFLabel("Custom Label")
.build();
}
// Add bitstream
try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) {
BitstreamBuilder
.createBitstream(context, publicItem1, is, "ExcludedBundle", false)
.withName("Bitstream2.jpg")
.withMimeType("image/jpeg")
.build();
}
context.restoreAuthSystemState();
// Expect canvas label, width and height to match bitstream description.
getClient().perform(get("/iiif/" + publicItem1.getID() + "/manifest"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.sequences[0].canvases", Matchers.hasSize(1)))
.andExpect(jsonPath("$.@context", is("http://iiif.io/api/presentation/2/context.json")))
.andExpect(jsonPath("$.sequences[0].canvases[0].@id",
Matchers.containsString("/iiif/" + publicItem1.getID() + "/canvas/c0")))
.andExpect(jsonPath("$.sequences[0].canvases[0].label", is("Custom Label")));
}
@Test @Test
public void findOneIIIFSearchableWithCustomBundleAndConfigIT() throws Exception { public void findOneIIIFSearchableWithCustomBundleAndConfigIT() throws Exception {
context.turnOffAuthorisationSystem(); context.turnOffAuthorisationSystem();

View File

@@ -99,7 +99,7 @@ public class QAEventMatcher {
hrefPrefix = "https://arxiv.org/abs/"; hrefPrefix = "https://arxiv.org/abs/";
break; break;
case "handle": case "handle":
hrefPrefix = "https://arxiv.org/abs/"; hrefPrefix = "https://hdl.handle.net/";
break; break;
case "urn": case "urn":
hrefPrefix = ""; hrefPrefix = "";

View File

@@ -14,7 +14,7 @@ import javax.xml.bind.JAXBException;
import eu.openaire.jaxb.helper.OpenAIREHandler; import eu.openaire.jaxb.helper.OpenAIREHandler;
import eu.openaire.jaxb.model.Response; import eu.openaire.jaxb.model.Response;
import org.dspace.external.OpenAIRERestConnector; import org.dspace.external.OpenaireRestConnector;
import org.mockito.AdditionalMatchers; import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentMatchers; import org.mockito.ArgumentMatchers;
import org.mockito.Mockito; import org.mockito.Mockito;
@@ -22,14 +22,14 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
/** /**
* Mock the OpenAIRE external source using a mock rest connector so that query * Mock the Openaire external source using a mock rest connector so that query
* will be resolved against static test files * will be resolved against static test files
* *
*/ */
public class MockOpenAIREFundingDataProvider extends OpenAIREFundingDataProvider { public class MockOpenaireFundingDataProvider extends OpenaireFundingDataProvider {
@Override @Override
public void init() throws IOException { public void init() throws IOException {
OpenAIRERestConnector restConnector = Mockito.mock(OpenAIRERestConnector.class); OpenaireRestConnector restConnector = Mockito.mock(OpenaireRestConnector.class);
when(restConnector.searchProjectByKeywords(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt(), when(restConnector.searchProjectByKeywords(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt(),
ArgumentMatchers.startsWith("mushroom"))).thenAnswer(new Answer<Response>() { ArgumentMatchers.startsWith("mushroom"))).thenAnswer(new Answer<Response>() {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Following OpenAIRE Guidelines 4 --> <!-- Following Openaire Guidelines 4 -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:doc="http://www.lyncode.com/xoai"> xmlns:doc="http://www.lyncode.com/xoai">
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/> <xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
@@ -12,7 +12,7 @@
<!-- <!--
Formatting dc.date.issued Formatting dc.date.issued
based on what OpenAIRE4 specifies for issued dates based on what Openaire4 specifies for issued dates
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationdate.html https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationdate.html
--> -->
<xsl:template <xsl:template

View File

@@ -47,7 +47,7 @@
</Context> </Context>
<!-- <!--
OpenAIRE Guidelines 3.0: Openaire Guidelines 3.0:
- https://guidelines.openaire.eu/ - https://guidelines.openaire.eu/
@@ -55,10 +55,10 @@
- Predefined DSpace fields don't allow to set this up with a default. - Predefined DSpace fields don't allow to set this up with a default.
--> -->
<Context baseurl="openaire" name="OpenAIRE Context"> <Context baseurl="openaire" name="Openaire Context">
<!-- Date format, field prefixes, etc are ensured by the transformer --> <!-- Date format, field prefixes, etc are ensured by the transformer -->
<Transformer ref="openaireTransformer"/> <Transformer ref="openaireTransformer"/>
<!-- OpenAIRE filter --> <!-- Openaire filter -->
<Filter ref="openAireFilter"/> <Filter ref="openAireFilter"/>
<!-- Just an alias, in fact it returns all items within the driver context --> <!-- Just an alias, in fact it returns all items within the driver context -->
<Set ref="openaireSet"/> <Set ref="openaireSet"/>
@@ -66,12 +66,12 @@
<Format ref="oaidc"/> <Format ref="oaidc"/>
<Format ref="mets"/> <Format ref="mets"/>
<Description> <Description>
This contexts complies with OpenAIRE Guidelines for Literature Repositories v3.0. This contexts complies with Openaire Guidelines for Literature Repositories v3.0.
</Description> </Description>
</Context> </Context>
<!-- <!--
OpenAIRE Guidelines 4.0: Openaire Guidelines 4.0:
- https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/ - https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/
@@ -79,15 +79,15 @@
- Predefined DSpace fields don't allow to set this up with a default. - Predefined DSpace fields don't allow to set this up with a default.
--> -->
<Context baseurl="openaire4" name="OpenAIRE 4 Context"> <Context baseurl="openaire4" name="Openaire 4 Context">
<!-- Date format, field prefixes, etc are ensured by the transformer --> <!-- Date format, field prefixes, etc are ensured by the transformer -->
<Transformer ref="openaire4Transformer"/> <Transformer ref="openaire4Transformer"/>
<!-- OpenAIRE filter --> <!-- Openaire filter -->
<Filter ref="openAIRE4Filter"/> <Filter ref="openaire4Filter"/>
<!-- Metadata Formats --> <!-- Metadata Formats -->
<Format ref="oaiopenaire"/> <Format ref="oaiopenaire"/>
<Description> <Description>
This contexts complies with OpenAIRE Guidelines for Literature Repositories v4.0. This contexts complies with Openaire Guidelines for Literature Repositories v4.0.
</Description> </Description>
</Context> </Context>
@@ -176,7 +176,7 @@
<Namespace>http://irdb.nii.ac.jp/oai</Namespace> <Namespace>http://irdb.nii.ac.jp/oai</Namespace>
<SchemaLocation>http://irdb.nii.ac.jp/oai/junii2-3-1.xsd</SchemaLocation> <SchemaLocation>http://irdb.nii.ac.jp/oai/junii2-3-1.xsd</SchemaLocation>
</Format> </Format>
<!-- Metadata format based on the OpenAIRE 4.0 guidelines <!-- Metadata format based on the Openaire 4.0 guidelines
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/use_of_oai_pmh.html https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/use_of_oai_pmh.html
--> -->
<Format id="oaiopenaire"> <Format id="oaiopenaire">
@@ -250,13 +250,13 @@
</Definition> </Definition>
</Filter> </Filter>
<!-- OpenAIRE filter for records returned by OAI-PMH. <!-- Openaire filter for records returned by OAI-PMH.
By default, return an Item record: By default, return an Item record:
* If a Title & Author field both exist * If a Title & Author field both exist
* AND a valid DRIVER Document Type exists * AND a valid DRIVER Document Type exists
* AND Item is either publicly accessible OR Withdrawn (for tombstones) * AND Item is either publicly accessible OR Withdrawn (for tombstones)
* AND the OpenAIRE "dc.relation" is specified * AND the Openaire "dc.relation" is specified
This filter is only used in the OpenAIRE context ([oai]/openaire). This filter is only used in the Openaire context ([oai]/openaire).
--> -->
<Filter id="openAireFilter"> <Filter id="openAireFilter">
<Definition> <Definition>
@@ -319,7 +319,7 @@
</Definition> </Definition>
</Filter> </Filter>
<!-- OpenAIRE4 filter for records returned by OAI-PMH. <!-- Openaire4 filter for records returned by OAI-PMH.
By default, return an Item record: By default, return an Item record:
* If it is publicly accessible * If it is publicly accessible
* * OR it has been withdrawn (in order to display a tombstone record). * * OR it has been withdrawn (in order to display a tombstone record).
@@ -328,7 +328,7 @@
* limiting the results only to Publications as expected * limiting the results only to Publications as expected
This filter is used by the default context ([oai]/request). This filter is used by the default context ([oai]/request).
--> -->
<Filter id="openAIRE4Filter"> <Filter id="openaire4Filter">
<Definition> <Definition>
<And> <And>
<LeftCondition> <LeftCondition>
@@ -457,7 +457,7 @@
<!-- This condition determines if an Item has a "dc.rights" field <!-- This condition determines if an Item has a "dc.rights" field
specifying "open access", which is required for DRIVER specifying "open access", which is required for DRIVER
OR "openAccess", which is required by OpenAIRE. --> OR "openAccess", which is required by Openaire. -->
<CustomCondition id="driverAccessCondition"> <CustomCondition id="driverAccessCondition">
<Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class> <Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class>
<Configuration> <Configuration>
@@ -483,7 +483,7 @@
</CustomCondition> </CustomCondition>
<!-- This condition determines if an Item has a "dc.relation" field <!-- This condition determines if an Item has a "dc.relation" field
which specifies the openAIRE project ID. --> which specifies the openaire project ID. -->
<CustomCondition id="openaireRelationCondition"> <CustomCondition id="openaireRelationCondition">
<Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class> <Class>org.dspace.xoai.filter.DSpaceAtLeastOneMetadataFilter</Class>
<Configuration> <Configuration>

View File

@@ -3,34 +3,16 @@ This sample license is provided for informational purposes only.
NON-EXCLUSIVE DISTRIBUTION LICENSE NON-EXCLUSIVE DISTRIBUTION LICENSE
By signing and submitting this license, you (the author(s) or copyright By signing and submitting this license, you (the author(s) or copyright owner) grants to DSpace University (DSU) the non-exclusive right to reproduce, translate (as defined below), and/or distribute your submission (including the abstract) worldwide in print and electronic format and in any medium, including but not limited to audio or video.
owner) grants to DSpace University (DSU) the non-exclusive right to reproduce,
translate (as defined below), and/or distribute your submission (including
the abstract) worldwide in print and electronic format and in any medium,
including but not limited to audio or video.
You agree that DSU may, without changing the content, translate the You agree that DSU may, without changing the content, translate the submission to any medium or format for the purpose of preservation.
submission to any medium or format for the purpose of preservation.
You also agree that DSU may keep more than one copy of this submission for You also agree that DSU may keep more than one copy of this submission for purposes of security, back-up and preservation.
purposes of security, back-up and preservation.
You represent that the submission is your original work, and that you have You represent that the submission is your original work, and that you have the right to grant the rights contained in this license. You also represent that your submission does not, to the best of your knowledge, infringe upon anyone's copyright.
the right to grant the rights contained in this license. You also represent
that your submission does not, to the best of your knowledge, infringe upon
anyone's copyright.
If the submission contains material for which you do not hold copyright, If the submission contains material for which you do not hold copyright, you represent that you have obtained the unrestricted permission of the copyright owner to grant DSU the rights required by this license, and that such third-party owned material is clearly identified and acknowledged within the text or content of the submission.
you represent that you have obtained the unrestricted permission of the
copyright owner to grant DSU the rights required by this license, and that
such third-party owned material is clearly identified and acknowledged
within the text or content of the submission.
IF THE SUBMISSION IS BASED UPON WORK THAT HAS BEEN SPONSORED OR SUPPORTED IF THE SUBMISSION IS BASED UPON WORK THAT HAS BEEN SPONSORED OR SUPPORTED BY AN AGENCY OR ORGANIZATION OTHER THAN DSU, YOU REPRESENT THAT YOU HAVE FULFILLED ANY RIGHT OF REVIEW OR OTHER OBLIGATIONS REQUIRED BY SUCH CONTRACT OR AGREEMENT.
BY AN AGENCY OR ORGANIZATION OTHER THAN DSU, YOU REPRESENT THAT YOU HAVE
FULFILLED ANY RIGHT OF REVIEW OR OTHER OBLIGATIONS REQUIRED BY SUCH
CONTRACT OR AGREEMENT.
DSU will clearly identify your name(s) as the author(s) or owner(s) of the DSU will clearly identify your name(s) as the author(s) or owner(s) of the submission, and will not make any alteration, other than as allowed by this license, to your submission.
submission, and will not make any alteration, other than as allowed by this
license, to your submission.

View File

@@ -2,15 +2,17 @@
## ##
## Parameters: {0} Collections updates ## Parameters: {0} Collections updates
## {1} Communities updates ## {1} Communities updates
#set($subject = "${config.get('dspace.name')} Subscription") #set($subject = "${config.get('dspace.name')} Subscriptions")
This email is sent from ${config.get('dspace.name')} based on the chosen subscription preferences. This email is sent from ${config.get('dspace.name')} based on the chosen subscription preferences.
Communities #if( not( "$params[0]" == "" ))
----------- Community Subscriptions:
------------------------
List of changed items : ${params[0]} List of changed items : ${params[0]}
Collections #end
----------- #if( not( "$params[1]" == "" ))
Collection Subscriptions:
-------------------------
List of changed items : ${params[1]} List of changed items : ${params[1]}
#end

View File

@@ -185,28 +185,28 @@
<type>extract</type> <type>extract</type>
</step-definition> </step-definition>
<!-- OpenAIRE submission steps/forms --> <!-- Openaire submission steps/forms -->
<step-definition id="openAIREProjectForm" mandatory="true"> <step-definition id="openaireProjectForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREPersonForm" mandatory="true"> <step-definition id="openairePersonForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREOrganizationForm" mandatory="true"> <step-definition id="openaireOrganizationForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREPublicationPageoneForm" mandatory="true"> <step-definition id="openairePublicationPageoneForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
</step-definition> </step-definition>
<step-definition id="openAIREPublicationPagetwoForm" mandatory="true"> <step-definition id="openairePublicationPagetwoForm" mandatory="true">
<heading>submit.progressbar.describe.stepone</heading> <heading>submit.progressbar.describe.stepone</heading>
<processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class> <processing-class>org.dspace.app.rest.submit.step.DescribeStep</processing-class>
<type>submission-form</type> <type>submission-form</type>
@@ -361,13 +361,13 @@
<step id="license"/> <step id="license"/>
</submission-process> </submission-process>
<!-- OpenAIRE submission processes --> <!-- Openaire submission processes -->
<submission-process name="openAIREPublicationSubmission"> <submission-process name="openairePublicationSubmission">
<step id="collection"/> <step id="collection"/>
<!--Step will be to Describe the item. --> <!--Step will be to Describe the item. -->
<step id="openAIREPublicationPageoneForm"/> <step id="openairePublicationPageoneForm"/>
<step id="openAIREPublicationPagetwoForm"/> <step id="openairePublicationPagetwoForm"/>
<!--Step will be to Upload the item --> <!--Step will be to Upload the item -->
<!-- step id="upload-with-embargo"/--> <!-- step id="upload-with-embargo"/-->
@@ -376,17 +376,17 @@
<!--Step will be to Sign off on the License --> <!--Step will be to Sign off on the License -->
<step id="license"/> <step id="license"/>
</submission-process> </submission-process>
<submission-process name="openAIREPersonSubmission"> <submission-process name="openairePersonSubmission">
<step id="collection"/> <step id="collection"/>
<step id="openAIREPersonForm"/> <step id="openairePersonForm"/>
</submission-process> </submission-process>
<submission-process name="openAIREProjectSubmission"> <submission-process name="openaireProjectSubmission">
<step id="collection"/> <step id="collection"/>
<step id="openAIREProjectForm"/> <step id="openaireProjectForm"/>
</submission-process> </submission-process>
<submission-process name="openAIREOrganizationSubmission"> <submission-process name="openaireOrganizationSubmission">
<step id="collection"/> <step id="collection"/>
<step id="openAIREOrganizationForm"/> <step id="openaireOrganizationForm"/>
</submission-process> </submission-process>
</submission-definitions> </submission-definitions>

View File

@@ -16,20 +16,20 @@
# The accessToken it only has a validity of one hour # The accessToken it only has a validity of one hour
# For more details about the token, please check: https://develop.openaire.eu/personalToken.html # For more details about the token, please check: https://develop.openaire.eu/personalToken.html
# #
# the current OpenAIRE Rest client implementation uses basic authentication # the current Openaire Rest client implementation uses basic authentication
# Described here: https://develop.openaire.eu/basic.html # Described here: https://develop.openaire.eu/basic.html
# #
# ---- Token usage required definitions ---- # ---- Token usage required definitions ----
# you can override this settings in your local.cfg file - can be true/false # you can override this settings in your local.cfg file - can be true/false
openaire.token.enabled = false openaire.token.enabled = false
# URL of the OpenAIRE authentication and authorization service # URL of the Openaire authentication and authorization service
openaire.token.url = https://aai.openaire.eu/oidc/token openaire.token.url = https://aai.openaire.eu/oidc/token
# you will be required to register at OpenAIRE (https://services.openaire.eu/uoa-user-management/registeredServices) # you will be required to register at Openaire (https://services.openaire.eu/uoa-user-management/registeredServices)
# and create your service in order to get the following data: # and create your service in order to get the following data:
openaire.token.clientId = CLIENT_ID_HERE openaire.token.clientId = CLIENT_ID_HERE
openaire.token.clientSecret = CLIENT_SECRET_HERE openaire.token.clientSecret = CLIENT_SECRET_HERE
# URL of OpenAIRE Rest API # URL of Openaire Rest API
openaire.api.url = https://api.openaire.eu openaire.api.url = https://api.openaire.eu

View File

@@ -3,10 +3,11 @@
#---------------------------------------------------------------# #---------------------------------------------------------------#
# Configuration properties used by data correction service # # Configuration properties used by data correction service #
#---------------------------------------------------------------# #---------------------------------------------------------------#
# Quality Assurance enable property, false by default
qaevents.enabled = false
qaevents.solr.server = ${solr.server}/${solr.multicorePrefix}qaevent qaevents.solr.server = ${solr.server}/${solr.multicorePrefix}qaevent
# A POST to these url(s) will be done to notify oaire of decision taken for each qaevents # A POST to these url(s) will be done to notify oaire of decision taken for each qaevents
qaevents.openaire.acknowledge-url = https://beta.api-broker.openaire.eu/feedback/events # qaevents.openaire.acknowledge-url = https://beta.api-broker.openaire.eu/feedback/events
#qaevents.openaire.acknowledge-url
# The list of the supported events incoming from openaire (see also dspace/config/spring/api/qaevents.xml) # The list of the supported events incoming from openaire (see also dspace/config/spring/api/qaevents.xml)
# add missing abstract suggestion # add missing abstract suggestion

View File

@@ -54,6 +54,7 @@ rest.properties.exposed = google.recaptcha.mode
rest.properties.exposed = cc.license.jurisdiction rest.properties.exposed = cc.license.jurisdiction
rest.properties.exposed = identifiers.item-status.register-doi rest.properties.exposed = identifiers.item-status.register-doi
rest.properties.exposed = authentication-password.domain.valid rest.properties.exposed = authentication-password.domain.valid
rest.properties.exposed = handle.canonical.prefix
#---------------------------------------------------------------# #---------------------------------------------------------------#
# These configs are used by the deprecated REST (v4-6) module # # These configs are used by the deprecated REST (v4-6) module #

View File

@@ -2,13 +2,13 @@
<!DOCTYPE dspace-dc-types SYSTEM "dspace-dc-types.dtd"> <!DOCTYPE dspace-dc-types SYSTEM "dspace-dc-types.dtd">
<!-- <!--
This document was based on the OpenAIRE4 Guidelines for Literature Repositories This document was based on the Openaire4 Guidelines for Literature Repositories
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/index.html https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/index.html
--> -->
<dspace-dc-types> <dspace-dc-types>
<dspace-header> <dspace-header>
<title>OpenAIRE4 fields definition</title> <title>Openaire4 fields definition</title>
</dspace-header> </dspace-header>
<dc-schema> <dc-schema>
@@ -108,7 +108,7 @@
</dc-type> </dc-type>
<!-- <!--
OpenAIRE4 Guidelines Openaire4 Guidelines
21 - Geolocation --> 21 - Geolocation -->
<dc-type> <dc-type>
<schema>datacite</schema> <schema>datacite</schema>
@@ -116,7 +116,7 @@
<scope_note>Spatial region or named place where the data was gathered or about which the data is focused.</scope_note> <scope_note>Spatial region or named place where the data was gathered or about which the data is focused.</scope_note>
</dc-type> </dc-type>
<!-- specific type required by OpenAIRE4 Guidelines --> <!-- specific type required by Openaire4 Guidelines -->
<dc-type> <dc-type>
<schema>datacite</schema> <schema>datacite</schema>
<element>subject</element> <element>subject</element>

View File

@@ -237,7 +237,7 @@
<scope_note>Contains all uuids of PUBLICATIONS which link to the current ISSUE via a "latest" relationship. In other words, this stores all relationships pointing to the current ISSUE from any PUBLICATION, implying that the ISSUE is marked as "latest". Internally used by DSpace to support versioning. Do not manually add, remove or edit values.</scope_note> <scope_note>Contains all uuids of PUBLICATIONS which link to the current ISSUE via a "latest" relationship. In other words, this stores all relationships pointing to the current ISSUE from any PUBLICATION, implying that the ISSUE is marked as "latest". Internally used by DSpace to support versioning. Do not manually add, remove or edit values.</scope_note>
</dc-type> </dc-type>
<!-- OpenAIRE4 Guidelines - required relationships --> <!-- Openaire4 Guidelines - required relationships -->
<dc-type> <dc-type>
<schema>relation</schema> <schema>relation</schema>

View File

@@ -42,10 +42,10 @@
</scope_note> </scope_note>
</dc-type> </dc-type>
<!-- OpenAIRE4 Guidelines --> <!-- Openaire4 Guidelines -->
<!-- 2.2.2.1. Attribute nameType (R) --> <!-- 2.2.2.1. Attribute nameType (R) -->
<!-- and 3.2.6. Subproperty nameIdentifier (R) --> <!-- and 3.2.6. Subproperty nameIdentifier (R) -->
<!-- specific type required by OpenAIRE4 Guidelines --> <!-- specific type required by Openaire4 Guidelines -->
<dc-type> <dc-type>
<schema>organization</schema> <schema>organization</schema>

View File

@@ -17,7 +17,7 @@
--> -->
<!-- <!--
OpenAIRE4 Guidelines Openaire4 Guidelines
2.2.3. Subproperty givenName (R) 2.2.3. Subproperty givenName (R)
The personal or first name of the author. --> The personal or first name of the author. -->
<dc-type> <dc-type>
@@ -29,7 +29,7 @@
</dc-type> </dc-type>
<!-- <!--
OpenAIRE4 Guidelines Openaire4 Guidelines
2.2.4. Subproperty familyName (R) 2.2.4. Subproperty familyName (R)
The surname or last name of the author. --> The surname or last name of the author. -->
<dc-type> <dc-type>
@@ -74,7 +74,7 @@
</dc-type> </dc-type>
<!-- <!--
OpenAIRE4 Guidelines Openaire4 Guidelines
2.2.6. Subproperty affiliation (R) 2.2.6. Subproperty affiliation (R)
The organizational or institutional affiliation of the creator (occurrence: 0-n). --> The organizational or institutional affiliation of the creator (occurrence: 0-n). -->
<dc-type> <dc-type>
@@ -84,10 +84,10 @@
<scope_note>The organizational or institutional affiliation of the creator</scope_note> <scope_note>The organizational or institutional affiliation of the creator</scope_note>
</dc-type> </dc-type>
<!-- OpenAIRE4 Guidelines --> <!-- Openaire4 Guidelines -->
<!-- 2.2.2.1. Attribute nameType (R) --> <!-- 2.2.2.1. Attribute nameType (R) -->
<!-- and 3.2.6. Subproperty nameIdentifier (R) --> <!-- and 3.2.6. Subproperty nameIdentifier (R) -->
<!-- specific type required by OpenAIRE4 Guidelines --> <!-- specific type required by Openaire4 Guidelines -->
<dc-type> <dc-type>
<schema>person</schema> <schema>person</schema>
<element>identifier</element> <element>identifier</element>

View File

@@ -18,7 +18,7 @@
--> -->
<!-- <!--
OpenAIRE4 Guidelines Openaire4 Guidelines
4.2.2. Subproperty funderName (M) 4.2.2. Subproperty funderName (M)
Name of the funding provider (occurrence: 1). Mandatory if FundingReference is used. --> Name of the funding provider (occurrence: 1). Mandatory if FundingReference is used. -->
<dc-type> <dc-type>
@@ -29,7 +29,7 @@
</dc-type> </dc-type>
<!-- <!--
OpenAIRE4 Guidelines Openaire4 Guidelines
4.2.3. Subproperty funderIdentifier (R) 4.2.3. Subproperty funderIdentifier (R)
Unique identifier of the funding entity (occurrence: 0-1). Unique identifier of the funding entity (occurrence: 0-1).
--> -->

View File

@@ -110,8 +110,8 @@
<!-- search for an entity that can be a Person or an OrgUnit --> <!-- search for an entity that can be a Person or an OrgUnit -->
<entry key="personOrOrgunit" value-ref="personOrOrgunit"/> <entry key="personOrOrgunit" value-ref="personOrOrgunit"/>
<!-- OpenAIRE4 guidelines - search for an OrgUnit that have a specific dc.type=FundingOrganization --> <!-- Openaire4 guidelines - search for an OrgUnit that have a specific dc.type=FundingOrganization -->
<entry key="openAIREFundingAgency" value-ref="openAIREFundingAgency"/> <entry key="openaireFundingAgency" value-ref="openaireFundingAgency"/>
<entry key="eperson_claims" value-ref="eperson_claims"/> <entry key="eperson_claims" value-ref="eperson_claims"/>
</map> </map>
</property> </property>
@@ -2027,7 +2027,7 @@
<property name="spellCheckEnabled" value="true"/> <property name="spellCheckEnabled" value="true"/>
</bean> </bean>
<bean id="openAIREFundingAgency" class="org.dspace.discovery.configuration.DiscoveryConfiguration" <bean id="openaireFundingAgency" class="org.dspace.discovery.configuration.DiscoveryConfiguration"
scope="prototype"> scope="prototype">
<property name="id" value="fundingAgency"/> <property name="id" value="fundingAgency"/>
<property name="indexAlways" value="true"/> <property name="indexAlways" value="true"/>

View File

@@ -7,7 +7,7 @@
http://www.springframework.org/schema/util/spring-util.xsd" http://www.springframework.org/schema/util/spring-util.xsd"
default-lazy-init="true"> default-lazy-init="true">
<bean id="openAIRERestConnector" class="org.dspace.external.OpenAIRERestConnector"> <bean id="openaireRestConnector" class="org.dspace.external.OpenaireRestConnector">
<constructor-arg value="${openaire.api.url:https://api.openaire.eu}"/> <constructor-arg value="${openaire.api.url:https://api.openaire.eu}"/>
<property name="tokenEnabled" value="${openaire.token.enabled:false}"/> <property name="tokenEnabled" value="${openaire.token.enabled:false}"/>
<property name="tokenServiceUrl" value="${openaire.token.url:https://aai.openaire.eu/oidc/token}"/> <property name="tokenServiceUrl" value="${openaire.token.url:https://aai.openaire.eu/oidc/token}"/>
@@ -15,9 +15,9 @@
<property name="clientSecret" value="${openaire.token.clientSecret}"/> <property name="clientSecret" value="${openaire.token.clientSecret}"/>
</bean> </bean>
<bean class="org.dspace.external.provider.impl.OpenAIREFundingDataProvider" init-method="init"> <bean class="org.dspace.external.provider.impl.OpenaireFundingDataProvider" init-method="init">
<property name="sourceIdentifier" value="openAIREFunding" /> <property name="sourceIdentifier" value="openaireFunding" />
<property name="connector" ref="openAIRERestConnector" /> <property name="connector" ref="openaireRestConnector" />
<property name="metadataFields" ref="mapOfmetadata"/> <property name="metadataFields" ref="mapOfmetadata"/>
<property name="supportedEntityTypes"> <property name="supportedEntityTypes">
<list> <list>

View File

@@ -286,7 +286,7 @@
</property> </property>
</bean> </bean>
<!-- An example of an OpenAIRE compliance filter based on the same rules in xoai.xml <!-- An example of an Openaire compliance filter based on the same rules in xoai.xml
some sub-statements are defined within this bean, and some are referenced from earlier definitions some sub-statements are defined within this bean, and some are referenced from earlier definitions
--> -->
<bean id="openaire_filter" class="org.dspace.content.logic.DefaultFilter"> <bean id="openaire_filter" class="org.dspace.content.logic.DefaultFilter">
@@ -329,7 +329,7 @@
</list> </list>
</property> </property>
</bean> </bean>
<!-- AND the dc.relation is a valid OpenAIRE identifier <!-- AND the dc.relation is a valid Openaire identifier
(starts with "info:eu-repo/grantAgreement/") --> (starts with "info:eu-repo/grantAgreement/") -->
<bean id="has-openaire-relation_condition" <bean id="has-openaire-relation_condition"
class="org.dspace.content.logic.condition.MetadataValueMatchCondition"> class="org.dspace.content.logic.condition.MetadataValueMatchCondition">

View File

@@ -9,11 +9,11 @@
<context:annotation-config /> <!-- allows us to use spring annotations in beans --> <context:annotation-config /> <!-- allows us to use spring annotations in beans -->
<bean id="qaEventsDao" class="org.dspace.qaevent.dao.impl.QAEventsDaoImpl" /> <bean id="qaEventsDao" class="org.dspace.qaevent.dao.impl.QAEventsDAOImpl" />
<bean id="openaireBrokerClient" class="eu.dnetlib.broker.BrokerClient" /> <bean id="openaireBrokerClient" class="eu.dnetlib.broker.BrokerClient" />
<bean id="brokerClientFactory" class="org.dspace.qaevent.service.impl.BrokerClientFactoryImpl" /> <bean id="openaireClientFactory" class="org.dspace.qaevent.service.impl.OpenaireClientFactoryImpl" />
<bean id="org.dspace.qaevent.service.QAEventActionService" class="org.dspace.qaevent.service.impl.QAEventActionServiceImpl"> <bean id="org.dspace.qaevent.service.QAEventActionService" class="org.dspace.qaevent.service.impl.QAEventActionServiceImpl">
<property name="topicsToActions"> <property name="topicsToActions">

View File

@@ -5,7 +5,7 @@
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="import-openaire-events" class="org.dspace.qaevent.script.OpenaireEventsImportCliScriptConfiguration" primary="true"> <bean id="import-openaire-events" class="org.dspace.qaevent.script.OpenaireEventsImportCliScriptConfiguration" primary="true">
<property name="description" value="Import new openAIRE quality assurance broker events"/> <property name="description" value="Import new Openaire quality assurance broker events"/>
<property name="dspaceRunnableClass" value="org.dspace.qaevent.script.OpenaireEventsImportCli"/> <property name="dspaceRunnableClass" value="org.dspace.qaevent.script.OpenaireEventsImportCli"/>
</bean> </bean>

View File

@@ -9,7 +9,7 @@
<!-- This primary attribute is present so that we can assure that in the REST layer we'll always use this <!-- This primary attribute is present so that we can assure that in the REST layer we'll always use this
bean if it is present--> bean if it is present-->
<bean id="import-openaire-events" class="org.dspace.qaevent.script.OpenaireEventsImportScriptConfiguration" primary="true"> <bean id="import-openaire-events" class="org.dspace.qaevent.script.OpenaireEventsImportScriptConfiguration" primary="true">
<property name="description" value="Import new openAIRE quality assurance broker events"/> <property name="description" value="Import new openaire quality assurance broker events"/>
<property name="dspaceRunnableClass" value="org.dspace.qaevent.script.OpenaireEventsImport"/> <property name="dspaceRunnableClass" value="org.dspace.qaevent.script.OpenaireEventsImport"/>
</bean> </bean>

View File

@@ -819,9 +819,9 @@
</form> </form>
<!-- OpenAIRE specific forms --> <!-- Openaire specific forms -->
<form name="openAIREPublicationPageoneForm"> <form name="openairePublicationPageoneForm">
<row> <row>
<field> <field>
<dc-schema>dc</dc-schema> <dc-schema>dc</dc-schema>
@@ -1071,7 +1071,7 @@
</row> </row>
</form> </form>
<form name="openAIREPublicationPagetwoForm"> <form name="openairePublicationPagetwoForm">
<row> <row>
<field> <field>
<dc-schema>dc</dc-schema> <dc-schema>dc</dc-schema>
@@ -1129,7 +1129,7 @@
<dc-element>relation</dc-element> <dc-element>relation</dc-element>
<input-type>onebox</input-type> <input-type>onebox</input-type>
</linked-metadata-field> </linked-metadata-field>
<externalsources>openAIREFunding</externalsources> <externalsources>openaireFunding</externalsources>
<required></required> <required></required>
</relation-field> </relation-field>
</row> </row>
@@ -1182,7 +1182,7 @@
</row> </row>
</form> </form>
<form name="openAIREPersonForm"> <form name="openairePersonForm">
<row> <row>
<field> <field>
<dc-schema>person</dc-schema> <dc-schema>person</dc-schema>
@@ -1246,7 +1246,7 @@
</row> </row>
</form> </form>
<form name="openAIREProjectForm"> <form name="openaireProjectForm">
<row> <row>
<field> <field>
<dc-schema>dc</dc-schema> <dc-schema>dc</dc-schema>
@@ -1269,7 +1269,7 @@
<row> <row>
<relation-field> <relation-field>
<relationship-type>isFundingAgencyOfProject</relationship-type> <relationship-type>isFundingAgencyOfProject</relationship-type>
<search-configuration>openAIREFundingAgency</search-configuration> <search-configuration>openaireFundingAgency</search-configuration>
<repeatable>false</repeatable> <repeatable>false</repeatable>
<name-variants>false</name-variants> <name-variants>false</name-variants>
<label>Funding Agency</label> <label>Funding Agency</label>
@@ -1302,7 +1302,7 @@
</row> </row>
</form> </form>
<form name="openAIREOrganizationForm"> <form name="openaireOrganizationForm">
<row> <row>
<field> <field>
<dc-schema>organization</dc-schema> <dc-schema>organization</dc-schema>
@@ -1537,7 +1537,7 @@
</pair> </pair>
</value-pairs> </value-pairs>
<!-- OpenAIRE specific value pairs --> <!-- Openaire specific value pairs -->
<value-pairs value-pairs-name="openaire_license_types" dc-term="license"> <value-pairs value-pairs-name="openaire_license_types" dc-term="license">
@@ -1579,7 +1579,7 @@
</value-pairs> </value-pairs>
<!-- OpenAIRE document types <!-- Openaire document types
https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationtype.html https://openaire-guidelines-for-literature-repository-managers.readthedocs.io/en/v4.0.0/field_publicationtype.html
Based on COAR Vocabularies -> Controlled Vocabulary for Resource Type Genres (Version 2.0): Based on COAR Vocabularies -> Controlled Vocabulary for Resource Type Genres (Version 2.0):
http://vocabularies.coar-repositories.org/documentation/resource_types/ http://vocabularies.coar-repositories.org/documentation/resource_types/

View File

@@ -160,29 +160,6 @@
<filter class="solr.TrimFilterFactory" /> <filter class="solr.TrimFilterFactory" />
</analyzer> </analyzer>
</fieldType> </fieldType>
<!-- This field is used for store metadatavalue (with authority, prefered label, variants and
language) to use in projection -->
<fieldType name="dspaceMetadataProjection" class="solr.TextField" sortMissingLast="true" omitNorms="true">
<analyzer>
<!--Treats the entire field as a single token, regardless of its content-->
<tokenizer class="solr.KeywordTokenizerFactory"/>
</analyzer>
</fieldType>
<!--This field is used for auto complete features in the discovery search-->
<fieldType name="dspaceAutoComplete" class="solr.TextField" sortMissingLast="true" omitNorms="true">
<analyzer>
<tokenizer class="solr.KeywordTokenizerFactory"/>
<!--Lower cases our values-->
<filter class="solr.LowerCaseFilterFactory" />
<!--Discards common words.-->
<filter class="solr.StopFilterFactory" />
<!-- The TrimFilter removes any leading or trailing whitespace -->
<filter class="solr.TrimFilterFactory" />
</analyzer>
</fieldType>
<fieldType name="keywordFilter" class="solr.TextField" sortMissingLast="true" omitNorms="true"> <fieldType name="keywordFilter" class="solr.TextField" sortMissingLast="true" omitNorms="true">
<analyzer> <analyzer>

View File

@@ -17,19 +17,22 @@ FROM solr:${SOLR_VERSION}-slim
ENV AUTHORITY_CONFIGSET_PATH=/opt/solr/server/solr/configsets/authority/conf \ ENV AUTHORITY_CONFIGSET_PATH=/opt/solr/server/solr/configsets/authority/conf \
OAI_CONFIGSET_PATH=/opt/solr/server/solr/configsets/oai/conf \ OAI_CONFIGSET_PATH=/opt/solr/server/solr/configsets/oai/conf \
SEARCH_CONFIGSET_PATH=/opt/solr/server/solr/configsets/search/conf \ SEARCH_CONFIGSET_PATH=/opt/solr/server/solr/configsets/search/conf \
STATISTICS_CONFIGSET_PATH=/opt/solr/server/solr/configsets/statistics/conf STATISTICS_CONFIGSET_PATH=/opt/solr/server/solr/configsets/statistics/conf \
QAEVENT_CONFIGSET_PATH=/opt/solr/server/solr/configsets/qaevent/conf
USER root USER root
RUN mkdir -p $AUTHORITY_CONFIGSET_PATH && \ RUN mkdir -p $AUTHORITY_CONFIGSET_PATH && \
mkdir -p $OAI_CONFIGSET_PATH && \ mkdir -p $OAI_CONFIGSET_PATH && \
mkdir -p $SEARCH_CONFIGSET_PATH && \ mkdir -p $SEARCH_CONFIGSET_PATH && \
mkdir -p $STATISTICS_CONFIGSET_PATH mkdir -p $STATISTICS_CONFIGSET_PATH && \
mkdir -p $QAEVENT_CONFIGSET_PATH
COPY dspace/solr/authority/conf/* $AUTHORITY_CONFIGSET_PATH/ COPY dspace/solr/authority/conf/* $AUTHORITY_CONFIGSET_PATH/
COPY dspace/solr/oai/conf/* $OAI_CONFIGSET_PATH/ COPY dspace/solr/oai/conf/* $OAI_CONFIGSET_PATH/
COPY dspace/solr/search/conf/* $SEARCH_CONFIGSET_PATH/ COPY dspace/solr/search/conf/* $SEARCH_CONFIGSET_PATH/
COPY dspace/solr/statistics/conf/* $STATISTICS_CONFIGSET_PATH/ COPY dspace/solr/statistics/conf/* $STATISTICS_CONFIGSET_PATH/
COPY dspace/solr/qaevent/conf/* $QAEVENT_CONFIGSET_PATH/
RUN chown -R solr:solr /opt/solr/server/solr/configsets RUN chown -R solr:solr /opt/solr/server/solr/configsets