diff --git a/.github/workflows/api-doc.yml b/.github/workflows/api-doc.yml deleted file mode 100644 index 1ff7189c..00000000 --- a/.github/workflows/api-doc.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: api-doc - -# https://dev.folio.org/guides/api-doc/ - -# API_TYPES: string: The space-separated list of types to consider. -# One or more of 'RAML OAS'. -# e.g. 'OAS' -# -# API_DIRECTORIES: string: The space-separated list of directories to search -# for API description files. -# e.g. 'src/main/resources/openapi' -# NOTE: -- Also add each separate path to each of the "on: paths:" sections. -# e.g. 'src/main/resources/openapi/**' -# -# API_EXCLUDES: string: The space-separated list of directories and files -# to exclude from traversal, in addition to the default exclusions. -# e.g. '' - -env: - API_TYPES: 'OAS' - API_DIRECTORIES: 'src/main/resources/openapi' - API_EXCLUDES: '' - OUTPUT_DIR: 'folio-api-docs' - AWS_S3_BUCKET: 'foliodocs' - AWS_S3_FOLDER: 'api' - AWS_S3_REGION: 'us-east-1' - AWS_S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }} - AWS_S3_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }} - -on: - workflow_dispatch: - push: - branches: [ main, master ] - paths: - - 'src/main/resources/openapi/**' - tags: '[vV][0-9]+.[0-9]+.[0-9]+*' - -jobs: - api-doc: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.REF }} - submodules: recursive - - name: Prepare folio-tools - run: | - git clone https://github.com/folio-org/folio-tools - cd folio-tools/api-doc \ - && yarn install \ - && pip3 install -r requirements.txt - - name: Obtain version if release tag - if: ${{ startsWith(github.ref, 'refs/tags/v') }} - run: | - version=$(echo ${GITHUB_REF#refs/tags/[vV]} | awk -F'.' '{ printf("%d.%d", $1, $2) }') - echo "VERSION_MAJ_MIN=${version}" >> $GITHUB_ENV - - name: Set some vars - run: | - echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - name: Report some info - run: | - echo "REPO_NAME=${{ env.REPO_NAME }}" - - name: Do api-doc - run: | - if test -n "${{ env.VERSION_MAJ_MIN }}"; then - echo "Docs for release version ${{ env.VERSION_MAJ_MIN }}" - option_release=$(echo "--version ${{ env.VERSION_MAJ_MIN }}") - else - option_release="" - fi - python3 folio-tools/api-doc/api_doc.py \ - --loglevel info \ - --types ${{ env.API_TYPES }} \ - --directories ${{ env.API_DIRECTORIES }} \ - --excludes ${{ env.API_EXCLUDES }} \ - --output ${{ env.OUTPUT_DIR }} $option_release - - name: Show generated files - working-directory: ${{ env.OUTPUT_DIR }} - run: ls -R - - name: Publish to AWS S3 - uses: sai-sharan/aws-s3-sync-action@v0.1.0 - with: - access_key: ${{ env.AWS_S3_ACCESS_KEY_ID }} - secret_access_key: ${{ env.AWS_S3_ACCESS_KEY }} - region: ${{ env.AWS_S3_REGION }} - source: ${{ env.OUTPUT_DIR }} - destination_bucket: ${{ env.AWS_S3_BUCKET }} - destination_prefix: ${{ env.AWS_S3_FOLDER }} - delete: false - quiet: false - diff --git a/.github/workflows/api-lint.yml b/.github/workflows/api-lint.yml index 07b3c6b1..f41fb351 100644 --- a/.github/workflows/api-lint.yml +++ b/.github/workflows/api-lint.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive - name: Prepare folio-tools diff --git a/.github/workflows/api-schema-lint.yml b/.github/workflows/api-schema-lint.yml new file mode 100644 index 00000000..396b70ca --- /dev/null +++ b/.github/workflows/api-schema-lint.yml @@ -0,0 +1,47 @@ +name: api-schema-lint + +# https://dev.folio.org/guides/describe-schema/ + +# API_DIRECTORIES: string: The space-separated list of directories to search +# for JSON Schema files. +# e.g. 'src/main/resources/openapi' +# NOTE: -- Also add each separate path to each of the "on: paths:" sections. +# e.g. 'src/main/resources/openapi/**' +# +# API_EXCLUDES: string: The space-separated list of directories and files +# to exclude from traversal, in addition to the default exclusions. +# e.g. '' + +env: + API_DIRECTORIES: 'src/main/resources/openapi' + API_EXCLUDES: 'example' + +on: + workflow_dispatch: + push: + paths: + - 'src/main/resources/openapi/**' + pull_request: + paths: + - 'src/main/resources/openapi/**' + +jobs: + api-schema-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: recursive + - name: Prepare folio-tools + run: | + git clone https://github.com/folio-org/folio-tools + cd folio-tools/api-schema-lint \ + && yarn install \ + && pip3 install -r requirements.txt + - name: Do api-schema-lint + run: | + python3 folio-tools/api-schema-lint/api_schema_lint.py \ + --loglevel info \ + --directories ${{ env.API_DIRECTORIES }} \ + --excludes ${{ env.API_EXCLUDES }} diff --git a/.github/workflows/k8s-deploy-latest.yml b/.github/workflows/k8s-deploy-latest.yml new file mode 100644 index 00000000..de082775 --- /dev/null +++ b/.github/workflows/k8s-deploy-latest.yml @@ -0,0 +1,27 @@ +# Very simple workflow to deploy the *latest* published container image with the 'latest' tag. +# This does not post updated module descriptors to okapi, update permissions or enable +# new versions of a module with the tenant. If that is needed, it should be done manually +# via the Okapi API. + +name: k8s-deploy-latest + +env: + K8S_NAMESPACE: 'folio-dev-new' + K8S_DEPLOYMENT: 'mod-inventory-update-dev' + +on: + workflow_dispatch + +jobs: + k8s-deploy-latest: + + runs-on: ubuntu-latest + steps: + - name: Deploy latest to K8s + uses: actions-hub/kubectl@v1.34.1 + env: + KUBE_CONFIG: ${{ secrets.FOLIO_DEV_NEW_SA_KUBECONFIG }} + with: + args: + -n ${{ env.K8S_NAMESPACE }} rollout restart deployment ${{ env.K8S_DEPLOYMENT }} + diff --git a/.github/workflows/mvn-dev-build-deploy.yml b/.github/workflows/mvn-dev-build-deploy.yml new file mode 100644 index 00000000..9a8f150a --- /dev/null +++ b/.github/workflows/mvn-dev-build-deploy.yml @@ -0,0 +1,136 @@ +name: mvn-dev-build-deploy + +on: + push: + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +env: + PUBLISH_BRANCH: 'deployment' + OKAPI_URL: 'https://folio-dev-new-okapi.folio-dev.indexdata.com' + OKAPI_SECRET_USER: "${{ secrets.FOLIO_DEV_NEW_OKAPI_USER }}" + OKAPI_SECRET_PASSWORD: "${{ secrets.FOLIO_DEV_NEW_OKAPI_PASSWORD }}" + OK_SESSION: 'session1' + +jobs: + mvn-dev-build-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + submodules: recursive + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + java-version: 21 + distribution: 'temurin' # Alternative distribution options are available. + cache: 'maven' + + - name: Prepare okclient + run: git clone https://github.com/indexdata/okclient + + - name: Ensure OK and FOLIO login + # So do not proceed with other workflow steps if not available. + run: | + source okclient/ok.sh + OK -S ${{ env.OK_SESSION }} \ + -h ${{ env.OKAPI_URL }} \ + -t "supertenant" \ + -u ${{ env.OKAPI_SECRET_USER }} \ + -p ${{ env.OKAPI_SECRET_PASSWORD }} + OK -S ${{ env.OK_SESSION }} -x + + - name: Gather some variables + run: | + echo "MODULE_NAME=$(mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout)" >> $GITHUB_ENV + echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + echo "CURRENT_BRANCH=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV + + - name: Set module version + run: | + echo "MODULE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)-${SHA_SHORT}" >> $GITHUB_ENV + + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Cache Maven packages + uses: actions/cache@v4 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + - name: Testcontainers Docker api version hack + run: echo api.version=1.44 >> ~/.docker-java.properties + + - name: Maven build + run: mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install org.jacoco:jacoco-maven-plugin:report + + - name: SQ analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=indexdata -Dsonar.projectKey=indexdata_${{ github.event.repository.name }} + + - name: Update ModuleDescriptor Id + run: | + if test -f "$MOD_DESCRIPTOR"; then + echo "Found $MOD_DESCRIPTOR" + cat <<< $(jq '.id = "${{ env.MODULE_NAME}}-${{ env.MODULE_VERSION }}"' $MOD_DESCRIPTOR) > $MOD_DESCRIPTOR + echo "MODULE_DESCRIPTOR=$MOD_DESCRIPTOR" >> $GITHUB_ENV + else + echo "Could not find $MOD_DESCRIPTOR" + exit 1 + fi + env: + MOD_DESCRIPTOR: './target/ModuleDescriptor.json' + + - name: Read ModuleDescriptor + id: moduleDescriptor + uses: juliangruber/read-file-action@v1 + with: + path: ${{ env.MODULE_DESCRIPTOR }} + + - name: Login to Index Data Docker Hub account + if: ${{ env.CURRENT_BRANCH == env.PUBLISH_BRANCH }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and publish Docker image + if: ${{ env.CURRENT_BRANCH == env.PUBLISH_BRANCH }} + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: indexdata/${{ env.MODULE_NAME }}:${{ env.MODULE_VERSION }},indexdata/${{ env.MODULE_NAME }}:latest + + - name: Publish ModuleDescriptor to Okapi + if: ${{ env.CURRENT_BRANCH == env.PUBLISH_BRANCH }} + run: | + source okclient/ok.sh + echo "Do login ..." + OK -S ${{ env.OK_SESSION }} \ + -h ${{ env.OKAPI_URL }} \ + -t "supertenant" \ + -u ${{ env.OKAPI_SECRET_USER }} \ + -p ${{ env.OKAPI_SECRET_PASSWORD }} + echo "Post the MD and report the response status ..." + OK -S ${{ env.OK_SESSION }} _/proxy/modules \ + -X post -f ${{ env.MODULE_DESCRIPTOR }} + declare -n NAMEREF_STATUS=${{ env.OK_SESSION }}_HTTPStatus + echo "Response status: $NAMEREF_STATUS" + echo "Do logout ..." + OK -S ${{ env.OK_SESSION }} -x + + - name: Print module version to job summary + run: | + echo "#### Module Version: ${{ env.MODULE_VERSION }}" >> $GITHUB_STEP_SUMMARY diff --git a/src/main/java/org/folio/inventoryupdate/importing/moduledata/ImportJob.java b/src/main/java/org/folio/inventoryupdate/importing/moduledata/ImportJob.java index f016c72a..3c649ad4 100644 --- a/src/main/java/org/folio/inventoryupdate/importing/moduledata/ImportJob.java +++ b/src/main/java/org/folio/inventoryupdate/importing/moduledata/ImportJob.java @@ -6,8 +6,10 @@ import io.vertx.core.json.JsonObject; import io.vertx.sqlclient.SqlResult; import io.vertx.sqlclient.templates.RowMapper; +import io.vertx.sqlclient.templates.SqlTemplate; import io.vertx.sqlclient.templates.TupleMapper; import java.time.LocalDateTime; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -327,8 +329,9 @@ public Future createDatabase(TenantPgPool pool) { "CREATE TABLE IF NOT EXISTS " + pool.getSchema() + "." + table() + "(" + dbColumnNameAndType(ID) + " PRIMARY KEY, " - + dbColumnNameAndType(CHANNEL_ID) + " NOT NULL " - + " REFERENCES " + pool.getSchema() + "." + Tables.CHANNEL + " (" + new Channel().dbColumnName(ID) + "), " + + dbColumnNameAndType(CHANNEL_ID) + " NOT NULL CONSTRAINT import_job_channel_id_fkey " + + "REFERENCES " + pool.getSchema() + "." + Tables.CHANNEL + " (" + new Channel().dbColumnName(ID) + ") " + + " ON DELETE CASCADE, " + dbColumnNameAndType(CHANNEL_NAME) + ", " + dbColumnNameAndType(IMPORT_TYPE) + ", " + dbColumnNameAndType(TRANSFORMATION) + ", " @@ -343,6 +346,14 @@ public Future createDatabase(TenantPgPool pool) { ).mapEmpty(); } + public Future countImportJobsByChannelId(TenantPgPool pool, String channelId) { + return SqlTemplate.forQuery(pool.getPool(), + "SELECT COUNT(*) AS import_jobs_count FROM " + pool.getSchema() + "." + table() + + " WHERE " + dbColumnName(CHANNEL_ID) + " = #{channelId}") + .execute(Collections.singletonMap("channelId", channelId)) + .map(rows -> rows.iterator().next().getInteger("import_jobs_count")); + } + public record ImportJobRecord(UUID id, UUID channelId, String channelName, diff --git a/src/main/java/org/folio/inventoryupdate/importing/moduledata/LogLine.java b/src/main/java/org/folio/inventoryupdate/importing/moduledata/LogLine.java index 886de213..57aea281 100644 --- a/src/main/java/org/folio/inventoryupdate/importing/moduledata/LogLine.java +++ b/src/main/java/org/folio/inventoryupdate/importing/moduledata/LogLine.java @@ -182,14 +182,14 @@ public Future createDatabase(TenantPgPool pool) { "CREATE TABLE IF NOT EXISTS " + pool.getSchema() + "." + table() + "(" + dbColumnName(ID) + " UUID PRIMARY KEY, " - + dbColumnName(IMPORT_JOB_ID) + " UUID NOT NULL REFERENCES " - + pool.getSchema() + "." + Tables.IMPORT_JOB + " (" + new ImportJob().dbColumnName(ID) + "), " + + dbColumnName(IMPORT_JOB_ID) + " UUID NOT NULL CONSTRAINT log_statement_import_job_id_fkey REFERENCES " + + pool.getSchema() + "." + Tables.IMPORT_JOB + " (" + new ImportJob().dbColumnName(ID) + ") " + + "ON DELETE CASCADE, " + dbColumnName(TIME_STAMP) + " TIMESTAMP NOT NULL, " + dbColumnName(JOB_LABEL) + " TEXT NOT NULL, " + dbColumnName(LOG_STATEMENT) + " TEXT NOT NULL, " + metadata.columnsDdl() + ")", - "CREATE INDEX IF NOT EXISTS log_statement_import_job_id_idx " + " ON " + pool.getSchema() + "." + table() + "(" + dbColumnName(IMPORT_JOB_ID) + ")" ).mapEmpty(); diff --git a/src/main/java/org/folio/inventoryupdate/importing/moduledata/RecordFailure.java b/src/main/java/org/folio/inventoryupdate/importing/moduledata/RecordFailure.java index 902f2058..a54ffc80 100644 --- a/src/main/java/org/folio/inventoryupdate/importing/moduledata/RecordFailure.java +++ b/src/main/java/org/folio/inventoryupdate/importing/moduledata/RecordFailure.java @@ -254,8 +254,9 @@ public Future createDatabase(TenantPgPool pool) { "CREATE TABLE IF NOT EXISTS " + pool.getSchema() + "." + table() + "(" + dbColumnNameAndType(ID) + " PRIMARY KEY, " - + dbColumnNameAndType(IMPORT_JOB_ID) + " NOT NULL REFERENCES " - + pool.getSchema() + "." + Tables.IMPORT_JOB + "(" + new ImportJob().dbColumnName(ID) + "), " + + dbColumnNameAndType(IMPORT_JOB_ID) + " NOT NULL CONSTRAINT record_failure_import_job_id_fkey " + + "REFERENCES " + pool.getSchema() + "." + Tables.IMPORT_JOB + + "(" + new ImportJob().dbColumnName(ID) + ") ON DELETE CASCADE, " + dbColumnNameAndType(RECORD_NUMBER) + ", " + dbColumnNameAndType(TIME_STAMP) + ", " + dbColumnNameAndType(SOURCE_FILE_NAME) + ", " @@ -264,7 +265,6 @@ public Future createDatabase(TenantPgPool pool) { + dbColumnNameAndType(TRANSFORMED_RECORD) + " NOT NULL, " + metadata.columnsDdl() + ")", - "CREATE INDEX IF NOT EXISTS record_failure_import_job_id_idx " + " ON " + pool.getSchema() + "." + table() + "(" + dbColumnName(IMPORT_JOB_ID) + ")" ).mapEmpty(); diff --git a/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/Channels.java b/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/Channels.java index 6f5089b3..c2493d37 100644 --- a/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/Channels.java +++ b/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/Channels.java @@ -9,6 +9,7 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.folio.inventoryupdate.importing.moduledata.Channel; +import org.folio.inventoryupdate.importing.moduledata.ImportJob; import org.folio.inventoryupdate.importing.moduledata.database.EntityStorage; import org.folio.inventoryupdate.importing.moduledata.database.SqlQuery; import org.folio.inventoryupdate.importing.moduledata.database.Tables; @@ -91,12 +92,25 @@ public static Future putChannel(ServiceRequest request) { public static Future deleteChannel(ServiceRequest request) { String channelId = request.requestParam("id"); + boolean force = "true".equalsIgnoreCase(request.requestParam("force")); return getChannelByTagOrUuid(request, channelId).compose(channel -> { if (channel == null) { return responseText(request.routingContext(), 404) .end("Found no channel with tag or id " + channelId + " to delete.").mapEmpty(); } else { - return deleteEntityAndRespond(request, new Channel()).compose(na -> decommission(request)).mapEmpty(); + if (force) { + return deleteEntityAndRespond(request, new Channel()).compose(na -> decommission(request)).mapEmpty(); + } else { + return new ImportJob().countImportJobsByChannelId(request.entityStorage().getTenantPool(), channelId) + .compose(jobsCount -> { + if (jobsCount > 0) { + return responseText(request.routingContext(), 400).end("Channel not deleted because it has " + jobsCount + + " logged import jobs. To delete all logs together with the channel, use parameter ?force=true"); + } else { + return deleteEntityAndRespond(request, new Channel()).compose(na -> decommission(request)).mapEmpty(); + } + }); + } } }); } diff --git a/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/LogPurging.java b/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/LogPurging.java index fefb63ab..0a4076dc 100644 --- a/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/LogPurging.java +++ b/src/main/java/org/folio/inventoryupdate/importing/service/delivery/respond/LogPurging.java @@ -11,8 +11,6 @@ import org.apache.logging.log4j.Logger; import org.folio.inventoryupdate.importing.foliodata.SettingsClient; import org.folio.inventoryupdate.importing.moduledata.ImportJob; -import org.folio.inventoryupdate.importing.moduledata.LogLine; -import org.folio.inventoryupdate.importing.moduledata.RecordFailure; import org.folio.inventoryupdate.importing.moduledata.database.Tables; import org.folio.inventoryupdate.importing.service.ServiceRequest; import org.folio.inventoryupdate.importing.utils.Miscellaneous; @@ -49,38 +47,6 @@ private Future purgePastJobsBySetting(ServiceRequest request, String purge } private Future purgePreviousJobsByAge(LocalDateTime untilDate) { - return deleteLogs(untilDate) - .compose(deletedLogs -> deleteFailedRecords(untilDate)) - .compose(deletedFailedRecords -> deleteImportJobs(untilDate)); - } - - private Future deleteLogs(LocalDateTime untilDate) { - return SqlTemplate.forUpdate(pool.getPool(), - "DELETE FROM " + pool.getSchema() + "." + Tables.LOG_STATEMENT - + " WHERE " + new LogLine().field(LogLine.IMPORT_JOB_ID).columnName() - + " IN (SELECT " + new ImportJob().field(ImportJob.ID).columnName() - + " FROM " + pool.getSchema() + "." + Tables.IMPORT_JOB - + " WHERE " + new ImportJob().field(ImportJob.STARTED).columnName() + " < #{untilDate} )") - .execute(Collections.singletonMap("untilDate", untilDate)) - .onSuccess(result -> logger.info("{} log lines deleted", result.rowCount())) - .onFailure(error -> logger.error("{} (occurred when attempting to delete logs)", error.getMessage())) - .mapEmpty(); - } - - private Future deleteFailedRecords(LocalDateTime untilDate) { - return SqlTemplate.forUpdate(pool.getPool(), - "DELETE FROM " + pool.getSchema() + "." + Tables.RECORD_FAILURE - + " WHERE " + new RecordFailure().field(LogLine.IMPORT_JOB_ID).columnName() - + " IN (SELECT " + new ImportJob().field(ImportJob.ID).columnName() - + " FROM " + pool.getSchema() + "." + Tables.IMPORT_JOB - + " WHERE " + new ImportJob().field(ImportJob.STARTED).columnName() + " < #{untilDate} )") - .execute(Collections.singletonMap("untilDate", untilDate)) - .onSuccess(result -> logger.info("{} failed records deleted", result.rowCount())) - .onFailure(error -> logger.error("{} (occurred when attempting to delete failed records)", error.getMessage())) - .mapEmpty(); - } - - private Future deleteImportJobs(LocalDateTime untilDate) { return SqlTemplate.forUpdate(pool.getPool(), "DELETE FROM " + pool.getSchema() + "." + Tables.IMPORT_JOB + " WHERE " + new ImportJob().field(ImportJob.STARTED).columnName() + " <#{untilDate} ") diff --git a/src/test/java/org/folio/inventoryupdate/unittests/ImportTests.java b/src/test/java/org/folio/inventoryupdate/unittests/ImportTests.java index f6c49a7a..10818cc0 100644 --- a/src/test/java/org/folio/inventoryupdate/unittests/ImportTests.java +++ b/src/test/java/org/folio/inventoryupdate/unittests/ImportTests.java @@ -81,7 +81,7 @@ public void cleanUp() { .put("module_from", "mod-inventory-update-1.0.0") .put("purge", true), null); fakeFolioApis.settingsStorage.wipeMockRecords(); - deleteFileQueues(); + //deleteFileQueues(); super.cleanUp(); } @@ -683,11 +683,21 @@ public void canPostGetPutDeleteChannel() { putJsonObject(Service.PATH_CHANNELS + "/" + Files.JSON_CHANNEL.getString("id"), update, 200); putJsonObject(Service.PATH_CHANNELS + "/" + UUID.randomUUID(), update, 404); getRecords(Service.PATH_CHANNELS).body("totalRecords", is(1)); + // Can delete channel with no logged jobs deleteRecord(Service.PATH_CHANNELS, Files.JSON_CHANNEL.getString("id"), 200); getRecords(Service.PATH_CHANNELS).body("totalRecords", is(0)); - deleteRecord(Service.PATH_CHANNELS, Files.JSON_CHANNEL.getString("id"), 404); + // Can create disabled channel postJsonObject(PATH_CHANNELS, Files.JSON_CHANNEL.copy().put("enabled", false)); + // Can only delete channel with logged jobs if `force` set to `true` + postJsonObject(Service.PATH_IMPORT_JOBS, Files.JSON_IMPORT_JOB); + deleteRecord(Service.PATH_CHANNELS, Files.JSON_CHANNEL.getString("id"), 400); + getRecords(Service.PATH_CHANNELS).body("totalRecords", is(1)); + deleteRecord(Service.PATH_CHANNELS, Files.JSON_CHANNEL.getString("id"), "force=true", 200); + getRecords(Service.PATH_CHANNELS).body("totalRecords", is(0)); + deleteRecord(Service.PATH_CHANNELS, Files.JSON_CHANNEL.getString("id"), 404); + + } @Test @@ -1545,6 +1555,16 @@ ValidatableResponse deleteRecord(String api, String id, int statusCode) { .statusCode(statusCode); } + ValidatableResponse deleteRecord(String api, String id, String argument, int statusCode) { + return given() + .baseUri(BASE_URI_INVENTORY_UPDATE) + .header(Service.OKAPI_TENANT) + .header(Service.OKAPI_URL) + .delete(api + "/" + id + "?" + argument) + .then() + .statusCode(statusCode); + } + ValidatableResponse getRecords(String api) { return given() .baseUri(BASE_URI_INVENTORY_UPDATE)