From 76e7fe90fd786f38f18bcf50f500f38b0204052c Mon Sep 17 00:00:00 2001 From: EthanFreestone <54310740+EthanFreestone@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:47:34 +0100 Subject: [PATCH 1/4] Eureka permission changes -- validate module descriptor (#825) * ci: Turn on validate workflow in order to change module descriptor perms to fit Eureka shape * ci: Format JSON in ModuleDescriptor * chore: Bring refdata perms in line with licenses PR: https://github.com/folio-org/mod-licenses/pull/271 * chore: Bring custom properties permissions in line with licenses PR * docs: Added section to README about how to run the validator locally should a developer wish to do so * chore: Change clone permission to match licenses PR * chore: Change `erm.admin.action` to match licenses PR (No group so is likely surfaced elsewhere and may require changes) * chore: STS permissions cleanup * chore: Job permission tweaks (Added logging granular perms, added fileobject perm, added logging perm group) * refactor: Whitsepace change in URLMappings Not real change, just changing out whitespace for consistency * chore: Cleanup jobs by type permission * chore: Cleanup entitlementOptions perms (separate granular permission for static vs dynamic endpoints) * chore: Added property specific KB validate permission to protect that endpoint * chore: Tweak to resources on an agreement perms * chore: Tweak export permission setup -- This feels pretty gross, multiple ways to get exactly the same stuff * Tweak electronic vs all resource collection get * chore: Change package content permissions and package sorce get permissions * chore: Extend to add usage data providers collection perm--need to check that it makes sense to include that in agrement view perm * chore: Add separate perm for related entitlements * chore: Log entry permission Added separate logEntry colleciton permission * chore: Added separate validate permission for subscription agreement property level endpoint * chore: linkedLicenses collection permission (Check with Owen whether to include that in permission group or not) * chore: linkedLicenses collection permission (Check with Owen whether to include that in permission group or not) * chore: Tweak electronic titles permission * chore: Add "replaces" for refdata permissions, and dot separates * chore: Added replaces for erm.admin.action * chore: All Module Descriptor changes discussed on call with Owen * chore: More module des descriptor tweaks * chore: Final module descriptor tweaks * chore: Accidentally didn't change over all contexts perms to "collection" basis --- .github/workflows/validate-module.yml | 138 +- README.md | 32 + .../controllers/org/olf/UrlMappings.groovy | 8 +- .../main/okapi/ModuleDescriptor-template.json | 1534 +++++++++++++---- 4 files changed, 1258 insertions(+), 454 deletions(-) diff --git a/.github/workflows/validate-module.yml b/.github/workflows/validate-module.yml index 85be5c9c..9c9a0715 100644 --- a/.github/workflows/validate-module.yml +++ b/.github/workflows/validate-module.yml @@ -1,71 +1,71 @@ -# name: Validate module +name: Validate module -# on: -# push: +on: + push: -# jobs: -# run: -# name: Validate module descriptor -# runs-on: ubuntu-latest -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 -# - name: Get Pull Request Number -# id: pr_number -# run: echo "pull_request_number=$(gh pr view --json number -q .number || echo "")" >> $GITHUB_OUTPUT -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# - name: Setup java -# uses: actions/setup-java@v4 -# with: -# distribution: 'temurin' # See 'Supported distributions' for available options -# java-version: '17' -# - name: Set up Maven -# uses: stCarolas/setup-maven@v5 -# with: -# maven-version: 3.8.2 -# - name: Set up settings file -# uses: 1arp/create-a-file-action@0.4.5 -# with: -# path: 'service' -# file: 'settings.xml' -# content: -# -# -# -# folioMavenProfile -# -# -# folio-nexus -# FOLIO Maven repository -# https://repository.folio.org/repository/maven-folio -# -# -# -# -# -# folioMavenProfile -# -# -# - name: Run validator -# run: mvn org.folio:folio-module-descriptor-validator:1.0.0:validate -DmoduleDescriptorFile=service/src/main/okapi/ModuleDescriptor-template.json -s service/settings.xml -l validate_module_descriptor_output.txt -# - name: Upload validator result -# uses: actions/upload-artifact@v4 -# if: always() -# with: -# name: validate_module_descriptor_output -# path: | -# validate_module_descriptor_output.txt -# retention-days: 1 -# - name: Setup validate_module_descriptor_errors file -# if: failure() -# run: echo "$(cat validate_module_descriptor_output.txt)" | egrep "\[ERROR\]\s*(\"key\"|\"value\")" | sed 's/\[ERROR\]\(\s*\)//;s/\"value\"\(\s*\):\(\s*\)\(.*\)/\3\n/;s/"key\"\(\s*\):\(\s*\)\(.*\)/\3/' | tee validate_module_descriptor_errors.txt -# - name: Comment failures on PR -# if: failure() -# run: | -# # Use GitHub API to create a comment on the PR -# PR_NUMBER=${{ steps.pr_number.outputs.pull_request_number }} -# GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} -# COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" -# echo "SENDING TO: $COMMENT_URL" -# curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL --data "{ \"body\": $(cat validate_module_descriptor_errors.txt | jq -Rs) }" \ No newline at end of file +jobs: + run: + name: Validate module descriptor + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Get Pull Request Number + id: pr_number + run: echo "pull_request_number=$(gh pr view --json number -q .number || echo "")" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Setup java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' # See 'Supported distributions' for available options + java-version: '17' + - name: Set up Maven + uses: stCarolas/setup-maven@v5 + with: + maven-version: 3.8.2 + - name: Set up settings file + uses: 1arp/create-a-file-action@0.4.5 + with: + path: 'service' + file: 'settings.xml' + content: + + + + folioMavenProfile + + + folio-nexus + FOLIO Maven repository + https://repository.folio.org/repository/maven-folio + + + + + + folioMavenProfile + + + - name: Run validator + run: mvn org.folio:folio-module-descriptor-validator:1.0.0:validate -DmoduleDescriptorFile=service/src/main/okapi/ModuleDescriptor-template.json -s service/settings.xml -l validate_module_descriptor_output.txt + - name: Upload validator result + uses: actions/upload-artifact@v4 + if: always() + with: + name: validate_module_descriptor_output + path: | + validate_module_descriptor_output.txt + retention-days: 1 + - name: Setup validate_module_descriptor_errors file + if: failure() + run: echo "$(cat validate_module_descriptor_output.txt)" | egrep "\[ERROR\]\s*(\"key\"|\"value\")" | sed 's/\[ERROR\]\(\s*\)//;s/\"value\"\(\s*\):\(\s*\)\(.*\)/\3\n/;s/"key\"\(\s*\):\(\s*\)\(.*\)/\3/' | tee validate_module_descriptor_errors.txt + - name: Comment failures on PR + if: failure() + run: | + # Use GitHub API to create a comment on the PR + PR_NUMBER=${{ steps.pr_number.outputs.pull_request_number }} + GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" + echo "SENDING TO: $COMMENT_URL" + curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL --data "{ \"body\": $(cat validate_module_descriptor_errors.txt | jq -Rs) }" \ No newline at end of file diff --git a/README.md b/README.md index 4a3b117a..519ad241 100644 --- a/README.md +++ b/README.md @@ -168,3 +168,35 @@ If you create a Service in Kubernetes named "okapi" and expose a port for Hazelc +## Validating module descriptor +There is a github action created to run the module descriptor validation on `push`, but if a developer wishes to run the validation locally there is some setup that needs doing. The validation script is a Maven plugin which does not work natively with our gradle based apps. +### Maven +Developer will need maven cli `mvn` installed on their machine. +### settings.xml +Create a `settings.xml` file within the "service" directory (DO NOT MERGE THIS) containing the following: +``` + + + + folioMavenProfile + + + folio-nexus + FOLIO Maven repository + https://repository.folio.org/repository/maven-folio + + + + + + folioMavenProfile + + +``` +### Running +Finally the developer can run this command from the root directory (ie `mod-agreements` not `mod-agreements/service`) +``` +mvn org.folio:folio-module-descriptor-validator:1.0.0:validate -DmoduleDescriptorFile=service/src/main/okapi/ModuleDescriptor-template.json -s service/settings.xml -l validate_module_descriptor_output.txt +``` + +This will create a file called `validate_module_descriptor_output.txt` containing the output of the validator. The github action does some cleanup and comments the errors on a PR (if present). The `grep`/`sed` commands with regex can be found in the workflow file `.github/validate-module`. \ No newline at end of file diff --git a/service/grails-app/controllers/org/olf/UrlMappings.groovy b/service/grails-app/controllers/org/olf/UrlMappings.groovy index 3356b29a..249d72c3 100644 --- a/service/grails-app/controllers/org/olf/UrlMappings.groovy +++ b/service/grails-app/controllers/org/olf/UrlMappings.groovy @@ -50,10 +50,10 @@ class UrlMappings { } '/usageDataProviders' { - controller = 'usageDataProvider' - method = 'GET' - filters = { "owner==${params.subscriptionAgreementId}" } - } + controller = 'usageDataProvider' + method = 'GET' + filters = { "owner==${params.subscriptionAgreementId}" } + } // Root level extensions collection { diff --git a/service/src/main/okapi/ModuleDescriptor-template.json b/service/src/main/okapi/ModuleDescriptor-template.json index fadf69bf..d5dcbe29 100644 --- a/service/src/main/okapi/ModuleDescriptor-template.json +++ b/service/src/main/okapi/ModuleDescriptor-template.json @@ -5,491 +5,988 @@ { "id": "erm", "version": "${info.app.minorVersion}", - "handlers" : [ + "handlers": [ { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/sas", - "permissionsRequired": [ "erm.agreements.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/publicLookup", - "modulePermissions": [ "licenses.licenses.item.get" ], + "modulePermissions": [ + "licenses.licenses.item.get" + ], "permissionsRequired": [] - },{ - "methods": ["GET"], + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}", - "permissionsRequired": [ "erm.agreements.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.item.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/export", - "permissionsRequired": [ "erm.agreements.export" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.export" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/export/*", - "permissionsRequired": [ "erm.agreements.export" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements_with_resource_subset.export" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/linkedLicenses", - "permissionsRequired": [ "erm.agreements.linkedLicenses.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.linkedLicenses.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/linkedLicenses", - "permissionsRequired": [ "erm.agreements.linkedLicenses.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.linkedLicenses.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/usageDataProviders", - "permissionsRequired": [ "erm.agreements.usageDataProviders.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.usageDataProviders.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/usageDataProviders", - "permissionsRequired": [ "erm.agreements.usageDataProviders.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.usageDataProviders.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/resources", - "permissionsRequired": [ "erm.agreements.item.resources.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.resources.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/resources/*", - "permissionsRequired": [ "erm.agreements.item.resources.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.resources.subset.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/resources/export", - "permissionsRequired": [ "erm.agreements.export" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.agreements.resources.export" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sas/{id}/resources/export/*", - "permissionsRequired": [ "erm.agreements.export" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.agreements.resources.subset_by_format.export" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/sas", - "permissionsRequired": [ "erm.agreements.item.post" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.agreements.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/sas/{id}", - "permissionsRequired": [ "erm.agreements.item.put" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.agreements.item.put" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/sas/{id}/clone", - "permissionsRequired": [ "erm.agreements.item.clone" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.agreements.clone.execute" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/sas/{id}", - "permissionsRequired": [ "erm.agreements.item.delete" ] + "permissionsRequired": [ + "erm.agreements.item.delete" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/export", - "permissionsRequired": [ "erm.agreements.export" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.all.agreements.resources.export" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/export/*", - "permissionsRequired": [ "erm.agreements.export" ] + "permissionsRequired": [ + "erm.all.agreements.resources.subset_by_format.export" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/entitlements", - "permissionsRequired": [ "erm.entitlements.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.entitlements.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/entitlements/{id}", - "permissionsRequired": [ "erm.entitlements.item.get" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.entitlements.item.get" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/entitlements/{id}", - "permissionsRequired": [ "erm.entitlements.item.put" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.entitlements.item.put" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/entitlements", - "permissionsRequired": [ "erm.entitlements.item.post" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.entitlements.item.post" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/entitlements/{id}", - "permissionsRequired": [ "erm.entitlements.item.delete" ] + "permissionsRequired": [ + "erm.entitlements.item.delete" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/files", - "permissionsRequired": [ "erm.files.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.files.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/files/{id}", - "permissionsRequired": [ "erm.files.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.files.item.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/files/{id}/raw", - "permissionsRequired": [ "erm.files.item.download" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.files.item.download" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/files", - "permissionsRequired": [ "erm.files.item.post" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.files.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/files/{id}", - "permissionsRequired": [ "erm.files.item.put" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.files.item.put" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/files/{id}", - "permissionsRequired": [ "erm.files.item.delete" ] + "permissionsRequired": [ + "erm.files.item.delete" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/contacts", - "permissionsRequired": [ "erm.contacts.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.contacts.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/contacts/{id}", - "permissionsRequired": [ "erm.contacts.item.get" ] + "permissionsRequired": [ + "erm.contacts.item.get" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/org", - "permissionsRequired": [ "erm.orgs.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.orgs.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/org/{id}", - "permissionsRequired": [ "erm.orgs.item.get" ] + "permissionsRequired": [ + "erm.orgs.item.get" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/packages", - "permissionsRequired": [ "erm.packages.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.packages.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/packages/{id}", - "permissionsRequired": [ "erm.packages.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.packages.item.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/packages/{id}/content", - "permissionsRequired": [ "erm.packages.item.content.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.packages.content.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/packages/{id}/content/*", - "permissionsRequired": [ "erm.packages.item.content.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.packages.content.subset.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/packages/sources", - "permissionsRequired": [ "erm.packages.collection.get" ] + "permissionsRequired": [ + "erm.packages.sources.collection.get" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs", - "permissionsRequired": [ "erm.jobs.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}", - "permissionsRequired": [ "erm.jobs.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.item.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}/fullLog", - "permissionsRequired": [ "erm.jobs.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.fullLog.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}/errorLog", - "permissionsRequired": [ "erm.jobs.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.errorLog.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}/infoLog", - "permissionsRequired": [ "erm.jobs.item.get" ] + "permissionsRequired": [ + "erm.jobs.infoLog.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}/fullLogStream", - "permissionsRequired": [ "erm.jobs.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.fullLogStream.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}/errorLogStream", - "permissionsRequired": [ "erm.jobs.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.errorLogStream.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}/infoLogStream", - "permissionsRequired": [ "erm.jobs.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.infoLogStream.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/{id}/downloadFileObject", - "permissionsRequired": [ "erm.jobs.item.get" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.jobs.fileObject.download" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/jobs/{type}", - "permissionsRequired": [ "erm.jobs.item.post" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.jobs.item.post" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/jobs/{id}", - "permissionsRequired": [ "erm.jobs.item.delete" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.jobs.item.delete" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/jobs/type/{type}", - "permissionsRequired": [ "erm.jobs.collection.get" ] + "permissionsRequired": [ + "erm.jobs_by_type.collection.get" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/refdata", - "permissionsRequired": [ "erm.refdata.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.refdata.category.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/refdata/{domain}/{property}", - "permissionsRequired": [ "erm.refdata.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.refdata.value.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/refdata/{id}", - "permissionsRequired": [ "erm.refdata.item.get" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.refdata.category.item.get" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/refdata", - "permissionsRequired": [ "erm.refdata.item.post" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.refdata.category.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/refdata/{id}", - "permissionsRequired": [ "erm.refdata.item.put" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.refdata.category.item.put" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/refdata/{id}", - "permissionsRequired": [ "erm.refdata.item.delete" ] + "permissionsRequired": [ + "erm.refdata.category.item.delete" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/kbs", - "permissionsRequired": [ "erm.kbs.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.kbs.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/kbs/{id}", - "permissionsRequired": [ "erm.kbs.item.get" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.kbs.item.get" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/kbs", - "permissionsRequired": [ "erm.kbs.item.post" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.kbs.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/kbs/{id}", - "permissionsRequired": [ "erm.kbs.item.put" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.kbs.item.put" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/kbs/{id}", - "permissionsRequired": [ "erm.kbs.item.delete" ] + "permissionsRequired": [ + "erm.kbs.item.delete" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/resource", - "permissionsRequired": [ "erm.resources.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.resources.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/resource/electronic", - "permissionsRequired": [ "erm.resources.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.resources.electronic.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/resource/{id}", - "permissionsRequired": [ "erm.resources.item.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.resources.item.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/resource/{id}/entitlements", - "permissionsRequired": [ "erm.resources.item.entitlement.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.resources.entitlement.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/resource/{id}/entitlements/related", - "permissionsRequired": [ "erm.resources.item.entitlement.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.resources.entitlement.related.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/resource/{id}/entitlementOptions", - "permissionsRequired": [ "erm.resources.item.entitlementOptions.get" ] + "permissionsRequired": [ + "erm.resources.entitlementOptions.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/resource/{id}/static/entitlementOptions", - "permissionsRequired": [ "erm.resources.item.entitlementOptions.get" ] + "permissionsRequired": [ + "erm.resources.entitlementOptions.static.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/titles", - "permissionsRequired": [ "erm.titles.collection.get" ] + "permissionsRequired": [ + "erm.titles.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/titles/electronic", - "permissionsRequired": [ "erm.titles.collection.get" ] + "permissionsRequired": [ + "erm.titles.electronic.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/titles/entitled", - "permissionsRequired": [ "erm.titles.collection.get" ] + "permissionsRequired": [ + "erm.titles.entitled.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/titles/{id}", - "permissionsRequired": [ "erm.titles.item.get" ] + "permissionsRequired": [ + "erm.titles.item.get" + ] }, { - "methods": ["PUT"], + "methods": [ + "PUT" + ], "pathPattern": "/erm/titles/{id}", - "permissionsRequired": [ "erm.titles.item.put" ] + "permissionsRequired": [ + "erm.titles.item.put" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/pci", - "permissionsRequired": [ "erm.pci.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.pci.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/pci/{id}", - "permissionsRequired": [ "erm.pci.item.get" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.pci.item.get" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/pci/{id}", - "permissionsRequired": [ "erm.pci.item.put" ] + "permissionsRequired": [ + "erm.pci.item.put" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/platforms", - "permissionsRequired": [ "erm.platforms.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.platforms.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/platforms/{id}", - "permissionsRequired": [ "erm.platforms.item.get" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.platforms.item.get" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/platforms/{id}", - "permissionsRequired": [ "erm.platforms.item.put" ] + "permissionsRequired": [ + "erm.platforms.item.put" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/pti", - "permissionsRequired": [ "erm.pti.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.pti.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/pti/{id}", - "permissionsRequired": [ "erm.pti.item.get" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.pti.item.get" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/pti/{id}", - "permissionsRequired": [ "erm.pti.item.put" ] + "permissionsRequired": [ + "erm.pti.item.put" + ] }, - { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/custprops", - "permissionsRequired": [ "erm.custprops.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.custprops.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/custprops/{id}", - "permissionsRequired": [ "erm.custprops.item.get" ] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.custprops.item.get" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/custprops", - "permissionsRequired": [ "erm.custprops.item.post" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.custprops.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/custprops/{id}", - "permissionsRequired": [ "erm.custprops.item.put" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.custprops.item.put" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/custprops/{id}", - "permissionsRequired": [ "erm.custprops.item.delete" ] + "permissionsRequired": [ + "erm.custprops.item.delete" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/custprops/contexts", - "permissionsRequired": [ "erm.custprops.collection.get" ] + "permissionsRequired": [ + "erm.custprops.contexts.collection.get" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/erm/packages/import", - "permissionsRequired": [ "erm.packages.collection.import" ] + "permissionsRequired": [ + "erm.packages.collection.import" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/erm/validate/subscriptionAgreement", - "permissionsRequired": [ "erm.agreements.validate" ] + "permissionsRequired": [ + "erm.agreements.validate" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/erm/validate/subscriptionAgreement/*", - "permissionsRequired": [ "erm.agreements.validate" ] + "permissionsRequired": [ + "erm.agreements.property.validate" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/erm/validate/remoteKB", - "permissionsRequired": [ "erm.kbs.validate" ] + "permissionsRequired": [ + "erm.kbs.validate" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/erm/validate/remoteKB/*", - "permissionsRequired": [ "erm.kbs.validate" ] + "permissionsRequired": [ + "erm.kbs.property.validate" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/sts", - "permissionsRequired": ["erm.sts.collection.get"] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.sts.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sts/{id}", - "permissionsRequired": ["erm.sts.item.get"] - },{ - "methods": ["POST"], + "permissionsRequired": [ + "erm.sts.item.get" + ] + }, + { + "methods": [ + "POST" + ], "pathPattern": "/erm/sts", - "permissionsRequired": ["erm.sts.item.post"] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "erm.sts.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/erm/sts/{id}", - "permissionsRequired": ["erm.sts.item.put"] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "erm.sts.item.put" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/erm/sts/{id}", - "permissionsRequired": ["erm.sts.item.delete"] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.sts.item.delete" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sts/template", - "permissionsRequired": ["erm.sts.template"] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "erm.sts.template.generate" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/erm/sts/template/{id}", - "permissionsRequired": ["erm.sts.collection.get"] + "permissionsRequired": [ + "erm.sts_for_platform_id.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/entitlementLogEntry", - "permissionsRequired": ["erm.entitlements.collection.get"] + "permissionsRequired": [ + "erm.entitlements.logEntry.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/erm/admin/*", - "permissionsRequired": ["erm.admin.action"] + "permissionsRequired": [ + "erm.admin.action.execute" + ] }, { - "methods": [ "GET" ], + "methods": [ + "GET" + ], "pathPattern": "/erm/settings*", - "permissionsRequired": [ "erm.settings.get" ] + "permissionsRequired": [ + "erm.settings.get" + ] }, { - "methods": [ "POST" ], + "methods": [ + "POST" + ], "pathPattern": "/erm/settings*", - "permissionsRequired": [ "erm.settings.post" ] + "permissionsRequired": [ + "erm.settings.post" + ] }, { - "methods": [ "PUT" ], + "methods": [ + "PUT" + ], "pathPattern": "/erm/settings*", - "permissionsRequired": [ "erm.settings.put" ] + "permissionsRequired": [ + "erm.settings.put" + ] }, { - "methods": [ "DELETE" ], + "methods": [ + "DELETE" + ], "pathPattern": "/erm/settings*", - "permissionsRequired": [ "erm.settings.delete" ] + "permissionsRequired": [ + "erm.settings.delete" + ] }, { - "methods": [ "POST" ], + "methods": [ + "POST" + ], "pathPattern": "/erm/pushKB*", - "permissionsRequired": [ "erm.pushkb.post" ] + "permissionsRequired": [ + "erm.pushkb.post" + ] }, { - "methods": [ "GET" ], + "methods": [ + "GET" + ], "pathPattern": "/erm/pushKB*", - "permissionsRequired": [ "erm.pushkb.get" ] + "permissionsRequired": [ + "erm.pushkb.get" + ] } ] }, @@ -499,22 +996,31 @@ "version": "1.0", "handlers": [ { - "methods": [ "GET" ], + "methods": [ + "GET" + ], "pathPattern": "/dashboard/definitions", "permissionsRequired": [] } ] }, { - "id" : "_tenant", - "version" : "1.2", - "interfaceType" : "system", - "handlers" : [ { - "methods" : [ "POST", "DELETE" ], - "pathPattern" : "/_/tenant" - }, { - "methods" : [ "POST" ], - "pathPattern" : "/_/tenant/disable" + "id": "_tenant", + "version": "1.2", + "interfaceType": "system", + "handlers": [ + { + "methods": [ + "POST", + "DELETE" + ], + "pathPattern": "/_/tenant" + }, + { + "methods": [ + "POST" + ], + "pathPattern": "/_/tenant/disable" } ] }, @@ -524,8 +1030,10 @@ "interfaceType": "system", "handlers": [ { - "permissionsRequired" : [], - "methods": [ "GET" ], + "permissionsRequired": [], + "methods": [ + "GET" + ], "pathPattern": "/erm/admin/triggerHousekeeping", "unit": "hour", "delay": "24" @@ -545,39 +1053,106 @@ "description": "Get an agreement record" }, { - "permissionName": "erm.agreements.item.resources.get", - "displayName": "Agreements item resources get", - "description": "Get an agreement record's resources" + "permissionName": "erm.agreements.resources.collection.get", + "displayName": "Agreements resources get", + "description": "Get an agreement record's resources", + "replaces": [ + "erm.agreements.item.resources.get" + ] + }, + { + "permissionName": "erm.agreements.resources.subset.collection.get", + "displayName": "Agreements resources subset get", + "description": "Get a subset of an agreement record's resources (eg \"future\" or \"dropped\")", + "replaces": [ + "erm.agreements.item.resources.get" + ] }, { "permissionName": "erm.agreements.export", "displayName": "Agreements export", - "description": "Export an agreement or its resources" + "description": "Export an agreement" + }, + { + "permissionName": "erm.agreements_with_resource_subset.export", + "displayName": "Agreements subset export", + "description": "Export some slice of an agreement" + }, + { + "permissionName": "erm.agreements.resources.export", + "displayName": "Agreements resources export", + "description": "Export the resources for an agreement" + }, + { + "permissionName": "erm.agreements.resources.subset_by_format.export", + "displayName": "Agreements resources specific export", + "description": "Export the resources for an agreement as a subset and/or specifying a format" + }, + { + "permissionName": "erm.all.agreements.resources.export", + "displayName": "All agreements export resources", + "description": "Export all resources for all agreements" + }, + { + "permissionName": "erm.all.agreements.resources.subset_by_format.export", + "displayName": "Agreements direct subset export", + "description":"Export a subset of all resources across all agreements" + }, + { + "permissionName": "erm.agreements.linkedLicenses.collection.get", + "displayName": "Agreements linked licenses collection get", + "description": "Get the linked licenses for an agreement", + "replaces": [ + "erm.agreements.linkedLicenses.get" + ] + }, + { + "permissionName": "erm.linkedLicenses.collection.get", + "displayName": "Linked licenses collection get", + "description": "Get collection of linked license records", + "replaces": [ + "erm.agreements.linkedLicenses.get" + ] }, { - "permissionName": "erm.agreements.linkedLicenses.get", - "displayName": "Agreements linked licenses get", - "description": "Get the linked licenses for an agreement" + "permissionName": "erm.usageDataProviders.collection.get", + "displayName": "Usage data providers collection get", + "description": "Get usage data providers", + "replaces": [ + "erm.agreements.usageDataProviders.get" + ] }, { - "permissionName": "erm.agreements.usageDataProviders.get", - "displayName": "Agreements usage data providers get", + "permissionName": "erm.agreements.usageDataProviders.collection.get", + "displayName": "Agreements usage data providers collection get", "description": "Get the usage data providers for an agreement" }, { "permissionName": "erm.agreements.validate", - "displayName": "Validate subscription agreement post", - "description": "Validate subscription agreement information" + "displayName": "Validate subscription agreement", + "description": "Validate a subscription agreement record" + }, + { + "permissionName": "erm.agreements.property.validate", + "displayName": "Validate SubscriptionAgreement property", + "description": "Validate a property on a SubscriptionAgreement record" }, { "permissionName": "erm.agreements.view", "subPermissions": [ "erm.agreements.collection.get", "erm.agreements.item.get", - "erm.agreements.item.resources.get", + "erm.agreements.resources.collection.get", + "erm.agreements.resources.subset.collection.get", "erm.agreements.export", - "erm.agreements.linkedLicenses.get", - "erm.agreements.usageDataProviders.get", + "erm.agreements_with_resource_subset.export", + "erm.agreements.resources.export", + "erm.agreements.resources.subset_by_format.export", + "erm.all.agreements.resources.export", + "erm.all.agreements.resources.subset_by_format.export", + "erm.agreements.linkedLicenses.collection.get", + "erm.usageDataProviders.collection.get", + "erm.agreements.usageDataProviders.collection.get", "erm.resources.view" ] }, @@ -592,9 +1167,12 @@ "description": "Put an agreement" }, { - "permissionName": "erm.agreements.item.clone", + "permissionName": "erm.agreements.clone.execute", "displayName": "Agreements item clone", - "description": "Clone an agreement" + "description": "Clone an agreement", + "replaces": [ + "erm.agreements.item.clone" + ] }, { "permissionName": "erm.agreements.edit", @@ -602,8 +1180,9 @@ "erm.agreements.view", "erm.agreements.item.post", "erm.agreements.item.put", - "erm.agreements.item.clone", - "erm.agreements.validate" + "erm.agreements.clone.execute", + "erm.agreements.validate", + "erm.agreements.property.validate" ] }, { @@ -709,22 +1288,40 @@ "displayName": "Packages collection get", "description": "Get a collection of package records" }, + { + "permissionName": "erm.packages.sources.collection.get", + "displayName": "Packages sources get", + "description": "Get sources for all package records" + }, { "permissionName": "erm.packages.item.get", "displayName": "Package item get", "description": "Get package record" }, { - "permissionName": "erm.packages.item.content.get", + "permissionName": "erm.packages.content.collection.get", "displayName": "Package item content get", - "description": "Get package record's contents" + "description": "Get package record's contents", + "replaces": [ + "erm.packages.item.content.get" + ] + }, + { + "permissionName": "erm.packages.content.subset.collection.get", + "displayName": "Package item content subset get", + "description": "Get a subset of a package record's contents", + "replaces": [ + "erm.packages.item.content.get" + ] }, { "permissionName": "erm.packages.view", "subPermissions": [ "erm.packages.collection.get", + "erm.packages.sources.collection.get", "erm.packages.item.get", - "erm.packages.item.content.get" + "erm.packages.content.collection.get", + "erm.packages.content.subset.collection.get" ] }, { @@ -732,16 +1329,70 @@ "displayName": "Jobs collection get", "description": "Get a collection of job records" }, + { + "permissionName": "erm.jobs_by_type.collection.get", + "displayName": "Jobs by type collection get", + "description": "Get a collection of job records by job type" + }, { "permissionName": "erm.jobs.item.get", "displayName": "Job item get", "description": "Get job record" }, + { + "permissionName": "erm.jobs.fullLog.collection.get", + "displayName": "Job full log get", + "description": "Get full logs for job record" + }, + { + "permissionName": "erm.jobs.infoLog.collection.get", + "displayName": "Job info log get", + "description": "Get info logs for job record" + }, + { + "permissionName": "erm.jobs.errorLog.collection.get", + "displayName": "Job error log get", + "description": "Get error logs for job record" + }, + { + "permissionName": "erm.jobs.fullLogStream.get", + "displayName": "Job full log get", + "description": "Get full log stream for job record" + }, + { + "permissionName": "erm.jobs.infoLogStream.get", + "displayName": "Job info log get", + "description": "Get info log stream for job record" + }, + { + "permissionName": "erm.jobs.errorLogStream.get", + "displayName": "Job error log get", + "description": "Get error log stream for job record" + }, + { + "permissionName": "erm.jobs.fileObject.download", + "displayName": "Job file object download", + "description": "Download file object for job record" + }, + { + "permissionName": "erm.jobs.logs.view", + "subPermissions": [ + "erm.jobs.fullLog.collection.get", + "erm.jobs.fullLogStream.get", + "erm.jobs.infoLog.collection.get", + "erm.jobs.infoLogStream.get", + "erm.jobs.errorLog.collection.get", + "erm.jobs.errorLogStream.get" + ] + }, { "permissionName": "erm.jobs.view", "subPermissions": [ "erm.jobs.collection.get", - "erm.jobs.item.get" + "erm.jobs_by_type.collection.get", + "erm.jobs.item.get", + "erm.jobs.logs.view", + "erm.jobs.fileObject.download" ] }, { @@ -768,50 +1419,74 @@ ] }, { - "permissionName": "erm.refdata.collection.get", - "displayName": "Refdata collection get", - "description": "Get a collection of refdata records" + "permissionName": "erm.refdata.category.collection.get", + "displayName": "Refdata category collection get", + "description": "Get a collection of refdata category records", + "replaces": [ + "erm.refdata.collection.get" + ] }, { - "permissionName": "erm.refdata.item.get", - "displayName": "Refdata item get", - "description": "Get refdata record" + "permissionName": "erm.refdata.value.collection.get", + "displayName": "Refdata value collection get", + "description": "Get a collection of refdata value records", + "replaces": [ + "erm.refdata.collection.get" + ] + }, + { + "permissionName": "erm.refdata.category.item.get", + "displayName": "Refdata item category get", + "description": "Get refdata category record", + "replaces": [ + "erm.refdata.item.get" + ] }, { "permissionName": "erm.refdata.view", "subPermissions": [ - "erm.refdata.collection.get", - "erm.refdata.item.get" + "erm.refdata.value.collection.get", + "erm.refdata.category.collection.get", + "erm.refdata.category.item.get" ] }, { - "permissionName": "erm.refdata.item.post", - "displayName": "Refdata item post", - "description": "Post refdata record" + "permissionName": "erm.refdata.category.item.post", + "displayName": "Refdata category item post", + "description": "Post refdata category record", + "replaces": [ + "erm.refdata.item.post" + ] }, { - "permissionName": "erm.refdata.item.put", - "displayName": "Refdata item put", - "description": "Put refdata record" + "permissionName": "erm.refdata.category.item.put", + "displayName": "Refdata category item put", + "description": "Put refdata category record", + "replaces": [ + "erm.refdata.item.put" + ] }, { "permissionName": "erm.refdata.edit", "subPermissions": [ "erm.refdata.view", - "erm.refdata.item.post", - "erm.refdata.item.put" + "erm.refdata.category.item.post", + "erm.refdata.category.item.put" ] }, { - "permissionName": "erm.refdata.item.delete", - "displayName": "Refdata item delete", - "description": "Delete refdata record" + "permissionName": "erm.refdata.category.item.delete", + "displayName": "Refdata category item delete", + "description": "Delete refdata category record", + "replaces": [ + "erm.refdata.item.delete" + ] }, { "permissionName": "erm.refdata.manage", "subPermissions": [ "erm.refdata.edit", - "erm.refdata.item.delete" + "erm.refdata.category.item.delete" ] }, { @@ -826,8 +1501,13 @@ }, { "permissionName": "erm.kbs.validate", - "displayName": "Validate knowledge base post", - "description": "Validate knowledge base information" + "displayName": "Validate RemoteKB", + "description": "Validate a RemoteKB record" + }, + { + "permissionName": "erm.kbs.property.validate", + "displayName": "Validate RemoteKB property", + "description": "Validate a property on a RemoteKB record" }, { "permissionName": "erm.kbs.view", @@ -852,7 +1532,8 @@ "erm.kbs.view", "erm.kbs.item.post", "erm.kbs.item.put", - "erm.kbs.validate" + "erm.kbs.validate", + "erm.kbs.property.validate" ] }, { @@ -959,28 +1640,58 @@ "displayName": "Resources collection get", "description": "Get a collection of resource records" }, + { + "permissionName": "erm.resources.electronic.collection.get", + "displayName": "Resources electronic collection get", + "description": "Get a collection of electronic resource records" + }, { "permissionName": "erm.resources.item.get", "displayName": "Resources item get", "description": "Get resource record" }, { - "permissionName": "erm.resources.item.entitlement.get", - "displayName": "Resources item entitlement get", - "description": "Get resource's entitlement records" + "permissionName": "erm.resources.entitlement.collection.get", + "displayName": "Resources entitlement get", + "description": "Get entitlement records for a resource", + "replaces": [ + "erm.resources.item.entitlement.get" + ] + }, + { + "permissionName": "erm.resources.entitlement.related.collection.get", + "displayName": "Resources related entitlement get", + "description": "Get related entitlement records for a resource", + "replaces": [ + "erm.resources.item.entitlement.get" + ] + }, + { + "permissionName": "erm.resources.entitlementOptions.collection.get", + "displayName": "Resources item entitlement options get", + "description": "Get resource's entitlement options records", + "replaces": [ + "erm.resources.item.entitlementOptions.get" + ] }, { - "permissionName": "erm.resources.item.entitlementOptions.get", + "permissionName": "erm.resources.entitlementOptions.static.collection.get", "displayName": "Resources item entitlement options get", - "description": "Get resource's entitlement options records" + "description": "Get resource's entitlement options records", + "replaces": [ + "erm.resources.item.entitlementOptions.get" + ] }, { "permissionName": "erm.resources.view", "subPermissions": [ "erm.resources.collection.get", + "erm.resources.electronic.collection.get", "erm.resources.item.get", - "erm.resources.item.entitlement.get", - "erm.resources.item.entitlementOptions.get", + "erm.resources.entitlement.collection.get", + "erm.resources.entitlement.related.collection.get", + "erm.resources.entitlementOptions.collection.get", + "erm.resources.entitlementOptions.static.collection.get", "erm.entitlements.view", "erm.kbs.collection.get" ] @@ -990,6 +1701,11 @@ "displayName": "Entitlements collection get", "description": "Get a collection of entitlement records" }, + { + "permissionName": "erm.entitlements.logEntry.collection.get", + "displayName": "Entitlements log entry get", + "description": "Get entitlement log entries" + }, { "permissionName": "erm.entitlements.item.get", "displayName": "Entitlements item get", @@ -999,6 +1715,7 @@ "permissionName": "erm.entitlements.view", "subPermissions": [ "erm.entitlements.collection.get", + "erm.entitlements.logEntry.collection.get", "erm.entitlements.item.get" ] }, @@ -1038,6 +1755,16 @@ "displayName": "Title collection get", "description": "Get a collection of title records" }, + { + "permissionName": "erm.titles.electronic.collection.get", + "displayName": "Electronic title collection get", + "description": "Get a collection of electronic title records" + }, + { + "permissionName": "erm.titles.entitled.collection.get", + "displayName": "Entitled title collection get", + "description": "Get a collection of entitled title records" + }, { "permissionName": "erm.titles.item.get", "displayName": "Title item get", @@ -1047,6 +1774,8 @@ "permissionName": "erm.titles.view", "subPermissions": [ "erm.titles.collection.get", + "erm.titles.electronic.collection.get", + "erm.titles.entitled.collection.get", "erm.titles.item.get" ] }, @@ -1067,6 +1796,11 @@ "displayName": "Custom properties collection get", "description": "Get a collection of custom properties records" }, + { + "permissionName": "erm.custprops.contexts.collection.get", + "displayName": "Custom properties contexts collection get", + "description": "Get all contexts for agreements custom properties" + }, { "permissionName": "erm.custprops.item.get", "displayName": "Custom property item get", @@ -1076,6 +1810,7 @@ "permissionName": "erm.custprops.view", "subPermissions": [ "erm.custprops.collection.get", + "erm.custprops.contexts.collection.get", "erm.custprops.item.get" ] }, @@ -1119,6 +1854,11 @@ "displayName": "String templates collection get", "description": "Get a collection of string templates" }, + { + "permissionName": "erm.sts_for_platform_id.collection.get", + "displayName": "String templates for id collection get", + "description": "Get a collection of string templates for a platform id" + }, { "permissionName": "erm.sts.item.get", "displayName": "String templates item get", @@ -1128,6 +1868,7 @@ "permissionName": "erm.sts.view", "subPermissions": [ "erm.sts.collection.get", + "erm.sts_for_platform_id.collection.get", "erm.sts.item.get" ] }, @@ -1162,14 +1903,20 @@ ] }, { - "permissionName": "erm.sts.template", - "displayName": "String templates perform template", - "description": "Performs string templating" + "permissionName": "erm.sts.template.generate", + "displayName": "String templates perform template (DEPRECATED)", + "description": "Performs string templating (DEPRECATED)", + "replaces": [ + "erm.sts.template" + ] }, { - "permissionName": "erm.admin.action", + "permissionName": "erm.admin.action.execute", "displayName": "Admin endpoint perform action", - "description": "Performs action from admin endpoint" + "description": "Performs action from admin endpoint", + "replaces": [ + "erm.admin.action" + ] }, { "permissionName": "erm.settings.get", @@ -1270,19 +2017,44 @@ "HostConfig": { "Memory": 2147483648, "PortBindings": { - "8080/tcp": [{ "HostPort": "%p" }] + "8080/tcp": [ + { + "HostPort": "%p" + } + ] } } }, - "dockerPull" : false, + "dockerPull": false, "env": [ - { "name": "JAVA_OPTIONS", "value": "-server -XX:+UseContainerSupport -XX:MaxRAMPercentage=67.0 -XX:+PrintFlagsFinal" }, - { "name": "DB_HOST", "value": "postgres" }, - { "name": "DB_PORT", "value": "5432" }, - { "name": "DB_USERNAME", "value": "folio_admin" }, - { "name": "DB_PASSWORD", "value": "folio_admin" }, - { "name": "DB_DATABASE", "value": "okapi_modules" }, - { "name": "DB_MAXPOOLSIZE", "value": "50" } + { + "name": "JAVA_OPTIONS", + "value": "-server -XX:+UseContainerSupport -XX:MaxRAMPercentage=67.0 -XX:+PrintFlagsFinal" + }, + { + "name": "DB_HOST", + "value": "postgres" + }, + { + "name": "DB_PORT", + "value": "5432" + }, + { + "name": "DB_USERNAME", + "value": "folio_admin" + }, + { + "name": "DB_PASSWORD", + "value": "folio_admin" + }, + { + "name": "DB_DATABASE", + "value": "okapi_modules" + }, + { + "name": "DB_MAXPOOLSIZE", + "value": "50" + } ] } -} +} \ No newline at end of file From 75a8bdceb076a02346853ec92a4afc66dfcf4ade Mon Sep 17 00:00:00 2001 From: Ethan Freestone Date: Thu, 3 Oct 2024 16:02:59 +0100 Subject: [PATCH 2/4] fix: Fixed concurrent jobs reading from config --- .../olf/general/jobs/JobRunnerService.groovy | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/service/grails-app/services/org/olf/general/jobs/JobRunnerService.groovy b/service/grails-app/services/org/olf/general/jobs/JobRunnerService.groovy index ed6c197d..1e942784 100644 --- a/service/grails-app/services/org/olf/general/jobs/JobRunnerService.groovy +++ b/service/grails-app/services/org/olf/general/jobs/JobRunnerService.groovy @@ -88,13 +88,21 @@ order by pj.dateCreated @PostConstruct void init() { // Set up the Executor - if ( grailsApplication.config.concurrentJobsGlobal instanceof Integer && grailsApplication.config.concurrentJobsGlobal > 0 ) - CONCURRENT_JOBS_GLOBAL = grailsApplication.config.concurrentJobsGlobal; + try { + def concurrentJobsGlobalConfig = grailsApplication.config.getProperty('concurrentJobsGlobal', int); + if (concurrentJobsGlobalConfig > 0) { + CONCURRENT_JOBS_GLOBAL = concurrentJobsGlobalConfig; + } + } catch (Exception e) { + log.error("Failed to read concurrentJobsGlobal from config: ${e}") + } + log.info("Configured jobConcurrency: ${CONCURRENT_JOBS_GLOBAL}") // Base the number of small jobs executable on the limit imposed on the default runner. taskConcurrency = CONCURRENT_JOBS_GLOBAL * 2 - - // SO: This is not ideal. We don't want to limit jobs globally to 1 ideally. It should be + log.info("Configured taskConcurrency: ${taskConcurrency}") + + // SO: This is not ideal. We don't want to limit jobs globally to 1 ideally. It should be // 1 per tenant, but that will involve implementing custom handling for the queue and executor. // While we only have 1 tenant, this will suffice. executorSvc = new ThreadPoolExecutor( @@ -111,7 +119,7 @@ order by pj.dateCreated 5, TimeUnit.SECONDS, // Makes the above wait time in 'seconds' new LinkedBlockingQueue() // Blocking queue - ) + ) // Raise an event to say we are ready. notify('jobs:job_runner_ready') From 91c9102af64696bfd89d8bf1804d843e7cce43c7 Mon Sep 17 00:00:00 2001 From: Ethan Freestone Date: Thu, 3 Oct 2024 16:03:46 +0100 Subject: [PATCH 3/4] chore: GrailsApplication getProperty Fixed other instances where grailsApplication.config.property pattern was used. Also included defaulting to 3 concurrent jobs total in dc profile --- service/grails-app/conf/application-dc.yml | 1 + service/grails-app/init/org/olf/BootStrap.groovy | 10 +++++----- .../org/olf/DependentModuleProxyService.groovy | 4 ++-- .../services/org/olf/ErmHousekeepingService.groovy | 10 +++++----- service/src/main/okapi/tenant/sample_data/_data.groovy | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/service/grails-app/conf/application-dc.yml b/service/grails-app/conf/application-dc.yml index a2ea3f03..5ea2f5a2 100644 --- a/service/grails-app/conf/application-dc.yml +++ b/service/grails-app/conf/application-dc.yml @@ -27,6 +27,7 @@ okapi: logging: config: classpath:logback-development.xml +concurrentJobsGlobal: "${CONCURRENT_JOBS_GLOBAL:3}" # register: true diff --git a/service/grails-app/init/org/olf/BootStrap.groovy b/service/grails-app/init/org/olf/BootStrap.groovy index c2c18729..9b7d4360 100644 --- a/service/grails-app/init/org/olf/BootStrap.groovy +++ b/service/grails-app/init/org/olf/BootStrap.groovy @@ -22,17 +22,17 @@ class BootStrap { }); log.info("mod-agreements startup report"); - log.info("${grailsApplication.getMetadata().getApplicationName()} (${grailsApplication.config?.info?.app?.version}) initialising"); + log.info("${grailsApplication.getMetadata().getApplicationName()} (${grailsApplication.config?.getProperty('info.app.version')}) initialising"); log.info(" build number -> ${grailsApplication.metadata['build.number']}"); log.info(" build revision -> ${grailsApplication.metadata['build.git.revision']}"); log.info(" build branch -> ${grailsApplication.metadata['build.git.branch']}"); log.info(" build commit -> ${grailsApplication.metadata['build.git.commit']}"); log.info(" build time -> ${grailsApplication.metadata['build.time']}"); log.info(" build host -> ${grailsApplication.metadata['build.host']}"); - log.info(" Base JDBC URL -> ${grailsApplication.config.dataSource.url} / ${grailsApplication.config.dataSource.username}"); - log.info(" default_aws_region -> ${grailsApplication.config.kiwt?.filestore?.aws_region}"); - log.info(" default_aws_url -> ${grailsApplication.config.kiwt?.filestore?.aws_url}"); - log.info(" default_aws_bucket -> ${grailsApplication.config.kiwt?.filestore?.aws_bucket}"); + log.info(" Base JDBC URL -> ${grailsApplication.config.getProperty('dataSource.url')} / ${grailsApplication.config.getProperty('dataSource.username')}"); + log.info(" default_aws_region -> ${grailsApplication.config.getProperty('kiwt.filestore.aws_region')}"); + log.info(" default_aws_url -> ${grailsApplication.config.getProperty('kiwt.filestore.aws_url')}"); + log.info(" default_aws_bucket -> ${grailsApplication.config.getProperty('kiwt.filestore.aws_bucket')}"); Map env = System.getenv(); env.each { name,value -> diff --git a/service/grails-app/services/org/olf/DependentModuleProxyService.groovy b/service/grails-app/services/org/olf/DependentModuleProxyService.groovy index 712f62b5..dab86d77 100644 --- a/service/grails-app/services/org/olf/DependentModuleProxyService.groovy +++ b/service/grails-app/services/org/olf/DependentModuleProxyService.groovy @@ -31,7 +31,7 @@ public class DependentModuleProxyService { log.debug "No local org for ${orgName}. Check vendors." def mod_vendor_lookup_result = null; - if ( grailsApplication.config.useModVendors ) { + if ( grailsApplication.config.getProperty('useModVendors', boolean) ) { // This fetches a max of 2 (we should decide how to handle multiple matches) vendors with an exact name match. mod_vendor_lookup_result = okapiClient.get("/vendor", [ limit: 2, @@ -68,7 +68,7 @@ public class DependentModuleProxyService { case 0: // No match // We should add in an option to create vendors if users configure that - if ( grailsApplication.config.createMissingVendors ) { + if ( grailsApplication.config.getProperty('createMissingVendors', boolean) ) { throw new RuntimeException("Not yet implemented"); } diff --git a/service/grails-app/services/org/olf/ErmHousekeepingService.groovy b/service/grails-app/services/org/olf/ErmHousekeepingService.groovy index 813ccc26..2c2b2338 100644 --- a/service/grails-app/services/org/olf/ErmHousekeepingService.groovy +++ b/service/grails-app/services/org/olf/ErmHousekeepingService.groovy @@ -52,11 +52,11 @@ public class ErmHousekeepingService { RefdataValue.lookupOrCreate('FileStorageEngines', 'LOB'); RefdataValue.lookupOrCreate('FileStorageEngines', 'S3'); - def default_aws_region = grailsApplication.config.kiwt?.filestore?.aws_region - def default_aws_url = grailsApplication.config.kiwt?.filestore?.aws_url - def default_aws_secret = grailsApplication.config.kiwt?.filestore?.aws_secret - def default_aws_bucket = grailsApplication.config.kiwt?.filestore?.aws_bucket - def default_aws_access_key_id = grailsApplication.config.kiwt?.filestore?.aws_access_key_id + def default_aws_region = grailsApplication.config.getProperty('kiwt.filestore.aws_region') + def default_aws_url = grailsApplication.config.getProperty('kiwt.filestore.aws_url') + def default_aws_secret = grailsApplication.config.getProperty('kiwt.filestore.aws_secret') + def default_aws_bucket = grailsApplication.config.getProperty('kiwt.filestore.aws_bucket') + def default_aws_access_key_id = grailsApplication.config.getProperty('kiwt.filestore.aws_access_key_id') // Bootstrap any app settings we may need [ diff --git a/service/src/main/okapi/tenant/sample_data/_data.groovy b/service/src/main/okapi/tenant/sample_data/_data.groovy index b1421091..11a91e80 100644 --- a/service/src/main/okapi/tenant/sample_data/_data.groovy +++ b/service/src/main/okapi/tenant/sample_data/_data.groovy @@ -32,7 +32,7 @@ RemoteKB.findByName('GOKb') ?: (new RemoteKB( uri:'https://gokb.org/gokb/oai/index', fullPrefix:'gokb', rectype: RemoteKB.RECTYPE_PACKAGE, - active:Boolean.FALSE, + active:Boolean.TRUE, supportsHarvesting:true, activationEnabled:false ).save(failOnError:true)) From 932238d65594aaa0046e942bb0ce60cbbb78eb8c Mon Sep 17 00:00:00 2001 From: EthanFreestone <54310740+EthanFreestone@users.noreply.github.com> Date: Fri, 4 Oct 2024 12:29:53 +0100 Subject: [PATCH 4/4] chore: Undo a stupidly merged change to _data.groovy... (#827) I made a note to devs and somehow managed to ignore it entirely while doing other things. Extremely silly mistake to make :') --- service/src/main/okapi/tenant/sample_data/_data.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/src/main/okapi/tenant/sample_data/_data.groovy b/service/src/main/okapi/tenant/sample_data/_data.groovy index 11a91e80..b1421091 100644 --- a/service/src/main/okapi/tenant/sample_data/_data.groovy +++ b/service/src/main/okapi/tenant/sample_data/_data.groovy @@ -32,7 +32,7 @@ RemoteKB.findByName('GOKb') ?: (new RemoteKB( uri:'https://gokb.org/gokb/oai/index', fullPrefix:'gokb', rectype: RemoteKB.RECTYPE_PACKAGE, - active:Boolean.TRUE, + active:Boolean.FALSE, supportsHarvesting:true, activationEnabled:false ).save(failOnError:true))