diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cb6923c1c5d8..c806918d98bd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -170,7 +170,7 @@ codemagic.yaml /libs/clients/national-registry/v3/ @island-is/hugsmidjan /libs/clients/district-commissioners-licenses/ @island-is/hugsmidjan /libs/clients/regulations/ @island-is/hugsmidjan -/libs/clients/vehicles/ @island-is/hugsmidjan +/libs/clients/vehicles/ @island-is/hugsmidjan @island-is/stefna /libs/clients/vehicles-mileage/ @island-is/hugsmidjan /libs/clients/user-notification/ @island-is/hugsmidjan /libs/clients/passports/ @island-is/hugsmidjan diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml index f38de1e06222..5814f17b60ff 100644 --- a/.github/actions/cache/action.yml +++ b/.github/actions/cache/action.yml @@ -54,13 +54,13 @@ runs: # force-cache-save: ${{ inputs.force-cache-save }} - name: Give cache server a bit of time to recover - if: always() && inputs.retry > 0 && steps.cache_try_number_one.outputs.success != 'true' + if: ${{ !cancelled() && inputs.retry > 0 && steps.cache_try_number_one.outputs.success != 'true' }} shell: bash run: sleep 10 - name: Cache try number two id: cache_try_number_two - if: always() && inputs.retry > 0 && steps.cache_try_number_one.outputs.success != 'true' + if: ${{ !cancelled() && inputs.retry > 0 && steps.cache_try_number_one.outputs.success != 'true' }} uses: island-is/cache@v0.3 with: path: ${{ inputs.path }} @@ -68,13 +68,13 @@ runs: # force-cache-save: ${{ inputs.force-cache-save }} - name: Give cache server a bit of time to recover - if: always() && inputs.retry > 1 && steps.cache_try_number_one.outputs.success != 'true' && steps.cache_try_number_two.outputs.success != 'true' + if: ${{ !cancelled() && inputs.retry > 1 && steps.cache_try_number_one.outputs.success != 'true' && steps.cache_try_number_two.outputs.success != 'true' }} shell: bash run: sleep 10 - name: Cache try number three id: cache_try_number_three - if: always() && inputs.retry > 1 && steps.cache_try_number_one.outputs.success != 'true' && steps.cache_try_number_two.outputs.success != 'true' + if: ${{ !cancelled() && inputs.retry > 1 && steps.cache_try_number_one.outputs.success != 'true' && steps.cache_try_number_two.outputs.success != 'true' }} uses: island-is/cache@v0.3 with: path: ${{ inputs.path }} @@ -83,7 +83,7 @@ runs: - name: Gather outputs id: computed_outputs - if: always() + if: ${{ always() }} # We want to always report status, even when cancelled shell: bash run: | if [[ "${{ steps.cache_try_number_one.outputs.success }}" == "true" ]] || \ diff --git a/.github/workflows/config-values.yaml b/.github/workflows/config-values.yaml index e8ee060b7add..12a6369c0696 100644 --- a/.github/workflows/config-values.yaml +++ b/.github/workflows/config-values.yaml @@ -36,14 +36,15 @@ jobs: - name: Select secret envs to check id: select_envs run: | + set -euo pipefail GIT_BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF/refs\/heads\//}}" # ENVS=("dev" "staging") ENVS=("dev" "staging") - if [[ $GIT_BRANCH =~ ^release\/ ]]; then + if [[ "$GIT_BRANCH" =~ ^release\/ ]]; then echo "Adding prod environments to test set" ENVS+=("prod") fi - ENVS_JSON=$(printf '%s\n' "${ENVS[@]}" | jq -R . | jq -s . | tr -d '[:space:]') + ENVS_JSON="$(printf '%s\n' "${ENVS[@]}" | jq -R . | jq -s . | tr -d '[:space:]')" echo "ENVS={\"env\":$ENVS_JSON}" >> "$GITHUB_OUTPUT" helm-values-validation: @@ -134,7 +135,7 @@ jobs: prod: arn:aws:iam::251502586493:role/list-ssm-parameters dev: arn:aws:iam::013313053092:role/list-ssm-parameters staging: arn:aws:iam::261174024191:role/list-ssm-parameters - run: echo "ROLE=$${{ matrix.env }}" >> $GITHUB_ENV + run: echo "ROLE=$${{ matrix.env }}" >> "$GITHUB_ENV" - name: Get local secrets working-directory: infra run: node -r esbuild-register src/secrets.ts get-all-required-secrets --env=${{ matrix.env }} >> LOCAL_SECRETS @@ -174,8 +175,8 @@ jobs: working-directory: infra shell: /bin/bash {0} run: | - missing=$(grep -vxFf CLOUD_SECRETS LOCAL_SECRETS) - if [[ "$missing" != "" ]]; then + set -euo pipefail + if missing="$(grep -vxFf CLOUD_SECRETS LOCAL_SECRETS)"; then echo "Required secrets not available in environment ${{ matrix.env }}:" while IFS= read -r secret ; do echo $secret; done <<< "$missing" exit 1 diff --git a/.github/workflows/external-checks.yml b/.github/workflows/external-checks.yml index 5b35b5239f39..5707795d4f9b 100644 --- a/.github/workflows/external-checks.yml +++ b/.github/workflows/external-checks.yml @@ -23,9 +23,10 @@ jobs: fetch-depth: 0 - name: Check if codeowners file changed run: | - MERGE_BASE=$(git merge-base ${{github.event.pull_request.base.sha}} $GITHUB_SHA) - if ! git diff --exit-code --name-only $MERGE_BASE..$GITHUB_SHA .github/CODEOWNERS; then - echo "CHECK=true" >> $GITHUB_ENV + set -euo pipefail + MERGE_BASE="$(git merge-base "${{github.event.pull_request.base.sha}}" "$GITHUB_SHA")" + if ! git diff --exit-code --name-only "$MERGE_BASE".."$GITHUB_SHA" .github/CODEOWNERS; then + echo "CHECK=true" >> "$GITHUB_ENV" if [[ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]]; then echo "Changes to codeowners are not allowed on forks!" exit 1 @@ -34,7 +35,8 @@ jobs: - name: Check indentation if: env.CHECK == 'true' run: | - no_different_indentations=$(cat .github/CODEOWNERS | grep -v '^#' | awk NF | sed s/@.*//g | awk '{ print length }' | sort | uniq | wc -l) + set -euo pipefail + no_different_indentations="$(cat .github/CODEOWNERS | grep -v '^#' | awk NF | sed s/@.*//g | awk '{ print length }' | sort | uniq | wc -l)" if [[ "$no_different_indentations" != "1" ]]; then echo "CODEOWNERS has $no_different_indentations different indentations." echo "Make sure that all teams start in the same column." @@ -44,7 +46,8 @@ jobs: - name: Codeowners validation if: env.CHECK == 'true' run: | - curl -sfL https://raw.githubusercontent.com/mszostok/codeowners-validator/main/install.sh | sh -s -- -b $HOME v0.7.4 + set -euo pipefail + curl -sfL https://raw.githubusercontent.com/mszostok/codeowners-validator/main/install.sh | sh -s -- -b "$HOME" v0.7.4 REPOSITORY_PATH="." \ GITHUB_ACCESS_TOKEN="$GH_TOKEN" \ EXPERIMENTAL_CHECKS="notowned" \ diff --git a/.github/workflows/pullrequest-close.yml b/.github/workflows/pullrequest-close.yml index 6658211bc80b..5d3e066f3866 100644 --- a/.github/workflows/pullrequest-close.yml +++ b/.github/workflows/pullrequest-close.yml @@ -18,25 +18,28 @@ jobs: steps: - name: Get git branch run: | + set -euo pipefail GIT_BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF/refs\/heads\//}}" - echo "GIT_BRANCH=$GIT_BRANCH" >> $GITHUB_ENV + echo "GIT_BRANCH=$GIT_BRANCH" >> "$GITHUB_ENV" - name: Generate deployment branch name id: git-branch-deploy run: | - GIT_BRANCH_DEPLOY=$GIT_BRANCH + set -euo pipefail + GIT_BRANCH_DEPLOY="$GIT_BRANCH" if [[ ! ("$GIT_BRANCH_DEPLOY" =~ "feature/") ]]; then # If event is pull request but branch is not prefixed with feature/ GIT_BRANCH_DEPLOY=feature/$GIT_BRANCH_DEPLOY fi # Avoid too long resource names - GIT_BRANCH_DEPLOY=${GIT_BRANCH_DEPLOY:0:50} - echo "GIT_BRANCH_DEPLOY=$GIT_BRANCH_DEPLOY" >> $GITHUB_ENV + GIT_BRANCH_DEPLOY="${GIT_BRANCH_DEPLOY:0:50}" + echo "GIT_BRANCH_DEPLOY=$GIT_BRANCH_DEPLOY" >> "$GITHUB_ENV" - name: Clean up feature env: SPINNAKER_WEBHOOK_TOKEN: ${{ secrets.SPINNAKER_WEBHOOK_TOKEN }} SPINNAKER_URL: https://spinnaker-gate.shared.devland.is run: | - curl $SPINNAKER_URL/webhooks/webhook/feature-cleanup -H "content-type: application/json" --data-binary @- < event.json # This is to increase the retention days for our GitHub Actions run events @@ -85,15 +87,16 @@ jobs: enable-cache: 'node_modules,cypress,generated-files' - run: | - echo "HEAD=$GITHUB_SHA" >> $GITHUB_ENV + set -euo pipefail + echo "HEAD=$GITHUB_SHA" >> "$GITHUB_ENV" export HEAD_REF="$GITHUB_HEAD_REF" export BASE_REF="$GITHUB_BASE_REF" - export PR_REF=$GITHUB_SHA + export PR_REF="$GITHUB_SHA" export SHELL=/usr/bin/bash export WORKFLOW_ID=pullrequest - source ./scripts/ci/00_prepare-base-tags.sh $(git merge-base HEAD $GITHUB_BASE_REF) - git checkout $GITHUB_SHA - echo "BASE=$BASE" >> $GITHUB_ENV + source ./scripts/ci/00_prepare-base-tags.sh "$(git merge-base HEAD "$GITHUB_BASE_REF")" + git checkout "$GITHUB_SHA" + echo "BASE=$BASE" >> "$GITHUB_ENV" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HTML_URL: ${{ github.event.pull_request.html_url }} @@ -110,28 +113,30 @@ jobs: - name: Set magic env if test-everything label is set if: ${{ contains(github.event.pull_request.labels.*.name, 'test everything') && steps.check-permission.outputs['user-permission'] == 'admin' }} run: | - echo "AFFECTED_ALL=7913-$GITHUB_HEAD_REF" >> $GITHUB_ENV + echo "AFFECTED_ALL=7913-$GITHUB_HEAD_REF" >> "$GITHUB_ENV" - name: Warn if user does not have the required permissions if: ${{ contains(github.event.pull_request.labels.*.name, 'test everything') && steps.check-permission.outputs['user-permission'] != 'admin' }} run: | echo "## WARN permissions" >> "$GITHUB_STEP_SUMMARY" - echo "User $GITHUB_ACTOR does not have the required permissions to apply the 'test everything' label" >> "$GITHUB_STEP_SUMMARY" + echo "User '$GITHUB_ACTOR' does not have the required permissions to apply the 'test everything' label" >> "$GITHUB_STEP_SUMMARY" - name: Prepare lint targets id: lint_projects run: | - CHUNKS=$(./scripts/ci/generate-chunks.sh lint) - if [[ $CHUNKS != "[]" ]]; then - echo "CHUNKS={\"projects\":$CHUNKS}" >> $GITHUB_OUTPUT + set -euo pipefail + CHUNKS="$(./scripts/ci/generate-chunks.sh lint)" + if [[ "$CHUNKS" != "[]" ]]; then + echo "CHUNKS={\"projects\":$CHUNKS}" >> "$GITHUB_OUTPUT" fi - name: Prepare test targets id: test_projects run: | - CHUNKS=$(./scripts/ci/generate-chunks.sh test) - if [[ $CHUNKS != "[]" ]]; then - echo "CHUNKS={\"projects\":$CHUNKS}" >> $GITHUB_OUTPUT + set -euo pipefail + CHUNKS="$(./scripts/ci/generate-chunks.sh test)" + if [[ "$CHUNKS" != "[]" ]]; then + echo "CHUNKS={\"projects\":$CHUNKS}" >> "$GITHUB_OUTPUT" fi - name: Prepare e2e targets @@ -139,20 +144,22 @@ jobs: env: CHUNK_SIZE: 1 run: | - CHUNKS=$(./scripts/ci/generate-chunks.sh e2e-ci) - if [[ $CHUNKS != "[]" ]]; then - echo "CHUNKS={\"projects\":$CHUNKS}" >> $GITHUB_OUTPUT + set -euo pipefail + CHUNKS="$(./scripts/ci/generate-chunks.sh e2e-ci)" + if [[ "$CHUNKS" != "[]" ]]; then + echo "CHUNKS={\"projects\":$CHUNKS}" >> "$GITHUB_OUTPUT" fi - echo "BUILD_ID=$GITHUB_RUN_ID-$GITHUB_RUN_NUMBER-$(uuidgen)" >> $GITHUB_OUTPUT + echo BUILD_ID="$GITHUB_RUN_ID-$GITHUB_RUN_NUMBER-$(uuidgen)" >> "$GITHUB_OUTPUT" - name: Prepare build targets id: build_projects env: CHUNK_SIZE: 4 run: | - CHUNKS=$(./scripts/ci/generate-chunks.sh build) - if [[ $CHUNKS != "[]" ]]; then - echo "CHUNKS={\"projects\":$CHUNKS}" >> $GITHUB_OUTPUT + set -euo pipefail + CHUNKS="$(./scripts/ci/generate-chunks.sh build)" + if [[ "$CHUNKS" != "[]" ]]; then + echo "CHUNKS={\"projects\":$CHUNKS}" >> "$GITHUB_OUTPUT" fi - name: Check release-manager approval id: check-release-manager-approval @@ -160,6 +167,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + set -euo pipefail node -r esbuild-register .github/actions/check-team-approval.ts release-managers tests: needs: @@ -240,7 +248,7 @@ jobs: enable-cache: 'node_modules,cypress,generated-files' - name: Running e2e tests - run: ./scripts/ci/40_e2e.sh ${AFFECTED_PROJECT} + run: ./scripts/ci/40_e2e.sh "${AFFECTED_PROJECT}" linting-workspace: needs: @@ -391,7 +399,7 @@ jobs: runs-on: ec2-runners container: image: public.ecr.aws/m3u4c4h9/island-is/actions-runner-public:latest - if: always() + if: ${{ !cancelled() }} needs: - prepare - linting-workspace diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 9806a1baa180..e9f872ed91f8 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -58,53 +58,55 @@ jobs: - name: Get git branch id: git-branch run: | + set -euo pipefail GIT_BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF/refs\/heads\//}}" - echo "GIT_BRANCH=${GIT_BRANCH}" >> $GITHUB_OUTPUT - echo "GIT_BRANCH=$GIT_BRANCH" >> $GITHUB_ENV + echo GIT_BRANCH="${GIT_BRANCH}" >> "$GITHUB_OUTPUT" + echo GIT_BRANCH="$GIT_BRANCH" >> "$GITHUB_ENV" - name: Generate deployment branch name id: git-branch-deploy run: | - export GIT_BRANCH_DEPLOY=$GIT_BRANCH + set -euo pipefail + export GIT_BRANCH_DEPLOY="$GIT_BRANCH" if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then if [[ ! ("$GIT_BRANCH_DEPLOY" =~ "feature/") ]]; then # If event is pull request but branch is not prefixed with feature/ - GIT_BRANCH_DEPLOY=feature/$GIT_BRANCH_DEPLOY + GIT_BRANCH_DEPLOY="feature/$GIT_BRANCH_DEPLOY" fi # Avoid too long resource names - GIT_BRANCH_DEPLOY=${GIT_BRANCH_DEPLOY:0:50} + GIT_BRANCH_DEPLOY="${GIT_BRANCH_DEPLOY:0:50}" fi - echo "GIT_BRANCH_DEPLOY=${GIT_BRANCH_DEPLOY}" >> $GITHUB_OUTPUT - echo "GIT_BRANCH_DEPLOY=$GIT_BRANCH_DEPLOY" >> $GITHUB_ENV - echo "FEATURE_NAME=$(echo $GIT_BRANCH_DEPLOY | cut -d"/" -f2- | tr -cd '[:alnum:]-' | tr '[:upper:]' '[:lower:]' | cut -c1-50)" >> $GITHUB_OUTPUT + echo GIT_BRANCH_DEPLOY="${GIT_BRANCH_DEPLOY}" >> "$GITHUB_OUTPUT" + echo GIT_BRANCH_DEPLOY="$GIT_BRANCH_DEPLOY" >> "$GITHUB_ENV" + echo FEATURE_NAME="$(echo "$GIT_BRANCH_DEPLOY" | cut -d"/" -f2- | tr -cd '[:alnum:]-' | tr '[:upper:]' '[:lower:]' | cut -c1-50)" >> "$GITHUB_OUTPUT" - name: Check if we want to run workflow id: should-run env: SPINNAKER_WEBHOOK_TOKEN: ${{ secrets.SPINNAKER_WEBHOOK_TOKEN }} run: | echo "GITHUB_EVENT_NAME is '$GITHUB_EVENT_NAME'" - echo "PRE_RELEASE=false" >> $GITHUB_OUTPUT + echo "PRE_RELEASE=false" >> "$GITHUB_OUTPUT" if [[ "$GITHUB_EVENT_NAME" == "create" ]]; then echo "Workflow was created because of branch creation. Branch name is '$GIT_BRANCH'" - for pre_pattern in $(echo $PRE_RELEASE_PATTERN | sed "s/,/ /g") + for pre_pattern in $(echo "$PRE_RELEASE_PATTERN" | sed "s/,/ /g") do echo "Checking branch against pre_release_pattern '$pre_pattern'" echo "Check if this is a pre-release and if it should generate a feature-deploy" if [[ "$GIT_BRANCH" =~ $pre_pattern ]]; then echo "'$GIT_BRANCH' matches 'pre_$pattern', continuing with build" - echo "PRE_CHECK=feature-deploy" >> $GITHUB_OUTPUT - echo "PRE_RELEASE=true" >> $GITHUB_OUTPUT + echo "PRE_CHECK=feature-deploy" >> "$GITHUB_OUTPUT" + echo "PRE_RELEASE=true" >> "$GITHUB_OUTPUT" exit 0 fi done - for pattern in $(echo $CREATE_PATTERNS | sed "s/,/ /g") + for pattern in $(echo "$CREATE_PATTERNS" | sed "s/,/ /g") do echo "Checking branch against pattern '$pattern'" if [[ "$GIT_BRANCH" =~ $pattern ]]; then echo "'$GIT_BRANCH' matches '$pattern', continuing with build" - echo "PRE_CHECK=push" >> $GITHUB_OUTPUT + echo "PRE_CHECK=push" >> "$GITHUB_OUTPUT" exit 0 fi done @@ -114,31 +116,31 @@ jobs: if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then if [[ "${{ github.event.action }}" == "labeled" ]]; then echo "Action is labeled, using label that was applied: '${{ github.event.label.name }}'" - deployFeature=$([[ "${{ github.event.label.name }}" == "deploy-feature" ]] && echo true || echo false ) + deployFeature="$([[ "${{ github.event.label.name }}" == "deploy-feature" ]] && echo true || echo false )" else echo "Action is ${{ github.event.action }}, using labels on PR" deployFeature=${{ contains(github.event.pull_request.labels.*.name, 'deploy-feature') }} fi if [[ "$deployFeature" == "true" ]]; then echo "Pull request contains deploy-feature label, continuing with feature deployment" - echo "PRE_CHECK=feature-deploy" >> $GITHUB_OUTPUT + echo "PRE_CHECK=feature-deploy" >> "$GITHUB_OUTPUT" exit 0 fi echo "Pull request does not have deploy-feature label, exiting..." exit 0 fi - for pre_pattern in $(echo $PRE_RELEASE_PATTERN | sed "s/,/ /g") + for pre_pattern in $(echo "$PRE_RELEASE_PATTERN" | sed "s/,/ /g") do echo "Checking branch against pre_release_pattern '$pre_pattern'" echo "Check if this is a pre-release and if it should generate a feature-deploy" if [[ "$GIT_BRANCH" =~ $pre_pattern ]]; then echo "'$GIT_BRANCH' matches 'pre_$pattern', continuing with build" - echo "PRE_CHECK=feature-deploy" >> $GITHUB_OUTPUT - echo "PRE_RELEASE=true" >> $GITHUB_OUTPUT + echo "PRE_CHECK=feature-deploy" >> "$GITHUB_OUTPUT" + echo "PRE_RELEASE=true" >> "$GITHUB_OUTPUT" exit 0 fi done - echo "PRE_CHECK=push" >> $GITHUB_OUTPUT + echo "PRE_CHECK=push" >> "$GITHUB_OUTPUT" prepare: runs-on: ec2-runners @@ -178,32 +180,34 @@ jobs: - name: Check node version run: | + set -euo pipefail node -v ls -l `which node` - name: Git refs id: git_refs run: | + set -euo pipefail if [[ -n "$GITHUB_BASE_REF" ]] then # a PR - GIT_BASE_BRANCH=$GITHUB_BASE_REF + GIT_BASE_BRANCH="$GITHUB_BASE_REF" else # on main GIT_BASE_BRANCH=main fi - echo "GIT_BRANCH=$GIT_BRANCH" >> $GITHUB_ENV - echo "GIT_BASE_BRANCH=$GIT_BASE_BRANCH" >> $GITHUB_ENV + echo GIT_BRANCH="$GIT_BRANCH" >> "$GITHUB_ENV" + echo GIT_BASE_BRANCH="$GIT_BASE_BRANCH" >> "$GITHUB_ENV" echo "Base branch is '${GIT_BASE_BRANCH}'" echo "Branch is '${GIT_BRANCH}'" - name: Checking out relevant branches for a pre-release if: needs.pre-checks.outputs.PRE_CHECK && needs.pre-checks.outputs.PRE_RELEASE == 'true' run: | - echo "Feature Name: $FEATURE_NAME" + echo "Feature Name: '$FEATURE_NAME'" git checkout main - git checkout $GITHUB_SHA + git checkout "$GITHUB_SHA" git config --global user.email "ci@island.is" git config --global user.name "CI Bot" @@ -211,22 +215,24 @@ jobs: - name: Checking out relevant branches for a PR if: needs.pre-checks.outputs.PRE_CHECK && needs.pre-checks.outputs.PRE_CHECK == 'feature-deploy' && !(needs.pre-checks.outputs.PRE_RELEASE == 'true') run: | - git checkout $GITHUB_HEAD_REF - git checkout $GITHUB_BASE_REF - git checkout $GITHUB_SHA + set -euo pipefail + git checkout "$GITHUB_HEAD_REF" + git checkout "$GITHUB_BASE_REF" + git checkout "$GITHUB_SHA" git config --global user.email "ci@island.is" git config --global user.name "CI Bot" - BASE_SHA=$(git merge-base HEAD $GITHUB_BASE_REF) - HEAD_SHA=$(git merge-base HEAD $GITHUB_HEAD_REF) - echo Current base SHA is $BASE_SHA and head SHA is $HEAD_SHA + BASE_SHA="$(git merge-base HEAD "$GITHUB_BASE_REF")" + HEAD_SHA="$(git merge-base HEAD "$GITHUB_HEAD_REF")" + echo "Current base SHA is '$BASE_SHA' and head SHA is '$HEAD_SHA'" echo "{\"base_sha\": \"$BASE_SHA\", \"head_sha\":\"$HEAD_SHA\"}" > event.json - name: Checking out relevant branches for a branch build if: ${{ !(needs.pre-checks.outputs.PRE_CHECK && needs.pre-checks.outputs.PRE_CHECK == 'feature-deploy') }} run: | + set -euo pipefail git checkout main - git checkout $GITHUB_SHA + git checkout "$GITHUB_SHA" # This is to increase the retention days for our GitHub Actions run events # See this for more information: @@ -243,27 +249,29 @@ jobs: id: nodejs_image continue-on-error: false run: | + set -euo pipefail export NODE_IMAGE_TAG="$(./scripts/ci/get-node-version.mjs)" - echo "NODE_IMAGE_TAG: ${NODE_IMAGE_TAG}" - echo "NODE_IMAGE_TAG=${NODE_IMAGE_TAG}" >> $GITHUB_OUTPUT - echo "NODE_IMAGE_TAG=${NODE_IMAGE_TAG}" >> $GITHUB_ENV - echo "**NODE_IMAGE_TAG** ${NODE_IMAGE_TAG}" >> $GITHUB_STEP_SUMMARY + echo "NODE_IMAGE_TAG: '${NODE_IMAGE_TAG}'" + echo NODE_IMAGE_TAG="${NODE_IMAGE_TAG}" >> "$GITHUB_OUTPUT" + echo NODE_IMAGE_TAG="${NODE_IMAGE_TAG}" >> "$GITHUB_ENV" + echo "**NODE_IMAGE_TAG** '${NODE_IMAGE_TAG}'" >> "$GITHUB_STEP_SUMMARY" - name: Generate docker image tag id: docker_tags run: | - export DOCKER_BRANCH_TAG=$(echo ${GIT_BRANCH:0:45} | tr "/." "-" ) + set -euo pipefail + export DOCKER_BRANCH_TAG="$(echo "${GIT_BRANCH:0:45}" | tr "/." "-" )" SHA="${{ github.event.pull_request.head.sha }}" echo "SHA='$SHA' retrieved from event" if [[ "$SHA" == "" ]]; then - SHA=$GITHUB_SHA + SHA="$GITHUB_SHA" echo "SHA='$SHA', retrived from action environment" fi echo "Using SHA='$SHA' as docker tag sha" - export DOCKER_TAG=${DOCKER_BRANCH_TAG}_${SHA:0:10}_${GITHUB_RUN_NUMBER} - echo "Docker tag will be ${DOCKER_TAG}" - echo "DOCKER_TAG=${DOCKER_TAG}" >> $GITHUB_OUTPUT - echo "DOCKER_TAG=$DOCKER_TAG" >> $GITHUB_ENV - echo "**Monorepo tag:** ${DOCKER_TAG}" >> $GITHUB_STEP_SUMMARY + export DOCKER_TAG="${DOCKER_BRANCH_TAG}_${SHA:0:10}_${GITHUB_RUN_NUMBER}" + echo "Docker tag will be '${DOCKER_TAG}'" + echo DOCKER_TAG="${DOCKER_TAG}" >> "$GITHUB_OUTPUT" + echo DOCKER_TAG="$DOCKER_TAG" >> "$GITHUB_ENV" + echo "**Monorepo tag:** '${DOCKER_TAG}'" >> "$GITHUB_STEP_SUMMARY" - name: Send Slack notification id: slack @@ -283,7 +291,7 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_RELEASE_WEBHOOK_URL }} # required - run: | - echo "HEAD=$GITHUB_SHA" >> $GITHUB_ENV + echo HEAD="$GITHUB_SHA" >> "$GITHUB_ENV" id: git_nx_head name: Preparing HEAD tag @@ -296,11 +304,11 @@ jobs: - name: Set Test Everything true run: | - echo "TEST_EVERYTHING=true" >> $GITHUB_ENV + echo "TEST_EVERYTHING=true" >> "$GITHUB_ENV" if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'test everything') - name: Set Test Everything false - run: echo "TEST_EVERYTHING=false" >> $GITHUB_ENV + run: echo "TEST_EVERYTHING=false" >> "$GITHUB_ENV" if: github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'test everything') - name: Preparing BASE tags @@ -310,10 +318,11 @@ jobs: HTML_URL: ${{ github.event.pull_request.html_url }} ISSUE_REPORTING_SLACK_WEBHOOK_URL: ${{ secrets.SLACK_BUILD_ISSUES_REPORTING_WEBHOOK_URL }} run: | + set -euo pipefail if [[ "${{needs.pre-checks.outputs.PRE_CHECK}}" == 'feature-deploy' && "${{needs.pre-checks.outputs.PRE_RELEASE}}" == 'false' ]]; then export HEAD_REF="$GITHUB_HEAD_REF" export BASE_REF="$GITHUB_BASE_REF" - export PR_REF=$GITHUB_SHA + export PR_REF="$GITHUB_SHA" else export HEAD_REF="$GIT_BRANCH" export BASE_REF="$GIT_BASE_BRANCH" @@ -321,10 +330,10 @@ jobs: fi export WORKFLOW_ID=push export SHELL=/usr/bin/bash - source ./scripts/ci/00_prepare-base-tags.sh $(git merge-base HEAD $GITHUB_BASE_REF) - git checkout $GITHUB_SHA - echo "BASE=$BASE" >> $GITHUB_ENV - echo "LAST_GOOD_BUILD_DOCKER_TAG=${LAST_GOOD_BUILD_DOCKER_TAG}" >> $GITHUB_OUTPUT + source ./scripts/ci/00_prepare-base-tags.sh $(git merge-base HEAD "$GITHUB_BASE_REF") + git checkout "$GITHUB_SHA" + echo BASE="$BASE" >> "$GITHUB_ENV" + echo LAST_GOOD_BUILD_DOCKER_TAG="${LAST_GOOD_BUILD_DOCKER_TAG}" >> "$GITHUB_OUTPUT" - name: Docker login to ECR repo run: ./scripts/ci/docker-login-ecr.sh env: @@ -369,14 +378,15 @@ jobs: run: '[[ "${{ steps.cache-deps-base.outputs.success }}" != "false" ]] || exit 1' - name: set BRANCH env var - run: echo "BRANCH=$GIT_BRANCH" >> $GITHUB_ENV + run: echo BRANCH="$GIT_BRANCH" >> "$GITHUB_ENV" - name: Prepare test targets id: test_projects run: | - CHUNKS=$(./scripts/ci/generate-chunks.sh test) - if [[ $CHUNKS != "[]" ]]; then - echo "CHUNKS={\"projects\":$CHUNKS}" >> $GITHUB_OUTPUT + set -euo pipefail + CHUNKS="$(./scripts/ci/generate-chunks.sh test)" + if [[ "$CHUNKS" != "[]" ]]; then + echo CHUNKS="{\"projects\":$CHUNKS}" >> "$GITHUB_OUTPUT" fi env: SKIP_TESTS_ON_BRANCH: ${{ secrets.SKIP_TESTS_ON_BRANCH }} @@ -384,32 +394,35 @@ jobs: - name: Prepare docker build targets id: build_map run: | - CHUNKS=$(./scripts/ci/generate-build-chunks.sh docker-express docker-next docker-static docker-playwright docker-jest) + set -euo pipefail + CHUNKS="$(./scripts/ci/generate-build-chunks.sh docker-express docker-next docker-static docker-playwright docker-jest)" echo "CHUNKS: '$CHUNKS'" - if [[ $CHUNKS != "[]" ]]; then - echo "BUILD_CHUNKS=$CHUNKS" >> $GITHUB_OUTPUT + if [[ "$CHUNKS" != "[]" ]]; then + echo BUILD_CHUNKS="$CHUNKS" >> "$GITHUB_OUTPUT" fi - name: Gather unaffected docker images id: unaffected run: | - UNAFFECTED=$(./scripts/ci/list-unaffected.sh docker-next docker-express docker-static docker-playwright docker-jest) - echo "UNAFFECTED=$UNAFFECTED" >> $GITHUB_OUTPUT + set -euo pipefail + UNAFFECTED="$(./scripts/ci/list-unaffected.sh docker-next docker-express docker-static docker-playwright docker-jest)" + echo UNAFFECTED="$UNAFFECTED" >> "$GITHUB_OUTPUT" - name: check pre-release if: needs.pre-checks.outputs.PRE_RELEASE == 'true' run: | - echo "AFFECTED_ALL=7913-$GIT_BRANCH" >> $GITHUB_ENV + echo "AFFECTED_ALL=7913-$GIT_BRANCH" >> "$GITHUB_ENV" - name: Prepare deployment targets id: deploy_map if: needs.pre-checks.outputs.PRE_CHECK != 'push' run: | - export BASE=$(git merge-base HEAD $GITHUB_BASE_REF) - CHUNKS=$(./scripts/ci/generate-build-chunks.sh docker-express docker-next docker-static docker-playwright docker-jest) + set -euo pipefail + export BASE="$(git merge-base HEAD "$GITHUB_BASE_REF")" + CHUNKS="$(./scripts/ci/generate-build-chunks.sh docker-express docker-next docker-static docker-playwright docker-jest)" echo "CHUNKS: '$CHUNKS'" - if [[ $CHUNKS != "[]" ]]; then - echo "IMAGES=$(echo $CHUNKS | jq '.[] | fromjson | .projects' -r | tr '\n' ',')" >> $GITHUB_OUTPUT + if [[ "$CHUNKS" != "[]" ]]; then + echo "IMAGES="$(echo "$CHUNKS" | jq '.[] | fromjson | .projects' -r | tr '\n' ',')"" >> "$GITHUB_OUTPUT" fi tests: @@ -477,10 +490,11 @@ jobs: - name: Gather apps id: gather run: | - AFFECTED_PROJECTS=$(echo '${{ matrix.chunk }}' | jq -r '.projects') - DOCKER_TYPE=$(echo '${{ matrix.chunk }}' | jq -r '.docker_type') - echo "AFFECTED_PROJECTS=$AFFECTED_PROJECTS" >> $GITHUB_ENV - echo "DOCKER_TYPE=$DOCKER_TYPE" >> $GITHUB_ENV + set -euo pipefail + AFFECTED_PROJECTS="$(echo '${{ matrix.chunk }}' | jq -r '.projects')" + DOCKER_TYPE="$(echo '${{ matrix.chunk }}' | jq -r '.docker_type')" + echo AFFECTED_PROJECTS="$AFFECTED_PROJECTS" >> "$GITHUB_ENV" + echo DOCKER_TYPE="$DOCKER_TYPE" >> "$GITHUB_ENV" continue-on-error: true - uses: actions/checkout@v3 if: steps.gather.outcome == 'success' @@ -548,9 +562,9 @@ jobs: SHA: ${{ github.sha }} DOCKER_BASE_IMAGE_REGISTRY: ${{ env.DOCKER_BASE_IMAGE_REGISTRY }} run: | - echo Node image tag is: $NODE_IMAGE_TAG + echo "Node image tag is: '$NODE_IMAGE_TAG'" export EXTRA_DOCKER_BUILD_ARGS="--build-arg DOCKER_IMAGE_REGISTRY=$DOCKER_BASE_IMAGE_REGISTRY --build-arg GIT_SHA=$SHA --build-arg NODE_IMAGE_TAG=$NODE_IMAGE_TAG" - ./scripts/ci/run-in-parallel.sh 90_$DOCKER_TYPE + ./scripts/ci/run-in-parallel.sh "90_$DOCKER_TYPE" - name: Building Docker images Retry if: steps.gather.outcome == 'success' && steps.dockerbuild.outcome == 'failure' @@ -559,9 +573,9 @@ jobs: SHA: ${{ github.sha }} DOCKER_BASE_IMAGE_REGISTRY: ${{ env.DOCKER_BASE_IMAGE_REGISTRY }} run: | - echo Node image tag is: $NODE_IMAGE_TAG + echo "Node image tag is: '$NODE_IMAGE_TAG'" export EXTRA_DOCKER_BUILD_ARGS="--build-arg DOCKER_IMAGE_REGISTRY=$DOCKER_BASE_IMAGE_REGISTRY --build-arg GIT_SHA=$SHA --build-arg NODE_IMAGE_TAG=$NODE_IMAGE_TAG" - ./scripts/ci/run-in-parallel.sh 90_$DOCKER_TYPE + ./scripts/ci/run-in-parallel.sh "90_$DOCKER_TYPE" helm-docker-build: needs: @@ -599,11 +613,11 @@ jobs: echo Registry is: ${{env.DOCKER_BASE_IMAGE_REGISTRY}} echo Image tag is: ${{env.NODE_IMAGE_TAG}} export EXTRA_DOCKER_BUILD_ARGS="--build-arg DOCKER_IMAGE_REGISTRY=${{env.DOCKER_BASE_IMAGE_REGISTRY}} --build-arg NODE_IMAGE_TAG=${{env.NODE_IMAGE_TAG}}" - ./scripts/build-docker-container.sh $DOCKER_TAG - echo "COMMENT<> $GITHUB_ENV - echo "Affected services are: ${{needs.prepare.outputs.IMAGES}}" >> $GITHUB_ENV - docker run --rm ${DOCKER_REGISTRY}helm-config:$DOCKER_TAG ingress-comment --images=${{needs.prepare.outputs.IMAGES}} --chart=islandis --feature=$FEATURE_NAME >> $GITHUB_ENV - echo 'EOF' >> $GITHUB_ENV + ./scripts/build-docker-container.sh "$DOCKER_TAG" + echo "COMMENT<> "$GITHUB_ENV" + echo "Affected services are: ${{needs.prepare.outputs.IMAGES}}" >> "$GITHUB_ENV" + docker run --rm "${DOCKER_REGISTRY}helm-config:$DOCKER_TAG" ingress-comment --images=${{needs.prepare.outputs.IMAGES}} --chart=islandis --feature="$FEATURE_NAME" >> "$GITHUB_ENV" + echo 'EOF' >> "$GITHUB_ENV" env: PUBLISH: 'true' @@ -619,7 +633,7 @@ jobs: if: needs.pre-checks.outputs.PRE_CHECK == 'feature-deploy' && !(needs.pre-checks.outputs.PRE_RELEASE == 'true') uses: actions/github-script@v7 with: - github-token: ${{secrets.GITHUB_TOKEN}} + github-token: ${{ secrets.GITHUB_TOKEN }} script: | const updateComment = require('./.github/actions/update-comment.js') const { COMMENT } = process.env @@ -630,7 +644,7 @@ jobs: container: image: public.ecr.aws/m3u4c4h9/island-is/actions-runner-public:latest timeout-minutes: 5 - if: always() && needs.pre-checks.result == 'success' && needs.prepare.result != 'skipped' + if: ${{ !cancelled() && needs.pre-checks.result == 'success' && needs.prepare.result != 'skipped' }} needs: - pre-checks - docker-build @@ -660,7 +674,7 @@ jobs: runs-on: ec2-runners container: image: public.ecr.aws/m3u4c4h9/island-is/actions-runner-public:latest - if: always() && needs.retag-unaffected.result == 'success' && needs.helm-docker-build.result != 'failure' + if: ${{ !cancelled() && needs.retag-unaffected.result == 'success' && needs.helm-docker-build.result != 'failure' }} needs: - retag-unaffected - pre-checks @@ -678,7 +692,7 @@ jobs: SPINNAKER_WEBHOOK_TOKEN: ${{ secrets.SPINNAKER_WEBHOOK_TOKEN }} run: | echo "Sending webhook with branch: '$GIT_BRANCH_DEPLOY'" - curl $SPINNAKER_URL/webhooks/webhook/islandis -H "content-type: application/json" --data-binary @- < (Contact devops if you need access) Copy env variables as instructed [here](https://docs.devland.is/technical-overview/devops/dockerizing#troubleshooting) (image arrows 1,2,3) Paste env variables into terminal Run `./scripts/run-es-proxy.sh` from island.is root diff --git a/apps/air-discount-scheme/backend/project.json b/apps/air-discount-scheme/backend/project.json index 2fd07b9a13b5..db1c4b81a0e3 100644 --- a/apps/air-discount-scheme/backend/project.json +++ b/apps/air-discount-scheme/backend/project.json @@ -64,7 +64,7 @@ "dev-services": { "executor": "nx:run-commands", "options": { - "command": "docker-compose up -d", + "command": "docker compose up -d", "cwd": "apps/air-discount-scheme/backend" } }, diff --git a/apps/api/project.json b/apps/api/project.json index 2cf07ffbb76b..78cea5e5d878 100644 --- a/apps/api/project.json +++ b/apps/api/project.json @@ -74,7 +74,7 @@ "dev-services": { "executor": "nx:run-commands", "options": { - "command": "docker-compose up -d", + "command": "docker compose up -d", "cwd": "apps/api" } }, diff --git a/apps/application-system/api/project.json b/apps/application-system/api/project.json index 54ad13f86c6b..1e165146937e 100644 --- a/apps/application-system/api/project.json +++ b/apps/application-system/api/project.json @@ -124,7 +124,7 @@ "dev-services": { "executor": "nx:run-commands", "options": { - "command": "docker-compose up -d", + "command": "docker compose up -d", "cwd": "apps/application-system/api" } }, diff --git a/apps/financial-aid/backend/project.json b/apps/financial-aid/backend/project.json index 651a99f50265..d61db847acc7 100644 --- a/apps/financial-aid/backend/project.json +++ b/apps/financial-aid/backend/project.json @@ -61,7 +61,7 @@ "dev-services": { "executor": "nx:run-commands", "options": { - "command": "docker-compose up -d", + "command": "docker compose up -d", "cwd": "apps/financial-aid/backend" } }, diff --git a/apps/financial-aid/backend/src/app/guards/apiKey.guard.ts b/apps/financial-aid/backend/src/app/guards/apiKey.guard.ts index 0e07121eb27b..b42f8db7c5bd 100644 --- a/apps/financial-aid/backend/src/app/guards/apiKey.guard.ts +++ b/apps/financial-aid/backend/src/app/guards/apiKey.guard.ts @@ -1,4 +1,9 @@ -import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common' +import { + Injectable, + CanActivate, + ExecutionContext, + BadRequestException, +} from '@nestjs/common' import { ApiUserService } from '../modules/municipalityApiUsers/user.service' @Injectable() @@ -6,9 +11,23 @@ export class ApiKeyGuard implements CanActivate { constructor(private userService: ApiUserService) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest() + + const apiKey = request.headers['api-key'] + const municipalityCode = request.headers['municipality-code'] + + if (!apiKey && !municipalityCode) { + throw new BadRequestException('API-Key and Municipality-Code are missing') + } + if (!apiKey) { + throw new BadRequestException('API-Key is missing') + } + if (!municipalityCode) { + throw new BadRequestException('Municipality-Code is missing') + } + const user = await this.userService.findByMunicipalityCodeAndApiKey( - request.headers['api-key'], - request.headers['municipality-code'], + apiKey, + municipalityCode, ) if (!user) { diff --git a/apps/financial-aid/backend/src/app/modules/municipalityApiUsers/user.service.ts b/apps/financial-aid/backend/src/app/modules/municipalityApiUsers/user.service.ts index e337652138cc..34d8f24b4c37 100644 --- a/apps/financial-aid/backend/src/app/modules/municipalityApiUsers/user.service.ts +++ b/apps/financial-aid/backend/src/app/modules/municipalityApiUsers/user.service.ts @@ -1,4 +1,9 @@ -import { Injectable, NotFoundException } from '@nestjs/common' +import { + BadRequestException, + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common' import { InjectModel } from '@nestjs/sequelize' import CryptoJS from 'crypto-js' import { ApiUserModel } from './models/user.model' @@ -25,9 +30,18 @@ export class ApiUserService { }, }) - return keysWithMunicipalityCode.find( + if (keysWithMunicipalityCode.length === 0) { + throw new BadRequestException('Municipality-Code is invalid') + } + + const findKeysWithMunicipalityCode = keysWithMunicipalityCode.find( (m) => this.decryptApiKey(m).apiKey === apiKey, ) + + if (!findKeysWithMunicipalityCode) { + throw new UnauthorizedException('API-Key is invalid') + } + return findKeysWithMunicipalityCode } async findByMunicipalityCode( diff --git a/apps/financial-aid/open-api/src/app/app.dto.ts b/apps/financial-aid/open-api/src/app/app.dto.ts index edd6176e2aa8..b75c08ef58c2 100644 --- a/apps/financial-aid/open-api/src/app/app.dto.ts +++ b/apps/financial-aid/open-api/src/app/app.dto.ts @@ -7,16 +7,22 @@ import { ApplicationState } from '@island.is/financial-aid/shared/lib' export class FilterApplicationsDto { @IsNotEmpty() @IsString() - @ApiProperty() + @ApiProperty({ + description: 'Format: 2024-02-22 - year-month-date', + }) readonly startDate!: string @IsOptional() @IsString() - @ApiProperty() + @ApiProperty({ + description: 'Format: 2024-02-22 - year-month-date', + }) readonly endDate?: string @IsOptional() @IsString() - @ApiProperty() + @ApiProperty({ + description: 'States are: New, InProgress, DataNeeded, Rejected, Approved', + }) readonly state?: ApplicationState } diff --git a/apps/financial-aid/open-api/src/app/app.service.ts b/apps/financial-aid/open-api/src/app/app.service.ts index f85816afeb7a..6baea80baf15 100644 --- a/apps/financial-aid/open-api/src/app/app.service.ts +++ b/apps/financial-aid/open-api/src/app/app.service.ts @@ -9,6 +9,7 @@ import { LOGGER_PROVIDER } from '@island.is/logging' import appModuleConfig from './app.config' import { FilterApplicationsDto } from './app.dto' +import { isDateValid } from './helpers' import { ApplicationModel } from './models' @Injectable() @@ -33,6 +34,8 @@ export class AppService { `${this.config.backend.url}/api/financial-aid/open-api-applications/getAll`, ) url.searchParams.append('startDate', filters.startDate) + isDateValid(filters.startDate, 'startDate') + url.searchParams.append( 'endDate', filters.endDate ?? @@ -40,6 +43,9 @@ export class AppService { representation: 'date', }), ) + if (filters.endDate) { + isDateValid(filters.endDate, 'endDate') + } if (filters.state) { url.searchParams.append('state', filters.state) } diff --git a/apps/financial-aid/open-api/src/app/helpers.ts b/apps/financial-aid/open-api/src/app/helpers.ts new file mode 100644 index 000000000000..bcf6449cd88a --- /dev/null +++ b/apps/financial-aid/open-api/src/app/helpers.ts @@ -0,0 +1,39 @@ +import { BadRequestException } from '@nestjs/common' + +export const isDateValid = ( + startDate: string, + dateType: 'endDate' | 'startDate', +): boolean => { + // Regular expression to match the YYYY-MM-DD format + const regex = /^\d{4}-\d{2}-\d{2}$/ + if (!regex.test(startDate)) { + throw new BadRequestException( + `${dateType} is not formatted correctly, should be year-month-date e.g. 2024-02-22`, + ) + } + + // Parse the input string into a Date object + const date = new Date(startDate) + const isValidDate = date instanceof Date && !Number.isNaN(date.getTime()) + const [year, month, day] = startDate.split('-').map(Number) + const isCorrectDate = + date.getFullYear() === year && + date.getMonth() + 1 === month && + date.getDate() === day + + if (!isValidDate || !isCorrectDate) { + throw new BadRequestException(`${dateType} is not valid`) + } + + // Get the current date without the time portion + const today = new Date() + today.setHours(0, 0, 0, 0) + + // Check that the date is not in the future + const isNotInFuture = date <= today + if (!isNotInFuture) { + throw new BadRequestException(`${dateType} cannot be in the future`) + } + + return true +} diff --git a/apps/icelandic-names-registry/backend/project.json b/apps/icelandic-names-registry/backend/project.json index 2b7b46400384..c1ca0b147931 100644 --- a/apps/icelandic-names-registry/backend/project.json +++ b/apps/icelandic-names-registry/backend/project.json @@ -71,7 +71,7 @@ "dev-services": { "executor": "nx:run-commands", "options": { - "command": "docker-compose up -d", + "command": "docker compose up -d", "cwd": "apps/icelandic-names-registry/backend" } }, diff --git a/apps/judicial-system/api/src/app/app.module.ts b/apps/judicial-system/api/src/app/app.module.ts index f9600735f454..d73082ad8cd3 100644 --- a/apps/judicial-system/api/src/app/app.module.ts +++ b/apps/judicial-system/api/src/app/app.module.ts @@ -15,11 +15,12 @@ import { sharedAuthModuleConfig, } from '@island.is/judicial-system/auth' -import { environment } from '../environments' -import { BackendApi } from './data-sources/backend' import { AuthModule, authModuleConfig, + BackendModule, + backendModuleConfig, + BackendService, CaseListModule, CaseModule, DefendantModule, @@ -36,23 +37,28 @@ import { UserModule, } from './modules' -const debug = !environment.production +const production = process.env.NODE_ENV === 'production' +const debug = !production const playground = debug || process.env.GQL_PLAYGROUND_ENABLED === 'true' -const autoSchemaFile = environment.production +const autoSchemaFile = production ? true : 'apps/judicial-system/api/src/api.graphql' @Module({ imports: [ - GraphQLModule.forRoot({ + GraphQLModule.forRootAsync({ driver: ApolloDriver, - debug, - playground, - autoSchemaFile, - cache: 'bounded', - path: '/api/graphql', - context: ({ req }: never) => ({ req }), - dataSources: () => ({ backendApi: new BackendApi() }), + imports: [BackendModule], + useFactory: (backendService: BackendService) => ({ + debug, + playground, + autoSchemaFile, + cache: 'bounded', + path: '/api/graphql', + context: ({ req }: never) => ({ req }), + dataSources: () => ({ backendService }), + }), + inject: [BackendService], }), SharedAuthModule, AuditTrailModule, @@ -79,6 +85,7 @@ const autoSchemaFile = environment.production featureModuleConfig, authModuleConfig, defenderModuleConfig, + backendModuleConfig, ], }), ], diff --git a/apps/judicial-system/api/src/app/data-sources/index.ts b/apps/judicial-system/api/src/app/data-sources/index.ts deleted file mode 100644 index fb615de699bc..000000000000 --- a/apps/judicial-system/api/src/app/data-sources/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { BackendApi } from './backend' diff --git a/apps/judicial-system/api/src/app/modules/auth/auth.config.ts b/apps/judicial-system/api/src/app/modules/auth/auth.config.ts index 009cb67d773b..7147a0cb576a 100644 --- a/apps/judicial-system/api/src/app/modules/auth/auth.config.ts +++ b/apps/judicial-system/api/src/app/modules/auth/auth.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from '@island.is/nest/config' export const authModuleConfig = defineConfig({ name: 'AuthModule', load: (env) => ({ + production: env.optional('NODE_ENV') === 'production', scope: env.required('AUTH_IDS_SCOPE', 'openid profile'), clientId: env.required( 'AUTH_IDS_CLIENT_ID', diff --git a/apps/judicial-system/api/src/app/modules/auth/auth.controller.ts b/apps/judicial-system/api/src/app/modules/auth/auth.controller.ts index 9cf8b9aaee6c..4ad6f20aef72 100644 --- a/apps/judicial-system/api/src/app/modules/auth/auth.controller.ts +++ b/apps/judicial-system/api/src/app/modules/auth/auth.controller.ts @@ -21,7 +21,7 @@ import { CSRF_COOKIE_NAME, DEFENDER_CASES_ROUTE, EXPIRES_IN_MILLISECONDS, - IDS_ID_TOKEN, + IDS_ID_TOKEN_NAME, PRISON_CASES_ROUTE, USERS_ROUTE, } from '@island.is/judicial-system/consts' @@ -32,51 +32,49 @@ import { UserRole, } from '@island.is/judicial-system/types' -import { environment } from '../../../environments' import { authModuleConfig } from './auth.config' import { AuthService } from './auth.service' import { AuthUser, Cookie } from './auth.types' const REDIRECT_COOKIE_NAME = 'judicial-system.redirect' -const defaultCookieOptions: CookieOptions = { - secure: environment.production, - httpOnly: true, - sameSite: 'lax', -} +@Controller('api/auth') +export class AuthController { + private readonly defaultCookieOptions: CookieOptions = { + httpOnly: true, + sameSite: 'lax', + } -const REDIRECT_COOKIE: Cookie = { - name: REDIRECT_COOKIE_NAME, - options: { - ...defaultCookieOptions, - sameSite: 'none', - }, -} + private readonly redirectCookie: Cookie = { + name: REDIRECT_COOKIE_NAME, + options: { + ...this.defaultCookieOptions, + sameSite: 'none', + }, + } -const ACCESS_TOKEN_COOKIE: Cookie = { - name: ACCESS_TOKEN_COOKIE_NAME, - options: defaultCookieOptions, -} + private readonly accessTokenCookie: Cookie = { + name: ACCESS_TOKEN_COOKIE_NAME, + options: this.defaultCookieOptions, + } -const CODE_VERIFIER_COOKIE: Cookie = { - name: CODE_VERIFIER_COOKIE_NAME, - options: defaultCookieOptions, -} -const CSRF_COOKIE: Cookie = { - name: CSRF_COOKIE_NAME, - options: { - ...defaultCookieOptions, - httpOnly: false, - }, -} + private readonly codeVerifierCookie: Cookie = { + name: CODE_VERIFIER_COOKIE_NAME, + options: this.defaultCookieOptions, + } + private readonly csrfCookie: Cookie = { + name: CSRF_COOKIE_NAME, + options: { + ...this.defaultCookieOptions, + httpOnly: false, + }, + } -const ID_TOKEN: Cookie = { - name: IDS_ID_TOKEN, - options: defaultCookieOptions, -} + private readonly idToken: Cookie = { + name: IDS_ID_TOKEN_NAME, + options: this.defaultCookieOptions, + } -@Controller('api/auth') -export class AuthController { constructor( private readonly auditTrailService: AuditTrailService, private readonly authService: AuthService, @@ -86,7 +84,9 @@ export class AuthController { private readonly logger: Logger, @Inject(authModuleConfig.KEY) private readonly config: ConfigType, - ) {} + ) { + this.defaultCookieOptions.secure = this.config.production + } @Get('login') login( @@ -96,10 +96,13 @@ export class AuthController { ) { this.logger.debug('Received login request') - const { name, options } = REDIRECT_COOKIE + const { name, options } = this.redirectCookie res.clearCookie(name, options) - res.clearCookie(CODE_VERIFIER_COOKIE.name, CODE_VERIFIER_COOKIE.options) + res.clearCookie( + this.codeVerifierCookie.name, + this.codeVerifierCookie.options, + ) // Local development if (this.config.allowAuthBypass && nationalId) { @@ -145,9 +148,9 @@ export class AuthController { res .cookie(name, { redirectRoute }, options) .cookie( - CODE_VERIFIER_COOKIE.name, + this.codeVerifierCookie.name, codeVerifier, - CODE_VERIFIER_COOKIE.options, + this.codeVerifierCookie.options, ) .redirect(loginUrl) } @@ -163,8 +166,11 @@ export class AuthController { this.logger.debug('Received callback request') const { redirectRoute } = req.cookies[REDIRECT_COOKIE_NAME] ?? {} - const codeVerifier = req.cookies[CODE_VERIFIER_COOKIE.name] - res.clearCookie(CODE_VERIFIER_COOKIE.name, CODE_VERIFIER_COOKIE.options) + const codeVerifier = req.cookies[this.codeVerifierCookie.name] + res.clearCookie( + this.codeVerifierCookie.name, + this.codeVerifierCookie.options, + ) try { const idsTokens = await this.authService.fetchIdsToken(code, codeVerifier) @@ -205,12 +211,15 @@ export class AuthController { logout(@Res() res: Response, @Req() req: Request) { this.logger.debug('Received logout request') - const idToken = req.cookies[ID_TOKEN.name] + const idToken = req.cookies[this.idToken.name] - res.clearCookie(ACCESS_TOKEN_COOKIE.name, ACCESS_TOKEN_COOKIE.options) - res.clearCookie(CSRF_COOKIE.name, CSRF_COOKIE.options) - res.clearCookie(CODE_VERIFIER_COOKIE.name, CODE_VERIFIER_COOKIE.options) - res.clearCookie(ID_TOKEN.name, ID_TOKEN.options) + res.clearCookie(this.accessTokenCookie.name, this.accessTokenCookie.options) + res.clearCookie(this.csrfCookie.name, this.csrfCookie.options) + res.clearCookie( + this.codeVerifierCookie.name, + this.codeVerifierCookie.options, + ) + res.clearCookie(this.idToken.name, this.idToken.options) if (idToken) { return res.redirect( @@ -303,19 +312,19 @@ export class AuthController { AuditedAction.LOGIN, res .cookie( - CSRF_COOKIE.name, + this.csrfCookie.name, csrfToken as string, { - ...CSRF_COOKIE.options, + ...this.csrfCookie.options, maxAge: EXPIRES_IN_MILLISECONDS, } as CookieOptions, ) - .cookie(ACCESS_TOKEN_COOKIE.name, jwtToken, { - ...ACCESS_TOKEN_COOKIE.options, + .cookie(this.accessTokenCookie.name, jwtToken, { + ...this.accessTokenCookie.options, maxAge: EXPIRES_IN_MILLISECONDS, }) - .cookie(ID_TOKEN.name, idToken, { - ...ID_TOKEN.options, + .cookie(this.idToken.name, idToken, { + ...this.idToken.options, maxAge: EXPIRES_IN_MILLISECONDS, }) .redirect(redirectRoute), diff --git a/apps/judicial-system/api/src/app/modules/event-log/eventLog.config.ts b/apps/judicial-system/api/src/app/modules/backend/backend.config.ts similarity index 76% rename from apps/judicial-system/api/src/app/modules/event-log/eventLog.config.ts rename to apps/judicial-system/api/src/app/modules/backend/backend.config.ts index b96be6379b54..df8aa63968a3 100644 --- a/apps/judicial-system/api/src/app/modules/event-log/eventLog.config.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from '@island.is/nest/config' -export const eventLogModuleConfig = defineConfig({ - name: 'EventLogModule', +export const backendModuleConfig = defineConfig({ + name: 'BackendModule', load: (env) => ({ backendUrl: env.required('BACKEND_URL', 'http://localhost:3344'), secretToken: env.required( diff --git a/apps/judicial-system/api/src/app/modules/backend/backend.module.ts b/apps/judicial-system/api/src/app/modules/backend/backend.module.ts new file mode 100644 index 000000000000..a9379cc1a180 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/backend/backend.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common' + +import BackendService from './backend.service' + +@Module({ + providers: [BackendService], + exports: [BackendService], +}) +export class BackendModule {} diff --git a/apps/judicial-system/api/src/app/data-sources/backend.ts b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts similarity index 91% rename from apps/judicial-system/api/src/app/data-sources/backend.ts rename to apps/judicial-system/api/src/app/modules/backend/backend.service.ts index 545553ba7c76..95975ec85792 100644 --- a/apps/judicial-system/api/src/app/data-sources/backend.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts @@ -1,8 +1,9 @@ import { DataSource, DataSourceConfig } from 'apollo-datasource' import { Request } from 'express' -import { Injectable } from '@nestjs/common' +import { Inject, Injectable } from '@nestjs/common' +import { type ConfigType } from '@island.is/nest/config' import { ProblemError } from '@island.is/nest/problem' import { @@ -12,16 +13,15 @@ import { UserRole, } from '@island.is/judicial-system/types' -import { environment } from '../../environments' import { Case, RequestSignatureResponse, SendNotificationResponse, SignatureConfirmationResponse, -} from '../modules/case' -import { CaseListEntry } from '../modules/case-list' -import { Defendant, DeleteDefendantResponse } from '../modules/defendant' -import { CreateEventLogInput } from '../modules/event-log' +} from '../case' +import { CaseListEntry } from '../case-list' +import { Defendant, DeleteDefendantResponse } from '../defendant' +import { CreateEventLogInput } from '../event-log' import { CaseFile, DeleteFileResponse, @@ -29,25 +29,33 @@ import { SignedUrl, UpdateFilesResponse, UploadFileToCourtResponse, -} from '../modules/file' +} from '../file' import { CreateIndictmentCountInput, DeleteIndictmentCountInput, DeleteIndictmentCountResponse, IndictmentCount, UpdateIndictmentCountInput, -} from '../modules/indictment-count' -import { Institution } from '../modules/institution' +} from '../indictment-count' +import { Institution } from '../institution' import { PoliceCaseFile, PoliceCaseInfo, UploadPoliceCaseFileResponse, -} from '../modules/police' +} from '../police' +import { backendModuleConfig } from './backend.config' @Injectable() -export class BackendApi extends DataSource<{ req: Request }> { +export class BackendService extends DataSource<{ req: Request }> { private headers!: { [key: string]: string } + constructor( + @Inject(backendModuleConfig.KEY) + private readonly config: ConfigType, + ) { + super() + } + initialize(config: DataSourceConfig<{ req: Request }>): void { this.headers = { 'Content-Type': 'application/json', @@ -61,7 +69,7 @@ export class BackendApi extends DataSource<{ req: Request }> { options: RequestInit, transformer?: (data: unknown) => TResult, ): Promise { - return fetch(`${environment.backend.url}/api/${route}`, options).then( + return fetch(`${this.config.backendUrl}/api/${route}`, options).then( async (res) => { const response = await res.json() @@ -183,6 +191,10 @@ export class BackendApi extends DataSource<{ req: Request }> { return this.get(`case/${id}`, this.caseTransformer) } + getConnectedCases(id: string): Promise { + return this.get(`case/${id}/connectedCases`) + } + createCase(createCase: unknown): Promise { return this.post('case', createCase, this.caseTransformer) } @@ -407,10 +419,10 @@ export class BackendApi extends DataSource<{ req: Request }> { } createEventLog(eventLog: CreateEventLogInput, userRole?: UserRole) { - return fetch(`${environment.backend.url}/api/eventLog/event`, { + return fetch(`${this.config.backendUrl}/api/eventLog/event`, { method: 'POST', headers: { - authorization: `Bearer ${environment.backend.secretToken}`, + authorization: `Bearer ${this.config.secretToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ @@ -421,4 +433,4 @@ export class BackendApi extends DataSource<{ req: Request }> { } } -export default BackendApi +export default BackendService diff --git a/apps/judicial-system/api/src/app/modules/backend/index.ts b/apps/judicial-system/api/src/app/modules/backend/index.ts new file mode 100644 index 000000000000..76ceebd93a55 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/backend/index.ts @@ -0,0 +1 @@ +export { BackendService } from './backend.service' diff --git a/apps/judicial-system/api/src/app/modules/case-list/caseList.resolver.ts b/apps/judicial-system/api/src/app/modules/case-list/caseList.resolver.ts index f280d08218dc..b5e41b2c8d8a 100644 --- a/apps/judicial-system/api/src/app/modules/case-list/caseList.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/case-list/caseList.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CaseListQueryInput } from './dto/caseList.input' import { CaseListInterceptor } from './interceptors/caseList.interceptor' import { CaseListEntry } from './models/caseList.model' @@ -35,14 +35,15 @@ export class CaseListResolver { input: CaseListQueryInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug('Getting all cases') let result = this.auditTrailService.audit( user.id, AuditedAction.GET_CASES, - backendApi.getCases(), + backendService.getCases(), (cases: CaseListEntry[]) => cases.map((aCase) => aCase.id), ) diff --git a/apps/judicial-system/api/src/app/modules/case/case.resolver.ts b/apps/judicial-system/api/src/app/modules/case/case.resolver.ts index 31a8a82e38e0..9f972f19e60a 100644 --- a/apps/judicial-system/api/src/app/modules/case/case.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/case/case.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CaseQueryInput } from './dto/case.input' import { CreateCaseInput } from './dto/createCase.input' import { CreateCourtCaseInput } from './dto/createCourtCase.input' @@ -45,14 +45,34 @@ export class CaseResolver { @Args('input', { type: () => CaseQueryInput }) input: CaseQueryInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Getting case ${input.id}`) return this.auditTrailService.audit( user.id, AuditedAction.GET_CASE, - backendApi.getCase(input.id), + backendService.getCase(input.id), + input.id, + ) + } + + @Query(() => [Case], { nullable: true }) + async connectedCases( + @Args('input', { type: () => CaseQueryInput }) + input: CaseQueryInput, + @CurrentGraphQlUser() + user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + this.logger.debug('Getting connected cases') + + return this.auditTrailService.audit( + user.id, + AuditedAction.GET_CONNECTED_CASES, + backendService.getConnectedCases(input.id), input.id, ) } @@ -63,14 +83,15 @@ export class CaseResolver { @Args('input', { type: () => CreateCaseInput }) input: CreateCaseInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug('Creating a new case') return this.auditTrailService.audit( user.id, AuditedAction.CREATE_CASE, - backendApi.createCase(input), + backendService.createCase(input), (theCase) => theCase.id, ) } @@ -81,7 +102,8 @@ export class CaseResolver { @Args('input', { type: () => UpdateCaseInput }) input: UpdateCaseInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { id, ...updateCase } = input @@ -90,7 +112,7 @@ export class CaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_CASE, - backendApi.updateCase(id, updateCase), + backendService.updateCase(id, updateCase), id, ) } @@ -101,7 +123,8 @@ export class CaseResolver { @Args('input', { type: () => TransitionCaseInput }) input: TransitionCaseInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { id, ...transitionCase } = input @@ -110,7 +133,7 @@ export class CaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.TRANSITION_CASE, - backendApi.transitionCase(id, transitionCase), + backendService.transitionCase(id, transitionCase), id, ) } @@ -120,7 +143,8 @@ export class CaseResolver { @Args('input', { type: () => RequestSignatureInput }) input: RequestSignatureInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug( `Requesting signature of court record for case ${input.caseId}`, @@ -129,7 +153,7 @@ export class CaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.REQUEST_RULING_SIGNATURE, - backendApi.requestCourtRecordSignature(input.caseId), + backendService.requestCourtRecordSignature(input.caseId), input.caseId, ) } @@ -139,7 +163,8 @@ export class CaseResolver { @Args('input', { type: () => SignatureConfirmationQueryInput }) input: SignatureConfirmationQueryInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, documentToken } = input @@ -148,7 +173,7 @@ export class CaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.CONFIRM_RULING_SIGNATURE, - backendApi.getCourtRecordSignatureConfirmation(caseId, documentToken), + backendService.getCourtRecordSignatureConfirmation(caseId, documentToken), caseId, ) } @@ -158,14 +183,15 @@ export class CaseResolver { @Args('input', { type: () => RequestSignatureInput }) input: RequestSignatureInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Requesting signature of ruling for case ${input.caseId}`) return this.auditTrailService.audit( user.id, AuditedAction.REQUEST_RULING_SIGNATURE, - backendApi.requestRulingSignature(input.caseId), + backendService.requestRulingSignature(input.caseId), input.caseId, ) } @@ -175,7 +201,8 @@ export class CaseResolver { @Args('input', { type: () => SignatureConfirmationQueryInput }) input: SignatureConfirmationQueryInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, documentToken } = input @@ -184,7 +211,7 @@ export class CaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.CONFIRM_RULING_SIGNATURE, - backendApi.getRulingSignatureConfirmation(caseId, documentToken), + backendService.getRulingSignatureConfirmation(caseId, documentToken), caseId, ) } @@ -194,7 +221,8 @@ export class CaseResolver { @Args('input', { type: () => SendNotificationInput }) input: SendNotificationInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, ...sendNotification } = input @@ -203,7 +231,7 @@ export class CaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.SEND_NOTIFICATION, - backendApi.sendNotification(caseId, sendNotification), + backendService.sendNotification(caseId, sendNotification), caseId, ) } @@ -214,14 +242,15 @@ export class CaseResolver { @Args('input', { type: () => ExtendCaseInput }) input: ExtendCaseInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Extending case ${input.id}`) return this.auditTrailService.audit( user.id, AuditedAction.EXTEND_CASE, - backendApi.extendCase(input.id), + backendService.extendCase(input.id), (theCase) => theCase.id, ) } @@ -232,14 +261,15 @@ export class CaseResolver { @Args('input', { type: () => CreateCourtCaseInput }) input: CreateCourtCaseInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Creating court case for case ${input.caseId}`) return this.auditTrailService.audit( user.id, AuditedAction.CREATE_COURT_CASE, - backendApi.createCourtCase(input.caseId), + backendService.createCourtCase(input.caseId), input.caseId, ) } diff --git a/apps/judicial-system/api/src/app/modules/case/dto/updateCase.input.ts b/apps/judicial-system/api/src/app/modules/case/dto/updateCase.input.ts index 3778d0bf56ec..65059587bf76 100644 --- a/apps/judicial-system/api/src/app/modules/case/dto/updateCase.input.ts +++ b/apps/judicial-system/api/src/app/modules/case/dto/updateCase.input.ts @@ -501,4 +501,9 @@ export class UpdateCaseInput { @IsOptional() @Field(() => CourtSessionType, { nullable: true }) readonly courtSessionType?: CourtSessionType + + @Allow() + @IsOptional() + @Field(() => ID, { nullable: true }) + readonly mergeCaseId?: string } diff --git a/apps/judicial-system/api/src/app/modules/case/limitedAccessCase.resolver.ts b/apps/judicial-system/api/src/app/modules/case/limitedAccessCase.resolver.ts index e8c307fe3a6a..b4c3b0e3c478 100644 --- a/apps/judicial-system/api/src/app/modules/case/limitedAccessCase.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/case/limitedAccessCase.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CaseQueryInput } from './dto/case.input' import { TransitionCaseInput } from './dto/transitionCase.input' import { UpdateCaseInput } from './dto/updateCase.input' @@ -37,14 +37,15 @@ export class LimitedAccessCaseResolver { @Args('input', { type: () => CaseQueryInput }) input: CaseQueryInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Getting case ${input.id}`) return this.auditTrailService.audit( user.id, AuditedAction.GET_CASE, - backendApi.limitedAccessGetCase(input.id), + backendService.limitedAccessGetCase(input.id), input.id, ) } @@ -55,7 +56,8 @@ export class LimitedAccessCaseResolver { @Args('input', { type: () => UpdateCaseInput }) input: UpdateCaseInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { id, ...updateCase } = input @@ -64,7 +66,7 @@ export class LimitedAccessCaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_CASE, - backendApi.limitedAccessUpdateCase(id, updateCase), + backendService.limitedAccessUpdateCase(id, updateCase), id, ) } @@ -75,7 +77,8 @@ export class LimitedAccessCaseResolver { @Args('input', { type: () => TransitionCaseInput }) input: TransitionCaseInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { id, ...transitionCase } = input @@ -84,7 +87,7 @@ export class LimitedAccessCaseResolver { return this.auditTrailService.audit( user.id, AuditedAction.TRANSITION_CASE, - backendApi.limitedAccessTransitionCase(id, transitionCase), + backendService.limitedAccessTransitionCase(id, transitionCase), id, ) } diff --git a/apps/judicial-system/api/src/app/modules/defendant/defendant.resolver.ts b/apps/judicial-system/api/src/app/modules/defendant/defendant.resolver.ts index acbb47239649..7d65ea3f6375 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/defendant.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/defendant.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CreateDefendantInput } from './dto/createDefendant.input' import { DeleteDefendantInput } from './dto/deleteDefendant.input' import { UpdateDefendantInput } from './dto/updateDefendant.input' @@ -35,7 +35,8 @@ export class DefendantResolver { @Args('input', { type: () => CreateDefendantInput }) input: CreateDefendantInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, ...createDefendant } = input this.logger.debug(`Creating a new defendant for case ${caseId}`) @@ -43,7 +44,7 @@ export class DefendantResolver { return this.auditTrailService.audit( user.id, AuditedAction.CREATE_DEFENDANT, - backendApi.createDefendant(caseId, createDefendant), + backendService.createDefendant(caseId, createDefendant), (defendant) => defendant.id, ) } @@ -53,7 +54,8 @@ export class DefendantResolver { @Args('input', { type: () => UpdateDefendantInput }) input: UpdateDefendantInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, defendantId, ...updateDefendant } = input this.logger.debug(`Updating defendant ${defendantId} for case ${caseId}`) @@ -61,7 +63,7 @@ export class DefendantResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_DEFENDANT, - backendApi.updateDefendant(caseId, defendantId, updateDefendant), + backendService.updateDefendant(caseId, defendantId, updateDefendant), defendantId, ) } @@ -71,7 +73,8 @@ export class DefendantResolver { @Args('input', { type: () => DeleteDefendantInput }) input: DeleteDefendantInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, defendantId } = input this.logger.debug(`Deleting defendant ${defendantId} for case ${caseId}`) @@ -79,7 +82,7 @@ export class DefendantResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_DEFENDANT, - backendApi.deleteDefendant(caseId, defendantId), + backendService.deleteDefendant(caseId, defendantId), defendantId, ) } diff --git a/apps/judicial-system/api/src/app/modules/event-log/eventLog.resolver.ts b/apps/judicial-system/api/src/app/modules/event-log/eventLog.resolver.ts index dfdcc851da17..879382c207f5 100644 --- a/apps/judicial-system/api/src/app/modules/event-log/eventLog.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/event-log/eventLog.resolver.ts @@ -10,7 +10,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CreateEventLogInput } from '../event-log/dto/createEventLog.input' @UseGuards(JwtGraphQlAuthGuard) @@ -26,11 +26,12 @@ export class EventLogResolver { @Args('input', { type: () => CreateEventLogInput }) input: CreateEventLogInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Creating event log for case ${input.caseId}`) - const res = await backendApi.createEventLog(input, user.role) + const res = await backendService.createEventLog(input, user.role) return res.ok } diff --git a/apps/judicial-system/api/src/app/modules/file/file.resolver.ts b/apps/judicial-system/api/src/app/modules/file/file.resolver.ts index 9b02143d052c..574b00b67e1d 100644 --- a/apps/judicial-system/api/src/app/modules/file/file.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/file/file.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CreateFileInput } from './dto/createFile.input' import { CreatePresignedPostInput } from './dto/createPresignedPost.input' import { DeleteFileInput } from './dto/deleteFile.input' @@ -42,7 +42,8 @@ export class FileResolver { @Args('input', { type: () => CreatePresignedPostInput }) input: CreatePresignedPostInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, ...createPresignedPost } = input @@ -51,7 +52,7 @@ export class FileResolver { return this.auditTrailService.audit( user.id, AuditedAction.CREATE_PRESIGNED_POST, - backendApi.createCasePresignedPost(caseId, createPresignedPost), + backendService.createCasePresignedPost(caseId, createPresignedPost), caseId, ) } @@ -61,7 +62,8 @@ export class FileResolver { @Args('input', { type: () => CreateFileInput }) input: CreateFileInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, ...createFile } = input @@ -70,7 +72,7 @@ export class FileResolver { return this.auditTrailService.audit( user.id, AuditedAction.CREATE_FILE, - backendApi.createCaseFile(caseId, createFile), + backendService.createCaseFile(caseId, createFile), (file) => file.id, ) } @@ -80,7 +82,8 @@ export class FileResolver { @Args('input', { type: () => GetSignedUrlInput }) input: GetSignedUrlInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, id } = input @@ -89,7 +92,7 @@ export class FileResolver { return this.auditTrailService.audit( user.id, AuditedAction.GET_SIGNED_URL, - backendApi.getCaseFileSignedUrl(caseId, id), + backendService.getCaseFileSignedUrl(caseId, id), id, ) } @@ -99,7 +102,8 @@ export class FileResolver { @Args('input', { type: () => DeleteFileInput }) input: DeleteFileInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, id } = input @@ -108,7 +112,7 @@ export class FileResolver { return this.auditTrailService.audit( user.id, AuditedAction.DELETE_FILE, - backendApi.deleteCaseFile(caseId, id), + backendService.deleteCaseFile(caseId, id), id, ) } @@ -118,7 +122,8 @@ export class FileResolver { @Args('input', { type: () => UploadFileToCourtInput }) input: UploadFileToCourtInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, id } = input @@ -127,7 +132,7 @@ export class FileResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPLOAD_FILE_TO_COURT, - backendApi.uploadCaseFileToCourt(caseId, id), + backendService.uploadCaseFileToCourt(caseId, id), id, ) } @@ -137,7 +142,8 @@ export class FileResolver { @Args('input', { type: () => UpdateFilesInput }) input: UpdateFilesInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, files } = input @@ -146,7 +152,7 @@ export class FileResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_FILES, - backendApi.updateFiles(caseId, files), + backendService.updateFiles(caseId, files), (response) => response.caseFiles.map((f) => f.id), ) } diff --git a/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts b/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts index 4d4658fc533f..2d6fb1e19d0c 100644 --- a/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/file/limitedAccessFile.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CreateFileInput } from './dto/createFile.input' import { CreatePresignedPostInput } from './dto/createPresignedPost.input' import { DeleteFileInput } from './dto/deleteFile.input' @@ -38,7 +38,8 @@ export class LimitedAccessFileResolver { @Args('input', { type: () => CreatePresignedPostInput }) input: CreatePresignedPostInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, ...createPresignedPost } = input @@ -47,7 +48,7 @@ export class LimitedAccessFileResolver { return this.auditTrailService.audit( user.id, AuditedAction.CREATE_PRESIGNED_POST, - backendApi.limitedAccessCreateCasePresignedPost( + backendService.limitedAccessCreateCasePresignedPost( caseId, createPresignedPost, ), @@ -60,7 +61,8 @@ export class LimitedAccessFileResolver { @Args('input', { type: () => CreateFileInput }) input: CreateFileInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, ...createFile } = input @@ -69,7 +71,7 @@ export class LimitedAccessFileResolver { return this.auditTrailService.audit( user.id, AuditedAction.CREATE_FILE, - backendApi.limitedAccessCreateCaseFile(caseId, createFile), + backendService.limitedAccessCreateCaseFile(caseId, createFile), (file) => file.id, ) } @@ -79,7 +81,8 @@ export class LimitedAccessFileResolver { @Args('input', { type: () => GetSignedUrlInput }) input: GetSignedUrlInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, id } = input @@ -88,7 +91,7 @@ export class LimitedAccessFileResolver { return this.auditTrailService.audit( user.id, AuditedAction.GET_SIGNED_URL, - backendApi.limitedAccessGetCaseFileSignedUrl(caseId, id), + backendService.limitedAccessGetCaseFileSignedUrl(caseId, id), id, ) } @@ -98,7 +101,8 @@ export class LimitedAccessFileResolver { @Args('input', { type: () => DeleteFileInput }) input: DeleteFileInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, id } = input @@ -107,7 +111,7 @@ export class LimitedAccessFileResolver { return this.auditTrailService.audit( user.id, AuditedAction.DELETE_FILE, - backendApi.limitedAccessDeleteCaseFile(caseId, id), + backendService.limitedAccessDeleteCaseFile(caseId, id), id, ) } diff --git a/apps/judicial-system/api/src/app/modules/index.ts b/apps/judicial-system/api/src/app/modules/index.ts index 9585044893c3..de4bc6ecb048 100644 --- a/apps/judicial-system/api/src/app/modules/index.ts +++ b/apps/judicial-system/api/src/app/modules/index.ts @@ -14,3 +14,6 @@ export { DefenderModule } from './defender/defender.module' export { CaseListModule } from './case-list/caseList.module' export { defenderModuleConfig } from './defender/defender.config' export { EventLogModule } from './event-log/eventLog.module' +export { backendModuleConfig } from './backend/backend.config' +export { BackendService } from './backend/backend.service' +export { BackendModule } from './backend/backend.module' diff --git a/apps/judicial-system/api/src/app/modules/indictment-count/indictmentCount.resolver.ts b/apps/judicial-system/api/src/app/modules/indictment-count/indictmentCount.resolver.ts index a5edb85f5bcc..bff1cd420803 100644 --- a/apps/judicial-system/api/src/app/modules/indictment-count/indictmentCount.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/indictment-count/indictmentCount.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CreateIndictmentCountInput } from './dto/createIndictmentCount.input' import { DeleteIndictmentCountInput } from './dto/deleteIndictmentCount.input' import { UpdateIndictmentCountInput } from './dto/updateIndictmentCount.input' @@ -35,7 +35,8 @@ export class IndictmentCountResolver { @Args('input', { type: () => CreateIndictmentCountInput }) input: CreateIndictmentCountInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug( `Creating a new indictment count for case ${input.caseId}`, @@ -44,7 +45,7 @@ export class IndictmentCountResolver { return this.auditTrailService.audit( user.id, AuditedAction.CREATE_INDICTMENT_COUNT, - backendApi.createIndictmentCount(input), + backendService.createIndictmentCount(input), (theIndictmentCount) => theIndictmentCount.id, ) } @@ -54,7 +55,8 @@ export class IndictmentCountResolver { @Args('input', { type: () => UpdateIndictmentCountInput }) input: UpdateIndictmentCountInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug( `Updating indictment count ${input.indictmentCountId} for case ${input.caseId}`, @@ -63,7 +65,7 @@ export class IndictmentCountResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_INDICTMENT_COUNT, - backendApi.updateIndictmentCount(input), + backendService.updateIndictmentCount(input), input.indictmentCountId, ) } @@ -73,7 +75,8 @@ export class IndictmentCountResolver { @Args('input', { type: () => DeleteIndictmentCountInput }) input: DeleteIndictmentCountInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug( `Deleting indictment count ${input.indictmentCountId} for case ${input.caseId}`, @@ -82,7 +85,7 @@ export class IndictmentCountResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_INDICTMENT_COUNT, - backendApi.deleteIndictmentCount(input), + backendService.deleteIndictmentCount(input), input.indictmentCountId, ) } diff --git a/apps/judicial-system/api/src/app/modules/institution/institution.resolver.ts b/apps/judicial-system/api/src/app/modules/institution/institution.resolver.ts index b12661be9134..3ec0ae7029db 100644 --- a/apps/judicial-system/api/src/app/modules/institution/institution.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/institution/institution.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { Institution } from './institution.model' @UseGuards(JwtGraphQlAuthGuard) @@ -29,14 +29,15 @@ export class InstitutionResolver { @Query(() => [Institution], { nullable: true }) institutions( @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug('Getting all institutions') return this.auditTrailService.audit( user.id, AuditedAction.GET_INSTITUTIONS, - backendApi.getInstitutions(), + backendService.getInstitutions(), (institutions: Institution[]) => institutions.map((institution) => institution.id), ) diff --git a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts index edc1669d88c8..8d4fbf9cd90a 100644 --- a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { PoliceCaseFilesQueryInput } from './dto/policeCaseFiles.input' import { PoliceCaseInfoQueryInput } from './dto/policeCaseInfo.input' import { UploadPoliceCaseFileInput } from './dto/uploadPoliceCaseFile.input' @@ -36,14 +36,15 @@ export class PoliceResolver { @Args('input', { type: () => PoliceCaseFilesQueryInput }) input: PoliceCaseFilesQueryInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Getting all police case files for case ${input.caseId}`) return this.auditTrailService.audit( user.id, AuditedAction.GET_POLICE_CASE_FILES, - backendApi.getPoliceCaseFiles(input.caseId), + backendService.getPoliceCaseFiles(input.caseId), input.caseId, ) } @@ -53,14 +54,15 @@ export class PoliceResolver { @Args('input', { type: () => PoliceCaseInfoQueryInput }) input: PoliceCaseInfoQueryInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Getting all police case info for case ${input.caseId}`) return this.auditTrailService.audit( user.id, AuditedAction.GET_POLICE_CASE_INFO, - backendApi.getPoliceCaseInfo(input.caseId), + backendService.getPoliceCaseInfo(input.caseId), input.caseId, ) } @@ -70,7 +72,8 @@ export class PoliceResolver { @Args('input', { type: () => UploadPoliceCaseFileInput }) input: UploadPoliceCaseFileInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { caseId, ...uploadPoliceFile } = input this.logger.debug( @@ -80,7 +83,7 @@ export class PoliceResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPLOAD_POLICE_CASE_FILE, - backendApi.uploadPoliceFile(input.caseId, uploadPoliceFile), + backendService.uploadPoliceFile(input.caseId, uploadPoliceFile), input.caseId, ) } diff --git a/apps/judicial-system/api/src/app/modules/user/user.resolver.ts b/apps/judicial-system/api/src/app/modules/user/user.resolver.ts index 21118a2a0e32..e04adeddc6b0 100644 --- a/apps/judicial-system/api/src/app/modules/user/user.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/user/user.resolver.ts @@ -14,7 +14,7 @@ import { } from '@island.is/judicial-system/auth' import type { User as TUser } from '@island.is/judicial-system/types' -import { BackendApi } from '../../data-sources' +import { BackendService } from '../backend' import { CreateUserInput } from './dto/createUser.input' import { UpdateUserInput } from './dto/updateUser.input' import { UserQueryInput } from './dto/user.input' @@ -33,7 +33,8 @@ export class UserResolver { @Query(() => [User], { nullable: true }) users( @CurrentGraphQlUser() user: TUser, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, @Args('input', { type: () => UsersQueryInput, nullable: true }) input?: UsersQueryInput, ): Promise { @@ -42,7 +43,7 @@ export class UserResolver { return this.auditTrailService.audit( user.id, AuditedAction.GET_USERS, - backendApi.getUsers().then((users) => { + backendService.getUsers().then((users) => { if (!input?.role) { return users } @@ -59,14 +60,15 @@ export class UserResolver { @Args('input', { type: () => UserQueryInput }) input: UserQueryInput, @CurrentGraphQlUser() user: TUser, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug(`Getting user ${input.id}`) return this.auditTrailService.audit( user.id, AuditedAction.GET_USER, - backendApi.getUser(input.id), + backendService.getUser(input.id), (user: User) => user.id, ) } @@ -87,14 +89,15 @@ export class UserResolver { @Args('input', { type: () => CreateUserInput }) input: CreateUserInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { this.logger.debug('Creating user') return this.auditTrailService.audit( user.id, AuditedAction.CREATE_USER, - backendApi.createUser(input), + backendService.createUser(input), (theUser) => theUser.id, ) } @@ -105,7 +108,8 @@ export class UserResolver { @Args('input', { type: () => UpdateUserInput }) input: UpdateUserInput, @CurrentGraphQlUser() user: User, - @Context('dataSources') { backendApi }: { backendApi: BackendApi }, + @Context('dataSources') + { backendService }: { backendService: BackendService }, ): Promise { const { id, ...updateUser } = input @@ -114,7 +118,7 @@ export class UserResolver { return this.auditTrailService.audit( user.id, AuditedAction.UPDATE_USER, - backendApi.updateUser(id, updateUser), + backendService.updateUser(id, updateUser), id, ) } diff --git a/apps/judicial-system/api/src/environments/environment.ts b/apps/judicial-system/api/src/environments/environment.ts deleted file mode 100644 index 9593870f5cc9..000000000000 --- a/apps/judicial-system/api/src/environments/environment.ts +++ /dev/null @@ -1,25 +0,0 @@ -const devConfig = { - production: false, - backend: { - url: 'http://localhost:3344', - secretToken: 'secret-backend-api-token', - }, -} -if (process.env.NODE_ENV === 'production') { - if (!process.env.BACKEND_URL) { - throw new Error('Missing BACKEND_URL environment.') - } - if (!process.env.BACKEND_ACCESS_TOKEN) { - throw new Error('Missing BACKEND_ACCESS_TOKEN environment.') - } -} - -const prodConfig = { - production: true, - backend: { - url: process.env.BACKEND_URL, - secretToken: process.env.BACKEND_ACCESS_TOKEN ?? '', - }, -} - -export default process.env.NODE_ENV === 'production' ? prodConfig : devConfig diff --git a/apps/judicial-system/api/src/environments/index.ts b/apps/judicial-system/api/src/environments/index.ts deleted file mode 100644 index f1c9690a5bd4..000000000000 --- a/apps/judicial-system/api/src/environments/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as environment } from './environment' diff --git a/apps/judicial-system/backend/migrations/20210608081018-update-case.js b/apps/judicial-system/backend/migrations/20210608081018-update-case.js index d1f195c97dc0..c62c4346664b 100644 --- a/apps/judicial-system/backend/migrations/20210608081018-update-case.js +++ b/apps/judicial-system/backend/migrations/20210608081018-update-case.js @@ -1,3 +1,4 @@ +/* eslint-disable func-style */ 'use strict' const dayOfWeek = [ diff --git a/apps/judicial-system/backend/migrations/20210831141153-update-case.js b/apps/judicial-system/backend/migrations/20210831141153-update-case.js index 6f04a1c9e2b2..e935c275a6ae 100644 --- a/apps/judicial-system/backend/migrations/20210831141153-update-case.js +++ b/apps/judicial-system/backend/migrations/20210831141153-update-case.js @@ -1,3 +1,4 @@ +/* eslint-disable func-style */ 'use strict' const dayOfWeek = [ diff --git a/apps/judicial-system/backend/migrations/20210915100238-update-case.js b/apps/judicial-system/backend/migrations/20210915100238-update-case.js index b289a65be689..a116ca54bfa7 100644 --- a/apps/judicial-system/backend/migrations/20210915100238-update-case.js +++ b/apps/judicial-system/backend/migrations/20210915100238-update-case.js @@ -1,3 +1,4 @@ +/* eslint-disable func-style */ 'use strict' const laws = { diff --git a/apps/judicial-system/backend/project.json b/apps/judicial-system/backend/project.json index be771c66a772..64f1acf63fd6 100644 --- a/apps/judicial-system/backend/project.json +++ b/apps/judicial-system/backend/project.json @@ -63,7 +63,7 @@ "dev-services": { "executor": "nx:run-commands", "options": { - "command": "docker-compose up -d", + "command": "docker compose up -d", "cwd": "apps/judicial-system/backend" } }, diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts index 6765d80bf956..65fe1cfd719e 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts @@ -256,6 +256,12 @@ export class CaseController { update.appealRulingModifiedHistory = `${history}${today} - ${user.name} ${user.title}\n\n${update.appealRulingModifiedHistory}` } + if (update.mergeCaseId && theCase.state !== CaseState.RECEIVED) { + throw new BadRequestException( + 'Cannot merge case that is not in a received state', + ) + } + return this.caseService.update(theCase, update, user) as Promise // Never returns undefined } @@ -461,6 +467,33 @@ export class CaseController { return theCase } + @UseGuards(JwtAuthGuard, CaseExistsGuard) + @RolesRules( + districtCourtJudgeRule, + districtCourtRegistrarRule, + districtCourtAssistantRule, + ) + @Get('case/:caseId/connectedCases') + @ApiOkResponse({ type: [Case], description: 'Gets all connected cases' }) + async getConnectedCases( + @Param('caseId') caseId: string, + @CurrentCase() theCase: Case, + ): Promise { + this.logger.debug(`Getting connected cases for case ${caseId}`) + + if (!theCase.defendants || theCase.defendants.length === 0) { + return [] + } + + const connectedCases = await Promise.all( + theCase.defendants.map((defendant) => + this.caseService.getConnectedIndictmentCases(theCase.id, defendant), + ), + ) + + return connectedCases.flat() + } + @UseGuards( JwtAuthGuard, RolesGuard, diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index f0aaccb3b42d..f123ae6c2e4c 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -1,4 +1,4 @@ -import { Op } from 'sequelize' +import { Op, WhereOptions } from 'sequelize' import { Includeable, OrderItem, Transaction } from 'sequelize/types' import { Sequelize } from 'sequelize-typescript' @@ -20,6 +20,7 @@ import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' import { type ConfigType } from '@island.is/nest/config' +import { formatNationalId } from '@island.is/judicial-system/formatters' import { CaseMessage, MessageService, @@ -162,6 +163,7 @@ export interface UpdateCase | 'rulingSignatureDate' | 'judgeId' | 'courtSessionType' + | 'mergeCaseId' > { type?: CaseType state?: CaseState @@ -300,7 +302,36 @@ export const include: Includeable[] = [ }, { model: Notification, as: 'notifications' }, { model: Case, as: 'mergeCase' }, - { model: Case, as: 'mergedCases', separate: true }, + { + model: Case, + as: 'mergedCases', + where: { state: CaseState.COMPLETED }, + include: [ + { + model: CaseFile, + as: 'caseFiles', + required: false, + where: { + state: { [Op.not]: CaseFileState.DELETED }, + category: { + [Op.in]: [ + CaseFileCategory.INDICTMENT, + CaseFileCategory.COURT_RECORD, + CaseFileCategory.CRIMINAL_RECORD, + CaseFileCategory.COST_BREAKDOWN, + CaseFileCategory.CRIMINAL_RECORD_UPDATE, + ], + }, + }, + separate: true, + }, + { model: Institution, as: 'court' }, + { model: User, as: 'judge' }, + { model: User, as: 'prosecutor' }, + { model: Institution, as: 'prosecutorsOffice' }, + ], + separate: true, + }, ] export const order: OrderItem[] = [ @@ -1319,6 +1350,49 @@ export class CaseService { }) } + getConnectedIndictmentCases( + caseId: string, + defendant: Defendant, + ): Promise { + let whereClause: WhereOptions = { + type: CaseType.INDICTMENT, + id: { [Op.ne]: caseId }, + state: [CaseState.RECEIVED], + } + + if (defendant.noNationalId) { + whereClause = { + ...whereClause, + [Op.and]: [ + { '$defendants.national_id$': defendant.nationalId }, + { '$defendants.name$': defendant.name }, + ], + } + } else { + const formattedNationalId = formatNationalId(defendant.nationalId) + whereClause = { + ...whereClause, + [Op.or]: [ + { '$defendants.national_id$': defendant.nationalId }, + { '$defendants.national_id$': formattedNationalId }, + ], + } + } + + return this.caseModel.findAll({ + include: [ + { model: Defendant, as: 'defendants' }, + { + model: DateLog, + as: 'dateLogs', + }, + ], + order: [[{ model: DateLog, as: 'dateLogs' }, 'created', 'DESC']], + attributes: ['id', 'courtCaseNumber', 'type', 'state'], + where: whereClause, + }) + } + async create(caseToCreate: CreateCaseDto, user: TUser): Promise { return this.sequelize .transaction(async (transaction) => { diff --git a/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts b/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts index 8527a24d95da..cc82fdad76c9 100644 --- a/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/case/dto/updateCase.dto.ts @@ -508,4 +508,9 @@ export class UpdateCaseDto { @IsEnum(CourtSessionType) @ApiPropertyOptional({ enum: CourtSessionType }) readonly courtSessionType?: CourtSessionType + + @IsOptional() + @IsUUID() + @ApiPropertyOptional({ type: String }) + readonly mergeCaseId?: string } diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts b/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts index ca71abf169c6..857ce79b3907 100644 --- a/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts +++ b/apps/judicial-system/backend/src/app/modules/case/guards/rolesRules.ts @@ -99,6 +99,7 @@ const districtCourtFields: (keyof UpdateCaseDto)[] = [ 'indictmentRulingDecision', 'indictmentDecision', 'courtSessionType', + 'mergeCaseId', ] const courtOfAppealsFields: (keyof UpdateCaseDto)[] = [ diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCases.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCases.spec.ts new file mode 100644 index 000000000000..3df9370e67f4 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCases.spec.ts @@ -0,0 +1,74 @@ +import { uuid } from 'uuidv4' + +import { User } from '@island.is/judicial-system/types' + +import { createTestingCaseModule } from '../createTestingCaseModule' + +import { Case } from '../../models/case.model' + +interface Then { + result: Case[] + error: Error +} + +type GivenWhenThen = (caseId: string, theCase: Case) => Promise + +describe('CaseController - Get connected cases', () => { + const user = { id: uuid() } as User + let mockCaseModel: typeof Case + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { caseModel, caseController } = await createTestingCaseModule() + mockCaseModel = caseModel + + givenWhenThen = async (caseId: string, theCase: Case) => { + const then = {} as Then + + try { + then.result = await caseController.getConnectedCases(caseId, theCase) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('case has no defendants', () => { + const caseId = uuid() + const theCase = { id: caseId } as Case + let then: Then + + beforeEach(async () => { + then = await givenWhenThen(caseId, theCase) + }) + + it('should return an empty array', () => { + expect(then.result).toEqual([]) + }) + }) + + describe('case has defendants', () => { + const caseId = uuid() + const defendantId1 = uuid() + const defendantId2 = uuid() + const theCase = { + id: caseId, + defendants: [{ id: defendantId1 }, { id: defendantId2 }], + } as Case + const connectedCase1 = { id: uuid() } as Case + const connectedCase2 = { id: uuid() } as Case + let then: Then + + beforeEach(async () => { + const mockFindAll = mockCaseModel.findAll as jest.Mock + mockFindAll.mockResolvedValueOnce([connectedCase1, connectedCase2]) + then = await givenWhenThen(caseId, theCase) + }) + + it('should return the connected cases', () => { + expect(then.result).toEqual([connectedCase1, connectedCase2]) + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCasesGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCasesGuards.spec.ts new file mode 100644 index 000000000000..7f10970cca93 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCasesGuards.spec.ts @@ -0,0 +1,21 @@ +import { JwtAuthGuard } from '@island.is/judicial-system/auth' + +import { CaseController } from '../../case.controller' +import { CaseExistsGuard } from '../../guards/caseExists.guard' + +describe('CaseController - Get connected cases by case id guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + CaseController.prototype.getConnectedCases, + ) + }) + + it('should have the right guard configuration', () => { + expect(new guards[0]()).toBeInstanceOf(JwtAuthGuard) + expect(new guards[1]()).toBeInstanceOf(CaseExistsGuard) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCasesRolesRules.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCasesRolesRules.spec.ts new file mode 100644 index 000000000000..e0e39615675a --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/caseController/getConnectedCasesRolesRules.spec.ts @@ -0,0 +1,25 @@ +import { + districtCourtAssistantRule, + districtCourtJudgeRule, + districtCourtRegistrarRule, +} from '../../../../guards' +import { CaseController } from '../../case.controller' + +describe('CaseController - Get connected cases by case id rules', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let rules: any[] + + beforeEach(() => { + rules = Reflect.getMetadata( + 'roles-rules', + CaseController.prototype.getConnectedCases, + ) + }) + + it('should give permission to roles', () => { + expect(rules).toHaveLength(3) + expect(rules).toContain(districtCourtJudgeRule) + expect(rules).toContain(districtCourtRegistrarRule) + expect(rules).toContain(districtCourtAssistantRule) + }) +}) diff --git a/apps/judicial-system/message-handler/project.json b/apps/judicial-system/message-handler/project.json index b13fb708dc37..4f1859985370 100644 --- a/apps/judicial-system/message-handler/project.json +++ b/apps/judicial-system/message-handler/project.json @@ -50,7 +50,7 @@ "dev-services": { "executor": "nx:run-commands", "options": { - "command": "docker-compose up -d", + "command": "docker compose up -d", "cwd": "apps/judicial-system/message-handler" } }, diff --git a/apps/judicial-system/message-handler/src/main.ts b/apps/judicial-system/message-handler/src/main.ts index 08dee976b87d..0ec6688e8859 100644 --- a/apps/judicial-system/message-handler/src/main.ts +++ b/apps/judicial-system/message-handler/src/main.ts @@ -7,7 +7,7 @@ import { LoggingModule } from '@island.is/logging' import { AppModule, appModuleConfig, MessageHandlerService } from './app' -async function bootstrap() { +const bootstrap = async () => { const port = process.env.PORT || 3366 const app = await NestFactory.create(AppModule, { diff --git a/apps/judicial-system/scheduler/src/main.ts b/apps/judicial-system/scheduler/src/main.ts index 9672c65662e1..02ffdf88a0a0 100644 --- a/apps/judicial-system/scheduler/src/main.ts +++ b/apps/judicial-system/scheduler/src/main.ts @@ -2,7 +2,7 @@ import { NestFactory } from '@nestjs/core' import { AppModule, AppService } from './app' -async function bootstrap() { +const bootstrap = async () => { const app = await NestFactory.createApplicationContext(AppModule) app.enableShutdownHooks() const service = app.get(AppService) diff --git a/apps/judicial-system/web/src/components/AccordionItems/ConnectedCaseFilesAccordionItem/ConnectedCaseFilesAccordionItem.strings.ts b/apps/judicial-system/web/src/components/AccordionItems/ConnectedCaseFilesAccordionItem/ConnectedCaseFilesAccordionItem.strings.ts new file mode 100644 index 000000000000..83c7ba93cb39 --- /dev/null +++ b/apps/judicial-system/web/src/components/AccordionItems/ConnectedCaseFilesAccordionItem/ConnectedCaseFilesAccordionItem.strings.ts @@ -0,0 +1,10 @@ +import { defineMessages } from 'react-intl' + +export const strings = defineMessages({ + heading: { + id: 'judicial.system.core:connected_case_files_accordion.heading', + defaultMessage: 'Gögn úr máli {caseNumber}', + description: + 'Notaður sem titill fyrir gögn úr sameinuðu ákærumáli á dómara skjá', + }, +}) diff --git a/apps/judicial-system/web/src/components/AccordionItems/ConnectedCaseFilesAccordionItem/ConnectedCaseFilesAccordionItem.tsx b/apps/judicial-system/web/src/components/AccordionItems/ConnectedCaseFilesAccordionItem/ConnectedCaseFilesAccordionItem.tsx new file mode 100644 index 000000000000..cc7ac46083c5 --- /dev/null +++ b/apps/judicial-system/web/src/components/AccordionItems/ConnectedCaseFilesAccordionItem/ConnectedCaseFilesAccordionItem.tsx @@ -0,0 +1,38 @@ +import React, { FC } from 'react' +import { useIntl } from 'react-intl' + +import { AccordionItem } from '@island.is/island-ui/core' +import { IndictmentCaseFilesList } from '@island.is/judicial-system-web/src/components' +import { Case } from '@island.is/judicial-system-web/src/graphql/schema' + +import { strings } from './ConnectedCaseFilesAccordionItem.strings' + +interface Props { + connectedCase: Case +} + +const ConnectedCaseFilesAccordionItem: FC = ({ connectedCase }) => { + const { formatMessage } = useIntl() + const { caseFiles, courtCaseNumber } = connectedCase + + if (!courtCaseNumber || !caseFiles || caseFiles.length < 1) { + return null + } + + return ( + + + + ) +} + +export default ConnectedCaseFilesAccordionItem diff --git a/apps/judicial-system/web/src/components/AccordionItems/IndictmentsCaseFilesAccordionItem/IndictmentsCaseFilesAccordionItem.tsx b/apps/judicial-system/web/src/components/AccordionItems/IndictmentsCaseFilesAccordionItem/IndictmentsCaseFilesAccordionItem.tsx index 7616581dfdf7..bb109cf43db0 100644 --- a/apps/judicial-system/web/src/components/AccordionItems/IndictmentsCaseFilesAccordionItem/IndictmentsCaseFilesAccordionItem.tsx +++ b/apps/judicial-system/web/src/components/AccordionItems/IndictmentsCaseFilesAccordionItem/IndictmentsCaseFilesAccordionItem.tsx @@ -274,7 +274,7 @@ const CaseFile: FC = (props) => { - + {isEditing ? ( = ({ ) } -const IndictmentCaseFilesList: FC = ({ workingCase }) => { +const IndictmentCaseFilesList: FC = ({ + workingCase, + displayHeading = true, +}) => { const { formatMessage } = useIntl() const { user } = useContext(UserContext) const { onOpen, fileNotFound, dismissFileNotFound } = useFileList({ @@ -93,7 +97,9 @@ const IndictmentCaseFilesList: FC = ({ workingCase }) => { return ( <> - + {displayHeading && ( + + )} {indictments && indictments.length > 0 && ( diff --git a/apps/judicial-system/web/src/components/InfoCard/InfoCardActiveIndictment.tsx b/apps/judicial-system/web/src/components/InfoCard/InfoCardActiveIndictment.tsx index e0122148c690..4f8376b4f7ae 100644 --- a/apps/judicial-system/web/src/components/InfoCard/InfoCardActiveIndictment.tsx +++ b/apps/judicial-system/web/src/components/InfoCard/InfoCardActiveIndictment.tsx @@ -55,6 +55,51 @@ const InfoCardActiveIndictment = () => { ), }, ]} + additionalDataSections={[ + ...(workingCase.mergedCases + ? workingCase.mergedCases.map((mergedCase) => ({ + data: [ + { + title: formatMessage(core.policeCaseNumber), + value: mergedCase.policeCaseNumbers?.map((n) => ( + {n} + )), + }, + { + title: formatMessage(strings.mergedFromTitle), + value: {mergedCase.courtCaseNumber}, + }, + { + title: formatMessage(core.prosecutor), + value: mergedCase.prosecutorsOffice?.name, + }, + { + title: formatMessage(core.judge), + value: mergedCase.judge?.name, + }, + { + title: formatMessage(core.court), + value: mergedCase.court?.name, + }, + { + title: formatMessage(strings.offence), + value: ( + <> + {readableIndictmentSubtypes( + mergedCase.policeCaseNumbers, + mergedCase.indictmentSubtypes, + ).map((subtype, index) => ( + + {capitalize(subtype)} + + ))} + + ), + }, + ], + })) + : []), + ]} defendants={ workingCase.defendants ? { diff --git a/apps/judicial-system/web/src/components/InfoCard/InfoCardClosedIndictment/InfoCardClosedIndictment.tsx b/apps/judicial-system/web/src/components/InfoCard/InfoCardClosedIndictment/InfoCardClosedIndictment.tsx index 2f42a73d61aa..d0cc992fb5d5 100644 --- a/apps/judicial-system/web/src/components/InfoCard/InfoCardClosedIndictment/InfoCardClosedIndictment.tsx +++ b/apps/judicial-system/web/src/components/InfoCard/InfoCardClosedIndictment/InfoCardClosedIndictment.tsx @@ -89,6 +89,14 @@ const InfoCardClosedIndictment: FC = (props) => { title: formatMessage(core.prosecutor), value: `${workingCase.prosecutorsOffice?.name}`, }, + ...(workingCase.mergeCase + ? [ + { + title: formatMessage(strings.indictmentMergedTitle), + value: workingCase.mergeCase?.courtCaseNumber, + }, + ] + : []), { title: formatMessage(core.court), value: workingCase.court?.name, diff --git a/apps/judicial-system/web/src/components/InfoCard/InfoCardIndictment.strings.ts b/apps/judicial-system/web/src/components/InfoCard/InfoCardIndictment.strings.ts index e4ff9d014ddb..f936e77d0473 100644 --- a/apps/judicial-system/web/src/components/InfoCard/InfoCardIndictment.strings.ts +++ b/apps/judicial-system/web/src/components/InfoCard/InfoCardIndictment.strings.ts @@ -53,4 +53,14 @@ export const strings = defineMessages({ defaultMessage: 'Dagsetning áritunar', description: 'Notaður sem titill á "Dagsetning" hluta af yfirliti ákæru.', }, + indictmentMergedTitle: { + id: 'judicial.system.core:info_card_indictment.indictment_merged_title', + defaultMessage: 'Sameinað máli', + description: 'Notaður sem titill á "Sameinað" hluta af yfirliti ákæru.', + }, + mergedFromTitle: { + id: 'judicial.system.core:info_card_indictment.merged_from_title', + defaultMessage: 'Sameinað úr', + description: 'Notaður sem titill á "Sameinað úr" hluta af yfirliti ákæru.', + }, }) diff --git a/apps/judicial-system/web/src/components/Table/PastCasesTable/PastCasesTable.tsx b/apps/judicial-system/web/src/components/Table/PastCasesTable/PastCasesTable.tsx index e71b22747ffa..0d09eba068e3 100644 --- a/apps/judicial-system/web/src/components/Table/PastCasesTable/PastCasesTable.tsx +++ b/apps/judicial-system/web/src/components/Table/PastCasesTable/PastCasesTable.tsx @@ -145,7 +145,7 @@ const PastCasesTable: FC = ({ cases, loading = false, testid }) => { appealCaseNumber={column.appealCaseNumber} /> - + diff --git a/apps/judicial-system/web/src/components/Table/Table.css.ts b/apps/judicial-system/web/src/components/Table/Table.css.ts index df3f49d234ab..f3d806282e21 100644 --- a/apps/judicial-system/web/src/components/Table/Table.css.ts +++ b/apps/judicial-system/web/src/components/Table/Table.css.ts @@ -2,14 +2,9 @@ import { globalStyle, style } from '@vanilla-extract/css' import { theme } from '@island.is/island-ui/theme' -export const header = style({ - background: theme.color.blue100, -}) - export const table = style({ borderSpacing: 0, borderCollapse: 'collapse', - overflow: 'hidden', // Needed for Safari. width: '100%', @@ -46,30 +41,14 @@ export const largeColumn = style({ }, }, }) -export const blockColumn = style({ - display: 'block', - overflow: 'hidden', - textOverflow: 'ellipsis', -}) -export const deleteButtonContainer = style({ - maxWidth: '0', - height: '100%', - padding: 0, - transform: 'translate3d(2px, 0px, 0px)', -}) - -export const td = style({ - selectors: { - [`&:not(${deleteButtonContainer})`]: { - padding: `${theme.spacing[2]}px ${theme.spacing[3]}px`, - }, - '&.secondLast': { - marginLeft: 'auto', - height: '100%', - padding: 0, - }, - }, +export const smallContainer = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '34px', + height: '34px', + marginLeft: 'auto', }) globalStyle(`${table} td, th`, { @@ -83,11 +62,6 @@ globalStyle(`${table} td, th`, { textAlign: 'left', }) -export const expandLabel = style({ - color: theme.color.blue400, - cursor: 'pointer', -}) - export const row = style({ cursor: 'pointer', }) diff --git a/apps/judicial-system/web/src/components/Table/Table.tsx b/apps/judicial-system/web/src/components/Table/Table.tsx index 51abbc83881f..eefb4522490c 100644 --- a/apps/judicial-system/web/src/components/Table/Table.tsx +++ b/apps/judicial-system/web/src/components/Table/Table.tsx @@ -8,7 +8,7 @@ import React, { import { useIntl } from 'react-intl' import { useLocalStorage } from 'react-use' import parseISO from 'date-fns/parseISO' -import { AnimatePresence } from 'framer-motion' +import { AnimatePresence, motion } from 'framer-motion' import { Box, Text } from '@island.is/island-ui/core' import { theme } from '@island.is/island-ui/theme' @@ -126,7 +126,7 @@ const Table: FC = (props) => { } }, [data, sortConfig]) - return width < theme.breakpoints.md ? ( + return width < theme.breakpoints.lg ? ( <> {data.map((theCase: CaseListEntry) => ( @@ -205,30 +205,46 @@ const Table: FC = (props) => { data-testid="tableRow" > {columns.map((td) => ( - - {td.cell(row)} - + {td.cell(row)} ))} {generateContextMenuItems && ( - + {generateContextMenuItems(row).length > 0 && ( - + {isOpeningCaseId === row.id && showLoading ? ( - + - + ) : ( { evt.stopPropagation() }} - /> + > + + } /> )} diff --git a/apps/judicial-system/web/src/components/Table/TableSkeleton/TableSkeleton.tsx b/apps/judicial-system/web/src/components/Table/TableSkeleton/TableSkeleton.tsx index 7b99b7e0f664..4c850a5c28da 100644 --- a/apps/judicial-system/web/src/components/Table/TableSkeleton/TableSkeleton.tsx +++ b/apps/judicial-system/web/src/components/Table/TableSkeleton/TableSkeleton.tsx @@ -52,122 +52,122 @@ const TableSkeleton = () => { - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/apps/judicial-system/web/src/components/index.ts b/apps/judicial-system/web/src/components/index.ts index dc835ddff6c7..b7e8a1e97e8e 100644 --- a/apps/judicial-system/web/src/components/index.ts +++ b/apps/judicial-system/web/src/components/index.ts @@ -10,6 +10,7 @@ export { default as CheckboxList, type CheckboxInfo, } from './CheckboxList/CheckboxList' +export { default as ConnectedCaseFilesAccordionItem } from './AccordionItems/ConnectedCaseFilesAccordionItem/ConnectedCaseFilesAccordionItem' export { default as CommentsAccordionItem } from './AccordionItems/CommentsAccordionItem/CommentsAccordionItem' export { default as ConclusionDraft } from './ConclusionDraft/ConclusionDraft' export { default as ContextMenu } from './ContextMenu/ContextMenu' diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts index 9c4a47021732..e8ef5f5ff537 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.strings.ts @@ -145,4 +145,9 @@ export const strings = defineMessages({ description: 'Notaður sem skýritexti í tegund þinghalds valkosti á Niðurstaða skrefi.', }, + connectedCaseNumbersTitle: { + id: 'judicial.system.core:court.indictments.conclusion.connected_case_numbers_title', + defaultMessage: 'Málsnúmer', + description: 'Notaður sem titill á tengd mál textabox á Niðurstaða skrefi.', + }, }) diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx index 6ef21ea0ca90..5df2c6e08162 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Conclusion/Conclusion.tsx @@ -40,6 +40,7 @@ import { useUploadFiles, } from '@island.is/judicial-system-web/src/utils/hooks' +import SelectConnectedCase from './SelectConnectedCase' import { strings } from './Conclusion.strings' const courtSessionOptions = [ @@ -136,6 +137,11 @@ const Conclusion: FC = () => { break case IndictmentDecision.COMPLETING: update.indictmentRulingDecision = selectedDecision + update.mergeCaseId = + selectedDecision === CaseIndictmentRulingDecision.MERGE + ? workingCase.mergeCase?.id + : null + break case IndictmentDecision.REDISTRIBUTING: update.judgeId = null @@ -232,14 +238,21 @@ const Conclusion: FC = () => { file.status === 'done', ) ) - case CaseIndictmentRulingDecision.FINE: case CaseIndictmentRulingDecision.CANCELLATION: - case CaseIndictmentRulingDecision.MERGE: + case CaseIndictmentRulingDecision.FINE: return uploadFiles.some( (file) => file.category === CaseFileCategory.COURT_RECORD && file.status === 'done', ) + case CaseIndictmentRulingDecision.MERGE: + return Boolean( + uploadFiles.some( + (file) => + file.category === CaseFileCategory.COURT_RECORD && + file.status === 'done', + ) && workingCase.mergeCase?.id, + ) default: return false } @@ -469,6 +482,19 @@ const Conclusion: FC = () => { )} + {selectedDecision && + selectedDecision === CaseIndictmentRulingDecision.MERGE && ( + + + + + )} {selectedAction && ( > +} + +const SelectConnectedCase: FC = ({ workingCase, setWorkingCase }) => { + const { formatMessage } = useIntl() + + const { data: connectedCasesData, loading: connectedCasesLoading } = + useConnectedCasesQuery({ + variables: { + input: { + id: workingCase.id, + }, + }, + }) + + const setConnectedCase = async (connectedCaseId: string) => { + setWorkingCase((prevWorkingCase) => ({ + ...prevWorkingCase, + mergeCase: { + id: connectedCaseId, + }, + })) + } + + const connectedCases = connectedCasesData?.connectedCases?.map((aCase) => ({ + label: `${aCase.courtCaseNumber}`, + value: aCase.id, + })) as ConnectedCaseOption[] + + // For now we only want to allow cases with a single defendant to be able to merge + // in to another case + const mergeAllowed = workingCase.defendants?.length === 1 ? true : false + + const defaultConnectedCase = connectedCases?.find( + (connectedCase) => + workingCase.mergeCase && + connectedCase.value === workingCase.mergeCase?.id, + ) + + return !mergeAllowed ? ( + + ) : connectedCasesLoading ? ( + + + + ) : connectedCases?.length === 0 ? ( + + ) : ( + { /> - - + + + + + {/* idealPublishDate Input */} + + updateState('idealPublishDate', date) + } + hasError={ + draft.idealPublishDate.showError && + !!draft.idealPublishDate.error + } + errorMessage={ + draft.idealPublishDate.error && t(draft.idealPublishDate.error) + } + backgroundColor="blue" + /> + + + {/* Request fastTrack */} + + { + updateState('fastTrack', !draft.fastTrack.value) + }} + /> + + + + + + { - + {formatDate(draft.idealPublishDate.value) || t(editorMsgs.idealPublishDate_default)} diff --git a/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx b/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx index 85e7a814302d..4ce96964fa05 100644 --- a/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx +++ b/libs/portals/admin/regulations-admin/src/components/EditSignature.tsx @@ -3,10 +3,8 @@ import { AlertMessage, Box, Button, - Checkbox, Column, Columns, - DatePicker, Inline, Input, InputFileUpload, @@ -15,7 +13,6 @@ import { } from '@island.is/island-ui/core' import { useDraftingState } from '../state/useDraftingState' import { editorMsgs as msg } from '../lib/messages' -import { getMinPublishDate } from '../utils' import { EditorInput } from './EditorInput' import { HTMLText, PlainText, URLString } from '@island.is/regulations' @@ -216,42 +213,6 @@ export const EditSignature = () => { - - - {/* idealPublishDate Input */} - - updateState('idealPublishDate', date) - } - hasError={ - draft.idealPublishDate.showError && - !!draft.idealPublishDate.error - } - errorMessage={ - draft.idealPublishDate.error && - t(draft.idealPublishDate.error) - } - backgroundColor="blue" - /> - {/* Request fastTrack */} - { - updateState('fastTrack', !draft.fastTrack.value) - }} - /> - - {/* Clear idealPublishDate */} {!!draft.idealPublishDate.value && ( diff --git a/libs/portals/admin/regulations-admin/src/components/impacts/ImpactDate.tsx b/libs/portals/admin/regulations-admin/src/components/impacts/ImpactDate.tsx index 967928795eb3..14560cfc7830 100644 --- a/libs/portals/admin/regulations-admin/src/components/impacts/ImpactDate.tsx +++ b/libs/portals/admin/regulations-admin/src/components/impacts/ImpactDate.tsx @@ -1,7 +1,9 @@ -import { Box, Button, DatePicker } from '@island.is/island-ui/core' +import { Box, Button, DatePicker, Checkbox } from '@island.is/island-ui/core' import { impactMsgs } from '../../lib/messages' import { DraftImpactForm } from '../../state/types' import { useLocale } from '@island.is/localization' +import { useEffect, useState } from 'react' +import { MessageDescriptor } from 'react-intl' // --------------------------------------------------------------------------- @@ -15,37 +17,61 @@ export type ImpactDateProps = { export const ImpactDate = (props: ImpactDateProps) => { const { impact, onChange, size = 'half', minDate, readOnly } = props - + const [hasCustomDate, setHasCustomDate] = useState(false) + const [selectedDate, setSelectedDate] = useState() + const [errorMessage, setErrorMessage] = useState() const date = impact.date + useEffect(() => { + if (hasCustomDate) { + setSelectedDate(date.value || minDate) + } + }, [hasCustomDate, date.value, minDate]) + + useEffect(() => { + setErrorMessage(date.error) + }, [impact.date]) + const t = useLocale().formatMessage return ( - - {!readOnly && !!date.value && ( - - )} + + setHasCustomDate(!hasCustomDate)} + /> + + {hasCustomDate ? ( + <> + + {!readOnly && !!date.value && ( + + )} + + ) : undefined} ) } diff --git a/libs/portals/admin/regulations-admin/src/lib/messages.ts b/libs/portals/admin/regulations-admin/src/lib/messages.ts index 2a01cfcc7a69..a0e9585ad88d 100644 --- a/libs/portals/admin/regulations-admin/src/lib/messages.ts +++ b/libs/portals/admin/regulations-admin/src/lib/messages.ts @@ -386,6 +386,11 @@ export const impactMsgs = defineMessages({ defaultMessage: 'Ertu viss um að þú viljir eyða þessari skráningu?', }, + specificDateApply: { + id: 'ap.regulations-admin:change-applied-on-specific-date', + defaultMessage: 'Breyting tekur gildi á ákveðinni dagsetningu', + }, + // --------------------------------------------------------------------------- regExplainer: { diff --git a/libs/portals/core/src/components/PortalProvider.tsx b/libs/portals/core/src/components/PortalProvider.tsx index 8fbc414f1ec0..5aff6abb53a8 100644 --- a/libs/portals/core/src/components/PortalProvider.tsx +++ b/libs/portals/core/src/components/PortalProvider.tsx @@ -12,6 +12,7 @@ import { export type PortalMeta = { portalType: PortalType basePath: string + portalTitle: string } export type PortalContextProps = { diff --git a/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx b/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx index 99ffbce9135d..2734c258ba63 100644 --- a/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx +++ b/libs/portals/core/src/hooks/useSingleNavigationItem.spec.tsx @@ -73,7 +73,11 @@ describe('useSingleNavigationItem hook', () => { diff --git a/libs/portals/core/src/screens/ModuleRoute.tsx b/libs/portals/core/src/screens/ModuleRoute.tsx index 19b23deb566b..ef37780576d7 100644 --- a/libs/portals/core/src/screens/ModuleRoute.tsx +++ b/libs/portals/core/src/screens/ModuleRoute.tsx @@ -1,5 +1,6 @@ import React, { Suspense, useEffect } from 'react' import { useLocation } from 'react-router-dom' +import { useLocale } from '@island.is/localization' import { PortalRoute } from '../types/portalCore' import { usePortalMeta } from '../components/PortalProvider' import { plausiblePageviewDetail } from '../utils/plausible' @@ -12,8 +13,9 @@ type ModuleRouteProps = { export const ModuleRoute = React.memo(({ route }: ModuleRouteProps) => { const location = useLocation() - const { basePath } = usePortalMeta() + const { basePath, portalTitle } = usePortalMeta() const { userInfo } = useAuth() + const { formatMessage } = useLocale() useEffect(() => { if (route.element !== undefined) { @@ -25,6 +27,12 @@ export const ModuleRoute = React.memo(({ route }: ModuleRouteProps) => { ? 'company' : 'person', }) + + if (route.name && route.path !== '/') { + document.title = `${formatMessage(route.name)} - ${portalTitle}` + } else { + document.title = portalTitle + } } }, [basePath, location, route.path, route.element]) diff --git a/libs/service-portal/documents/src/hooks/useMailActionV2.ts b/libs/service-portal/documents/src/hooks/useMailActionV2.ts index eec6ed8c9fbe..c6bc0b567de5 100644 --- a/libs/service-portal/documents/src/hooks/useMailActionV2.ts +++ b/libs/service-portal/documents/src/hooks/useMailActionV2.ts @@ -91,7 +91,9 @@ export const useMailAction = () => { if (refetch) { refetch(fetchObject) } - toast.success(formatMessage(docMessages.successArchiveMulti)) + if (action === 'archive') { + toast.success(formatMessage(docMessages.successArchiveMulti)) + } } else { toast.error(formatMessage(m.errorTitle)) } diff --git a/libs/service-portal/petitions/src/lib/utils.ts b/libs/service-portal/petitions/src/lib/utils.ts index 62d52a4e71a8..62b96089e3c6 100644 --- a/libs/service-portal/petitions/src/lib/utils.ts +++ b/libs/service-portal/petitions/src/lib/utils.ts @@ -1,4 +1,3 @@ -import { Endorsement } from '@island.is/api/schema' import format from 'date-fns/format' export const formatDate = (date: string) => { @@ -9,16 +8,4 @@ export const formatDate = (date: string) => { } } -export const PAGE_SIZE = 10 - -export function paginate( - petitions: Endorsement[], - pageSize: number, - pageNumber: number, -) { - return petitions.slice((pageNumber - 1) * pageSize, pageNumber * pageSize) -} - -export function pages(petitionsLength: number) { - return Math.ceil(petitionsLength / PAGE_SIZE) -} +export const pageSize = 10 diff --git a/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/DownloadPdf/index.tsx b/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/DownloadPdf/index.tsx index 4dfbf450ed28..5fd7b36afc15 100644 --- a/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/DownloadPdf/index.tsx +++ b/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/DownloadPdf/index.tsx @@ -21,6 +21,7 @@ const MyPdfDocument = (data: { petitionSigners: PaginatedEndorsementResponse }) => { const { petition, petitionSigners } = data + return ( @@ -132,8 +133,8 @@ export const pdfStyles = StyleSheet.create({ paddingBottom: 30, }, tableView: { - paddingTop: 25, - borderTop: `1px solid ${dark200}`, + paddingTop: 20, + borderTop: `0.5px solid ${dark200}`, }, tableRow: { flexDirection: 'row', diff --git a/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/downloadCSV.ts b/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/downloadCSV.ts index 6b8f601cb8ad..04c4184a6658 100644 --- a/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/downloadCSV.ts +++ b/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/downloadCSV.ts @@ -5,18 +5,18 @@ import { } from '@island.is/api/schema' import { formatDate } from '../../../lib/utils' -export const getCSV = async ( - signers: PaginatedEndorsementResponse, - fileName: string, -) => { - const name = `${fileName}` - const dataArray = signers.data.map((item: Endorsement) => [ +export const getCSV = async (signers: PaginatedEndorsementResponse) => { + const dataArray = signers.data?.map((item: Endorsement) => [ formatDate(item.created) ?? '', item.meta.fullName ?? '', item.meta.locality ?? '', ]) - await downloadCSV(name, ['Dagsetning', 'Nafn', 'Sveitarfélag'], dataArray) + await downloadCSV( + 'Undirskriftalisti', + ['Dagsetning', 'Nafn', 'Sveitarfélag'], + dataArray, + ) } export const downloadCSV = async ( diff --git a/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx b/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx index 26e67558677e..7660415c2269 100644 --- a/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx +++ b/libs/service-portal/petitions/src/screens/PetitionsTable/ExportPetition/index.tsx @@ -1,5 +1,10 @@ -import { FC, ReactElement } from 'react' -import { Box, DropdownMenu, Button } from '@island.is/island-ui/core' +import { FC, ReactElement, useEffect, useState } from 'react' +import { + Box, + DropdownMenu, + Button, + FocusableBox, +} from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' import * as styles from '../styles.css' import { m } from '../../../lib/messages' @@ -7,17 +12,14 @@ import copyToClipboard from 'copy-to-clipboard' import { toast } from 'react-toastify' import { usePDF } from '@react-pdf/renderer' import MyPdfDocument from './DownloadPdf' -import { - EndorsementList, - PaginatedEndorsementResponse, -} from '@island.is/api/schema' +import { EndorsementList } from '@island.is/api/schema' import cn from 'classnames' +import { useGetAllPetitionEndorsements } from '../../hooks' +import { getCSV } from './downloadCSV' interface Props { petition?: EndorsementList - petitionSigners: PaginatedEndorsementResponse petitionId: string - onGetCSV: () => void dropdownItems?: { href?: string onClick?: () => void @@ -34,22 +36,29 @@ const baseUrl = `${document.location.origin}/undirskriftalistar/` const DropdownExport: FC> = ({ petition, - petitionSigners, petitionId, - onGetCSV, dropdownItems = [], }) => { useNamespaces('sp.petitions') const { formatMessage } = useLocale() - const [document] = usePDF({ + const [canGetAllEndorsements, setCanGetAllEndorsements] = useState(false) + const { allEndorsements } = useGetAllPetitionEndorsements( + petitionId, + canGetAllEndorsements, + ) + + const [instance, updateInstance] = usePDF({ document: ( - + ), }) - if (document.error) { - console.warn(document.error) - } + + useEffect(() => { + if (allEndorsements.data?.length > 0) { + updateInstance() + } + }, [allEndorsements]) return ( @@ -68,56 +77,58 @@ const DropdownExport: FC> = ({ {formatMessage(m.copyLinkToList)} - { - return ( - + ) + }, + }, + { + title: formatMessage(m.asPdf), + render: () => ( + - {formatMessage(m.linkToList)} - - ) + {formatMessage(m.asPdf)} + + ), + }, + { + onClick: () => getCSV(allEndorsements), + title: formatMessage(m.asCsv), }, - }, - { - title: formatMessage(m.asPdf), - render: () => ( - - {formatMessage(m.asPdf)} - - ), - }, - { - onClick: () => onGetCSV(), - title: formatMessage(m.asCsv), - }, - ...dropdownItems, - ]} - title={formatMessage(m.downloadPetitions)} - /> + ...dropdownItems, + ]} + title={formatMessage(m.downloadPetitions)} + /> + ) } diff --git a/libs/service-portal/petitions/src/screens/PetitionsTable/index.tsx b/libs/service-portal/petitions/src/screens/PetitionsTable/index.tsx index 6ee9ba4549ec..fd4fabdd3ac6 100644 --- a/libs/service-portal/petitions/src/screens/PetitionsTable/index.tsx +++ b/libs/service-portal/petitions/src/screens/PetitionsTable/index.tsx @@ -7,40 +7,31 @@ import { Text, } from '@island.is/island-ui/core' import { useLocale, useNamespaces } from '@island.is/localization' -import { formatDate, pages, PAGE_SIZE, paginate } from '../../lib/utils' +import { formatDate, pageSize } from '../../lib/utils' import { m } from '../../lib/messages' import DropdownExport from './ExportPetition' -import { - Endorsement, - EndorsementList, - PaginatedEndorsementResponse, -} from '@island.is/api/schema' -import { getCSV } from './ExportPetition/downloadCSV' +import { Endorsement, EndorsementList } from '@island.is/api/schema' +import { useGetPetitionEndorsementsPaginated } from '../hooks' const PetitionsTable = (data: { canEdit: boolean listId: string petition?: EndorsementList - petitionSigners: PaginatedEndorsementResponse }) => { useNamespaces('sp.petitions') const { formatMessage } = useLocale() const [page, setPage] = useState(1) - const [totalPages, setTotalPages] = useState(0) - const [petitionSigners, setPetitionSigners] = useState( - data.petitionSigners?.data ?? [], + const [cursor, setCursor] = useState('') + const [pageDirection, setPageDirection] = useState<'before' | 'after' | ''>( + '', ) - const handlePagination = (page: number, petitionSigners: Endorsement[]) => { - setPage(page) - setTotalPages(pages(petitionSigners?.length)) - setPetitionSigners(paginate(petitionSigners, PAGE_SIZE, page)) - } + const { endorsements, loadingEndorsements, refetch } = + useGetPetitionEndorsementsPaginated(data.listId, cursor, pageDirection) useEffect(() => { - setPetitionSigners(data.petitionSigners?.data ?? []) - handlePagination(1, data.petitionSigners?.data ?? []) - }, [data]) + refetch() + }, [cursor, pageDirection]) return ( @@ -48,12 +39,7 @@ const PetitionsTable = (data: { {formatMessage(m.petitionsOverview)} {data.canEdit && ( - getCSV(data.petitionSigners, 'Undirskriftalisti')} - /> + )} @@ -69,39 +55,52 @@ const PetitionsTable = (data: { - {petitionSigners?.map((petition: Endorsement) => { - return ( - - - {formatDate(petition.created)} - - - {petition.meta.fullName - ? petition.meta.fullName - : formatMessage(m.noName)} - - {data.canEdit && ( + {!loadingEndorsements && + endorsements.data?.map((petition: Endorsement) => { + return ( + + + {formatDate(petition.created)} + - {petition.meta.locality ? petition.meta.locality : ''} + {petition.meta.fullName + ? petition.meta.fullName + : formatMessage(m.noName)} - )} - - ) - })} + {data.canEdit && ( + + {petition.meta.locality ? petition.meta.locality : ''} + + )} + + ) + })} - {petitionSigners && !!petitionSigners.length ? ( + {endorsements && !!endorsements.data?.length ? ( ( + totalItems={endorsements.totalCount} + itemsPerPage={pageSize} + renderLink={(p, className, children) => ( - handlePagination(page, data.petitionSigners?.data) - } + component="button" + onClick={() => { + setPage(p) + if (p > page && endorsements.pageInfo.hasNextPage) { + setPageDirection('after') + setCursor(endorsements.pageInfo.endCursor ?? '') + } else if ( + p < page && + endorsements.pageInfo.hasPreviousPage + ) { + setPageDirection('before') + setCursor(endorsements.pageInfo.startCursor ?? '') + } + }} > {children} diff --git a/libs/service-portal/petitions/src/screens/ViewOwnedList/index.tsx b/libs/service-portal/petitions/src/screens/ViewOwnedList/index.tsx index 03ff62f036f5..b5a0c9848fbb 100644 --- a/libs/service-portal/petitions/src/screens/ViewOwnedList/index.tsx +++ b/libs/service-portal/petitions/src/screens/ViewOwnedList/index.tsx @@ -20,8 +20,8 @@ import { CloseList, OpenList } from '../queries' import Skeleton from '../Skeletons/Skeleton' import { Modal } from '@island.is/service-portal/core' import { + useGetPetitionEndorsementsPaginated, useGetSinglePetition, - useGetSinglePetitionEndorsements, } from '../hooks' import { PaginatedEndorsementResponse, @@ -66,8 +66,8 @@ const ViewOwnedList = () => { isListOpen ? new Date(petition?.closedDate) : undefined, ) - const { petitionEndorsements, loadingSigners } = - useGetSinglePetitionEndorsements(listId) + const { endorsements, loadingEndorsements } = + useGetPetitionEndorsementsPaginated(listId) useEffect(() => { setIsListOpen(new Date() <= new Date(petition?.closedDate)) @@ -113,7 +113,7 @@ const ViewOwnedList = () => { return ( - {!loadingPetition && !loadingSigners ? ( + {!loadingPetition && !loadingEndorsements ? ( <> @@ -137,7 +137,7 @@ const ViewOwnedList = () => { { - (petitionEndorsements as PaginatedEndorsementResponse) + (endorsements as PaginatedEndorsementResponse) .totalCount } @@ -299,14 +299,7 @@ const ViewOwnedList = () => { )} - + ) : ( diff --git a/libs/service-portal/petitions/src/screens/ViewSignedList/index.tsx b/libs/service-portal/petitions/src/screens/ViewSignedList/index.tsx index 09238b68ad51..fac42680959a 100644 --- a/libs/service-portal/petitions/src/screens/ViewSignedList/index.tsx +++ b/libs/service-portal/petitions/src/screens/ViewSignedList/index.tsx @@ -22,9 +22,9 @@ import { UnendorseList } from '../queries' import Skeleton from '../Skeletons/Skeleton' import { + useGetPetitionEndorsementsPaginated, useGetSingleEndorsement, useGetSinglePetition, - useGetSinglePetitionEndorsements, } from '../hooks' import { EndorsementList, @@ -44,7 +44,7 @@ const ViewSignedList = () => { const userHasSigned = useGetSingleEndorsement(listId) const [unSignList, { loading: isLoading }] = useMutation(UnendorseList, { onCompleted: () => { - refetchSinglePetitionEndorsements() + refetch() }, }) @@ -56,8 +56,7 @@ const ViewSignedList = () => { const [modalIsOpen, setModalIsOpen] = useState(false) - const { petitionEndorsements, refetchSinglePetitionEndorsements } = - useGetSinglePetitionEndorsements(listId) + const { endorsements, refetch } = useGetPetitionEndorsementsPaginated(listId) useEffect(() => { setHasSigned(userHasSigned ? true : false) @@ -115,7 +114,7 @@ const ViewSignedList = () => { { - (petitionEndorsements as PaginatedEndorsementResponse) + (endorsements as PaginatedEndorsementResponse) .totalCount } @@ -180,15 +179,7 @@ const ViewSignedList = () => { )} - {isListOpen && ( - - )} + {isListOpen && } ) : ( diff --git a/libs/service-portal/petitions/src/screens/hooks.ts b/libs/service-portal/petitions/src/screens/hooks.ts index 4f2117a4da0e..c9ba8496143d 100644 --- a/libs/service-portal/petitions/src/screens/hooks.ts +++ b/libs/service-portal/petitions/src/screens/hooks.ts @@ -7,75 +7,42 @@ import { GetSingleEndorsement, GetSinglePetitionList, } from './queries' -import { - EndorsementList, - ExistsEndorsementResponse, - PaginatedEndorsementListResponse, - PaginatedEndorsementResponse, -} from '@island.is/api/schema' - -interface UserSignedLists { - endorsementSystemUserEndorsements: PaginatedEndorsementResponse -} -interface UserOwnsLists { - endorsementSystemUserEndorsementLists: PaginatedEndorsementListResponse -} -interface PetitionLists { - endorsementSystemFindEndorsementLists: PaginatedEndorsementListResponse -} -interface SinglePetition { - endorsementSystemGetSingleEndorsementList?: EndorsementList -} -interface SinglePetitionEndorsements { - endorsementSystemGetEndorsements?: PaginatedEndorsementResponse -} -interface SingleEndorsement { - endorsementSystemGetSingleEndorsement?: ExistsEndorsementResponse -} +import { pageSize } from '../lib/utils' export const useGetAllPetitionLists = () => { - const { data: endorsementListsResponse } = useQuery( - GetAllEndorsementsLists, - { - variables: { - input: { - tags: 'generalPetition', - limit: 1000, - }, + const { data: endorsementListsResponse } = useQuery(GetAllEndorsementsLists, { + variables: { + input: { + tags: 'generalPetition', + limit: 1000, }, }, - ) + }) return endorsementListsResponse?.endorsementSystemFindEndorsementLists ?? [] } export const useGetListsUserSigned = () => { - const { data: endorsementResponse } = useQuery( - GetListsUserSigned, - { - variables: { - input: { - tags: 'generalPetition', - limit: 1000, - }, + const { data: endorsementResponse } = useQuery(GetListsUserSigned, { + variables: { + input: { + tags: 'generalPetition', + limit: 1000, }, }, - ) + }) return endorsementResponse?.endorsementSystemUserEndorsements ?? [] } export const useListsUserOwns = () => { - const { data: endorsementResponse } = useQuery( - EndorsementListsUserOwns, - { - variables: { - input: { - tags: 'generalPetition', - limit: 1000, - }, + const { data: endorsementResponse } = useQuery(EndorsementListsUserOwns, { + variables: { + input: { + tags: 'generalPetition', + limit: 1000, }, }, - ) + }) return endorsementResponse?.endorsementSystemUserEndorsementLists ?? [] } @@ -84,7 +51,7 @@ export const useGetSinglePetition = (listId: string) => { data: petition, refetch: refetchSinglePetition, loading: loadingPetition, - } = useQuery(GetSinglePetitionList, { + } = useQuery(GetSinglePetitionList, { variables: { input: { listId: listId, @@ -97,38 +64,64 @@ export const useGetSinglePetition = (listId: string) => { } export const useGetSingleEndorsement = (listId: string) => { - const { data: endorsement } = useQuery( - GetSingleEndorsement, - { - variables: { - input: { - listId: listId, - }, + const { data: endorsement } = useQuery(GetSingleEndorsement, { + variables: { + input: { + listId: listId, }, }, - ) + }) return endorsement?.endorsementSystemGetSingleEndorsement?.hasEndorsed } -export const useGetSinglePetitionEndorsements = (listId: string) => { +export const useGetPetitionEndorsementsPaginated = ( + listId: string, + cursor?: string, + pageDirection?: 'before' | 'after' | '', +) => { const { - data: endorsements, - refetch: refetchSinglePetitionEndorsements, - loading: loadingSigners, - } = useQuery(GetEndorsements, { + data: endorsementsPage, + loading: loadingEndorsements, + refetch, + } = useQuery(GetEndorsements, { variables: { input: { + before: pageDirection === 'before' ? cursor : '', + after: pageDirection === 'after' ? cursor : '', listId: listId, - limit: 1000, + limit: pageSize, }, }, }) - const petitionEndorsements = - endorsements?.endorsementSystemGetEndorsements ?? [] + const endorsements = endorsementsPage?.endorsementSystemGetEndorsements ?? [] + return { + endorsements, + refetch, + loadingEndorsements, + } +} + +export const useGetAllPetitionEndorsements = ( + listId: string, + canRetrieve: boolean, +) => { + const { data: endorsements, loading: loadingEndorsements } = useQuery( + GetEndorsements, + { + variables: { + input: { + listId: listId, + limit: 1000000, + }, + }, + skip: !canRetrieve, + }, + ) + + const allEndorsements = endorsements?.endorsementSystemGetEndorsements ?? [] return { - petitionEndorsements, - refetchSinglePetitionEndorsements, - loadingSigners, + allEndorsements, + loadingEndorsements, } } diff --git a/libs/service-portal/petitions/src/screens/queries.ts b/libs/service-portal/petitions/src/screens/queries.ts index aba56f248e54..f44d11b04366 100644 --- a/libs/service-portal/petitions/src/screens/queries.ts +++ b/libs/service-portal/petitions/src/screens/queries.ts @@ -88,6 +88,12 @@ export const GetEndorsements = gql` query endorsementSystemGetEndorsements($input: PaginatedEndorsementInput!) { endorsementSystemGetEndorsements(input: $input) { totalCount + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } data { id endorser diff --git a/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts b/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts index 39105b147f1a..58b075179ef7 100644 --- a/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts +++ b/libs/shared/connected/src/lib/generalPetition/GeneralPetitionLists/useGetPetitionLists.ts @@ -2,14 +2,6 @@ import gql from 'graphql-tag' import { useQuery } from '@apollo/client' import { useState } from 'react' -interface PetitionListResponse { - endorsementSystemGetGeneralPetitionLists: any -} - -interface PetitionListEndorsementsResponse { - endorsementSystemGetGeneralPetitionEndorsements: any -} - const GetGeneralPetitionLists = gql` query endorsementSystemGetGeneralPetitionLists( $input: EndorsementPaginationInput! @@ -37,27 +29,9 @@ const GetGeneralPetitionLists = gql` } ` -const GetGeneralPetitionListEndorsements = gql` - query endorsementSystemGetGeneralPetitionEndorsements( - $input: PaginatedEndorsementInput! - ) { - endorsementSystemGetGeneralPetitionEndorsements(input: $input) { - totalCount - data { - id - endorser - created - meta { - fullName - } - } - } - } -` - export const useGetPetitionLists = () => { const [pagination, setPagination] = useState({ after: '', before: '' }) - const { data, loading, error, fetchMore } = useQuery( + const { data, loading, error, fetchMore } = useQuery( GetGeneralPetitionLists, { variables: { @@ -124,23 +98,3 @@ export const useGetPetitionLists = () => { pageInfo: data?.endorsementSystemGetGeneralPetitionLists.pageInfo, } } - -export const useGetPetitionListEndorsements = (listId: string) => { - const { data: endorsementListsResponse } = - useQuery( - GetGeneralPetitionListEndorsements, - { - variables: { - input: { - listId: listId, - limit: 1000, - }, - }, - }, - ) - - return ( - endorsementListsResponse?.endorsementSystemGetGeneralPetitionEndorsements ?? - [] - ) -} diff --git a/libs/university-gateway/src/lib/types/modeOfDelivery.ts b/libs/university-gateway/src/lib/types/modeOfDelivery.ts index c848e9b9c76c..a023fdaf57c6 100644 --- a/libs/university-gateway/src/lib/types/modeOfDelivery.ts +++ b/libs/university-gateway/src/lib/types/modeOfDelivery.ts @@ -3,5 +3,4 @@ export enum ModeOfDelivery { REMOTE = 'REMOTE', ONLINE = 'ONLINE', MIXED = 'MIXED', - UNDEFINED = 'UNDEFINED', //TODO possibly stop allowing this value } diff --git a/package.json b/package.json index 214622f18a95..dfbacb32113c 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "@formatjs/intl-locale": "2.4.33", "@formatjs/intl-numberformat": "7.1.5", "@hookform/error-message": "2.0.1", - "@island.is/regulations-tools": "0.9.0", + "@island.is/regulations-tools": "0.9.1", "@keyv/redis": "2.6.1", "@livechat/widget-core": "1.3.2", "@nestjs/apollo": "10.1.0", @@ -181,7 +181,7 @@ "font-awesome": "4.7.0", "form-data": "3.0.0", "formik": "2.2.9", - "framer-motion": "6.5.1", + "framer-motion": "9.0.0", "fridagar": "^3.2.0", "fuse.js": "7.0.0", "glob": "10.3.3", @@ -363,6 +363,9 @@ "@storybook/react-webpack5": "7.6.9", "@storybook/source-loader": "7.0.20", "@storybook/theming": "7.0.20", + "@swc-node/register": "1.9.1", + "@swc/core": "1.5.7", + "@swc/helpers": "0.5.11", "@testing-library/cypress": "8.0.3", "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "15.0.6", diff --git a/tsconfig.base.json b/tsconfig.base.json index 762c99a5153f..87f7ee0d7d6d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -366,6 +366,9 @@ "@island.is/application/templates/financial-aid": [ "libs/application/templates/financial-aid/src/index.ts" ], + "@island.is/application/templates/financial-statement-cemetery": [ + "libs/application/templates/financial-statement-cemetery/src/index.ts" + ], "@island.is/application/templates/financial-statements-inao": [ "libs/application/templates/financial-statements-inao/src/index.ts" ], @@ -1080,7 +1083,13 @@ "@island.is/university-gateway": ["libs/university-gateway/src/index.ts"], "@island.is/user-monitoring": ["libs/user-monitoring/src/index.ts"], "@island.is/web/*": ["apps/web/*"], - "@island.is/web/component*": ["apps/web/component*/real.ts"] + "@island.is/web/component*": ["apps/web/component*/real.ts"], + "api/domains/financial-statement-cemetery": [ + "libs/api/domains/financial-statement-cemetery/src/index.ts" + ], + "financial-statement-cemetery": [ + "libs/application/templates/financial-statement-cemetery/src/index.ts" + ] } } } diff --git a/yarn.lock b/yarn.lock index 57a87eeaf986..75bda68988a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12131,9 +12131,9 @@ __metadata: languageName: unknown linkType: soft -"@island.is/regulations-tools@npm:0.9.0": - version: 0.9.0 - resolution: "@island.is/regulations-tools@npm:0.9.0" +"@island.is/regulations-tools@npm:0.9.1": + version: 0.9.1 + resolution: "@island.is/regulations-tools@npm:0.9.1" dependencies: "@hugsmidjan/htmldiff-js": ^1.3.0 "@hugsmidjan/qj": ^4.10.2 @@ -12147,7 +12147,7 @@ __metadata: peerDependencies: react: ">=16.8 <20" react-dom: ">=16.8 <20" - checksum: 9d4950e590721f78c60644ee7f2c16dfb132960c9383e71cce3dbe9037b8e734c6e8609eb44a623e843ec13187759efd45245078ba4e9c60d456068378a28e62 + checksum: e141dc6511c7e6e454389f44219a3784d8aa5072c2731020236b1c7964be524471644a0b3917296bb2a3505d451239639ab59f9e68960a25abef939360645c6f languageName: node linkType: hard @@ -13010,68 +13010,68 @@ __metadata: languageName: node linkType: hard -"@motionone/animation@npm:^10.12.0": - version: 10.14.0 - resolution: "@motionone/animation@npm:10.14.0" +"@motionone/animation@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/animation@npm:10.18.0" dependencies: - "@motionone/easing": ^10.14.0 - "@motionone/types": ^10.14.0 - "@motionone/utils": ^10.14.0 + "@motionone/easing": ^10.18.0 + "@motionone/types": ^10.17.1 + "@motionone/utils": ^10.18.0 tslib: ^2.3.1 - checksum: 664f7f036418e3604bdcb267636ed21057a2036a21cdff90f9fa49b62e788cdfc3149f6f9a27e9530ebfbee3a39d6e7dcd73adaa95d16af25bfcf1e01bd95cbb + checksum: 841cb9f4843a89e5e4560b9f960f52cbe78afc86f87c769f71e9edb3aadd53fb87982b7e11914428f228b29fd580756be531369c2ffac06432550afa4e87d1c3 languageName: node linkType: hard -"@motionone/dom@npm:10.12.0": - version: 10.12.0 - resolution: "@motionone/dom@npm:10.12.0" +"@motionone/dom@npm:^10.15.3": + version: 10.18.0 + resolution: "@motionone/dom@npm:10.18.0" dependencies: - "@motionone/animation": ^10.12.0 - "@motionone/generators": ^10.12.0 - "@motionone/types": ^10.12.0 - "@motionone/utils": ^10.12.0 + "@motionone/animation": ^10.18.0 + "@motionone/generators": ^10.18.0 + "@motionone/types": ^10.17.1 + "@motionone/utils": ^10.18.0 hey-listen: ^1.0.8 tslib: ^2.3.1 - checksum: 123356f28e44362c4f081aae3df22e576f46bfcb07e01257b2ac64a115668448f29b8de67e4b6e692c5407cffb78ffe7cf9fa1bc064007482bab5dd23a69d380 + checksum: b11f5366b05d1a93d7df0c91923f0339412e5eb65de2010b1d0484bcbb8027d352334722ce6b839f1be776585d849d1bcbee9d96b2445f6bb6e82301fe67bbeb languageName: node linkType: hard -"@motionone/easing@npm:^10.14.0": - version: 10.14.0 - resolution: "@motionone/easing@npm:10.14.0" +"@motionone/easing@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/easing@npm:10.18.0" dependencies: - "@motionone/utils": ^10.14.0 + "@motionone/utils": ^10.18.0 tslib: ^2.3.1 - checksum: c845b7a445d6dbfb6d9d830fe8c88050345988fb6e423e63c1962506995e99efe899d6e7b14a8960b7df27aa279dfdfb02202a20de01bc65dcbd416ec2315d37 + checksum: 6bd37f7a9d5a88f868cc0ad6e47d2ba8d9fefd7da84fccfea7ed77ec08c2e6d1e42df88dda462665102a5cf03f748231a1a077de7054b5a8ccb0fbf36f61b1e7 languageName: node linkType: hard -"@motionone/generators@npm:^10.12.0": - version: 10.14.0 - resolution: "@motionone/generators@npm:10.14.0" +"@motionone/generators@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/generators@npm:10.18.0" dependencies: - "@motionone/types": ^10.14.0 - "@motionone/utils": ^10.14.0 + "@motionone/types": ^10.17.1 + "@motionone/utils": ^10.18.0 tslib: ^2.3.1 - checksum: 14cb500441f7b19dd84672c00ebeda0380fa151922118ac7e1bd13bf7b7b6bd87f876454863f3acf0849bb9c7b83a496eb0fba8f7b4d720f99bf5fa2121f056f + checksum: 51a0e075681697b11d0771998cac8c76a745f00141502f81adb953896992b7f49478965e4afe696bc83361afaae8d2f1057d71c25b21035fe67258ff73764f1c languageName: node linkType: hard -"@motionone/types@npm:^10.12.0, @motionone/types@npm:^10.14.0": - version: 10.14.0 - resolution: "@motionone/types@npm:10.14.0" - checksum: 2fbd65f925c786b190d99867010960ffb458beda5f9b5499eeb0822094871ecbeded1aae917c194b605d80f0c56a55a13ff1e0fa4f6bad3ad8ff32fe5a3201dc +"@motionone/types@npm:^10.17.1": + version: 10.17.1 + resolution: "@motionone/types@npm:10.17.1" + checksum: 3fa74db64e371e61a7f7669d7d541d11c9a8dd871032d59c69041e3b2e07a67ad2ed8767cb9273bac90eed4e1f76efc1f14c8673c2e9a288f6070ee0fef64a25 languageName: node linkType: hard -"@motionone/utils@npm:^10.12.0, @motionone/utils@npm:^10.14.0": - version: 10.14.0 - resolution: "@motionone/utils@npm:10.14.0" +"@motionone/utils@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/utils@npm:10.18.0" dependencies: - "@motionone/types": ^10.14.0 + "@motionone/types": ^10.17.1 hey-listen: ^1.0.8 tslib: ^2.3.1 - checksum: eb4c0a50e458ba7e7944dba7d0b063665744fed60aef9b4670bbc26fe02bed43f26595e9515f8ca6e4899ff5f31e7d81708feb42c16bf6e91c871d310085bc48 + checksum: a27f9afde693a0cbbbcb33962b12bbe40dd2cfa514b0732f3c7953c5ef4beed738e1e8172a2de89e3b9f74a253ef0a70d7f3efb730be97b77d7176a3ffacb67a languageName: node linkType: hard @@ -17911,6 +17911,16 @@ __metadata: languageName: node linkType: hard +"@swc-node/core@npm:^1.13.1": + version: 1.13.2 + resolution: "@swc-node/core@npm:1.13.2" + peerDependencies: + "@swc/core": ">= 1.4.13" + "@swc/types": ">= 0.1" + checksum: 1ad4f5761f0bdf6833ce7bb35273241342ea1629c98d050ece0a0c9222ab91968b14a5788959898d6524fdafc96895374d182eb798aca996df253e9ff5f364f9 + languageName: node + linkType: hard + "@swc-node/core@npm:^1.8.2": version: 1.8.2 resolution: "@swc-node/core@npm:1.8.2" @@ -17920,6 +17930,23 @@ __metadata: languageName: node linkType: hard +"@swc-node/register@npm:1.9.1": + version: 1.9.1 + resolution: "@swc-node/register@npm:1.9.1" + dependencies: + "@swc-node/core": ^1.13.1 + "@swc-node/sourcemap-support": ^0.5.0 + colorette: ^2.0.20 + debug: ^4.3.4 + pirates: ^4.0.6 + tslib: ^2.6.2 + peerDependencies: + "@swc/core": ">= 1.4.13" + typescript: ">= 4.3" + checksum: 2e35e78cdde7684fc8e05b092d12c62b056e0c3a7eea1caa3a1e7f3fd80f818864388ef83e183ca66effb9ca566fa7309e2b7382b8d18dab65a1b2e1419d7546 + languageName: node + linkType: hard + "@swc-node/register@npm:^1.4.2": version: 1.4.2 resolution: "@swc-node/register@npm:1.4.2" @@ -17944,6 +17971,16 @@ __metadata: languageName: node linkType: hard +"@swc-node/sourcemap-support@npm:^0.5.0": + version: 0.5.1 + resolution: "@swc-node/sourcemap-support@npm:0.5.1" + dependencies: + source-map-support: ^0.5.21 + tslib: ^2.6.3 + checksum: 307be2a52c10f3899871dc316190584e7a6e48375de5b84638cd0ca96681c4ce89891b9f7e86dedb93aac106dea7eff42ac2192f443ac1a1242a206ec93d0caf + languageName: node + linkType: hard + "@swc/core-android-arm-eabi@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-android-arm-eabi@npm:1.2.168" @@ -17972,6 +18009,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-darwin-arm64@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-darwin-arm64@npm:1.5.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@swc/core-darwin-x64@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-darwin-x64@npm:1.2.168" @@ -17986,6 +18030,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-darwin-x64@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-darwin-x64@npm:1.5.7" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@swc/core-freebsd-x64@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-freebsd-x64@npm:1.2.168" @@ -18007,6 +18058,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm-gnueabihf@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.7" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@swc/core-linux-arm64-gnu@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-linux-arm64-gnu@npm:1.2.168" @@ -18021,6 +18079,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm64-gnu@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-linux-arm64-gnu@npm:1.5.7" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@swc/core-linux-arm64-musl@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-linux-arm64-musl@npm:1.2.168" @@ -18035,6 +18100,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-arm64-musl@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-linux-arm64-musl@npm:1.5.7" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@swc/core-linux-x64-gnu@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-linux-x64-gnu@npm:1.2.168" @@ -18049,6 +18121,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-x64-gnu@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-linux-x64-gnu@npm:1.5.7" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@swc/core-linux-x64-musl@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-linux-x64-musl@npm:1.2.168" @@ -18063,6 +18142,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-linux-x64-musl@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-linux-x64-musl@npm:1.5.7" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@swc/core-win32-arm64-msvc@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-win32-arm64-msvc@npm:1.2.168" @@ -18077,6 +18163,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-arm64-msvc@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-win32-arm64-msvc@npm:1.5.7" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@swc/core-win32-ia32-msvc@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-win32-ia32-msvc@npm:1.2.168" @@ -18091,6 +18184,13 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-ia32-msvc@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-win32-ia32-msvc@npm:1.5.7" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@swc/core-win32-x64-msvc@npm:1.2.168": version: 1.2.168 resolution: "@swc/core-win32-x64-msvc@npm:1.2.168" @@ -18105,6 +18205,59 @@ __metadata: languageName: node linkType: hard +"@swc/core-win32-x64-msvc@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core-win32-x64-msvc@npm:1.5.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@swc/core@npm:1.5.7": + version: 1.5.7 + resolution: "@swc/core@npm:1.5.7" + dependencies: + "@swc/core-darwin-arm64": 1.5.7 + "@swc/core-darwin-x64": 1.5.7 + "@swc/core-linux-arm-gnueabihf": 1.5.7 + "@swc/core-linux-arm64-gnu": 1.5.7 + "@swc/core-linux-arm64-musl": 1.5.7 + "@swc/core-linux-x64-gnu": 1.5.7 + "@swc/core-linux-x64-musl": 1.5.7 + "@swc/core-win32-arm64-msvc": 1.5.7 + "@swc/core-win32-ia32-msvc": 1.5.7 + "@swc/core-win32-x64-msvc": 1.5.7 + "@swc/counter": ^0.1.2 + "@swc/types": 0.1.7 + peerDependencies: + "@swc/helpers": ^0.5.0 + dependenciesMeta: + "@swc/core-darwin-arm64": + optional: true + "@swc/core-darwin-x64": + optional: true + "@swc/core-linux-arm-gnueabihf": + optional: true + "@swc/core-linux-arm64-gnu": + optional: true + "@swc/core-linux-arm64-musl": + optional: true + "@swc/core-linux-x64-gnu": + optional: true + "@swc/core-linux-x64-musl": + optional: true + "@swc/core-win32-arm64-msvc": + optional: true + "@swc/core-win32-ia32-msvc": + optional: true + "@swc/core-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc/helpers": + optional: true + checksum: 8e11626b782df914ee53dcb3e7f52e4bd2e1a896873c0e76ec674d19d05d87eec06e2223e0958d68ef1e0cdfb4cd505e3b1a297561e9506063738337f0c5409d + languageName: node + linkType: hard + "@swc/core@npm:^1.2.119, @swc/core@npm:^1.2.152": version: 1.2.168 resolution: "@swc/core@npm:1.2.168" @@ -18201,13 +18354,22 @@ __metadata: languageName: node linkType: hard -"@swc/counter@npm:^0.1.3": +"@swc/counter@npm:^0.1.2, @swc/counter@npm:^0.1.3": version: 0.1.3 resolution: "@swc/counter@npm:0.1.3" checksum: df8f9cfba9904d3d60f511664c70d23bb323b3a0803ec9890f60133954173047ba9bdeabce28cd70ba89ccd3fd6c71c7b0bd58be85f611e1ffbe5d5c18616598 languageName: node linkType: hard +"@swc/helpers@npm:0.5.11": + version: 0.5.11 + resolution: "@swc/helpers@npm:0.5.11" + dependencies: + tslib: ^2.4.0 + checksum: 5d85e641d993264f38871bf53e7509da959cdff7646a40d876153291146b9d0aa701518546e5bfef18fa17c5944333bbeb66c2f0d7a570e8c5535d0937d76bd9 + languageName: node + linkType: hard + "@swc/helpers@npm:0.5.5": version: 0.5.5 resolution: "@swc/helpers@npm:0.5.5" @@ -18246,7 +18408,7 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.7": +"@swc/types@npm:0.1.7, @swc/types@npm:^0.1.7": version: 0.1.7 resolution: "@swc/types@npm:0.1.7" dependencies: @@ -25898,7 +26060,7 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.10, colorette@npm:^2.0.19": +"colorette@npm:^2.0.10, colorette@npm:^2.0.19, colorette@npm:^2.0.20": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d @@ -32300,33 +32462,21 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:6.5.1": - version: 6.5.1 - resolution: "framer-motion@npm:6.5.1" +"framer-motion@npm:9.0.0": + version: 9.0.0 + resolution: "framer-motion@npm:9.0.0" dependencies: "@emotion/is-prop-valid": ^0.8.2 - "@motionone/dom": 10.12.0 - framesync: 6.0.1 + "@motionone/dom": ^10.15.3 hey-listen: ^1.0.8 - popmotion: 11.0.3 - style-value-types: 5.0.0 - tslib: ^2.1.0 + tslib: ^2.4.0 peerDependencies: - react: ">=16.8 || ^17.0.0 || ^18.0.0" - react-dom: ">=16.8 || ^17.0.0 || ^18.0.0" + react: ^18.0.0 + react-dom: ^18.0.0 dependenciesMeta: "@emotion/is-prop-valid": optional: true - checksum: 737959063137b4ccafe01e0ac0c9e5a9531bf3f729f62c34ca7a5d7955e6664f70affd22b044f7db51df41acb21d120a4f71a860e17a80c4db766ad66f2153a1 - languageName: node - linkType: hard - -"framesync@npm:6.0.1": - version: 6.0.1 - resolution: "framesync@npm:6.0.1" - dependencies: - tslib: ^2.1.0 - checksum: a23ebe8f7e20a32c0b99c2f8175b6f07af3ec6316aad52a2316316a6d011d717af8d2175dcc2827031c59fabb30232ed3e19a720a373caba7f070e1eae436325 + checksum: eaafa53fc5e48dc6ea241c6259fa192bd467fb46bda416bf0d0855a81ed48761f66905bb9f39185746c3a39e7bc6c2474a407153bbed553a25e9f3578c624c80 languageName: node linkType: hard @@ -35976,7 +36126,7 @@ __metadata: "@graphql-codegen/typescript-react-apollo": 3.3.3 "@graphql-codegen/typescript-resolvers": 2.7.3 "@hookform/error-message": 2.0.1 - "@island.is/regulations-tools": 0.9.0 + "@island.is/regulations-tools": 0.9.1 "@keyv/redis": 2.6.1 "@livechat/widget-core": 1.3.2 "@nestjs/apollo": 10.1.0 @@ -36025,6 +36175,9 @@ __metadata: "@storybook/react-webpack5": 7.6.9 "@storybook/source-loader": 7.0.20 "@storybook/theming": 7.0.20 + "@swc-node/register": 1.9.1 + "@swc/core": 1.5.7 + "@swc/helpers": 0.5.11 "@testing-library/cypress": 8.0.3 "@testing-library/jest-dom": 5.16.5 "@testing-library/react": 15.0.6 @@ -36162,7 +36315,7 @@ __metadata: font-awesome: 4.7.0 form-data: 3.0.0 formik: 2.2.9 - framer-motion: 6.5.1 + framer-motion: 9.0.0 fridagar: ^3.2.0 fuse.js: 7.0.0 glob: 10.3.3 @@ -44363,6 +44516,13 @@ __metadata: languageName: node linkType: hard +"pirates@npm:^4.0.6": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 46a65fefaf19c6f57460388a5af9ab81e3d7fd0e7bc44ca59d753cb5c4d0df97c6c6e583674869762101836d68675f027d60f841c105d72734df9dfca97cbcc6 + languageName: node + linkType: hard + "pkce-challenge@npm:^2.1.0": version: 2.1.0 resolution: "pkce-challenge@npm:2.1.0" @@ -44455,18 +44615,6 @@ __metadata: languageName: node linkType: hard -"popmotion@npm:11.0.3": - version: 11.0.3 - resolution: "popmotion@npm:11.0.3" - dependencies: - framesync: 6.0.1 - hey-listen: ^1.0.8 - style-value-types: 5.0.0 - tslib: ^2.1.0 - checksum: 9fe7d03b4ec0e85bfb9dadc23b745147bfe42e16f466ba06e6327197d0e38b72015afc2f918a8051dedc3680310417f346ffdc463be6518e2e92e98f48e30268 - languageName: node - linkType: hard - "portfinder@npm:^1.0.28": version: 1.0.28 resolution: "portfinder@npm:1.0.28" @@ -51113,16 +51261,6 @@ __metadata: languageName: node linkType: hard -"style-value-types@npm:5.0.0": - version: 5.0.0 - resolution: "style-value-types@npm:5.0.0" - dependencies: - hey-listen: ^1.0.8 - tslib: ^2.1.0 - checksum: 16d198302cd102edf9dba94e7752a2364c93b1eaa5cc7c32b42b28eef4af4ccb5149a3f16bc2a256adc02616a2404f4612bd15f3081c1e8ca06132cae78be6c0 - languageName: node - linkType: hard - "styled-components@npm:5.3.6": version: 5.3.6 resolution: "styled-components@npm:5.3.6" @@ -52646,6 +52784,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.6.3": + version: 2.6.3 + resolution: "tslib@npm:2.6.3" + checksum: 74fce0e100f1ebd95b8995fbbd0e6c91bdd8f4c35c00d4da62e285a3363aaa534de40a80db30ecfd388ed7c313c42d930ee0eaf108e8114214b180eec3dbe6f5 + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0"