From 29010bc37c863f0844d9cee37849bce4a90e960c Mon Sep 17 00:00:00 2001 From: EthanFreestone <54310740+EthanFreestone@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:52:37 +0100 Subject: [PATCH] Validate module descriptor (#113) * chore: Add validation script * ci: Fix gradle script * style: Fix JSON formatting * chore: Refdata endpoints in line with other module draft PRs * chore: Change ruleset status permissions to reflect execution instead of misleading "PUT" perms * chore: Swap out "serials-management.predictedPieceSets.item.post" permission for two procedural ones referencing the ability to generate predicted piece sets --- .github/workflows/gradle.yml | 28 +- .github/workflows/validate-module.yml | 71 ++++ .../main/okapi/ModuleDescriptor-template.json | 386 +++++++++++++----- 3 files changed, 366 insertions(+), 119 deletions(-) create mode 100644 .github/workflows/validate-module.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 0732ce2..955d44e 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -2,10 +2,10 @@ name: Grails CI on: push: branches: - - master + - master pull_request: branches: - - master + - master jobs: build: runs-on: ubuntu-latest @@ -13,18 +13,18 @@ jobs: run: working-directory: ./service steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'adopt' - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} restore-keys: | ${{ runner.os }}-gradle- @@ -32,7 +32,7 @@ jobs: - name: Start containers run: | cd ../tools/testing - docker-compose -f "docker-compose.yml" up -d + docker compose -f "docker-compose.yml" up -d - name: Build with Gradle run: | @@ -42,11 +42,11 @@ jobs: - name: Stop containers run: | cd ../tools/testing - docker-compose -f "docker-compose.yml" down -v + docker compose -f "docker-compose.yml" down -v - name: Upload Unit Test Results if: always() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Unit Test Results path: | @@ -55,11 +55,3 @@ jobs: service/build/reports/jacoco/test/**/*.html service/build/reports/jacoco/test/**/*.csv service/build/reports/jacoco/test/**/*.xml - - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v1 - if: always() - with: - files: | - service/build/test-results/**/*.xml - diff --git a/.github/workflows/validate-module.yml b/.github/workflows/validate-module.yml new file mode 100644 index 0000000..9c9a071 --- /dev/null +++ b/.github/workflows/validate-module.yml @@ -0,0 +1,71 @@ +name: Validate module + +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 diff --git a/service/src/main/okapi/ModuleDescriptor-template.json b/service/src/main/okapi/ModuleDescriptor-template.json index 3693de8..b69c298 100644 --- a/service/src/main/okapi/ModuleDescriptor-template.json +++ b/service/src/main/okapi/ModuleDescriptor-template.json @@ -5,151 +5,262 @@ { "id": "serials-management", "version": "${info.app.minorVersion}", - "handlers" : [ + "handlers": [ { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/refdata", - "permissionsRequired": [ "serials-management.refdata.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "serials-management.refdata.category.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/serials-management/refdata/{domain}/{property}", - "permissionsRequired": [ "serials-management.refdata.collection.get" ] - },{ - "methods": ["GET"], + "permissionsRequired": [ + "serials-management.refdata.value.collection.get" + ] + }, + { + "methods": [ + "GET" + ], "pathPattern": "/serials-management/refdata/{id}", - "permissionsRequired": [ "serials-management.refdata.item.get" ] + "permissionsRequired": [ + "serials-management.refdata.item.get" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/refdata", - "permissionsRequired": [ "serials-management.refdata.item.post" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "serials-management.refdata.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/serials-management/refdata/{id}", - "permissionsRequired": [ "serials-management.refdata.item.put" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "serials-management.refdata.item.put" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/serials-management/refdata/{id}", - "permissionsRequired": [ "serials-management.refdata.item.delete" ] + "permissionsRequired": [ + "serials-management.refdata.item.delete" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/serials", - "permissionsRequired": [ "serials-management.serials.collection.get" ], - "modulePermissions": ["orders.po-lines.item.get"] + "permissionsRequired": [ + "serials-management.serials.collection.get" + ], + "modulePermissions": [ + "orders.po-lines.item.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/serials/{id}", - "permissionsRequired": [ "serials-management.serials.item.get" ] + "permissionsRequired": [ + "serials-management.serials.item.get" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/serials", - "permissionsRequired": [ "serials-management.serials.item.post" ] - },{ - "methods": ["PUT"], + "permissionsRequired": [ + "serials-management.serials.item.post" + ] + }, + { + "methods": [ + "PUT" + ], "pathPattern": "/serials-management/serials/{id}", - "permissionsRequired": [ "serials-management.serials.item.put" ] - },{ - "methods": ["DELETE"], + "permissionsRequired": [ + "serials-management.serials.item.put" + ] + }, + { + "methods": [ + "DELETE" + ], "pathPattern": "/serials-management/serials/{id}", - "permissionsRequired": [ "serials-management.serials.item.delete" ] + "permissionsRequired": [ + "serials-management.serials.item.delete" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/rulesets", - "permissionsRequired": [ "serials-management.rulesets.collection.get" ] + "permissionsRequired": [ + "serials-management.rulesets.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/rulesets/{id}", - "permissionsRequired": [ "serials-management.rulesets.item.get" ] + "permissionsRequired": [ + "serials-management.rulesets.item.get" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/rulesets", - "permissionsRequired": [ "serials-management.rulesets.item.post" ] + "permissionsRequired": [ + "serials-management.rulesets.item.post" + ] }, { - "methods": ["DELETE"], + "methods": [ + "DELETE" + ], "pathPattern": "/serials-management/rulesets/{id}", - "permissionsRequired": [ "serials-management.rulesets.item.delete" ] + "permissionsRequired": [ + "serials-management.rulesets.item.delete" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/rulesets/{id}/active", - "permissionsRequired": [ "serials-management.rulesets.item.put" ] + "permissionsRequired": [ + "serials-management.rulesets.set-active.execute" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/rulesets/{id}/deprecated", - "permissionsRequired": [ "serials-management.rulesets.item.put" ] + "permissionsRequired": [ + "serials-management.rulesets.set-deprecated.execute" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/rulesets/{id}/draft", - "permissionsRequired": [ "serials-management.rulesets.item.put" ] + "permissionsRequired": [ + "serials-management.rulesets.set-draft.execute" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/predictedPieces", - "permissionsRequired": [ "serials-management.predictedPieceSets.collection.get" ] + "permissionsRequired": [ + "serials-management.predictedPieceSets.collection.get" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/predictedPieces/{id}", - "permissionsRequired": [ "serials-management.predictedPieceSets.item.get" ] + "permissionsRequired": [ + "serials-management.predictedPieceSets.item.get" + ] }, { - "methods": ["PUT"], + "methods": [ + "PUT" + ], "pathPattern": "/serials-management/predictedPieces/{id}", - "permissionsRequired": [ "serials-management.predictedPieceSets.item.put" ] + "permissionsRequired": [ + "serials-management.predictedPieceSets.item.put" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/predictedPieces/generate", - "permissionsRequired": [ "serials-management.predictedPieceSets.item.post" ] + "permissionsRequired": [ + "serials-management.predictedPieceSets.transient.generate" + ] }, { - "methods": ["POST"], + "methods": [ + "POST" + ], "pathPattern": "/serials-management/predictedPieces/create", - "permissionsRequired": [ "serials-management.predictedPieceSets.item.post" ] + "permissionsRequired": [ + "serials-management.predictedPieceSets.generate" + ] }, { - "methods": ["DELETE"], + "methods": [ + "DELETE" + ], "pathPattern": "/serials-management/predictedPieces/{id}", - "permissionsRequired": [ "serials-management.predictedPieceSets.item.delete" ] + "permissionsRequired": [ + "serials-management.predictedPieceSets.item.delete" + ] }, { - "methods": ["GET"], + "methods": [ + "GET" + ], "pathPattern": "/serials-management/locales", - "permissionsRequired": [ "serials-management.locales.collection.get" ] + "permissionsRequired": [ + "serials-management.locales.collection.get" + ] } ] }, { "id": "_tenant", "version": "1.2", - "interfaceType" : "system", + "interfaceType": "system", "handlers": [ { - "permissionsRequired" : [], - "methods": ["POST", "DELETE"], + "permissionsRequired": [], + "methods": [ + "POST", + "DELETE" + ], "pathPattern": "/_/tenant" - }, + }, { - "permissionsRequired" : [], - "methods" : [ "POST" ], - "pathPattern" : "/_/tenant/disable" + "permissionsRequired": [], + "methods": [ + "POST" + ], + "pathPattern": "/_/tenant/disable" } - ] } ], - "requires":[ - ], - "optional":[ + "requires": [], + "optional": [ { "id": "orders", "version": "12.0" @@ -161,19 +272,19 @@ "displayName": "settings get", "description": "get settings", "visible": false - }, + }, { "permissionName": "serials-management.settings.post", "displayName": "settings post", "description": "post settings", "visible": false - }, + }, { "permissionName": "serials-management.settings.put", "displayName": "settings put", "description": "put settings", "visible": false - }, + }, { "permissionName": "serials-management.settings.delete", "displayName": "settings delete", @@ -186,7 +297,7 @@ "description": "read settings", "visible": false, "subPermissions": [ - "serials-management.settings.get" + "serials-management.settings.get" ] }, { @@ -195,17 +306,29 @@ "description": "write settings", "visible": false, "subPermissions": [ - "serials-management.settings.read", - "serials-management.settings.post", - "serials-management.settings.put", - "serials-management.settings.delete" + "serials-management.settings.read", + "serials-management.settings.post", + "serials-management.settings.put", + "serials-management.settings.delete" ] }, { - "permissionName": "serials-management.refdata.collection.get", - "displayName": "Ref data collection get", - "description": "Get a collection of ref data records", - "visible": false + "permissionName": "serials-management.refdata.category.collection.get", + "displayName": "Ref data category collection get", + "description": "Get a collection of ref data category records", + "visible": false, + "replaces": [ + "serials-management.refdata.collection.get" + ] + }, + { + "permissionName": "serials-management.refdata.value.collection.get", + "displayName": "Ref data value collection get", + "description": "Get a collection of ref data value records", + "visible": false, + "replaces": [ + "serials-management.refdata.collection.get" + ] }, { "permissionName": "serials-management.refdata.item.get", @@ -236,7 +359,8 @@ "displayName": "Refdata read", "description": "Read refdata as items or collections", "subPermissions": [ - "serials-management.refdata.collection.get", + "serials-management.refdata.value.collection.get", + "serials-management.refdata.category.collection.get", "serials-management.refdata.item.get" ] }, @@ -329,16 +453,37 @@ "description": "Post a ruleset record" }, { - "permissionName": "serials-management.rulesets.item.put", - "displayName": "Ruleset item put", - "description": "Put a ruleset record" + "permissionName": "serials-management.rulesets.set-active.execute", + "displayName": "Set ruleset status active", + "description": "Permit the changing of a ruleset status to \"active\"", + "replaces": [ + "serials-management.rulesets.item.put" + ] + }, + { + "permissionName": "serials-management.rulesets.set-deprecated.execute", + "displayName": "Set ruleset status deprecated", + "description": "Permit the changing of a ruleset status to \"deprecated\"", + "replaces": [ + "serials-management.rulesets.item.put" + ] + }, + { + "permissionName": "serials-management.rulesets.set-draft.execute", + "displayName": "Set ruleset status draft", + "description": "Permit the changing of a ruleset status to \"draft\"", + "replaces": [ + "serials-management.rulesets.item.put" + ] }, { "permissionName": "serials-management.rulesets.edit", "subPermissions": [ "serials-management.rulesets.view", "serials-management.rulesets.item.post", - "serials-management.rulesets.item.put" + "serials-management.rulesets.set-active.execute", + "serials-management.rulesets.set-deprecated.execute", + "serials-management.rulesets.set-draft.execute" ] }, { @@ -371,9 +516,20 @@ ] }, { - "permissionName": "serials-management.predictedPieceSets.item.post", - "displayName": "Predicted piece set item post", - "description": "Post a predicted piece set record" + "permissionName": "serials-management.predictedPieceSets.transient.generate", + "displayName": "Predicted piece set preview generate", + "description": "Generate predicted piece set for preview without saving", + "replaces": [ + "serials-management.predictedPieceSets.item.post" + ] + }, + { + "permissionName": "serials-management.predictedPieceSets.generate", + "displayName": "Predicted piece set generate", + "description": "Generate predicted piece set", + "replaces": [ + "serials-management.predictedPieceSets.item.post" + ] }, { "permissionName": "serials-management.predictedPieceSets.item.put", @@ -389,7 +545,8 @@ "permissionName": "serials-management.predictedPieceSets.edit", "subPermissions": [ "serials-management.predictedPieceSets.view", - "serials-management.predictedPieceSets.item.post", + "serials-management.predictedPieceSets.transient.generate", + "serials-management.predictedPieceSets.generate", "serials-management.predictedPieceSets.item.put" ] }, @@ -409,20 +566,47 @@ "launchDescriptor": { "dockerImage": "${info.app.name}:${info.app.version}", "dockerArgs": { - "HostConfig": { + "HostConfig": { "Memory": 1073741824, - "PortBindings": { "8080/tcp": [{ "HostPort": "%p" }] } + "PortBindings": { + "8080/tcp": [ + { + "HostPort": "%p" + } + ] + } } }, - "dockerPull" : false, + "dockerPull": false, "env": [ - { "name": "JAVA_OPTIONS", "value": "-server -XX:+UseContainerSupport -XX:MaxRAMPercentage=55.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=55.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