From 9ac35646705c501de5e0d931eaf88f9e3a8ee5b6 Mon Sep 17 00:00:00 2001 From: Bupe <46236053+NgaseBupe@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:24:07 +0200 Subject: [PATCH 1/6] Task/ime 261 redo mapping graphql final UI (#60) * feat: Enhance GraphQL queries with additional fields for transfer details * Added optional chaining to safely access the uid=1000(harlem) gid=1000(harlem) groups=1000(harlem),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev),1001(docker) property of in the TransferDetails component. This prevents runtime errors when is null or undefined. * Added optional chaining to safely access the uid=1000(harlem) gid=1000(harlem) groups=1000(harlem),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),116(netdev),1001(docker) property of in the TransferDetails component. This prevents runtime errors when is null or undefined. * merged the code in preparation for enhancements * Make fx fields and buttons hidden * task/ime-242-verify_ui * updated technical details fields * task/ime-242-verify_ui * updated technical details mapping * updated mapping * updated mapping * Fix formatting issue * task/ime-242-verify_ui * task/ime-242-verify_ui * updated the models and removed Transaction model * feat: Update GraphQL queries to align with mock data structure * feat: Update GraphQL queries to align with mock data structure * changes * feat: Update GraphQL queries to align with mock data structure * updated types * updated mapping to types * updated mocks * updated dashboard ui * update: transfer summary dashboard ui and mapping * UI mapping modal * mapping done for modals * fixed lint errors * updated ui * update: transfer summary dashboard UI - Create new GraphQL query - Map UI to the updated GraphQL schema - Refine logic to display successful and failed transactions in doughnut charts - Adjust dashboard UI alignment * completed mapping * updated payer information modal * fix: white screen bug due to mock.ts * update: transfer query to also add events fields * fix: payer and payeeDFSP undefined bug in tranfer tables * updated final ui * fix: payerModal showing wrong fspId fix: settlement batch id null * update: UI mapping to new graphql changes updated graphl query types * update: transfer query to fetch FxQuote and FxTransfer events * add: settlement batch id - updated transfer query - updated graphql types - updated ui mapping to new graphQl * fix: conversion terms source currency maaping * fix: lint errors --------- Co-authored-by: Mayhem Co-authored-by: Naph Co-authored-by: Akhilesh Rawat --- .babelrc | 2 +- .circleci/config.yml | 6 +- .eslintrc.js | 3 +- .github/workflows/build.yaml | 100 +- .github/workflows/integration-test.yml | 182 +-- .stylelintrc.json | 4 +- .versionrc.js | 9 +- CHANGELOG.md | 52 +- LICENSE.md | 3 +- README.md | 1 - docker-compose.yaml | 4 +- docs/available-scripts-and-commands.md | 5 +- docs/coding-guidelines.md | 4 +- docs/contribution-rules.md | 4 +- docs/deploying-to-production.md | 2 - docs/development-steps.md | 5 +- docs/environment-variables.md | 21 +- docs/external-api.md | 2 +- docs/microfrontend-setup.md | 16 +- docs/structuring-the-code.md | 22 +- integration_test/README.md | 33 +- integration_test/chart_values/backend.yaml | 533 +++---- integration_test/chart_values/bof.yaml | 22 +- integration_test/kustomization.yaml | 4 +- integration_test/manifests/backend/ing.yaml | 20 +- .../manifests/backend/kustomization.yaml | 14 +- .../backend/patch_adapter_event_sidecar.yaml | 14 +- .../backend/patch_ledger_event_sidecar.yaml | 16 +- .../reporting-hub-bop-trx-ui/deploy.yaml | 14 +- .../kustomization.yaml | 4 +- .../reporting-hub-bop-trx-ui/svc.yaml | 6 +- integration_test/tests/.eslintrc.json | 21 +- integration_test/tests/src/config.ts | 2 +- .../src/page-objects/components/SideMenu.ts | 2 +- .../pages/TransfersTracingPage.ts | 63 +- .../tests/src/tests/FindTransfersPage.test.ts | 237 +-- lint-staged.config.js | 8 +- mock.json | 1312 +++++++++++++++++ public/index.html | 4 +- skaffold.yaml | 34 +- src/App/Transfers/Dashboard/ErrorSummary.tsx | 26 +- .../Dashboard/ErrorsByErrorCodeChart.tsx | 24 +- .../Dashboard/ErrorsByPayeeChart.tsx | 38 +- .../Dashboard/ErrorsByPayerChart.tsx | 40 +- .../Dashboard/ErrorsBySourceCurrencyChart.tsx | 95 ++ .../Dashboard/ErrorsByTargetCurrencyChart.tsx | 96 ++ .../Dashboard/TransferTotalSummary.tsx | 13 +- .../Dashboard/TransfersByCurrencyChart.tsx | 63 +- .../Dashboard/TransfersByPayeeChart.tsx | 42 +- .../Dashboard/TransfersByPayerChart.tsx | 45 +- .../TransfersBySourceCurrencyChart.tsx | 145 ++ .../TransfersByTargetCurrencyChart.tsx | 145 ++ src/App/Transfers/Dashboard/index.tsx | 20 +- src/App/Transfers/PartyModal/index.tsx | 51 +- src/App/Transfers/TransferDetails/index.tsx | 929 +++++++++++- src/App/Transfers/Transfers.scss | 10 + src/App/Transfers/slice.ts | 9 +- src/App/Transfers/types.ts | 5 +- src/App/Transfers/views.tsx | 94 +- src/apollo/mocks.ts | 177 ++- src/apollo/query.ts | 651 ++++++-- src/apollo/types.ts | 168 ++- src/components/DataLabel/DataLabel.css | 8 + tsconfig.json | 10 +- webpack.config.js | 4 +- 65 files changed, 4570 insertions(+), 1148 deletions(-) create mode 100644 mock.json create mode 100644 src/App/Transfers/Dashboard/ErrorsBySourceCurrencyChart.tsx create mode 100644 src/App/Transfers/Dashboard/ErrorsByTargetCurrencyChart.tsx create mode 100644 src/App/Transfers/Dashboard/TransfersBySourceCurrencyChart.tsx create mode 100644 src/App/Transfers/Dashboard/TransfersByTargetCurrencyChart.tsx diff --git a/.babelrc b/.babelrc index 866d946..c146ff1 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,5 @@ { - "presets": [ "@babel/env","@babel/preset-react", "@babel/preset-typescript"], + "presets": ["@babel/env", "@babel/preset-react", "@babel/preset-typescript"], "plugins": [ "@babel/proposal-class-properties", "@babel/proposal-object-rest-spread", diff --git a/.circleci/config.yml b/.circleci/config.yml index a99f1ee..354f0ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,7 +160,7 @@ jobs: - checkout - restore_cache: keys: - - dependency-cache-{{ checksum "yarn.lock" }} + - dependency-cache-{{ checksum "yarn.lock" }} - run: name: Configure git + ssh command: | @@ -211,7 +211,7 @@ jobs: description: ${RELEASE_CHANGES} file-path: CHANGELOG.md - slack/status: - webhook: "$SLACK_WEBHOOK_ANNOUNCEMENT" + webhook: '$SLACK_WEBHOOK_ANNOUNCEMENT' success_message: '*"${CIRCLE_PROJECT_REPONAME}"* - Release \`"v${RELEASE_TAG}"\` \nhttps://github.com/mojaloop/"${CIRCLE_PROJECT_REPONAME}"/releases/tag/"v${RELEASE_TAG}"' publish: @@ -244,7 +244,7 @@ jobs: docker push $DOCKER_ORG/$CIRCLE_PROJECT_REPONAME:${CIRCLE_TAG} - slack/status: fail_only: true - webhook: "$SLACK_WEBHOOK_ANNOUNCEMENT_CI_CD" + webhook: '$SLACK_WEBHOOK_ANNOUNCEMENT_CI_CD' failure_message: 'Publishing docker image failed for: \`"${DOCKER_ORG}/${CIRCLE_PROJECT_REPONAME}:${CIRCLE_TAG}"\`' #license-scan: diff --git a/.eslintrc.js b/.eslintrc.js index 1dc2f69..a440da3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,6 +31,7 @@ module.exports = { { singleQuote: true, trailingComma: 'all', + "endOfLine": "auto" }, ], 'react/prop-types': [ @@ -47,7 +48,7 @@ module.exports = { 'react/no-unescaped-entities': 'off', 'react/jsx-one-expression-per-line': 'off', 'react/jsx-wrap-multilines': 'off', - 'react/destructuring-assignment': 'off' + 'react/destructuring-assignment': 'off', }, overrides: [ { diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index db886ba..45b3639 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -7,61 +7,55 @@ on: types: [published] push: branches: - - '**' + - '**' jobs: main: runs-on: ubuntu-latest steps: - - - name: Docker meta - id: docker_meta - uses: docker/metadata-action@v3 - with: - # Except for Dockerhub, which is the default, tags need to have the registry name in - # them, e.g. ghcr.io for Github container registry, or gcr.io for Google container - # registry. - # Note that the generated tags will look like: - # ghcr.io/{owner}/{repo}:version - images: ghcr.io/${{ github.repository }} - # For a release v1.2.3, we'll produce the following image tags - # v1.2.3 - # 1.2.3 - # 1.2 - # latest - # For non-releases, we'll produce a long sha - flavor: | - latest=${{ github.event_name == 'release' }} - tags: | - type=match,pattern=v\d+\.\d+\.\d+,enable=${{ github.event_name == 'release' }} - type=match,pattern=v(\d+\.\d+\.\d+),enable=${{ github.event_name == 'release' }},group=1 - type=match,pattern=v(\d+\.\d+)\.\d+,enable=${{ github.event_name == 'release' }},group=1 - type=match,pattern=v(\d+)\.\d+\.\d+,group=1,enable=${{ !startsWith(github.ref, 'refs/tags/v0.') && github.event_name == 'release' }} - type=sha,event=push,value={{sha}},format=long,enable=${{ github.event_name != 'release' }} - labels: | - org.opencontainers.image.source=https://github.com/${{ github.repository }}/tree/${{ github.sha }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Login to GitHub Container Registry - if: ${{ github.event_name != 'pull_request' }} - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - id: docker_build - uses: docker/build-push-action@v2.2.1 - with: - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.docker_meta.outputs.tags }} - labels: ${{ steps.docker_meta.outputs.labels }} - - - name: Image digest - run: echo ${{ steps.docker_build.outputs.digest }} + - name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3 + with: + # Except for Dockerhub, which is the default, tags need to have the registry name in + # them, e.g. ghcr.io for Github container registry, or gcr.io for Google container + # registry. + # Note that the generated tags will look like: + # ghcr.io/{owner}/{repo}:version + images: ghcr.io/${{ github.repository }} + # For a release v1.2.3, we'll produce the following image tags + # v1.2.3 + # 1.2.3 + # 1.2 + # latest + # For non-releases, we'll produce a long sha + flavor: | + latest=${{ github.event_name == 'release' }} + tags: | + type=match,pattern=v\d+\.\d+\.\d+,enable=${{ github.event_name == 'release' }} + type=match,pattern=v(\d+\.\d+\.\d+),enable=${{ github.event_name == 'release' }},group=1 + type=match,pattern=v(\d+\.\d+)\.\d+,enable=${{ github.event_name == 'release' }},group=1 + type=match,pattern=v(\d+)\.\d+\.\d+,group=1,enable=${{ !startsWith(github.ref, 'refs/tags/v0.') && github.event_name == 'release' }} + type=sha,event=push,value={{sha}},format=long,enable=${{ github.event_name != 'release' }} + labels: | + org.opencontainers.image.source=https://github.com/${{ github.repository }}/tree/${{ github.sha }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to GitHub Container Registry + if: ${{ github.event_name != 'pull_request' }} + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2.2.1 + with: + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index d1cc552..42639bf 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -3,108 +3,108 @@ name: Integration test on: push: branches: - - '**' + - '**' jobs: manifest_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.3.4 - - uses: cachix/install-nix-action@v13 - with: - nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/8e4fe32876ca15e3d5eb3ecd3ca0b224417f5f17.tar.gz - - name: Install dependencies in environment - run: nix-env -if integration_test/default.nix - - name: Validate integration test manifest - run: kustomize build integration_test | kubeconform -strict -kubernetes-version 1.17.9 + - uses: actions/checkout@v2.3.4 + - uses: cachix/install-nix-action@v13 + with: + nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/8e4fe32876ca15e3d5eb3ecd3ca0b224417f5f17.tar.gz + - name: Install dependencies in environment + run: nix-env -if integration_test/default.nix + - name: Validate integration test manifest + run: kustomize build integration_test | kubeconform -strict -kubernetes-version 1.17.9 integration_test: timeout-minutes: 45 needs: manifest_check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - uses: cachix/install-nix-action@v13 - with: - nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/8e4fe32876ca15e3d5eb3ecd3ca0b224417f5f17.tar.gz - - - name: Install dependencies - run: nix-env -if integration_test/default.nix - - - name: Start cluster - run: minikube start --driver=docker --kubernetes-version=v1.21.5 - - - name: Install Mojaloop Charts - run: helm repo add mojaloop-charts https://docs.mojaloop.io/charts/repo - - - name: Install BizOps Backend - run: helm upgrade --install backend mojaloop-charts/backend --devel -f integration_test/chart_values/backend.yaml - - - name: Install BizOps Services - run: helm upgrade --install bof mojaloop-charts/bof --devel -f integration_test/chart_values/bof.yaml --version 1.0.0-557.7cb0c96 - - - name: Deploy - run: skaffold run -p integration-test - - - name: Wait for kube api server to process and create all resources - # This is because the wait step that follows this one does this: - # 1. retrieve list of pods - # 2. wait for list of pods - # Unfortunately, the list of pods might not be complete at step (1), as all pods may not yet - # be created, meaning the list of pods waited on in step (2) is not complete. We therefore - # wait some time here to allow that to finish before we retrieve the list of pods to wait on. - # 30s should be more than enough. - run: sleep 30s - - - name: Wait for deployment readiness - # Skaffold is supposed to do this, but for whatever reason, does not. At the time of writing, - # investigating this was not a priority. - run: timeout 900 kubectl wait --for=condition=Ready pod --all --timeout=900s - - - name: Port-forward the portal frontend ingress - run: kubectl port-forward -n ingress-nginx --address 0.0.0.0 svc/ingress-nginx-controller 3000:80 & - - - name: Port-forward voodoo-doll - run: kubectl port-forward --address 0.0.0.0 voodoo-doll 3030 & - - - name: Install test dependencies - working-directory: integration_test/tests - run: |- - npm ci - - - name: Run tests - working-directory: integration_test/tests - run: |- - TRANSFERS_MICROFRONTEND_ENDPOINT="http://localhost:3000" npm run test:headless - - - name: Archive test report - if: ${{ always() }} - uses: actions/upload-artifact@v2 - with: - name: test-report - path: integration_test/tests/report.html - - - name: Print docker containers to check any issues with the cluster - if: ${{ failure() }} - run: docker ps - - - name: Print voodoo doll logs - if: ${{ always() }} - run: kubectl logs voodoo-doll - - - name: Print resources - if: ${{ always() }} - run: kubectl get svc,deploy,sts,pv,pvc,configmap,job,pod -A - - - name: Describe resources - if: ${{ always() }} - run: kubectl describe svc,deploy,sts,pv,pvc,configmap,job,pod -A - - - name: Print secret values - if: ${{ always() }} - run: |- - kubectl get secrets -o json | jq -r '.items[] | { name: .metadata.name, data: .data | map_values(@base64d) }' + - uses: actions/checkout@v2 + + - uses: cachix/install-nix-action@v13 + with: + nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/8e4fe32876ca15e3d5eb3ecd3ca0b224417f5f17.tar.gz + + - name: Install dependencies + run: nix-env -if integration_test/default.nix + + - name: Start cluster + run: minikube start --driver=docker --kubernetes-version=v1.21.5 + + - name: Install Mojaloop Charts + run: helm repo add mojaloop-charts https://docs.mojaloop.io/charts/repo + + - name: Install BizOps Backend + run: helm upgrade --install backend mojaloop-charts/backend --devel -f integration_test/chart_values/backend.yaml + + - name: Install BizOps Services + run: helm upgrade --install bof mojaloop-charts/bof --devel -f integration_test/chart_values/bof.yaml --version 1.0.0-557.7cb0c96 + + - name: Deploy + run: skaffold run -p integration-test + + - name: Wait for kube api server to process and create all resources + # This is because the wait step that follows this one does this: + # 1. retrieve list of pods + # 2. wait for list of pods + # Unfortunately, the list of pods might not be complete at step (1), as all pods may not yet + # be created, meaning the list of pods waited on in step (2) is not complete. We therefore + # wait some time here to allow that to finish before we retrieve the list of pods to wait on. + # 30s should be more than enough. + run: sleep 30s + + - name: Wait for deployment readiness + # Skaffold is supposed to do this, but for whatever reason, does not. At the time of writing, + # investigating this was not a priority. + run: timeout 900 kubectl wait --for=condition=Ready pod --all --timeout=900s + + - name: Port-forward the portal frontend ingress + run: kubectl port-forward -n ingress-nginx --address 0.0.0.0 svc/ingress-nginx-controller 3000:80 & + + - name: Port-forward voodoo-doll + run: kubectl port-forward --address 0.0.0.0 voodoo-doll 3030 & + + - name: Install test dependencies + working-directory: integration_test/tests + run: |- + npm ci + + - name: Run tests + working-directory: integration_test/tests + run: |- + TRANSFERS_MICROFRONTEND_ENDPOINT="http://localhost:3000" npm run test:headless + + - name: Archive test report + if: ${{ always() }} + uses: actions/upload-artifact@v2 + with: + name: test-report + path: integration_test/tests/report.html + + - name: Print docker containers to check any issues with the cluster + if: ${{ failure() }} + run: docker ps + + - name: Print voodoo doll logs + if: ${{ always() }} + run: kubectl logs voodoo-doll + + - name: Print resources + if: ${{ always() }} + run: kubectl get svc,deploy,sts,pv,pvc,configmap,job,pod -A + + - name: Describe resources + if: ${{ always() }} + run: kubectl describe svc,deploy,sts,pv,pvc,configmap,job,pod -A + + - name: Print secret values + if: ${{ always() }} + run: |- + kubectl get secrets -o json | jq -r '.items[] | { name: .metadata.name, data: .data | map_values(@base64d) }' # - name: Setup tmate session # if: ${{ failure() }} diff --git a/.stylelintrc.json b/.stylelintrc.json index b65a598..762af03 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,8 +1,6 @@ { "extends": "stylelint-config-standard", - "plugins": [ - "stylelint-scss" - ], + "plugins": ["stylelint-scss"], "rules": { "at-rule-no-unknown": null, "scss/at-rule-no-unknown": true, diff --git a/.versionrc.js b/.versionrc.js index ede9e04..22ad19e 100644 --- a/.versionrc.js +++ b/.versionrc.js @@ -1,5 +1,6 @@ module.exports = { - header: '# Changelog: [mojaloop/reporting-hub-bop-trx-ui](https://github.com/mojaloop/reporting-hub-bop-trx-ui)', + header: + '# Changelog: [mojaloop/reporting-hub-bop-trx-ui](https://github.com/mojaloop/reporting-hub-bop-trx-ui)', types: [ { type: 'feat', section: 'Features' }, { type: 'fix', section: 'Bug Fixes' }, @@ -9,6 +10,6 @@ module.exports = { { type: 'style', section: 'Style Improvements' }, { type: 'refactor', section: 'Code Refactor' }, { type: 'perf', section: 'Performance' }, - { type: 'test', section: 'Tests' } - ] -} + { type: 'test', section: 'Tests' }, + ], +}; diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ca7816..da5404a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,123 +1,107 @@ # Changelog: [mojaloop/reporting-hub-bop-trx-ui](https://github.com/mojaloop/reporting-hub-bop-trx-ui) + ### [1.7.3](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.7.2...v1.7.3) (2024-10-25) ### [1.7.2](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.7.1...v1.7.2) (2022-01-17) - ### Bug Fixes -* group error charts by fsp ([#20](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/20)) ([5596d86](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/5596d86286d01640b43085fd21549d1ff13e3409)) +- group error charts by fsp ([#20](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/20)) ([5596d86](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/5596d86286d01640b43085fd21549d1ff13e3409)) ### [1.7.1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.7.0...v1.7.1) (2022-01-17) - ### Maintenance -* **deps:** bump follow-redirects from 1.14.3 to 1.14.7 ([#21](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/21)) ([9846217](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/9846217772b18b8ef3686a6bca2cc3d95d1b6152)) +- **deps:** bump follow-redirects from 1.14.3 to 1.14.7 ([#21](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/21)) ([9846217](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/9846217772b18b8ef3686a6bca2cc3d95d1b6152)) ## [1.7.0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.6.2...v1.7.0) (2021-12-09) - ### Features -* add cache busting and misc changes ([#19](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/19)) ([4ba3597](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/4ba3597fe695a16aec7def03b6da6e3b40a71a1c)) +- add cache busting and misc changes ([#19](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/19)) ([4ba3597](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/4ba3597fe695a16aec7def03b6da6e3b40a71a1c)) ### [1.6.2](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.6.1...v1.6.2) (2021-12-02) - ### Tests -* add test harness and integration tests ([#18](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/18)) ([733f031](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/733f0310f20a408ff4ddd5364f41f1f80d05de00)) +- add test harness and integration tests ([#18](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/18)) ([733f031](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/733f0310f20a408ff4ddd5364f41f1f80d05de00)) ### [1.6.1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.6.0...v1.6.1) (2021-12-02) - ### Bug Fixes -* fix account sort, move transferId field, standardize dates, etc ([#17](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/17)) ([c28a925](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/c28a92558e1a241a01abdf713f3ca4e692ca1ba2)) +- fix account sort, move transferId field, standardize dates, etc ([#17](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/17)) ([c28a925](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/c28a92558e1a241a01abdf713f3ca4e692ca1ba2)) ## [1.6.0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.5.2...v1.6.0) (2021-11-15) - ### Features -* add id search, clearable selects, fix cache bugs and data ([#16](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/16)) ([af7ec79](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/af7ec79480aca5542b7854bdb07804320ff5341e)) +- add id search, clearable selects, fix cache bugs and data ([#16](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/16)) ([af7ec79](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/af7ec79480aca5542b7854bdb07804320ff5341e)) ### [1.5.2](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.5.1...v1.5.2) (2021-11-11) - ### Bug Fixes -* adjust modal size and date filters ([#15](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/15)) ([f60518a](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/f60518a5af0db34d4632acb7010ba6e43dd1b324)) +- adjust modal size and date filters ([#15](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/15)) ([f60518a](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/f60518a5af0db34d4632acb7010ba6e43dd1b324)) ### [1.5.1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.5.0...v1.5.1) (2021-11-11) - ### Bug Fixes -* resolve graphql and charts bug, perform housekeeping ([#14](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/14)) ([faac640](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/faac64063c944512f09be35591ba9b58b8ce5e02)) +- resolve graphql and charts bug, perform housekeeping ([#14](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/14)) ([faac640](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/faac64063c944512f09be35591ba9b58b8ce5e02)) ## [1.5.0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.4.3...v1.5.0) (2021-11-01) - ### Features -* **#2541:** add transfer dashboard ([#13](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/13)) ([6229015](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/6229015f2b2d5ab2d341b63f5fc40ff12f1464f8)), closes [#2541](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/2541) +- **#2541:** add transfer dashboard ([#13](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/13)) ([6229015](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/6229015f2b2d5ab2d341b63f5fc40ff12f1464f8)), closes [#2541](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/2541) ### [1.4.3](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.4.2...v1.4.3) (2021-10-26) - ### Bug Fixes -* add null check for value ([#12](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/12)) ([b9fa37a](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/b9fa37a6f8715ea7ae36edd8abf7d65c77c0f43e)) +- add null check for value ([#12](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/12)) ([b9fa37a](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/b9fa37a6f8715ea7ae36edd8abf7d65c77c0f43e)) ### [1.4.2](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.4.1...v1.4.2) (2021-10-26) - ### Bug Fixes -* fix transfers query ([#11](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/11)) ([a989182](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/a989182363a314eb145275ce1f0d77baa5e5998e)) +- fix transfers query ([#11](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/11)) ([a989182](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/a989182363a314eb145275ce1f0d77baa5e5998e)) ### [1.4.1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.4.0...v1.4.1) (2021-10-25) - ### Bug Fixes -* fix variable names ([44a6fb0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/44a6fb0e436cc69d1e99c8cebbcce9cced7afbcb)) +- fix variable names ([44a6fb0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/44a6fb0e436cc69d1e99c8cebbcce9cced7afbcb)) ## [1.4.0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.3.0...v1.4.0) (2021-10-21) - ### Features -* add cookie passing flag for axios api calls for auth ([#9](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/9)) ([47a4f0a](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/47a4f0a3fc01c79234ea91bf139306b2de414b09)) +- add cookie passing flag for axios api calls for auth ([#9](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/9)) ([47a4f0a](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/47a4f0a3fc01c79234ea91bf139306b2de414b09)) ## [1.3.0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.2.1...v1.3.0) (2021-10-18) - ### Features -* update ui to handle reporting api ([#7](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/7)) ([9aaca07](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/9aaca079e8cdca1c951cbf3199114c255f7a59fa)) +- update ui to handle reporting api ([#7](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/7)) ([9aaca07](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/9aaca079e8cdca1c951cbf3199114c255f7a59fa)) ### [1.2.1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.2.0...v1.2.1) (2021-10-18) - ### Maintenance -* config docker to run as non root ([#8](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/8)) ([ceabd6d](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/ceabd6d4c47f7c7da09256e179995bb43bac2f3e)) +- config docker to run as non root ([#8](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/8)) ([ceabd6d](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/ceabd6d4c47f7c7da09256e179995bb43bac2f3e)) ## [1.2.0](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.1.1...v1.2.0) (2021-10-12) - ### Features -* **#2514:** add code for productionization ([#6](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/6)) ([4ba9b25](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/4ba9b253bf7f1e67a5f1760bb4d706b6f40a1d84)), closes [#2514](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/2514) +- **#2514:** add code for productionization ([#6](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/6)) ([4ba9b25](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/4ba9b253bf7f1e67a5f1760bb4d706b6f40a1d84)), closes [#2514](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/2514) ### [1.1.1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/compare/v1.1.0...v1.1.1) (2021-10-06) ## 1.1.0 (2021-10-01) - ### Features -* initial transfer microfrontend ([#1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/1)) ([5f4aab1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/5f4aab13e35c54b5f8beecf06bb26ebc318d3015)) +- initial transfer microfrontend ([#1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/issues/1)) ([5f4aab1](https://github.com/mojaloop/reporting-hub-bop-trx-ui/commit/5f4aab13e35c54b5f8beecf06bb26ebc318d3015)) diff --git a/LICENSE.md b/LICENSE.md index d28c9d1..3fdf6ab 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -2,7 +2,8 @@ Copyright © 2020-2024 Mojaloop Foundation -The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. +The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 +(the "License") and you may not use these files except in compliance with the [License](http://www.apache.org/licenses/LICENSE-2.0). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 diff --git a/README.md b/README.md index 1b16133..0d5f742 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,6 @@ Security measures are applied to the repository so that it is protected from (so For detailed instructions on how to contribute, please read the [contribution rules](./docs/contribution-rules.md) page. - ## How to deploy The app is configured to run in a docker image served by an embedded webserver; that makes it portable and convenient when has to run in a kuberneters environment. diff --git a/docker-compose.yaml b/docker-compose.yaml index 5297333..67ae370 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,4 +1,4 @@ -version: "3.7" +version: '3.7' networks: mojaloop-net: @@ -16,7 +16,7 @@ services: - REACT_APP_API_BASE_URL=http://localhost:3009/ - REACT_APP_MOCK_API=true ports: - - "8082:8082" + - '8082:8082' networks: - mojaloop-net healthcheck: diff --git a/docs/available-scripts-and-commands.md b/docs/available-scripts-and-commands.md index b38b810..01f0f53 100644 --- a/docs/available-scripts-and-commands.md +++ b/docs/available-scripts-and-commands.md @@ -1,11 +1,12 @@ ## Available Scripts and Commands ### Scripts + In the project directory, you can run: #### `yarn start` -Runs the app in the development mode over HTTPS.
+Runs the app in the development mode over HTTPS.
Open [https://localhost:3000](https://localhost:3000) to view it in the browser. **Note:** Self Signed Certificates are being used in dev mode; you are required to explicitely allow the browser to navigate to the page. @@ -38,4 +39,4 @@ Runs ESLint on the project source files. ### Commands -In the project root, you can run: \ No newline at end of file +In the project root, you can run: diff --git a/docs/coding-guidelines.md b/docs/coding-guidelines.md index 6dd4871..5d40b79 100644 --- a/docs/coding-guidelines.md +++ b/docs/coding-guidelines.md @@ -9,7 +9,7 @@ Configuration settings are described in [configuring the tools](./configuring-th ### Styling -The project uses the BEM naming convention. More about [BEM](http://getbem.com/). +The project uses the BEM naming convention. More about [BEM](http://getbem.com/). Style modules can be either plain CSS or SCSS. ### Typescript @@ -20,4 +20,4 @@ A good rule of thumb is to follow the [do's and dont's](https://www.typescriptla Usage of `any` is discouraged; a `// TODO` comment needs to be added in order to track the issue for a later fix. -**Note**: the command `yarn lint` can help identify wrong and missing types. \ No newline at end of file +**Note**: the command `yarn lint` can help identify wrong and missing types. diff --git a/docs/contribution-rules.md b/docs/contribution-rules.md index 79ea662..194b0be 100644 --- a/docs/contribution-rules.md +++ b/docs/contribution-rules.md @@ -6,7 +6,7 @@ There is a precise approach on how to contribute to this project and there are s - [Unit testing the code](#unit-testing-the-code) - [Versioning strategy](#versioning-strategy) -### Pushing to the repo +### Pushing to the repo When adding changes to the repo, make sure the following rules are respected: @@ -40,7 +40,7 @@ Views, routes, pages should be tested for assuring the right children components ### Versioning Strategy -Versioning is done via `yarn version`. +Versioning is done via `yarn version`. > :warning: Changing the version should be done in the last commit of a branch/PR. diff --git a/docs/deploying-to-production.md b/docs/deploying-to-production.md index 756f52c..617f9f9 100644 --- a/docs/deploying-to-production.md +++ b/docs/deploying-to-production.md @@ -3,5 +3,3 @@ All you need to do is produce a docker image of your app. Please visit the section [Build a local image](./docker.md#build-a-local-image) If you don't need to produce a docker image simply run `yarn build`. - - diff --git a/docs/development-steps.md b/docs/development-steps.md index 1278b93..f75fa66 100644 --- a/docs/development-steps.md +++ b/docs/development-steps.md @@ -26,11 +26,8 @@ At this point you should iterate over the last steps and do the following: ### Commit and push -It's a good practice to commit and push your changes frequently. +It's a good practice to commit and push your changes frequently. Pre-commit and pre-push git hooks have been setup to help identify and catch issues **locally** ahead of time; changes breaking tests won't be allowed to be pushed to the remote git repo. Additionally, a proper CI is setup using github actions. - - - diff --git a/docs/environment-variables.md b/docs/environment-variables.md index cf8e14a..32037bb 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -2,19 +2,18 @@ The application is driven by some environment variables used at build and runtime. - ### Always Available Environment Variables -| Name | Description | Default | -|---|---|---| -| `REACT_APP_NAME` | name extracted from package.json` | - | -| `REACT_APP_VERSION` | version extracted from package.json` | - | -| `REACT_APP_COMMIT` | commit extracted from git history | - | +| Name | Description | Default | +| ------------------- | ------------------------------------ | ------- | +| `REACT_APP_NAME` | name extracted from package.json` | - | +| `REACT_APP_VERSION` | version extracted from package.json` | - | +| `REACT_APP_COMMIT` | commit extracted from git history | - | ### Local and Production Runtime Config Variables -| Name | Description | Default | Used Locally | Used In A Deployment -|---|---|---|---|---| -| `REACT_APP_API_BASE_URL` | base url / path for api | /reporting-api | V | V | -| `REACT_APP_MOCK_API` | enables mock api locally | true | V | ? | -| `DEV_PORT` | webpack server dev http port | 3001 | V | | +| Name | Description | Default | Used Locally | Used In A Deployment | +| ------------------------ | ---------------------------- | -------------- | ------------ | -------------------- | +| `REACT_APP_API_BASE_URL` | base url / path for api | /reporting-api | V | V | +| `REACT_APP_MOCK_API` | enables mock api locally | true | V | ? | +| `DEV_PORT` | webpack server dev http port | 3001 | V | | diff --git a/docs/external-api.md b/docs/external-api.md index 5d6dc62..4462455 100644 --- a/docs/external-api.md +++ b/docs/external-api.md @@ -2,4 +2,4 @@ Your application is most likely going to communicate with an API. When running locally, you can use the environment variable `REACT_APP_API_BASE_URL` in `env.local` to specify the location of the api service. -For more informations on React variables check [here](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables). \ No newline at end of file +For more informations on React variables check [here](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables). diff --git a/docs/microfrontend-setup.md b/docs/microfrontend-setup.md index c244942..ab9fc65 100644 --- a/docs/microfrontend-setup.md +++ b/docs/microfrontend-setup.md @@ -10,7 +10,6 @@ It is responsible to export the children modules/app which will be loaded at run - [Webpack Module Federation](#webpack-module-federation) - [Loading Children Modules](#loading-children-modules) - ### Isolation And Defined Boundary - [Choosing A Unique Name](#choosing-a-unique-name) @@ -22,12 +21,12 @@ It is responsible to export the children modules/app which will be loaded at run It's important to note the remote has to use a unique name in order to work and not collide with other sibling remotes. The suggested practice is use such name as: + - Git repository - Webpack Module Federation [configuration](#webpack-module-federation) - Module (package.json) name - CSS wrapper name - #### Custom Redux Context The Redux store needs to be isolated and non accessible by the host or by the sibling remotes. @@ -38,7 +37,7 @@ For such reason, the store module (`src/store`) exports a custom React Context t import configureStore, { ReduxContext } from './store'; /// /// -function ExportableApp({ ...propsFromTheHost, }) { +function ExportableApp({ ...propsFromTheHost }) { return ( @@ -70,6 +69,7 @@ function ExportableModule() { ``` And defined the style accordingly in SCSS (CSS can work too) + ```scss .uniquely-chosen-remote-name { .header { @@ -84,7 +84,6 @@ And defined the style accordingly in SCSS (CSS can work too) } ``` - ### Webpack Module Federation The default configuration works when running the host locally, however it's necessary to adjust the settings before deploying online. @@ -141,10 +140,9 @@ Make sure you set the unique name in the following fields: - `ModuleFederationPlugin.library.name` - `ModuleFederationPlugin.filename` - #### Resources - - https://webpack.js.org/concepts/module-federation/ - - https://github.com/module-federation/module-federation-examples - - https://github.com/modusintegration/microfrontend-shell-boilerplate - - https://jamstack.org/ +- https://webpack.js.org/concepts/module-federation/ +- https://github.com/module-federation/module-federation-examples +- https://github.com/modusintegration/microfrontend-shell-boilerplate +- https://jamstack.org/ diff --git a/docs/structuring-the-code.md b/docs/structuring-the-code.md index bb8b031..a462278 100644 --- a/docs/structuring-the-code.md +++ b/docs/structuring-the-code.md @@ -13,18 +13,18 @@ To organize features logically, modules are nested following the Routing structu Example of a module structure: -- `index.ts` is the index file, re-exporting the whole module -- `components` contains the React components used in this module -- `Router.tsx` contains the React Router for the Auth module -- `Auth.scss` is responsible of styling the HTML -- `Auth.tsx` is the React view for the Auth module +- `index.ts` is the index file, re-exporting the whole module +- `components` contains the React components used in this module +- `Router.tsx` contains the React Router for the Auth module +- `Auth.scss` is responsible of styling the HTML +- `Auth.tsx` is the React view for the Auth module - `connectors.ts` contains the bindings between the actions and selectors to provide the React view with -- `hocs.tsx` contains additional higher order components -- `sagas.ts` contains, as the name suggests, the redux sagas for async operations -- `selectors.ts` contains the redux selectors for the portion of the redux state that belongs to Auth -- `slice.ts` contains the actions/reducer of the Auth module -- `helpers.ts` exports all the reusable helpers used in and outside the Auth module -- `types.ts` exports all the types used in the Auth module +- `hocs.tsx` contains additional higher order components +- `sagas.ts` contains, as the name suggests, the redux sagas for async operations +- `selectors.ts` contains the redux selectors for the portion of the redux state that belongs to Auth +- `slice.ts` contains the actions/reducer of the Auth module +- `helpers.ts` exports all the reusable helpers used in and outside the Auth module +- `types.ts` exports all the types used in the Auth module ### Utilities diff --git a/integration_test/README.md b/integration_test/README.md index d47bf69..b7e717a 100644 --- a/integration_test/README.md +++ b/integration_test/README.md @@ -1,6 +1,7 @@ ### E2E UI tests #### Structure + We aim to use page models. These are a simple abstraction of the UI to reduce duplication in the tests and speed UI and corresponding test refactoring. Not all tests use page models at the time of writing, but all new tests should. The rule you should use is this: if you find yourself writing a @@ -8,6 +9,7 @@ selector, you should instead use an existing page model (and extend it if necess exists for your current test, create a page model and place your selector there. References for those unfamiliar with page models: + - https://testcafe.io/documentation/402826/guides/concepts/page-model#why-use-page-model - https://github.com/SeleniumHQ/selenium/wiki/PageObjects - https://martinfowler.com/bliki/PageObject.html @@ -18,82 +20,103 @@ References for those unfamiliar with page models: Kubernetes 1.17 to 1.21 should work. You'll probably want at least four cores and 8gb mem. This is left as an exercise for the reader. Some suggestions: + 1. [Minikube](https://minikube.sigs.k8s.io/docs/) 2. [k3d](https://k3d.io/) 3. [KinD](https://kind.sigs.k8s.io/docs/) 4. [DigitalOcean](https://www.digitalocean.com/products/kubernetes/) #### Install application dependencies + You have two choices here: [with Nix](#with-nix) and [without Nix](#without-nix). The advantages of using Nix here are: + 1. Exactly the same versions of dependencies as CI, and other developers (except core system things like the kernel/container runtime). 2. Therefore, no need to track and manage dependency versions, simply run one command to get all required dependencies at the correct versions, and enter a shell with those dependencies. ##### With Nix + 1. Install nix: - ```sh - curl -L https://nixos.org/nix/install | sh -s -- --no-daemon - ``` - (From: https://nixos.org/manual/nix/stable/#sect-single-user-installation) + ```sh + curl -L https://nixos.org/nix/install | sh -s -- --no-daemon + ``` + (From: https://nixos.org/manual/nix/stable/#sect-single-user-installation) 2. Navigate to the `integration_test` directory of this project 3. Run `nix-shell` to be dropped into a shell containing all necessary dependencies ##### Without Nix + Install the following: + - Google Chrome (it's possible to use another browser, see [run tests with a different browser](#with-a-different-browser)) - Skaffold v1.28.0 or greater: https://github.com/GoogleContainerTools/skaffold/releases - Kustomize v4.0.5 or greater: https://github.com/kubernetes-sigs/kustomize/releases - A recent version of kubectl #### Deploy Mojaloop and dependencies to cluster + In the project root: + ```sh skaffold run -p backend ``` #### Port-forward the ingress and support service + ```sh kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 8000:80 kubectl port-forward voodoo-doll 3030 ``` #### Build and run the portal + From the project root: + ```sh yarn install yarn start ``` #### Install integration test npm dependencies + In the `integration_test/tests` directory: + ```sh npm ci ``` ### Run tests + In the `integration_test/tests` directory: + ```sh npm run test ``` #### View results + In the `integration_test/tests` directory: + ```sh $BROWSER results.html ``` #### Run a single test + ```sh npm run test -- -t 'name of test' ``` + E.g., for one of the login tests: + ```sh npm run test -- -t 'Log in with valid credentials' ``` #### With a different browser + ```sh BROWSER_TCAFE=chromium npm run test # or @@ -101,7 +124,9 @@ BROWSER_TCAFE=firefox npm run test ``` ### Clean up + In the project root: + ```sh skaffold delete -p backend ``` diff --git a/integration_test/chart_values/backend.yaml b/integration_test/chart_values/backend.yaml index eb482d7..d96492c 100644 --- a/integration_test/chart_values/backend.yaml +++ b/integration_test/chart_values/backend.yaml @@ -26,22 +26,22 @@ kafka: ## @param global.storageClass Global StorageClass for Persistent Volume(s) ## global: - imageRegistry: "" + imageRegistry: '' ## E.g. ## imagePullSecrets: ## - myRegistryKeySecretName ## imagePullSecrets: [] - storageClass: "" + storageClass: '' ## @section Common parameters ## @param nameOverride String to partially override kafka.fullname ## - nameOverride: "" + nameOverride: '' ## @param fullnameOverride String to fully override kafka.fullname ## - fullnameOverride: "kafka" + fullnameOverride: 'kafka' ## @param clusterDomain Default Kubernetes cluster domain ## clusterDomain: cluster.local @@ -131,22 +131,22 @@ kafka: ## zookeeper.connection.timeout.ms=6000 ## group.initial.rebalance.delay.ms=0 ## - config: "" + config: '' ## @param existingConfigmap ConfigMap with Kafka Configuration ## NOTE: This will override config AND any KAFKA_CFG_ environment variables. ## - existingConfigmap: "" + existingConfigmap: '' ## @param log4j An optional log4j.properties file to overwrite the default of the Kafka brokers. ## An optional log4j.properties file to overwrite the default of the Kafka brokers. ## See an example log4j.properties at: ## https://github.com/apache/kafka/blob/trunk/config/log4j.properties ## - log4j: "" + log4j: '' ## @param existingLog4jConfigMap The name of an existing ConfigMap containing a log4j.properties file. ## The name of an existing ConfigMap containing a log4j.properties file. ## NOTE: this will override log4j. ## - existingLog4jConfigMap: "" + existingLog4jConfigMap: '' ## @param heapOpts Kafka's Java Heap size ## heapOpts: -Xmx1024m -Xms1024m @@ -293,18 +293,18 @@ kafka: interBrokerUser: admin ## @param auth.sasl.jaas.interBrokerPassword Kafka inter broker communication password for SASL authentication ## - interBrokerPassword: "" + interBrokerPassword: '' ## @param auth.sasl.jaas.zookeeperUser Kafka Zookeeper user for SASL authentication ## - zookeeperUser: "" + zookeeperUser: '' ## @param auth.sasl.jaas.zookeeperPassword Kafka Zookeeper password for SASL authentication ## - zookeeperPassword: "" + zookeeperPassword: '' ## @param auth.sasl.jaas.existingSecret Name of the existing secret containing credentials for clientUsers, interBrokerUser and zookeeperUser ## Create this secret running the command below where SECRET_NAME is the name of the secret you want to create: ## kubectl create secret generic SECRET_NAME --from-literal=client-passwords=CLIENT_PASSWORD1,CLIENT_PASSWORD2 --from-literal=inter-broker-password=INTER_BROKER_PASSWORD --from-literal=zookeeper-password=ZOOKEEPER_PASSWORD ## - existingSecret: "" + existingSecret: '' ## @param auth.saslMechanisms DEPRECATED: use `auth.sasl.mechanisms` instead. ## saslMechanisms: plain,scram-sha-256,scram-sha-512 @@ -319,10 +319,10 @@ kafka: - user clientPasswords: [] interBrokerUser: admin - interBrokerPassword: "" - zookeeperUser: "" - zookeeperPassword: "" - existingSecret: "" + interBrokerPassword: '' + zookeeperUser: '' + zookeeperPassword: '' + existingSecret: '' ## TLS configuration ## tls: @@ -352,19 +352,19 @@ kafka: ## 5) Run the command below where SECRET_NAME is the name of the secret you want to create: ## kubectl create secret generic SECRET_NAME --from-file=./kafka.truststore.pem --from-file=./kafka-0.keystore.pem --from-file=./kafka-0.keystore.key --from-file=./kafka-1.keystore.pem --from-file=./kafka-1.keystore.key ... ## - existingSecret: "" + existingSecret: '' ## @param auth.tls.autoGenerated Generate automatically self-signed TLS certificates for Kafka brokers. Currently only supported if `auth.tls.type` is `pem` ## Note: ignored when using 'jks' format or `auth.tls.existingSecret` is not empty ## autoGenerated: false ## @param auth.tls.password Password to access the JKS files or PEM key when they are password-protected. ## - password: "" + password: '' ## @param auth.tls.jksTruststoreSecret Name of the existing secret containing your truststore if truststore not existing or different from the one in the `auth.tls.existingSecret` ## or is different from the one in the `auth.tls.existingSecret`. ## Note: ignored when using 'pem' format for certificates . ## - jksTruststoreSecret: "" + jksTruststoreSecret: '' ## @param auth.tls.jksKeystoreSAN The secret key from the `auth.tls.existingSecret` containing the keystore with a SAN certificate ## The SAN certificate in it should be issued with Subject Alternative Names for all headless services: ## - kafka-0.kafka-headless.kafka.svc.cluster.local @@ -372,11 +372,11 @@ kafka: ## - kafka-2.kafka-headless.kafka.svc.cluster.local ## Note: ignored when using 'pem' format for certificates. ## - jksKeystoreSAN: "" + jksKeystoreSAN: '' ## @param auth.tls.jksTruststore The secret key from the `auth.tls.existingSecret` or `auth.tls.jksTruststoreSecret` containing the truststore ## Note: ignored when using 'pem' format for certificates. ## - jksTruststore: "" + jksTruststore: '' ## @param auth.tls.endpointIdentificationAlgorithm The endpoint identification algorithm to validate server hostname using server certificate ## Disable server host name verification by setting it to an empty string. ## ref: https://docs.confluent.io/current/kafka/authentication_ssl.html#optional-settings @@ -384,19 +384,19 @@ kafka: endpointIdentificationAlgorithm: https ## @param auth.jksSecret DEPRECATED: use `auth.tls.existingSecret` instead. ## - jksSecret: "" + jksSecret: '' ## @param auth.jksTruststoreSecret DEPRECATED: use `auth.tls.jksTruststoreSecret` instead. ## - jksTruststoreSecret: "" + jksTruststoreSecret: '' ## @param auth.jksKeystoreSAN DEPRECATED: use `auth.tls.jksKeystoreSAN` instead. ## - jksKeystoreSAN: "" + jksKeystoreSAN: '' ## @param auth.jksTruststore DEPRECATED: use `auth.tls.jksTruststore` instead. ## - jksTruststore: "" + jksTruststore: '' ## @param auth.jksPassword DEPRECATED: use `auth.tls.password` instead. ## - jksPassword: "" + jksPassword: '' ## @param auth.tlsEndpointIdentificationAlgorithm DEPRECATED: use `auth.tls.endpointIdentificationAlgorithm` instead. ## tlsEndpointIdentificationAlgorithm: https @@ -413,7 +413,7 @@ kafka: ## @param listenerSecurityProtocolMap The protocol->listener mapping. Auto-calculated it's set to nil ## When it's nil, the listeners will be configured based on the authentication protocols (auth.clientProtocol and auth.interBrokerProtocol parameters) ## - listenerSecurityProtocolMap: "" + listenerSecurityProtocolMap: '' ## @param allowPlaintextListener Allow to use the PLAINTEXT listener ## allowPlaintextListener: true @@ -438,7 +438,7 @@ kafka: ## @param rollingUpdatePartition Partition update strategy ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions ## - rollingUpdatePartition: "" + rollingUpdatePartition: '' ## @param hostAliases Add deployment host aliases ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ ## @@ -450,7 +450,7 @@ kafka: ## @param schedulerName Name of the k8s scheduler (other than default) ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ ## - schedulerName: "" + schedulerName: '' ## @param podLabels Kafka pod labels ## Ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ ## @@ -462,11 +462,11 @@ kafka: ## @param priorityClassName Name of the existing priority class to be used by kafka pods ## Ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ ## - priorityClassName: "" + priorityClassName: '' ## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## - podAffinityPreset: "" + podAffinityPreset: '' ## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## @@ -477,12 +477,12 @@ kafka: nodeAffinityPreset: ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` ## - type: "" + type: '' ## @param nodeAffinityPreset.key Node label key to match Ignored if `affinity` is set. ## E.g. ## key: "kubernetes.io/e2e-az-name" ## - key: "" + key: '' ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set. ## E.g. ## values: @@ -510,7 +510,7 @@ kafka: ## @param terminationGracePeriodSeconds Seconds the pod needs to gracefully terminate ## ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#hook-handler-execution ## - terminationGracePeriodSeconds: "" + terminationGracePeriodSeconds: '' ## Kafka pods' Security Context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod ## @param podSecurityContext.enabled Enable security context for the pods @@ -598,7 +598,7 @@ kafka: create: false ## @param pdb.minAvailable Minimum number/percentage of pods that should remain scheduled ## - minAvailable: "" + minAvailable: '' ## @param pdb.maxUnavailable Maximum number/percentage of pods that may be made unavailable ## maxUnavailable: 1 @@ -638,12 +638,12 @@ kafka: ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport ## nodePorts: - client: "" - external: "" + client: '' + external: '' ## @param service.loadBalancerIP loadBalancerIP for Kafka Service ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer ## - loadBalancerIP: "" + loadBalancerIP: '' ## @param service.loadBalancerSourceRanges Address(es) that are allowed when service is LoadBalancer ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service ## Example: @@ -749,7 +749,7 @@ kafka: ## @param externalAccess.service.domain Domain or external ip used to configure Kafka external listener when service type is NodePort ## If not specified, the container will try to get the kubernetes node external IP ## - domain: "" + domain: '' ## @param externalAccess.service.annotations Service annotations for external access ## annotations: {} @@ -766,14 +766,14 @@ kafka: ## If defined, PVC must be created manually before volume will be bound ## The value is evaluated as a template ## - existingClaim: "" + existingClaim: '' ## @param persistence.storageClass PVC Storage Class for Kafka data volume ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is ## set, choosing the default provisioner. ## - storageClass: "" + storageClass: '' ## @param persistence.accessModes PV Access Mode ## accessModes: @@ -802,13 +802,13 @@ kafka: ## If defined, PVC must be created manually before volume will be bound ## The value is evaluated as a template ## - existingClaim: "" + existingClaim: '' ## @param logPersistence.existingLogClaim PV Storage Class ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is ## set, choosing the default provisioner. - existingLogClaim: "" + existingLogClaim: '' ## @param logPersistence.accessModes PV Access Mode ## accessModes: @@ -840,7 +840,7 @@ kafka: ## @param serviceAccount.name The name of the service account to use. If not set and `create` is `true`, a name is generated ## If not set and create is true, a name is generated using the kafka.serviceAccountName template ## - name: "" + name: '' ## @param serviceAccount.automountServiceAccountToken Allows auto mount of ServiceAccountToken on the serviceAccount created ## Can be set to false if pods using this serviceAccount do not need to use K8s API ## @@ -951,7 +951,7 @@ kafka: ## @param metrics.kafka.schedulerName Name of the k8s scheduler (other than default) for Kafka Exporter ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ ## - schedulerName: "" + schedulerName: '' ## @param metrics.kafka.extraFlags Extra flags to be passed to Kafka exporter ## Example: ## extraFlags: @@ -962,7 +962,7 @@ kafka: ## @param metrics.kafka.certificatesSecret Name of the existing secret containing the optional certificate and key files ## for Kafka Exporter client authentication ## - certificatesSecret: "" + certificatesSecret: '' ## @param metrics.kafka.tlsCert The secret key from the certificatesSecret if 'client-cert' key different from the default (cert-file) ## tlsCert: cert-file @@ -971,7 +971,7 @@ kafka: tlsKey: key-file ## @param metrics.kafka.tlsCaSecret Name of the existing secret containing the optional ca certificate for Kafka Exporter client authentication ## - tlsCaSecret: "" + tlsCaSecret: '' ## @param metrics.kafka.tlsCaCert The secret key from the certificatesSecret or tlsCaSecret if 'ca-cert' key different from the default (ca-file) ## tlsCaCert: ca-file @@ -1031,12 +1031,12 @@ kafka: ## @param metrics.kafka.service.nodePort Kubernetes HTTP node port ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport ## - nodePort: "" + nodePort: '' ## @param metrics.kafka.service.loadBalancerIP loadBalancerIP if service type is `LoadBalancer` ## Set the LoadBalancer service type to internal only ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer ## - loadBalancerIP: "" + loadBalancerIP: '' ## @param metrics.kafka.service.loadBalancerSourceRanges Load Balancer sources ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service ## Example: @@ -1047,13 +1047,13 @@ kafka: ## @param metrics.kafka.service.clusterIP Static clusterIP or None for headless services ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address ## - clusterIP: "" + clusterIP: '' ## @param metrics.kafka.service.annotations [object] Annotations for the Kafka Exporter Prometheus metrics service ## annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.metrics.kafka.service.port }}" - prometheus.io/path: "/metrics" + prometheus.io/scrape: 'true' + prometheus.io/port: '{{ .Values.metrics.kafka.service.port }}' + prometheus.io/path: '/metrics' ## Prometheus JMX Exporter: exposes the majority of Kafkas metrics ## jmx: @@ -1116,11 +1116,11 @@ kafka: ## @param metrics.jmx.service.nodePort Kubernetes HTTP node port ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport ## - nodePort: "" + nodePort: '' ## @param metrics.jmx.service.loadBalancerIP loadBalancerIP if service type is `LoadBalancer` ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer ## - loadBalancerIP: "" + loadBalancerIP: '' ## @param metrics.jmx.service.loadBalancerSourceRanges Load Balancer sources ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service ## Example: @@ -1131,13 +1131,13 @@ kafka: ## @param metrics.jmx.service.clusterIP Static clusterIP or None for headless services ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#choosing-your-own-ip-address ## - clusterIP: "" + clusterIP: '' ## @param metrics.jmx.service.annotations [object] Annotations for the JMX Exporter Prometheus metrics service ## annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.metrics.jmx.service.port }}" - prometheus.io/path: "/" + prometheus.io/scrape: 'true' + prometheus.io/port: '{{ .Values.metrics.jmx.service.port }}' + prometheus.io/path: '/' ## @param metrics.jmx.whitelistObjectNames Allows setting which JMX objects you want to expose to via JMX stats to JMX Exporter ## Only whitelisted values will be exposed via JMX Exporter. They must also be exposed via Rules. To expose all metrics ## (warning its crazy excessive and they aren't formatted in a prometheus style) (1) `whitelistObjectNames: []` @@ -1166,7 +1166,7 @@ kafka: ## @param metrics.jmx.existingConfigmap Name of existing ConfigMap with JMX exporter configuration ## NOTE: This will override metrics.jmx.config ## - existingConfigmap: "" + existingConfigmap: '' ## Prometheus Operator ServiceMonitor configuration ## serviceMonitor: @@ -1175,15 +1175,15 @@ kafka: enabled: false ## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is running ## - namespace: "" + namespace: '' ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint ## - interval: "" + interval: '' ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint ## - scrapeTimeout: "" + scrapeTimeout: '' ## @param metrics.serviceMonitor.selector ServiceMonitor selector labels ## ref: https://github.com/bitnami/charts/tree/master/bitnami/prometheus-operator#prometheus-configuration ## e.g: @@ -1213,7 +1213,7 @@ kafka: ## @param provisioning.schedulerName Name of the k8s scheduler (other than default) for kafka provisioning ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ ## - schedulerName: "" + schedulerName: '' ## @param provisioning.podAnnotations Provisioning Pod annotations. ## podAnnotations: {} @@ -1266,7 +1266,7 @@ kafka: ## If defined, PVC must be created manually before volume will be bound ## The value is evaluated as a template ## - existingClaim: "" + existingClaim: '' ## @param persistence.enabled Enable Zookeeper data persistence using PVC ## enabled: false @@ -1276,16 +1276,16 @@ kafka: enabled: false ## @param zookeeper.auth.clientUser User that will use Zookeeper clients to auth ## - clientUser: "" + clientUser: '' ## @param zookeeper.auth.clientPassword Password that will use Zookeeper clients to auth ## - clientPassword: "" + clientPassword: '' ## @param zookeeper.auth.serverUsers Comma, semicolon or whitespace separated list of user to be created. Specify them as a string, for example: "user1,user2,admin" ## - serverUsers: "" + serverUsers: '' ## @param zookeeper.auth.serverPasswords Comma, semicolon or whitespace separated list of passwords to assign to users when created. Specify them as a string, for example: "pass4user1, pass4user2, pass4admin" ## - serverPasswords: "" + serverPasswords: '' ## This value is only used when zookeeper.enabled is set to false ## externalZookeeper: @@ -1313,22 +1313,22 @@ mysql: ## @param global.storageClass Global StorageClass for Persistent Volume(s) ## global: - imageRegistry: "" + imageRegistry: '' ## E.g. ## imagePullSecrets: ## - myRegistryKeySecretName ## imagePullSecrets: [] - storageClass: "" + storageClass: '' ## @section Common parameters ## @param nameOverride String to partially override common.names.fullname template (will maintain the release name) ## - nameOverride: "" + nameOverride: '' ## @param fullnameOverride String to fully override common.names.fullname template ## - fullnameOverride: "mysql" + fullnameOverride: 'mysql' ## @param clusterDomain Cluster domain ## clusterDomain: cluster.local @@ -1344,7 +1344,7 @@ mysql: ## @param schedulerName Use an alternate scheduler, e.g. "stork". ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ ## - schedulerName: "" + schedulerName: '' ## Enable diagnostic mode in the deployment ## @@ -1401,7 +1401,7 @@ mysql: ## @param auth.rootPassword Password for the `root` user. Ignored if existing secret is provided ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-the-root-password-on-first-run ## - rootPassword: "rootPassword" + rootPassword: 'rootPassword' ## @param auth.database Name for a custom database to create ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-on-first-run ## @@ -1409,21 +1409,21 @@ mysql: ## @param auth.username Name for a custom user to create ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-user-on-first-run ## - username: "user" + username: 'user' ## @param auth.password Password for the new user. Ignored if existing secret is provided ## - password: "password" + password: 'password' ## @param auth.replicationUser MySQL replication user ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-up-a-replication-cluster ## replicationUser: replicator ## @param auth.replicationPassword MySQL replication user password. Ignored if existing secret is provided ## - replicationPassword: "" + replicationPassword: '' ## @param auth.existingSecret Use existing secret for password details. The secret has to contain the keys `mysql-root-password`, `mysql-replication-password` and `mysql-password` ## NOTE: When it's set the auth.rootPassword, auth.password, auth.replicationPassword are ignored. ## - existingSecret: "" + existingSecret: '' ## @param auth.forcePassword Force users to specify required passwords ## forcePassword: true @@ -1448,12 +1448,12 @@ mysql: ## # initdbScripts: {} initdbScripts: - # This script enables legacy authentication for MySQL v8. NodeJS MySQL Client currently does not support authentication plugins, reference: https://github.com/mysqljs/mysql/pull/2233 - enableLegacyAuth.sql: |- - ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; + # This script enables legacy authentication for MySQL v8. NodeJS MySQL Client currently does not support authentication plugins, reference: https://github.com/mysqljs/mysql/pull/2233 + enableLegacyAuth.sql: |- + ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; ## @param initdbScriptsConfigMap ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) ## - initdbScriptsConfigMap: "" + initdbScriptsConfigMap: '' ## @section MySQL Primary parameters @@ -1502,7 +1502,7 @@ mysql: ## @param primary.existingConfiguration Name of existing ConfigMap with MySQL Primary configuration. ## NOTE: When it's set the 'configuration' parameter is ignored ## - existingConfiguration: "" + existingConfiguration: '' ## @param primary.updateStrategy Update strategy type for the MySQL primary statefulset ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies ## @@ -1510,7 +1510,7 @@ mysql: ## @param primary.rollingUpdatePartition Partition update strategy for MySQL Primary statefulset ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions ## - rollingUpdatePartition: "" + rollingUpdatePartition: '' ## @param primary.podAnnotations [object] Additional pod annotations for MySQL primary pods ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ ## @@ -1518,7 +1518,7 @@ mysql: ## @param primary.podAffinityPreset MySQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## - podAffinityPreset: "" + podAffinityPreset: '' ## @param primary.podAntiAffinityPreset MySQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## @@ -1529,12 +1529,12 @@ mysql: nodeAffinityPreset: ## @param primary.nodeAffinityPreset.type MySQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## - type: "" + type: '' ## @param primary.nodeAffinityPreset.key MySQL primary node label key to match Ignored if `primary.affinity` is set. ## E.g. ## key: "kubernetes.io/e2e-az-name" ## - key: "" + key: '' ## @param primary.nodeAffinityPreset.values [array] MySQL primary node label values to match. Ignored if `primary.affinity` is set. ## E.g. ## values: @@ -1653,7 +1653,7 @@ mysql: ## E.g. ## extraFlags: "--max-connect-errors=1000 --max_connections=155" ## - extraFlags: "" + extraFlags: '' ## @param primary.extraEnvVars [array] Extra environment variables to be set on MySQL primary containers ## E.g. ## extraEnvVars: @@ -1663,10 +1663,10 @@ mysql: extraEnvVars: [] ## @param primary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL primary containers ## - extraEnvVarsCM: "" + extraEnvVarsCM: '' ## @param primary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL primary containers ## - extraEnvVarsSecret: "" + extraEnvVarsSecret: '' ## Enable persistence using Persistent Volume Claims ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ ## @@ -1677,7 +1677,7 @@ mysql: ## @param primary.persistence.existingClaim Name of an existing `PersistentVolumeClaim` for MySQL primary replicas ## NOTE: When it's set the rest of persistence parameters are ignored ## - existingClaim: "" + existingClaim: '' ## @param primary.persistence.storageClass MySQL primary persistent volume storage Class ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning @@ -1685,7 +1685,7 @@ mysql: ## set, choosing the default provisioner. (gp2 on AWS, standard on ## GKE, AWS & OpenStack) ## - storageClass: "" + storageClass: '' ## @param primary.persistence.annotations [object] MySQL primary persistent volume claim annotations ## annotations: {} @@ -1726,17 +1726,17 @@ mysql: ## @param primary.service.nodePort MySQL Primary K8s service node port ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport ## - nodePort: "" + nodePort: '' ## @param primary.service.clusterIP MySQL Primary K8s service clusterIP IP ## e.g: ## clusterIP: None ## - clusterIP: "" + clusterIP: '' ## @param primary.service.loadBalancerIP MySQL Primary loadBalancerIP if service type is `LoadBalancer` ## Set the LoadBalancer service type to internal only ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer ## - loadBalancerIP: "" + loadBalancerIP: '' ## @param primary.service.externalTrafficPolicy Enable client source IP preservation ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip ## @@ -1763,7 +1763,7 @@ mysql: minAvailable: 1 ## @param primary.pdb.maxUnavailable Maximum number/percentage of MySQL primary pods that may be made unavailable ## - maxUnavailable: "" + maxUnavailable: '' ## @param primary.podLabels [object] MySQL Primary pod label. If labels are same as commonLabels , this will take precedence ## podLabels: {} @@ -1816,7 +1816,7 @@ mysql: ## @param secondary.existingConfiguration Name of existing ConfigMap with MySQL Secondary configuration. ## NOTE: When it's set the 'configuration' parameter is ignored ## - existingConfiguration: "" + existingConfiguration: '' ## @param secondary.updateStrategy Update strategy type for the MySQL secondary statefulset ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies ## @@ -1824,7 +1824,7 @@ mysql: ## @param secondary.rollingUpdatePartition Partition update strategy for MySQL Secondary statefulset ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions ## - rollingUpdatePartition: "" + rollingUpdatePartition: '' ## @param secondary.podAnnotations [object] Additional pod annotations for MySQL secondary pods ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ ## @@ -1832,7 +1832,7 @@ mysql: ## @param secondary.podAffinityPreset MySQL secondary pod affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## - podAffinityPreset: "" + podAffinityPreset: '' ## @param secondary.podAntiAffinityPreset MySQL secondary pod anti-affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## Allowed values: soft, hard @@ -1844,12 +1844,12 @@ mysql: nodeAffinityPreset: ## @param secondary.nodeAffinityPreset.type MySQL secondary node affinity preset type. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` ## - type: "" + type: '' ## @param secondary.nodeAffinityPreset.key MySQL secondary node label key to match Ignored if `secondary.affinity` is set. ## E.g. ## key: "kubernetes.io/e2e-az-name" ## - key: "" + key: '' ## @param secondary.nodeAffinityPreset.values [array] MySQL secondary node label values to match. Ignored if `secondary.affinity` is set. ## E.g. ## values: @@ -1968,7 +1968,7 @@ mysql: ## E.g. ## extraFlags: "--max-connect-errors=1000 --max_connections=155" ## - extraFlags: "" + extraFlags: '' ## @param secondary.extraEnvVars [array] An array to add extra environment variables on MySQL secondary containers ## E.g. ## extraEnvVars: @@ -1978,10 +1978,10 @@ mysql: extraEnvVars: [] ## @param secondary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL secondary containers ## - extraEnvVarsCM: "" + extraEnvVarsCM: '' ## @param secondary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL secondary containers ## - extraEnvVarsSecret: "" + extraEnvVarsSecret: '' ## Enable persistence using Persistent Volume Claims ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ ## @@ -1996,7 +1996,7 @@ mysql: ## set, choosing the default provisioner. (gp2 on AWS, standard on ## GKE, AWS & OpenStack) ## - storageClass: "" + storageClass: '' ## @param secondary.persistence.annotations [object] MySQL secondary persistent volume claim annotations ## annotations: {} @@ -2037,17 +2037,17 @@ mysql: ## @param secondary.service.nodePort MySQL secondary Kubernetes service node port ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport ## - nodePort: "" + nodePort: '' ## @param secondary.service.clusterIP MySQL secondary Kubernetes service clusterIP IP ## e.g: ## clusterIP: None ## - clusterIP: "" + clusterIP: '' ## @param secondary.service.loadBalancerIP MySQL secondary loadBalancerIP if service type is `LoadBalancer` ## Set the LoadBalancer service type to internal only ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer ## - loadBalancerIP: "" + loadBalancerIP: '' ## @param secondary.service.externalTrafficPolicy Enable client source IP preservation ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip ## @@ -2074,7 +2074,7 @@ mysql: minAvailable: 1 ## @param secondary.pdb.maxUnavailable Maximum number/percentage of MySQL secondary pods that may be made unavailable ## - maxUnavailable: "" + maxUnavailable: '' ## @param secondary.podLabels [object] Additional pod labels for MySQL secondary pods ## podLabels: {} @@ -2091,7 +2091,7 @@ mysql: ## @param serviceAccount.name Name of the created ServiceAccount ## If not set and create is true, a name is generated using the mysql.fullname template ## - name: "" + name: '' ## @param serviceAccount.annotations [object] Annotations for MySQL Service Account ## annotations: {} @@ -2203,8 +2203,8 @@ mysql: type: ClusterIP port: 9104 annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.metrics.service.port }}" + prometheus.io/scrape: 'true' + prometheus.io/port: '{{ .Values.metrics.service.port }}' ## @param metrics.extraArgs.primary [array] Extra args to be passed to mysqld_exporter on Primary pods ## @param metrics.extraArgs.secondary [array] Extra args to be passed to mysqld_exporter on Secondary pods ## ref: https://github.com/prometheus/mysqld_exporter/ @@ -2308,7 +2308,7 @@ mysql: enabled: false ## @param metrics.serviceMonitor.namespace Specify the namespace in which the serviceMonitor resource will be created ## - namespace: "" + namespace: '' ## @param metrics.serviceMonitor.interval Specify the interval at which metrics should be scraped ## interval: 30s @@ -2316,7 +2316,7 @@ mysql: ## e.g: ## scrapeTimeout: 30s ## - scrapeTimeout: "" + scrapeTimeout: '' ## @param metrics.serviceMonitor.relabellings [array] Specify Metric Relabellings to add to the scrape endpoint ## relabellings: [] @@ -2351,8 +2351,9 @@ kowl: ingress: enabled: true - className: "" - annotations: {} + className: '' + annotations: + {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: @@ -2369,14 +2370,14 @@ kowl: ## MongoDB Backend Dependency reporting-events-db: enabled: true - fullnameOverride: "reporting-events-db" + fullnameOverride: 'reporting-events-db' auth: rootUser: root - rootPassword: "rootPassword" - username: "user" - password: "password" - database: "default" + rootPassword: 'rootPassword' + username: 'user' + password: 'password' + database: 'default' resources: requests: @@ -2412,22 +2413,22 @@ keto: ## @param global.storageClass Global StorageClass for Persistent Volume(s) ## global: - imageRegistry: "" + imageRegistry: '' ## E.g. ## imagePullSecrets: ## - myRegistryKeySecretName ## imagePullSecrets: [] - storageClass: "" + storageClass: '' ## @section Common parameters ## @param nameOverride String to partially override kafka.fullname ## - nameOverride: "" + nameOverride: '' ## @param fullnameOverride String to fully override kafka.fullname ## - fullnameOverride: "keto" + fullnameOverride: 'keto' ## @param clusterDomain Default Kubernetes cluster domain ## clusterDomain: cluster.local @@ -2479,7 +2480,7 @@ keto: pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. # -- Ory KETO version - tag: "v0.6.0-alpha.1-sqlite" + tag: 'v0.6.0-alpha.1-sqlite' imagePullSecrets: [] @@ -2490,11 +2491,12 @@ keto: annotations: {} # -- The name of the service account to use. # If not set and create is true, a name is generated using the fullname template - name: "" + name: '' podAnnotations: {} - podSecurityContext: {} + podSecurityContext: + {} # fsGroup: 2000 # https://github.com/kubernetes/kubernetes/issues/57601 @@ -2504,7 +2506,7 @@ keto: securityContext: capabilities: drop: - - ALL + - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 100 @@ -2517,7 +2519,7 @@ keto: ingress: read: enabled: true - className: "" + className: '' annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" @@ -2532,7 +2534,7 @@ keto: # - keto.local write: enabled: false - className: "" + className: '' annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" @@ -2566,10 +2568,10 @@ keto: secretAnnotations: # Create the secret before installation, and only then. This saves the secret from regenerating during an upgrade # pre-upgrade is needed to upgrade from 0.7.0 to newer. Can be deleted afterwards. - helm.sh/hook-weight: "0" - helm.sh/hook: "pre-install, pre-upgrade" - helm.sh/hook-delete-policy: "before-hook-creation" - helm.sh/resource-policy: "keep" + helm.sh/hook-weight: '0' + helm.sh/hook: 'pre-install, pre-upgrade' + helm.sh/hook-delete-policy: 'before-hook-creation' + helm.sh/resource-policy: 'keep' keto: # https://www.ory.sh/keto/docs/reference/configuration @@ -2590,7 +2592,8 @@ keto: autoMigrate: false - resources: {} + resources: + {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following @@ -2680,7 +2683,7 @@ oathkeeper: # -- If enabled, a demo deployment with exemplary access rules and JSON Web Key Secrets will be generated. demo: false - fullnameOverride: "oathkeeper" + fullnameOverride: 'oathkeeper' # -- Configures the Kubernetes service service: # -- Configures the Kubernetes service for the proxy port. @@ -2725,10 +2728,10 @@ oathkeeper: proxy: # -- En-/Disable the proxy ingress. enabled: true - className: "" + className: '' annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" hosts: - host: www-bof.local paths: @@ -2736,20 +2739,20 @@ oathkeeper: pathType: ImplementationSpecific annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2 - # tls: [] - # hosts: - # - www-bof.local - # - secretName: oathkeeper-proxy-example-tls + # tls: [] + # hosts: + # - www-bof.local + # - secretName: oathkeeper-proxy-example-tls api: # -- En-/Disable the api ingress. enabled: false - className: "" + className: '' annotations: {} - # If you do want to specify annotations, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'annotations:'. - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" + # If you do want to specify annotations, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'annotations:'. + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" hosts: - host: api-oathkeeper.local paths: @@ -2778,18 +2781,18 @@ oathkeeper: preserve_path: true # this means we automatically sweep up all the metadata kratos provides for use # in, for example, the JWT, if we ever have more - extra_from: "@this" + extra_from: '@this' # kratos will be configured to put the subject from the IdP here - subject_from: "identity.traits.subject" + subject_from: 'identity.traits.subject' only: - - ory_kratos_session + - ory_kratos_session oauth2_introspection: enabled: true config: introspection_url: https://wso2-identity-server.local:9443/oauth2/introspect introspection_request_headers: # Configure the following with base64 encoded string contains admin:password of wso2 server - authorization: "Basic YWRtaW46YWRtaW4=" + authorization: 'Basic YWRtaW46YWRtaW4=' # cache: # # disabled to make debugging easier. enable for caching. # enabled: false @@ -2800,7 +2803,7 @@ oathkeeper: config: # the check URL for Keto. This will be POST'd to. See https://www.ory.sh/keto/docs/reference/rest-api#operation/postCheck remote: http://keto-read/check - payload: "" + payload: '' mutators: id_token: enabled: true @@ -2828,13 +2831,13 @@ oathkeeper: # set this to whatever the main URL is, it'll ensure that browser errors redirect there to: http://www-bof.local/ when: - - error: - - unauthorized - - forbidden - request: - header: - accept: - - text/html + - error: + - unauthorized + - forbidden + request: + header: + accept: + - text/html serve: proxy: port: 4455 @@ -2861,7 +2864,7 @@ oathkeeper: manage: true # -- name of the secret to use. If empty, defaults to {{ include "oathkeeper.fullname" . }} - name: "oathkeeper-jwks" + name: 'oathkeeper-jwks' # -- default mount path for the kubernetes secret mountPath: /etc/secrets @@ -2883,7 +2886,7 @@ oathkeeper: securityContext: capabilities: drop: - - ALL + - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 @@ -2954,7 +2957,6 @@ oathkeeper: # lines, adjust them as necessary, and remove the curly braces after 'annotations:'. # e.g. sidecar.istio.io/rewriteAppHTTPProbers: "true" - # -- Configure node affinity affinity: {} @@ -2973,9 +2975,8 @@ oathkeeper: oathkeeperFullnameOverride: 'oathkeeper' deployment: envs: - - name: authorizersAvailable - value: allow,deny,noop,remote_json - + - name: authorizersAvailable + value: allow,deny,noop,remote_json ## Ory Kratos ## If mysql is used as DB, run the following command from the kratos container to setup the DB schemas @@ -2992,7 +2993,7 @@ kratos: maxSurge: 30% maxUnavailable: 0 - fullnameOverride: "kratos" + fullnameOverride: 'kratos' service: admin: @@ -3022,16 +3023,17 @@ kratos: secretAnnotations: # Create the secret before installation, and only then. This saves the secret from regenerating during an upgrade # pre-upgrade is needed to upgrade from 0.7.0 to newer. Can be deleted afterwards. - helm.sh/hook-weight: "0" - helm.sh/hook: "pre-install, pre-upgrade" - helm.sh/hook-delete-policy: "before-hook-creation" - helm.sh/resource-policy: "keep" + helm.sh/hook-weight: '0' + helm.sh/hook: 'pre-install, pre-upgrade' + helm.sh/hook-delete-policy: 'before-hook-creation' + helm.sh/resource-policy: 'keep' ingress: admin: enabled: false - className: "" - annotations: {} + className: '' + annotations: + {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: @@ -3043,18 +3045,17 @@ kratos: # hosts: # - kratos-admin.local public: - ## Disabling the following required ingress for fixing an issue with ingress in K8S version 1.22+ - ## For K8S versions less than 1.22, we can just enable this and use the config - ## We can't use this config in K8S 1.22+ unless we upgrade the kratos chart version + ## Disabling the following required ingress for fixing an issue with ingress in K8S version 1.22+ + ## For K8S versions less than 1.22, we can just enable this and use the config + ## We can't use this config in K8S 1.22+ unless we upgrade the kratos chart version enabled: false - className: "" + className: '' annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2 # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - - - host: www-bof.local + - host: www-bof.local paths: - /kratos(/|$)(.*) tls: [] @@ -3111,7 +3112,7 @@ kratos: # -- You can add multiple identity schemas here identitySchemas: - "identity.default.schema.json": | + 'identity.default.schema.json': | { "$id": "https://mojaloop.io/kratos-schema/identity.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", @@ -3167,30 +3168,30 @@ kratos: enabled: true config: providers: - - id: idp - provider: generic - # TODO both the client_id and client_secret need to be set appropriately to the client supporting authorization code grants with openid - # TODO these can alternatively be set via environment variable from a k8s secret - client_id: provide-client-id-here - client_secret: provide-client-secret-here - # mapper_url: file:///etc/config2/oidc.jsonnet - mapper_url: base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dFZhcignY2xhaW1zJyk7Cgp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgICBuYW1lOiBjbGFpbXMubmFtZSwKICAgICAgc3ViamVjdDogY2xhaW1zLnN1YgogICAgfSwKICB9LAp9 - # issuer_url is the OpenID Connect Server URL. You can leave this empty if `provider` is not set to `generic`. - # If set, neither `auth_url` nor `token_url` are required. - issuer_url: https://wso2-identity-server.local:9443/oauth2/oidcdiscovery - - # auth_url is the authorize url, typically something like: https://example.org/oauth2/auth - # Should only be used when the OAuth2 / OpenID Connect server is not supporting OpenID Connect Discovery and when - # `provider` is set to `generic`. - # auth_url: http://openid-connect-provider/oauth2/auth - - # token_url is the token url, typically something like: https://example.org/oauth2/token - # Should only be used when the OAuth2 / OpenID Connect server is not supporting OpenID Connect Discovery and when - # `provider` is set to `generic`. - # token_url: http://openid-connect-provider/oauth2/token - scope: - # # TODO adjust requested scope based on IdP (WSO2) documentation - - openid + - id: idp + provider: generic + # TODO both the client_id and client_secret need to be set appropriately to the client supporting authorization code grants with openid + # TODO these can alternatively be set via environment variable from a k8s secret + client_id: provide-client-id-here + client_secret: provide-client-secret-here + # mapper_url: file:///etc/config2/oidc.jsonnet + mapper_url: base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dFZhcignY2xhaW1zJyk7Cgp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgICBuYW1lOiBjbGFpbXMubmFtZSwKICAgICAgc3ViamVjdDogY2xhaW1zLnN1YgogICAgfSwKICB9LAp9 + # issuer_url is the OpenID Connect Server URL. You can leave this empty if `provider` is not set to `generic`. + # If set, neither `auth_url` nor `token_url` are required. + issuer_url: https://wso2-identity-server.local:9443/oauth2/oidcdiscovery + + # auth_url is the authorize url, typically something like: https://example.org/oauth2/auth + # Should only be used when the OAuth2 / OpenID Connect server is not supporting OpenID Connect Discovery and when + # `provider` is set to `generic`. + # auth_url: http://openid-connect-provider/oauth2/auth + + # token_url is the token url, typically something like: https://example.org/oauth2/token + # Should only be used when the OAuth2 / OpenID Connect server is not supporting OpenID Connect Discovery and when + # `provider` is set to `generic`. + # token_url: http://openid-connect-provider/oauth2/token + scope: + # # TODO adjust requested scope based on IdP (WSO2) documentation + - openid flows: # error: # ui_url: http://www-bof.local/selfui/error @@ -3379,7 +3380,7 @@ kratos: securityContext: capabilities: drop: - - ALL + - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 100 @@ -3430,7 +3431,7 @@ kratos: ## MySQL DB for Kratos kratos-db: enabled: false - fullnameOverride: "kratos-db" + fullnameOverride: 'kratos-db' image: registry: docker.io repository: bitnami/mysql @@ -3460,7 +3461,7 @@ kratos-db: ## @param auth.rootPassword Password for the `root` user. Ignored if existing secret is provided ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-the-root-password-on-first-run ## - rootPassword: "rootPassword" + rootPassword: 'rootPassword' ## @param auth.database Name for a custom database to create ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-on-first-run ## @@ -3468,21 +3469,21 @@ kratos-db: ## @param auth.username Name for a custom user to create ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-user-on-first-run ## - username: "user" + username: 'user' ## @param auth.password Password for the new user. Ignored if existing secret is provided ## - password: "password" + password: 'password' ## @param auth.replicationUser MySQL replication user ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-up-a-replication-cluster ## replicationUser: replicator ## @param auth.replicationPassword MySQL replication user password. Ignored if existing secret is provided ## - replicationPassword: "" + replicationPassword: '' ## @param auth.existingSecret Use existing secret for password details. The secret has to contain the keys `mysql-root-password`, `mysql-replication-password` and `mysql-password` ## NOTE: When it's set the auth.rootPassword, auth.password, auth.replicationPassword are ignored. ## - existingSecret: "" + existingSecret: '' ## @param auth.forcePassword Force users to specify required passwords ## forcePassword: true @@ -3507,12 +3508,12 @@ kratos-db: ## # initdbScripts: {} initdbScripts: - # This script enables legacy authentication for MySQL v8. NodeJS MySQL Client currently does not support authentication plugins, reference: https://github.com/mysqljs/mysql/pull/2233 - enableLegacyAuth.sql: |- - ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; + # This script enables legacy authentication for MySQL v8. NodeJS MySQL Client currently does not support authentication plugins, reference: https://github.com/mysqljs/mysql/pull/2233 + enableLegacyAuth.sql: |- + ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; ## @param initdbScriptsConfigMap ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) ## - initdbScriptsConfigMap: "" + initdbScriptsConfigMap: '' ## @section MySQL Primary parameters @@ -3561,7 +3562,7 @@ kratos-db: ## @param primary.existingConfiguration Name of existing ConfigMap with MySQL Primary configuration. ## NOTE: When it's set the 'configuration' parameter is ignored ## - existingConfiguration: "" + existingConfiguration: '' ## @param primary.updateStrategy Update strategy type for the MySQL primary statefulset ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies ## @@ -3569,7 +3570,7 @@ kratos-db: ## @param primary.rollingUpdatePartition Partition update strategy for MySQL Primary statefulset ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions ## - rollingUpdatePartition: "" + rollingUpdatePartition: '' ## @param primary.podAnnotations [object] Additional pod annotations for MySQL primary pods ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ ## @@ -3577,7 +3578,7 @@ kratos-db: ## @param primary.podAffinityPreset MySQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## - podAffinityPreset: "" + podAffinityPreset: '' ## @param primary.podAntiAffinityPreset MySQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## @@ -3588,12 +3589,12 @@ kratos-db: nodeAffinityPreset: ## @param primary.nodeAffinityPreset.type MySQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## - type: "" + type: '' ## @param primary.nodeAffinityPreset.key MySQL primary node label key to match Ignored if `primary.affinity` is set. ## E.g. ## key: "kubernetes.io/e2e-az-name" ## - key: "" + key: '' ## @param primary.nodeAffinityPreset.values [array] MySQL primary node label values to match. Ignored if `primary.affinity` is set. ## E.g. ## values: @@ -3712,7 +3713,7 @@ kratos-db: ## E.g. ## extraFlags: "--max-connect-errors=1000 --max_connections=155" ## - extraFlags: "" + extraFlags: '' ## @param primary.extraEnvVars [array] Extra environment variables to be set on MySQL primary containers ## E.g. ## extraEnvVars: @@ -3722,10 +3723,10 @@ kratos-db: extraEnvVars: [] ## @param primary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL primary containers ## - extraEnvVarsCM: "" + extraEnvVarsCM: '' ## @param primary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL primary containers ## - extraEnvVarsSecret: "" + extraEnvVarsSecret: '' ## Enable persistence using Persistent Volume Claims ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ ## @@ -3736,7 +3737,7 @@ kratos-db: ## @param primary.persistence.existingClaim Name of an existing `PersistentVolumeClaim` for MySQL primary replicas ## NOTE: When it's set the rest of persistence parameters are ignored ## - existingClaim: "" + existingClaim: '' ## @param primary.persistence.storageClass MySQL primary persistent volume storage Class ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning @@ -3744,7 +3745,7 @@ kratos-db: ## set, choosing the default provisioner. (gp2 on AWS, standard on ## GKE, AWS & OpenStack) ## - storageClass: "" + storageClass: '' ## @param primary.persistence.annotations [object] MySQL primary persistent volume claim annotations ## annotations: {} @@ -3785,17 +3786,17 @@ kratos-db: ## @param primary.service.nodePort MySQL Primary K8s service node port ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport ## - nodePort: "" + nodePort: '' ## @param primary.service.clusterIP MySQL Primary K8s service clusterIP IP ## e.g: ## clusterIP: None ## - clusterIP: "" + clusterIP: '' ## @param primary.service.loadBalancerIP MySQL Primary loadBalancerIP if service type is `LoadBalancer` ## Set the LoadBalancer service type to internal only ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer ## - loadBalancerIP: "" + loadBalancerIP: '' ## @param primary.service.externalTrafficPolicy Enable client source IP preservation ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip ## @@ -3822,7 +3823,7 @@ kratos-db: minAvailable: 1 ## @param primary.pdb.maxUnavailable Maximum number/percentage of MySQL primary pods that may be made unavailable ## - maxUnavailable: "" + maxUnavailable: '' ## @param primary.podLabels [object] MySQL Primary pod label. If labels are same as commonLabels , this will take precedence ## podLabels: {} @@ -3839,7 +3840,7 @@ kratos-db: ## @param serviceAccount.name Name of the created ServiceAccount ## If not set and create is true, a name is generated using the mysql.fullname template ## - name: "" + name: '' ## @param serviceAccount.annotations [object] Annotations for MySQL Service Account ## annotations: {} @@ -3951,8 +3952,8 @@ kratos-db: type: ClusterIP port: 9104 annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.metrics.service.port }}" + prometheus.io/scrape: 'true' + prometheus.io/port: '{{ .Values.metrics.service.port }}' ## @param metrics.extraArgs.primary [array] Extra args to be passed to mysqld_exporter on Primary pods ## @param metrics.extraArgs.secondary [array] Extra args to be passed to mysqld_exporter on Secondary pods ## ref: https://github.com/prometheus/mysqld_exporter/ @@ -4056,7 +4057,7 @@ kratos-db: enabled: true ## @param metrics.serviceMonitor.namespace Specify the namespace in which the serviceMonitor resource will be created ## - namespace: "" + namespace: '' ## @param metrics.serviceMonitor.interval Specify the interval at which metrics should be scraped ## interval: 30s @@ -4064,7 +4065,7 @@ kratos-db: ## e.g: ## scrapeTimeout: 30s ## - scrapeTimeout: "" + scrapeTimeout: '' ## @param metrics.serviceMonitor.relabellings [array] Specify Metric Relabellings to add to the scrape endpoint ## relabellings: [] @@ -4080,7 +4081,7 @@ kratos-db: ## MySQL DB for Keto keto-db: enabled: false - fullnameOverride: "keto-db" + fullnameOverride: 'keto-db' image: registry: docker.io repository: bitnami/mysql @@ -4110,7 +4111,7 @@ keto-db: ## @param auth.rootPassword Password for the `root` user. Ignored if existing secret is provided ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-the-root-password-on-first-run ## - rootPassword: "rootPassword" + rootPassword: 'rootPassword' ## @param auth.database Name for a custom database to create ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-on-first-run ## @@ -4118,21 +4119,21 @@ keto-db: ## @param auth.username Name for a custom user to create ## ref: https://github.com/bitnami/bitnami-docker-mysql/blob/master/README.md#creating-a-database-user-on-first-run ## - username: "user" + username: 'user' ## @param auth.password Password for the new user. Ignored if existing secret is provided ## - password: "password" + password: 'password' ## @param auth.replicationUser MySQL replication user ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-up-a-replication-cluster ## replicationUser: replicator ## @param auth.replicationPassword MySQL replication user password. Ignored if existing secret is provided ## - replicationPassword: "" + replicationPassword: '' ## @param auth.existingSecret Use existing secret for password details. The secret has to contain the keys `mysql-root-password`, `mysql-replication-password` and `mysql-password` ## NOTE: When it's set the auth.rootPassword, auth.password, auth.replicationPassword are ignored. ## - existingSecret: "" + existingSecret: '' ## @param auth.forcePassword Force users to specify required passwords ## forcePassword: true @@ -4157,12 +4158,12 @@ keto-db: ## # initdbScripts: {} initdbScripts: - # This script enables legacy authentication for MySQL v8. NodeJS MySQL Client currently does not support authentication plugins, reference: https://github.com/mysqljs/mysql/pull/2233 - enableLegacyAuth.sql: |- - ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; + # This script enables legacy authentication for MySQL v8. NodeJS MySQL Client currently does not support authentication plugins, reference: https://github.com/mysqljs/mysql/pull/2233 + enableLegacyAuth.sql: |- + ALTER USER 'user'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; ## @param initdbScriptsConfigMap ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) ## - initdbScriptsConfigMap: "" + initdbScriptsConfigMap: '' ## @section MySQL Primary parameters @@ -4211,7 +4212,7 @@ keto-db: ## @param primary.existingConfiguration Name of existing ConfigMap with MySQL Primary configuration. ## NOTE: When it's set the 'configuration' parameter is ignored ## - existingConfiguration: "" + existingConfiguration: '' ## @param primary.updateStrategy Update strategy type for the MySQL primary statefulset ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies ## @@ -4219,7 +4220,7 @@ keto-db: ## @param primary.rollingUpdatePartition Partition update strategy for MySQL Primary statefulset ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions ## - rollingUpdatePartition: "" + rollingUpdatePartition: '' ## @param primary.podAnnotations [object] Additional pod annotations for MySQL primary pods ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ ## @@ -4227,7 +4228,7 @@ keto-db: ## @param primary.podAffinityPreset MySQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## - podAffinityPreset: "" + podAffinityPreset: '' ## @param primary.podAntiAffinityPreset MySQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity ## @@ -4238,12 +4239,12 @@ keto-db: nodeAffinityPreset: ## @param primary.nodeAffinityPreset.type MySQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` ## - type: "" + type: '' ## @param primary.nodeAffinityPreset.key MySQL primary node label key to match Ignored if `primary.affinity` is set. ## E.g. ## key: "kubernetes.io/e2e-az-name" ## - key: "" + key: '' ## @param primary.nodeAffinityPreset.values [array] MySQL primary node label values to match. Ignored if `primary.affinity` is set. ## E.g. ## values: @@ -4362,7 +4363,7 @@ keto-db: ## E.g. ## extraFlags: "--max-connect-errors=1000 --max_connections=155" ## - extraFlags: "" + extraFlags: '' ## @param primary.extraEnvVars [array] Extra environment variables to be set on MySQL primary containers ## E.g. ## extraEnvVars: @@ -4372,10 +4373,10 @@ keto-db: extraEnvVars: [] ## @param primary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL primary containers ## - extraEnvVarsCM: "" + extraEnvVarsCM: '' ## @param primary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL primary containers ## - extraEnvVarsSecret: "" + extraEnvVarsSecret: '' ## Enable persistence using Persistent Volume Claims ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ ## @@ -4386,7 +4387,7 @@ keto-db: ## @param primary.persistence.existingClaim Name of an existing `PersistentVolumeClaim` for MySQL primary replicas ## NOTE: When it's set the rest of persistence parameters are ignored ## - existingClaim: "" + existingClaim: '' ## @param primary.persistence.storageClass MySQL primary persistent volume storage Class ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning @@ -4394,7 +4395,7 @@ keto-db: ## set, choosing the default provisioner. (gp2 on AWS, standard on ## GKE, AWS & OpenStack) ## - storageClass: "" + storageClass: '' ## @param primary.persistence.annotations [object] MySQL primary persistent volume claim annotations ## annotations: {} @@ -4435,17 +4436,17 @@ keto-db: ## @param primary.service.nodePort MySQL Primary K8s service node port ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport ## - nodePort: "" + nodePort: '' ## @param primary.service.clusterIP MySQL Primary K8s service clusterIP IP ## e.g: ## clusterIP: None ## - clusterIP: "" + clusterIP: '' ## @param primary.service.loadBalancerIP MySQL Primary loadBalancerIP if service type is `LoadBalancer` ## Set the LoadBalancer service type to internal only ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer ## - loadBalancerIP: "" + loadBalancerIP: '' ## @param primary.service.externalTrafficPolicy Enable client source IP preservation ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip ## @@ -4472,7 +4473,7 @@ keto-db: minAvailable: 1 ## @param primary.pdb.maxUnavailable Maximum number/percentage of MySQL primary pods that may be made unavailable ## - maxUnavailable: "" + maxUnavailable: '' ## @param primary.podLabels [object] MySQL Primary pod label. If labels are same as commonLabels , this will take precedence ## podLabels: {} @@ -4489,7 +4490,7 @@ keto-db: ## @param serviceAccount.name Name of the created ServiceAccount ## If not set and create is true, a name is generated using the mysql.fullname template ## - name: "" + name: '' ## @param serviceAccount.annotations [object] Annotations for MySQL Service Account ## annotations: {} @@ -4601,8 +4602,8 @@ keto-db: type: ClusterIP port: 9104 annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.metrics.service.port }}" + prometheus.io/scrape: 'true' + prometheus.io/port: '{{ .Values.metrics.service.port }}' ## @param metrics.extraArgs.primary [array] Extra args to be passed to mysqld_exporter on Primary pods ## @param metrics.extraArgs.secondary [array] Extra args to be passed to mysqld_exporter on Secondary pods ## ref: https://github.com/prometheus/mysqld_exporter/ @@ -4706,7 +4707,7 @@ keto-db: enabled: true ## @param metrics.serviceMonitor.namespace Specify the namespace in which the serviceMonitor resource will be created ## - namespace: "" + namespace: '' ## @param metrics.serviceMonitor.interval Specify the interval at which metrics should be scraped ## interval: 30s @@ -4714,7 +4715,7 @@ keto-db: ## e.g: ## scrapeTimeout: 30s ## - scrapeTimeout: "" + scrapeTimeout: '' ## @param metrics.serviceMonitor.relabellings [array] Specify Metric Relabellings to add to the scrape endpoint ## relabellings: [] @@ -4736,5 +4737,5 @@ wso2: createSecrets: true secrets: wso2-is-admin-creds: - password: "admin" + password: 'admin' # - chart-example.local diff --git a/integration_test/chart_values/bof.yaml b/integration_test/chart_values/bof.yaml index 90c0dd9..b452e6a 100644 --- a/integration_test/chart_values/bof.yaml +++ b/integration_test/chart_values/bof.yaml @@ -7,16 +7,16 @@ global: adminApiSvc: - host: "centralledger-service" + host: 'centralledger-service' port: 80 keto: - readURL: "http://keto-read:80" - writeURL: "http://keto-write:80" + readURL: 'http://keto-read:80' + writeURL: 'http://keto-write:80' wso2: identityServer: - host: "wso2-identity-server.local" + host: 'wso2-identity-server.local' port: 9443 - userListURL: "http://wso2-identity-server.local:9443/scim2/Users" + userListURL: 'http://wso2-identity-server.local:9443/scim2/Users' user: 'admin' secret: name: wso2-is-admin-creds @@ -55,11 +55,13 @@ role-assignment-service: annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2 configFiles: - default.json: { - "ROLES_LIST": [ - "USER_ROLE_abc7a2fd-4acf-4547-a194-1673f63eb37c", - "ADMIN_ROLE_6c1ec084-86d4-4915-ba81-6c59b87a65a6" - ] + default.json: + { + 'ROLES_LIST': + [ + 'USER_ROLE_abc7a2fd-4acf-4547-a194-1673f63eb37c', + 'ADMIN_ROLE_6c1ec084-86d4-4915-ba81-6c59b87a65a6', + ], } reporting-hub-bop-api-svc: diff --git a/integration_test/kustomization.yaml b/integration_test/kustomization.yaml index f67f303..9c100f3 100644 --- a/integration_test/kustomization.yaml +++ b/integration_test/kustomization.yaml @@ -1,3 +1,3 @@ resources: -- manifests/reporting-hub-bop-trx-ui -- manifests/backend + - manifests/reporting-hub-bop-trx-ui + - manifests/backend diff --git a/integration_test/manifests/backend/ing.yaml b/integration_test/manifests/backend/ing.yaml index 0ddca59..e5b0d79 100644 --- a/integration_test/manifests/backend/ing.yaml +++ b/integration_test/manifests/backend/ing.yaml @@ -6,13 +6,13 @@ metadata: name: reporting-hub-bop-trx-ui spec: rules: - - http: - paths: - - path: /()(.*) - backend: - serviceName: reporting-hub-bop-trx-ui - servicePort: http - - path: /reporting-api(/|$)(.*) - backend: - serviceName: bof-reporting-hub-bop-api-svc - servicePort: 80 + - http: + paths: + - path: /()(.*) + backend: + serviceName: reporting-hub-bop-trx-ui + servicePort: http + - path: /reporting-api(/|$)(.*) + backend: + serviceName: bof-reporting-hub-bop-api-svc + servicePort: 80 diff --git a/integration_test/manifests/backend/kustomization.yaml b/integration_test/manifests/backend/kustomization.yaml index 5949df5..7c855a6 100644 --- a/integration_test/manifests/backend/kustomization.yaml +++ b/integration_test/manifests/backend/kustomization.yaml @@ -1,11 +1,11 @@ resources: -- github.com/partiallyordered/mojaloop-kustomize/base/nginx-ingress-controller?ref=ee2ffcc2588dcefda74ed6b4f2e47a7388f67f4e -- github.com/partiallyordered/mojaloop-kustomize/base/mojaloop/central?ref=3903827ee6b00e8efdca9a39d3fe0774b27e93e3 -- github.com/partiallyordered/voodoo-doll/kubernetes?ref=ad96349ef8fe91a422377be8f53aea56aee60c8e -- secret_copy.yaml -- ing.yaml -- ml_adapter_sidecar_config.yaml -- central_ledger_sidecar_config.yaml + - github.com/partiallyordered/mojaloop-kustomize/base/nginx-ingress-controller?ref=ee2ffcc2588dcefda74ed6b4f2e47a7388f67f4e + - github.com/partiallyordered/mojaloop-kustomize/base/mojaloop/central?ref=3903827ee6b00e8efdca9a39d3fe0774b27e93e3 + - github.com/partiallyordered/voodoo-doll/kubernetes?ref=ad96349ef8fe91a422377be8f53aea56aee60c8e + - secret_copy.yaml + - ing.yaml + - ml_adapter_sidecar_config.yaml + - central_ledger_sidecar_config.yaml configMapGenerator: [] diff --git a/integration_test/manifests/backend/patch_adapter_event_sidecar.yaml b/integration_test/manifests/backend/patch_adapter_event_sidecar.yaml index fa7e6f5..08f2f7b 100644 --- a/integration_test/manifests/backend/patch_adapter_event_sidecar.yaml +++ b/integration_test/manifests/backend/patch_adapter_event_sidecar.yaml @@ -7,7 +7,7 @@ spec: spec: containers: - name: ml-api-adapter-service-sidecar - image: "mojaloop/event-sidecar:v11.0.1" + image: 'mojaloop/event-sidecar:v11.0.1' imagePullPolicy: IfNotPresent command: - npm @@ -32,17 +32,17 @@ spec: mountPath: /opt/event-sidecar/config env: - name: LOG_LEVEL - value: "info" + value: 'info' - name: LOG_FILTER - value: "error, warn, info" + value: 'error, warn, info' - name: EVENT_SDK_LOG_FILTER - value: "audit:*, log:info, log:warn, log:error" + value: 'audit:*, log:info, log:warn, log:error' - name: EVENT_SDK_LOG_METADATA_ONLY - value: "true" + value: 'true' volumes: - name: ml-api-adapter-service-sidecar-volume configMap: name: ml-api-adapter-service-sidecar items: - - key: default.json - path: default.json + - key: default.json + path: default.json diff --git a/integration_test/manifests/backend/patch_ledger_event_sidecar.yaml b/integration_test/manifests/backend/patch_ledger_event_sidecar.yaml index 2e1a746..d0f6a4d 100644 --- a/integration_test/manifests/backend/patch_ledger_event_sidecar.yaml +++ b/integration_test/manifests/backend/patch_ledger_event_sidecar.yaml @@ -9,9 +9,9 @@ spec: - name: centralledger-service env: - name: EVENT_SDK_SIDECAR_DISABLED - value: "false" + value: 'false' - name: centralledger-service-sidecar - image: "mojaloop/event-sidecar:v11.0.1" + image: 'mojaloop/event-sidecar:v11.0.1' imagePullPolicy: IfNotPresent command: - npm @@ -36,17 +36,17 @@ spec: mountPath: /opt/event-sidecar/config env: - name: LOG_LEVEL - value: "info" + value: 'info' - name: LOG_FILTER - value: "error, warn, info" + value: 'error, warn, info' - name: EVENT_SDK_LOG_FILTER - value: "audit:*, log:info, log:warn, log:error" + value: 'audit:*, log:info, log:warn, log:error' - name: EVENT_SDK_LOG_METADATA_ONLY - value: "true" + value: 'true' volumes: - name: sidecar-volume configMap: name: centralledger-service-sidecar items: - - key: default.json - path: default.json + - key: default.json + path: default.json diff --git a/integration_test/manifests/reporting-hub-bop-trx-ui/deploy.yaml b/integration_test/manifests/reporting-hub-bop-trx-ui/deploy.yaml index f0149a6..1df72af 100644 --- a/integration_test/manifests/reporting-hub-bop-trx-ui/deploy.yaml +++ b/integration_test/manifests/reporting-hub-bop-trx-ui/deploy.yaml @@ -13,10 +13,10 @@ spec: app.kubernetes.io/name: reporting-hub-bop-trx-ui spec: containers: - - name: app - image: ghcr.io/mojaloop/reporting-hub-bop-trx-ui:v0.0.0 - ports: - - containerPort: 8082 - env: - - name: REACT_APP_API_BASE_URL - value: /reporting-api + - name: app + image: ghcr.io/mojaloop/reporting-hub-bop-trx-ui:v0.0.0 + ports: + - containerPort: 8082 + env: + - name: REACT_APP_API_BASE_URL + value: /reporting-api diff --git a/integration_test/manifests/reporting-hub-bop-trx-ui/kustomization.yaml b/integration_test/manifests/reporting-hub-bop-trx-ui/kustomization.yaml index 371d3ea..cf7d28d 100644 --- a/integration_test/manifests/reporting-hub-bop-trx-ui/kustomization.yaml +++ b/integration_test/manifests/reporting-hub-bop-trx-ui/kustomization.yaml @@ -1,3 +1,3 @@ resources: -- deploy.yaml -- svc.yaml + - deploy.yaml + - svc.yaml diff --git a/integration_test/manifests/reporting-hub-bop-trx-ui/svc.yaml b/integration_test/manifests/reporting-hub-bop-trx-ui/svc.yaml index a421b8d..1d7c4ea 100644 --- a/integration_test/manifests/reporting-hub-bop-trx-ui/svc.yaml +++ b/integration_test/manifests/reporting-hub-bop-trx-ui/svc.yaml @@ -6,8 +6,8 @@ metadata: app.kubernetes.io/name: reporting-hub-bop-trx-ui spec: ports: - - port: 80 - targetPort: 8082 - name: http + - port: 80 + targetPort: 8082 + name: http selector: app.kubernetes.io/name: reporting-hub-bop-trx-ui diff --git a/integration_test/tests/.eslintrc.json b/integration_test/tests/.eslintrc.json index 2ed2507..68be6d7 100644 --- a/integration_test/tests/.eslintrc.json +++ b/integration_test/tests/.eslintrc.json @@ -11,23 +11,10 @@ }, "rules": { "max-len": 100, - "indent": [ - "error", - 2, - { "SwitchCase": 1 } - ], - "linebreak-style": [ - 2, - "unix" - ], - "quotes": [ - 2, - "single" - ], - "semi": [ - 2, - "always" - ], + "indent": ["error", 2, { "SwitchCase": 1 }], + "linebreak-style": [2, "unix"], + "quotes": [2, "single"], + "semi": [2, "always"], "no-console": "off", "no-prototype-builtins": "off" } diff --git a/integration_test/tests/src/config.ts b/integration_test/tests/src/config.ts index 69a57c8..706e1ff 100644 --- a/integration_test/tests/src/config.ts +++ b/integration_test/tests/src/config.ts @@ -1,4 +1,4 @@ - /************************************************************************** +/************************************************************************** * (C) Copyright ModusBox Inc. 2020 - All rights reserved. * * * * This file is made available under the terms of the license agreement * diff --git a/integration_test/tests/src/page-objects/components/SideMenu.ts b/integration_test/tests/src/page-objects/components/SideMenu.ts index 63b2ef2..4bc69ca 100644 --- a/integration_test/tests/src/page-objects/components/SideMenu.ts +++ b/integration_test/tests/src/page-objects/components/SideMenu.ts @@ -1,4 +1,4 @@ -import { Selector } from "testcafe"; +import { Selector } from 'testcafe'; export const SideMenu = { transfersButton: Selector('.rc-menu-item').withText('Find Transfers'), diff --git a/integration_test/tests/src/page-objects/pages/TransfersTracingPage.ts b/integration_test/tests/src/page-objects/pages/TransfersTracingPage.ts index 659c680..6b96623 100644 --- a/integration_test/tests/src/page-objects/pages/TransfersTracingPage.ts +++ b/integration_test/tests/src/page-objects/pages/TransfersTracingPage.ts @@ -1,47 +1,50 @@ import { Selector, t } from 'testcafe'; export type TransferRow = { - row: Selector, - id: Selector, - transferState: Selector, - type: Selector, - currency: Selector, - amount: Selector, - payerDFSPId: Selector, - payeeDFSPId: Selector, - settlementBatchId: Selector, - dateSubmitted: Selector, -} + row: Selector; + id: Selector; + transferState: Selector; + type: Selector; + currency: Selector; + amount: Selector; + payerDFSPId: Selector; + payeeDFSPId: Selector; + settlementBatchId: Selector; + dateSubmitted: Selector; +}; export const FindTransfersPage = { transferIdSearchField: Selector('.placeholder').withText('Transfer ID').sibling('.rc-textfield'), currencyFilterField: Selector('.placeholder').withText('Currency').sibling('.rc-textfield'), - transferStateFilterField: Selector('.placeholder').withText('Transfer State').sibling('.rc-select'), + transferStateFilterField: Selector('.placeholder') + .withText('Transfer State') + .sibling('.rc-select'), clearFiltersButton: Selector('.rc-button').withText('Clear Filters'), findTransfersButton: Selector('.rc-button').withText('Find Transfers'), async getResultRows(): Promise { const rows = Selector('.rc-table__body__row'); const length = await rows.count; - return Array - .from({ length }) - .map((_, i) => ({ - row: rows.nth(i), - id: rows.nth(i).find('.rc-table__body__cell').nth(0), - transferState: rows.nth(i).find('.rc-table__body__cell').nth(1), - type: rows.nth(i).find('.rc-table__body__cell').nth(2), - currency: rows.nth(i).find('.rc-table__body__cell').nth(3), - amount: rows.nth(i).find('.rc-table__body__cell').nth(4), - payerDFSPId: rows.nth(i).find('.rc-table__body__cell').nth(5), - payeeDFSPId: rows.nth(i).find('.rc-table__body__cell').nth(6), - settlementBatchId: rows.nth(i).find('.rc-table__body__cell').nth(7), - dateSubmitted: rows.nth(i).find('.rc-table__body__cell').nth(8), - })); + return Array.from({ length }).map((_, i) => ({ + row: rows.nth(i), + id: rows.nth(i).find('.rc-table__body__cell').nth(0), + transferState: rows.nth(i).find('.rc-table__body__cell').nth(1), + type: rows.nth(i).find('.rc-table__body__cell').nth(2), + currency: rows.nth(i).find('.rc-table__body__cell').nth(3), + amount: rows.nth(i).find('.rc-table__body__cell').nth(4), + payerDFSPId: rows.nth(i).find('.rc-table__body__cell').nth(5), + payeeDFSPId: rows.nth(i).find('.rc-table__body__cell').nth(6), + settlementBatchId: rows.nth(i).find('.rc-table__body__cell').nth(7), + dateSubmitted: rows.nth(i).find('.rc-table__body__cell').nth(8), + })); }, - getTransferDetailsModal: (transferId: string) => Selector('.rc-modal__header__title') - .withText(`Transfer ${transferId} Details`) - .parent().parent('.rc-modal'), - selectTransferStateFilter: (transferState: string) => t.click(Selector('.rc-select__option__label').withText(transferState).parent()).wait(1000), + getTransferDetailsModal: (transferId: string) => + Selector('.rc-modal__header__title') + .withText(`Transfer ${transferId} Details`) + .parent() + .parent('.rc-modal'), + selectTransferStateFilter: (transferState: string) => + t.click(Selector('.rc-select__option__label').withText(transferState).parent()).wait(1000), getChart: (chartId: string) => Selector(`#${chartId}`), }; diff --git a/integration_test/tests/src/tests/FindTransfersPage.test.ts b/integration_test/tests/src/tests/FindTransfersPage.test.ts index a65d4ac..91cc3f4 100644 --- a/integration_test/tests/src/tests/FindTransfersPage.test.ts +++ b/integration_test/tests/src/tests/FindTransfersPage.test.ts @@ -5,28 +5,29 @@ import { SideMenu } from '../page-objects/components/SideMenu'; import { VoodooClient, protocol } from 'mojaloop-voodoo-client'; import { v4 as uuidv4 } from 'uuid'; -fixture `Find Transfers Feature` - .page`${config.transfersMicrofrontendEndpoint}` +fixture`Find Transfers Feature`.page`${config.transfersMicrofrontendEndpoint}` .before(async (ctx) => { - const cli = new VoodooClient('ws://localhost:3030/voodoo', { defaultTimeout: config.voodooTimeoutMs }); + const cli = new VoodooClient('ws://localhost:3030/voodoo', { + defaultTimeout: config.voodooTimeoutMs, + }); await cli.connected(); const hubAccounts: protocol.HubAccount[] = [ { - type: "HUB_MULTILATERAL_SETTLEMENT", - currency: "USD", + type: 'HUB_MULTILATERAL_SETTLEMENT', + currency: 'USD', }, { - type: "HUB_RECONCILIATION", - currency: "USD", + type: 'HUB_RECONCILIATION', + currency: 'USD', }, { - type: "HUB_MULTILATERAL_SETTLEMENT", - currency: "EUR", + type: 'HUB_MULTILATERAL_SETTLEMENT', + currency: 'EUR', }, { - type: "HUB_RECONCILIATION", - currency: "EUR", + type: 'HUB_RECONCILIATION', + currency: 'EUR', }, ]; await cli.createHubAccounts(hubAccounts); @@ -45,51 +46,58 @@ fixture `Find Transfers Feature` ctx.transfers = []; // Run two transfers - const transfers1: protocol.TransferMessage[] = [{ - msg_sender: participants[1].name, - msg_recipient: participants[0].name, - currency: 'USD', - amount: '10', - transfer_id: uuidv4(), - }]; + const transfers1: protocol.TransferMessage[] = [ + { + msg_sender: participants[1].name, + msg_recipient: participants[0].name, + currency: 'USD', + amount: '10', + transfer_id: uuidv4(), + }, + ]; ctx.transfers.push(transfers1[0]); await cli.completeTransfers(transfers1); - const transfers2: protocol.TransferMessage[] = [{ - msg_sender: participants[0].name, - msg_recipient: participants[1].name, - currency: 'USD', - amount: '10', - transfer_id: uuidv4(), - }]; + const transfers2: protocol.TransferMessage[] = [ + { + msg_sender: participants[0].name, + msg_recipient: participants[1].name, + currency: 'USD', + amount: '10', + transfer_id: uuidv4(), + }, + ]; ctx.transfers.push(transfers2[0]); await cli.completeTransfers(transfers2); - const transfers3: protocol.TransferMessage[] = [{ - msg_sender: participants[2].name, - msg_recipient: participants[3].name, - currency: 'EUR', - amount: '10', - transfer_id: uuidv4(), - }]; + const transfers3: protocol.TransferMessage[] = [ + { + msg_sender: participants[2].name, + msg_recipient: participants[3].name, + currency: 'EUR', + amount: '10', + transfer_id: uuidv4(), + }, + ]; ctx.transfers.push(transfers3[0]); await cli.completeTransfers(transfers3); // This transfer will fail and become 'ABORTED' due to invalid currency - const transfers4: protocol.TransferMessage[] = [{ - msg_sender: participants[2].name, - msg_recipient: participants[3].name, - currency: 'AUD', - amount: '10', - transfer_id: uuidv4(), - }]; + const transfers4: protocol.TransferMessage[] = [ + { + msg_sender: participants[2].name, + msg_recipient: participants[3].name, + currency: 'AUD', + amount: '10', + transfer_id: uuidv4(), + }, + ]; ctx.transfers.push(transfers4[0]); await cli.completeTransfers(transfers4); }) .beforeEach(async (t) => { await waitForReact(); - await t - .click(SideMenu.transfersButton).wait(1000); + await t.click(SideMenu.transfersButton).wait(1000); }); test.meta({ @@ -111,7 +119,7 @@ test.meta({ const transferIds = t.fixtureCtx.transfers.map((t: protocol.TransferMessage) => t.transfer_id); const rowIds = await Promise.all(rows.map((r: TransferRow) => r.id.innerText)); - for(let i = 0; i < transferIds.length; i++) { + for (let i = 0; i < transferIds.length; i++) { await t.expect(rowIds).contains(transferIds[i], 'rows dont contain transfers'); } }); @@ -144,27 +152,25 @@ test.meta({ test.meta({ ID: '', STORY: '', -})('Navigate to Technical Details Tab of Transfer Modal popup', - async (t) => { - // navigate to the find transfers page - await t.click(SideMenu.transfersButton).wait(1000); +})('Navigate to Technical Details Tab of Transfer Modal popup', async (t) => { + // navigate to the find transfers page + await t.click(SideMenu.transfersButton).wait(1000); - // click the find transfers button (no filters selected by default) - await t.click(FindTransfersPage.findTransfersButton); + // click the find transfers button (no filters selected by default) + await t.click(FindTransfersPage.findTransfersButton); - // get all rows found - const rows = await FindTransfersPage.getResultRows(); + // get all rows found + const rows = await FindTransfersPage.getResultRows(); - // click the first found row - await t.click(rows[0].row); - const transferId = await rows[0].id.innerText; + // click the first found row + await t.click(rows[0].row); + const transferId = await rows[0].id.innerText; - const popup = FindTransfersPage.getTransferDetailsModal(transferId); - await t.expect(popup.exists).ok('Transfer details popup not found in dom'); - await t.click(popup.find('.rc-tabs__tab').withText('Technical Details')).wait(1000); - t.expect(popup.find('.rc-tabs__tab--selected').withText('Technical Details')); - }, -); + const popup = FindTransfersPage.getTransferDetailsModal(transferId); + await t.expect(popup.exists).ok('Transfer details popup not found in dom'); + await t.click(popup.find('.rc-tabs__tab').withText('Technical Details')).wait(1000); + t.expect(popup.find('.rc-tabs__tab--selected').withText('Technical Details')); +}); test.meta({ ID: '', @@ -175,7 +181,9 @@ test.meta({ await t.click(SideMenu.transfersButton).wait(1000); // Enter transfer id into search field - await t.typeText(FindTransfersPage.transferIdSearchField, t.fixtureCtx.transfers[0].transfer_id).wait(1000); + await t + .typeText(FindTransfersPage.transferIdSearchField, t.fixtureCtx.transfers[0].transfer_id) + .wait(1000); // click the find transfers button (no filters selected by default) await t.click(FindTransfersPage.findTransfersButton).wait(1000); @@ -185,7 +193,9 @@ test.meta({ await t.expect(rows.length).eql(1); const rowIds = await Promise.all(rows.map((r: TransferRow) => r.id.innerText)); - await t.expect(rowIds[0]).eql(t.fixtureCtx.transfers[0].transfer_id, 'rows dont contain transfer'); + await t + .expect(rowIds[0]) + .eql(t.fixtureCtx.transfers[0].transfer_id, 'rows dont contain transfer'); }); test.meta({ @@ -205,12 +215,11 @@ test.meta({ const rows = await FindTransfersPage.getResultRows(); const rowCurrency = await Promise.all(rows.map((r: TransferRow) => r.currency.innerText)); - for(let i = 0; i < rows.length; i++) { + for (let i = 0; i < rows.length; i++) { await t.expect(rowCurrency[i]).contains('EUR', 'rows dont contain transfers'); } }); - test.meta({ ID: '', STORY: '', @@ -229,7 +238,7 @@ test.meta({ const rows = await FindTransfersPage.getResultRows(); const rowState = await Promise.all(rows.map((r: TransferRow) => r.transferState.innerText)); - for(let i = 0; i < rows.length; i++) { + for (let i = 0; i < rows.length; i++) { await t.expect(rowState[i]).contains('ABORTED', 'rows dont contain transfers'); } }); @@ -245,78 +254,78 @@ test.only.meta({ // click the find transfers button (no filters selected by default) await t.click(FindTransfersPage.findTransfersButton).wait(1000); - await t.expect(FindTransfersPage.getChart('TransfersByCurrencyChart').exists).ok('TransfersByCurrencyChart not found'); - await t.expect(FindTransfersPage.getChart('TransfersByPayerChart').exists).ok('TransfersByPayerChart not found'); - await t.expect(FindTransfersPage.getChart('TransfersByPayeeChart').exists).ok('TransfersByPayeeChart not found'); - await t.expect(FindTransfersPage.getChart('ErrorsByErrorCodeChart').exists).ok('ErrorsByErrorCodeChart not found'); - await t.expect(FindTransfersPage.getChart('ErrorsByPayerChart').exists).ok('ErrorsByPayerChart not found'); - await t.expect(FindTransfersPage.getChart('ErrorsByPayeeChart').exists).ok('ErrorsByPayeeChart not found'); + await t + .expect(FindTransfersPage.getChart('TransfersByCurrencyChart').exists) + .ok('TransfersByCurrencyChart not found'); + await t + .expect(FindTransfersPage.getChart('TransfersByPayerChart').exists) + .ok('TransfersByPayerChart not found'); + await t + .expect(FindTransfersPage.getChart('TransfersByPayeeChart').exists) + .ok('TransfersByPayeeChart not found'); + await t + .expect(FindTransfersPage.getChart('ErrorsByErrorCodeChart').exists) + .ok('ErrorsByErrorCodeChart not found'); + await t + .expect(FindTransfersPage.getChart('ErrorsByPayerChart').exists) + .ok('ErrorsByPayerChart not found'); + await t + .expect(FindTransfersPage.getChart('ErrorsByPayeeChart').exists) + .ok('ErrorsByPayeeChart not found'); }); test.skip.meta({ ID: '', STORY: '', -})('Filter Transfers by Payer FSPID, Payer Id and Payer value', - async (t) => { - // To be able to perform filters on Payer/Payee we need to - // perform quotes with the participants so that they populate the - // `quoteParty` table. This will involve having to rework the harness - // to use the testing toolkit. - }, -); +})('Filter Transfers by Payer FSPID, Payer Id and Payer value', async (t) => { + // To be able to perform filters on Payer/Payee we need to + // perform quotes with the participants so that they populate the + // `quoteParty` table. This will involve having to rework the harness + // to use the testing toolkit. +}); test.skip.meta({ ID: '', STORY: '', -})('Filter Transfers by Payee FSPID, Payee Id and Payee value', - async (t) => { - // To be able to perform filters on Payer/Payee we need to - // perform quotes with the participants so that they populate the - // `quoteParty` table. This will involve having to rework the harness - // to use the testing toolkit. - }, -); +})('Filter Transfers by Payee FSPID, Payee Id and Payee value', async (t) => { + // To be able to perform filters on Payer/Payee we need to + // perform quotes with the participants so that they populate the + // `quoteParty` table. This will involve having to rework the harness + // to use the testing toolkit. +}); test.skip.meta({ ID: '', STORY: '', -})('Navigate to Party Events Modal of Transfer Modal', - async (t) => { - // This requires upgrading the test harness to initiate party lookup - // requests with the ALS. Will also require using the TTK and adding the - // account-lookup-service sidecar. - }, -); +})('Navigate to Party Events Modal of Transfer Modal', async (t) => { + // This requires upgrading the test harness to initiate party lookup + // requests with the ALS. Will also require using the TTK and adding the + // account-lookup-service sidecar. +}); test.skip.meta({ ID: '', STORY: '', -})('Navigate to Quote Events Modal of Transfer Modal', - async (t) => { - // This requires upgrading the test harness to initiate quote requests - // requests with the quoting service. Will also require using the TTK - // and adding the quoting-service sidecar. - }, -); +})('Navigate to Quote Events Modal of Transfer Modal', async (t) => { + // This requires upgrading the test harness to initiate quote requests + // requests with the quoting service. Will also require using the TTK + // and adding the quoting-service sidecar. +}); test.skip.meta({ ID: '', STORY: '', -})('Navigate to Transfer Events Modal of Transfer Modal', - async (t) => { - // This requires upgrading the test harness to initiate party lookup - // requests with the ALS. This requires the adding of the - // central-ledger-handler-* sidecars. - }, -); +})('Navigate to Transfer Events Modal of Transfer Modal', async (t) => { + // This requires upgrading the test harness to initiate party lookup + // requests with the ALS. This requires the adding of the + // central-ledger-handler-* sidecars. +}); test.skip.meta({ ID: '', STORY: '', -})('Navigate to Settlement Events Modal of Transfer Modal', - async (t) => { - // This requires upgrading the test harness to initiate settlement - // requests with the central-settlements service. - // This requires the adding of the central-settlement sidecar. - }, -); +})('Navigate to Settlement Events Modal of Transfer Modal', async (t) => { + // This requires upgrading the test harness to initiate settlement + // requests with the central-settlements service. + // This requires the adding of the central-settlement sidecar. +}); diff --git a/lint-staged.config.js b/lint-staged.config.js index d18a953..098e232 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -1,8 +1,8 @@ module.exports = { - "src/**/*.{js,ts,tsx,json}": [ - "./node_modules/.bin/eslint --fix", - "./node_modules/.bin/prettier --write", - "yarn lint", + 'src/**/*.{js,ts,tsx,json}': [ + './node_modules/.bin/eslint --fix', + './node_modules/.bin/prettier --write', + 'yarn lint', ], 'src/**/*.css': ['./node_modules/.bin/stylelint --fix', './node_modules/.bin/prettier --write'], 'src/**/*.scss': [ diff --git a/mock.json b/mock.json new file mode 100644 index 0000000..e9ed7d8 --- /dev/null +++ b/mock.json @@ -0,0 +1,1312 @@ +[ + { + "transferId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "transactionId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "sourceAmount": 100.5, + "sourceCurrency": "USD", + "targetAmount": 91.5, + "targetCurrency": "EUR", + "createdAt": "2023-09-25T12:34:56Z", + "lastUpdated": "2023-09-25T12:34:56Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "", + "dateTime": "", + "reason": "" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "16436", + "payerDFSP": "payerDFSP001", + "payerDFSPProxy": null, + "payeeDFSP": "payeeDFSP002", + "payeeDFSPProxy": "CInternationalClearing", + "positionChanges": [ + { + "participantName": "payerDFSP001", + "currency": "USD", + "ledgerType": "POSITION", + "dateTime": "2016-05-24T08:38:08.699-04:00", + "updatedPosition": "10010.10", + "change": "10.10" + } + ], + "payerParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+123456789", + "partyName": "John Doe", + "supportedCurrencies": "USD" + }, + "payeeParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+987654321", + "partyName": "Jane Smith", + "supportedCurrencies": "EUR" + }, + "quoteRequest": { + "quoteId": "quote789", + "amountType": "SEND", + "amount": { + "currency": "USD", + "amount": "123.45" + }, + "fees": { + "currency": "USD", + "amount": "1.45" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "AED", + "amount": "123.45" + }, + "payeeReceiveAmount": { + "currency": "AED", + "amount": "123.45" + }, + "payeeFspFee": { + "currency": "AED", + "amount": "123.45" + }, + "payeeFspCommission": { + "currency": "AED", + "amount": "123.45" + }, + "expiration": "2016-05-24T08:38:08.699-04:00", + "geoCode": { + "latitude": "+45.4215", + "longitude": "+75.6972" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "conversionId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "conversionCommitRequestId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2016-05-24T08:38:08.699-04:00", + "reason": "" + } + ], + "counterPartyFSP": "FXP", + "conversionType": "Payer DFSP Conversion", + "conversionSettlementWindowId": "16436", + "conversionTerms": { + "conversionId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "determiningTransferId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "initiatingFsp": "string", + "counterPartyFsp": "string", + "amountType": "RECEIVE", + "sourceAmount": { + "currency": "AED", + "amount": "123.45" + }, + "targetAmount": { + "currency": "AED", + "amount": "123.45" + }, + "expiration": "2016-05-24T08:38:08.699-04:00", + "charges": [ + { + "chargeType": "string", + "sourceAmount": { + "currency": "AED", + "amount": "123.45" + }, + "targetAmount": { + "currency": "AED", + "amount": "123.45" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "transactionId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "sourceAmount": 100.5, + "sourceCurrency": "USD", + "targetAmount": 91.5, + "targetCurrency": "EUR", + "createdAt": "2023-09-25T12:34:56Z", + "lastUpdated": "2023-09-25T12:34:56Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-09-25T12:30:00Z", + "reason": "Initial Reserve" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "16436", + "payerDFSP": "payerDFSP001", + "payerDFSPProxy": null, + "payeeDFSP": "payeeDFSP002", + "payeeDFSPProxy": "CInternationalClearing", + "positionChanges": [ + { + "participantName": "payerDFSP001", + "currency": "USD", + "ledgerType": "POSITION", + "dateTime": "2023-09-25T12:35:00Z", + "updatedPosition": "10010.10", + "change": "10.10" + } + ], + "payerParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+123456789", + "partyName": "John Doe", + "supportedCurrencies": "USD" + }, + "payeeParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+987654321", + "partyName": "Jane Smith", + "supportedCurrencies": "EUR" + }, + "quoteRequest": { + "quoteId": "quote789", + "amountType": "SEND", + "amount": { + "currency": "USD", + "amount": "123.45" + }, + "fees": { + "currency": "USD", + "amount": "1.45" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "AED", + "amount": "123.45" + }, + "payeeReceiveAmount": { + "currency": "AED", + "amount": "123.45" + }, + "payeeFspFee": { + "currency": "AED", + "amount": "123.45" + }, + "payeeFspCommission": { + "currency": "AED", + "amount": "123.45" + }, + "expiration": "2023-09-25T13:00:00Z", + "geoCode": { + "latitude": "+45.4215", + "longitude": "-75.6972" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "conversionId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "conversionCommitRequestId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-09-25T12:32:00Z", + "reason": "" + } + ], + "counterPartyFSP": "FXP", + "conversionType": "Payer DFSP Conversion", + "conversionSettlementWindowId": "16436", + "conversionTerms": { + "conversionId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "determiningTransferId": "b51ec534-ee48-4575-b6a9-ead2955b8069", + "initiatingFsp": "payerDFSP001", + "counterPartyFsp": "payeeDFSP002", + "amountType": "RECEIVE", + "sourceAmount": { + "currency": "USD", + "amount": "123.45" + }, + "targetAmount": { + "currency": "EUR", + "amount": "123.45" + }, + "expiration": "2023-09-25T13:00:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "USD", + "amount": "1.23" + }, + "targetAmount": { + "currency": "EUR", + "amount": "1.15" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "b22ac534-aa88-4575-b6a9-ead2955b8069", + "transactionId": "c67dd534-fg77-4575-b6a9-ead2955b8069", + "sourceAmount": 200.75, + "sourceCurrency": "USD", + "targetAmount": 182.5, + "targetCurrency": "GBP", + "createdAt": "2023-10-01T08:00:00Z", + "lastUpdated": "2023-10-01T08:30:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-01T08:15:00Z", + "reason": "Initial Reserve" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "17436", + "payerDFSP": "payerDFSP003", + "payerDFSPProxy": null, + "payeeDFSP": "payeeDFSP004", + "payeeDFSPProxy": "BInternationalClearing", + "positionChanges": [ + { + "participantName": "payerDFSP003", + "currency": "USD", + "ledgerType": "POSITION", + "dateTime": "2023-10-01T08:20:00Z", + "updatedPosition": "20010.75", + "change": "10.75" + } + ], + "payerParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payer001@example.com", + "partyName": "Alice Brown", + "supportedCurrencies": "USD" + }, + "payeeParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payee002@example.com", + "partyName": "Bob White", + "supportedCurrencies": "GBP" + }, + "quoteRequest": { + "quoteId": "quote456", + "amountType": "SEND", + "amount": { + "currency": "USD", + "amount": "200.75" + }, + "fees": { + "currency": "USD", + "amount": "2.50" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "GBP", + "amount": "182.50" + }, + "payeeReceiveAmount": { + "currency": "GBP", + "amount": "182.50" + }, + "payeeFspFee": { + "currency": "GBP", + "amount": "2.50" + }, + "payeeFspCommission": { + "currency": "GBP", + "amount": "1.50" + }, + "expiration": "2023-10-01T09:00:00Z", + "geoCode": { + "latitude": "+51.5074", + "longitude": "-0.1278" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "b22ac534-aa88-4575-b6a9-ead2955b8069", + "conversionId": "c67dd534-fg77-4575-b6a9-ead2955b8069", + "conversionCommitRequestId": "b22ac534-aa88-4575-b6a9-ead2955b8069", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-01T08:25:00Z", + "reason": "" + } + ], + "counterPartyFSP": "FXP", + "conversionType": "Payer DFSP Conversion", + "conversionSettlementWindowId": "17436", + "conversionTerms": { + "conversionId": "c67dd534-fg77-4575-b6a9-ead2955b8069", + "determiningTransferId": "b22ac534-aa88-4575-b6a9-ead2955b8069", + "initiatingFsp": "payerDFSP003", + "counterPartyFsp": "payeeDFSP004", + "amountType": "RECEIVE", + "sourceAmount": { + "currency": "USD", + "amount": "200.75" + }, + "targetAmount": { + "currency": "GBP", + "amount": "182.50" + }, + "expiration": "2023-10-01T09:00:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "USD", + "amount": "2.50" + }, + "targetAmount": { + "currency": "GBP", + "amount": "1.82" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "f928cb2b-d8f9-4a1f-bc3d-0f70363f1095", + "transactionId": "b0d91d3f-8f8b-46c7-bb4e-f3bbac29d342", + "sourceAmount": 300.0, + "sourceCurrency": "USD", + "targetAmount": 275.0, + "targetCurrency": "GBP", + "createdAt": "2023-10-02T10:00:00Z", + "lastUpdated": "2023-10-02T10:30:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-02T10:05:00Z", + "reason": "Initial Reserve" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "18436", + "payerDFSP": "payerDFSP004", + "payerDFSPProxy": null, + "payeeDFSP": "payeeDFSP005", + "payeeDFSPProxy": "DInternationalClearing", + "positionChanges": [ + { + "participantName": "payerDFSP004", + "currency": "USD", + "ledgerType": "POSITION", + "dateTime": "2023-10-02T10:10:00Z", + "updatedPosition": "15010.00", + "change": "10.00" + } + ], + "payerParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payer004@example.com", + "partyName": "Charlie Green", + "supportedCurrencies": "USD" + }, + "payeeParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payee005@example.com", + "partyName": "David Black", + "supportedCurrencies": "GBP" + }, + "quoteRequest": { + "quoteId": "quote123", + "amountType": "SEND", + "amount": { + "currency": "USD", + "amount": "300.00" + }, + "fees": { + "currency": "USD", + "amount": "5.00" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "GBP", + "amount": "275.00" + }, + "payeeReceiveAmount": { + "currency": "GBP", + "amount": "275.00" + }, + "payeeFspFee": { + "currency": "GBP", + "amount": "5.00" + }, + "payeeFspCommission": { + "currency": "GBP", + "amount": "2.50" + }, + "expiration": "2023-10-02T11:00:00Z", + "geoCode": { + "latitude": "+40.7128", + "longitude": "-74.0060" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "f928cb2b-d8f9-4a1f-bc3d-0f70363f1095", + "conversionId": "b0d91d3f-8f8b-46c7-bb4e-f3bbac29d342", + "conversionCommitRequestId": "f928cb2b-d8f9-4a1f-bc3d-0f70363f1095", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-02T10:15:00Z", + "reason": "" + } + ], + "counterPartyFSP": "FXP", + "conversionType": "Payer DFSP Conversion", + "conversionSettlementWindowId": "18436", + "conversionTerms": { + "conversionId": "b0d91d3f-8f8b-46c7-bb4e-f3bbac29d342", + "determiningTransferId": "f928cb2b-d8f9-4a1f-bc3d-0f70363f1095", + "initiatingFsp": "payerDFSP004", + "counterPartyFsp": "payeeDFSP005", + "amountType": "RECEIVE", + "sourceAmount": { + "currency": "USD", + "amount": "300.00" + }, + "targetAmount": { + "currency": "GBP", + "amount": "275.00" + }, + "expiration": "2023-10-02T11:00:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "USD", + "amount": "5.00" + }, + "targetAmount": { + "currency": "GBP", + "amount": "4.50" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "34a7c2a1-f16a-4f62-8375-99a6887e2c0d", + "transactionId": "96b6d1e9-f10c-46d4-bcdf-2d675aa4581e", + "sourceAmount": 450.0, + "sourceCurrency": "USD", + "targetAmount": 415.0, + "targetCurrency": "EUR", + "createdAt": "2023-10-03T12:00:00Z", + "lastUpdated": "2023-10-03T12:10:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-03T12:05:00Z", + "reason": "Funds Reserved" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "18437", + "payerDFSP": "payerDFSP005", + "payerDFSPProxy": "ABCExchange", + "payeeDFSP": "payeeDFSP006", + "payeeDFSPProxy": null, + "positionChanges": [ + { + "participantName": "payerDFSP005", + "currency": "USD", + "ledgerType": "POSITION", + "dateTime": "2023-10-03T12:06:00Z", + "updatedPosition": "10000.00", + "change": "5.00" + } + ], + "payerParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payer005@example.com", + "partyName": "Eve White", + "supportedCurrencies": "USD" + }, + "payeeParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payee006@example.com", + "partyName": "Frank Yellow", + "supportedCurrencies": "EUR" + }, + "quoteRequest": { + "quoteId": "quote456", + "amountType": "SEND", + "amount": { + "currency": "USD", + "amount": "450.00" + }, + "fees": { + "currency": "USD", + "amount": "7.00" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "EUR", + "amount": "415.00" + }, + "payeeReceiveAmount": { + "currency": "EUR", + "amount": "415.00" + }, + "payeeFspFee": { + "currency": "EUR", + "amount": "5.00" + }, + "payeeFspCommission": { + "currency": "EUR", + "amount": "2.00" + }, + "expiration": "2023-10-03T13:00:00Z", + "geoCode": { + "latitude": "+48.8566", + "longitude": "+2.3522" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "34a7c2a1-f16a-4f62-8375-99a6887e2c0d", + "conversionId": "96b6d1e9-f10c-46d4-bcdf-2d675aa4581e", + "conversionCommitRequestId": "34a7c2a1-f16a-4f62-8375-99a6887e2c0d", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-03T12:06:30Z", + "reason": "" + } + ], + "counterPartyFSP": "XYZForex", + "conversionType": "Payee DFSP Conversion", + "conversionSettlementWindowId": "18437", + "conversionTerms": { + "conversionId": "96b6d1e9-f10c-46d4-bcdf-2d675aa4581e", + "determiningTransferId": "34a7c2a1-f16a-4f62-8375-99a6887e2c0d", + "initiatingFsp": "payerDFSP005", + "counterPartyFsp": "payeeDFSP006", + "amountType": "RECEIVE", + "sourceAmount": { + "currency": "USD", + "amount": "450.00" + }, + "targetAmount": { + "currency": "EUR", + "amount": "415.00" + }, + "expiration": "2023-10-03T13:00:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "USD", + "amount": "7.00" + }, + "targetAmount": { + "currency": "EUR", + "amount": "6.50" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "72d6e7f2-49ff-4b96-9a55-95e4a63d7e17", + "transactionId": "207b57be-d773-40a5-8286-cc54d17d207a", + "sourceAmount": 600.0, + "sourceCurrency": "GBP", + "targetAmount": 800.0, + "targetCurrency": "USD", + "createdAt": "2023-10-03T14:00:00Z", + "lastUpdated": "2023-10-03T14:30:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-03T14:05:00Z", + "reason": "Initial Reserve" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "18438", + "payerDFSP": "payerDFSP006", + "payerDFSPProxy": "ABCExchange", + "payeeDFSP": "payeeDFSP007", + "payeeDFSPProxy": null, + "positionChanges": [ + { + "participantName": "payerDFSP006", + "currency": "GBP", + "ledgerType": "POSITION", + "dateTime": "2023-10-03T14:10:00Z", + "updatedPosition": "9500.00", + "change": "10.00" + } + ], + "payerParty": { + "partyIdType": "PHONE", + "partyIdentifier": "+441234567890", + "partyName": "George Blue", + "supportedCurrencies": "GBP" + }, + "payeeParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payee007@example.com", + "partyName": "Helen Pink", + "supportedCurrencies": "USD" + }, + "quoteRequest": { + "quoteId": "quote789", + "amountType": "SEND", + "amount": { + "currency": "GBP", + "amount": "600.00" + }, + "fees": { + "currency": "GBP", + "amount": "10.00" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "USD", + "amount": "800.00" + }, + "payeeReceiveAmount": { + "currency": "USD", + "amount": "800.00" + }, + "payeeFspFee": { + "currency": "USD", + "amount": "8.00" + }, + "payeeFspCommission": { + "currency": "USD", + "amount": "4.00" + }, + "expiration": "2023-10-03T15:00:00Z", + "geoCode": { + "latitude": "+51.5074", + "longitude": "-0.1278" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "72d6e7f2-49ff-4b96-9a55-95e4a63d7e17", + "conversionId": "207b57be-d773-40a5-8286-cc54d17d207a", + "conversionCommitRequestId": "72d6e7f2-49ff-4b96-9a55-95e4a63d7e17", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-03T14:15:00Z", + "reason": "" + } + ], + "counterPartyFSP": "XYZForex", + "conversionType": "Payer DFSP Conversion", + "conversionSettlementWindowId": "18438", + "conversionTerms": { + "conversionId": "207b57be-d773-40a5-8286-cc54d17d207a", + "determiningTransferId": "72d6e7f2-49ff-4b96-9a55-95e4a63d7e17", + "initiatingFsp": "payerDFSP006", + "counterPartyFsp": "payeeDFSP007", + "amountType": "SEND", + "sourceAmount": { + "currency": "GBP", + "amount": "600.00" + }, + "targetAmount": { + "currency": "USD", + "amount": "800.00" + }, + "expiration": "2023-10-03T15:00:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "GBP", + "amount": "10.00" + }, + "targetAmount": { + "currency": "USD", + "amount": "13.00" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "c9b4a7f3-9ad7-4c65-8c0b-94fd63c7db15", + "transactionId": "a5e8d9d1-6439-46f2-9b2f-b00dcf2971a6", + "sourceAmount": 320.0, + "sourceCurrency": "CAD", + "targetAmount": 250.0, + "targetCurrency": "USD", + "createdAt": "2023-10-04T08:30:00Z", + "lastUpdated": "2023-10-04T08:40:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-04T08:35:00Z", + "reason": "Initial Reservation" + } + ], + "transactionType": "TRANSFER", + "errorCode": null, + "transferSettlementWindowId": "18439", + "payerDFSP": "payerDFSP007", + "payerDFSPProxy": "GlobalBank", + "payeeDFSP": "payeeDFSP008", + "payeeDFSPProxy": "NorthAmericaPay", + "positionChanges": [ + { + "participantName": "payerDFSP007", + "currency": "CAD", + "ledgerType": "POSITION", + "dateTime": "2023-10-04T08:36:00Z", + "updatedPosition": "9800.00", + "change": "5.00" + } + ], + "payerParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+14161234567", + "partyName": "Ivy Green", + "supportedCurrencies": "CAD" + }, + "payeeParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payee008@example.com", + "partyName": "Jack Black", + "supportedCurrencies": "USD" + }, + "quoteRequest": { + "quoteId": "quote1234", + "amountType": "SEND", + "amount": { + "currency": "CAD", + "amount": "320.00" + }, + "fees": { + "currency": "CAD", + "amount": "5.00" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "USD", + "amount": "250.00" + }, + "payeeReceiveAmount": { + "currency": "USD", + "amount": "250.00" + }, + "payeeFspFee": { + "currency": "USD", + "amount": "4.00" + }, + "payeeFspCommission": { + "currency": "USD", + "amount": "3.00" + }, + "expiration": "2023-10-04T09:30:00Z", + "geoCode": { + "latitude": "+43.6532", + "longitude": "-79.3832" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "c9b4a7f3-9ad7-4c65-8c0b-94fd63c7db15", + "conversionId": "a5e8d9d1-6439-46f2-9b2f-b00dcf2971a6", + "conversionCommitRequestId": "c9b4a7f3-9ad7-4c65-8c0b-94fd63c7db15", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-04T08:37:00Z", + "reason": "" + } + ], + "counterPartyFSP": "XYZForex", + "conversionType": "Payee DFSP Conversion", + "conversionSettlementWindowId": "18439", + "conversionTerms": { + "conversionId": "a5e8d9d1-6439-46f2-9b2f-b00dcf2971a6", + "determiningTransferId": "c9b4a7f3-9ad7-4c65-8c0b-94fd63c7db15", + "initiatingFsp": "payerDFSP007", + "counterPartyFsp": "payeeDFSP008", + "amountType": "SEND", + "sourceAmount": { + "currency": "CAD", + "amount": "320.00" + }, + "targetAmount": { + "currency": "USD", + "amount": "250.00" + }, + "expiration": "2023-10-04T09:30:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "CAD", + "amount": "5.00" + }, + "targetAmount": { + "currency": "USD", + "amount": "3.80" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "6f2b48ea-2d3b-4e82-b738-bb9b283d8d9b", + "transactionId": "b7c9c5d2-5401-451b-bc22-7dd383f0f1b8", + "sourceAmount": 1500.0, + "sourceCurrency": "JPY", + "targetAmount": 13.0, + "targetCurrency": "USD", + "createdAt": "2023-10-05T09:15:00Z", + "lastUpdated": "2023-10-05T09:25:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-05T09:18:00Z", + "reason": "Initial Reserve" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "18440", + "payerDFSP": "payerDFSP008", + "payerDFSPProxy": "EastAsiaBank", + "payeeDFSP": "payeeDFSP009", + "payeeDFSPProxy": "AmericanGateway", + "positionChanges": [ + { + "participantName": "payerDFSP008", + "currency": "JPY", + "ledgerType": "POSITION", + "dateTime": "2023-10-05T09:19:00Z", + "updatedPosition": "500000.00", + "change": "1500.00" + } + ], + "payerParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+811234567890", + "partyName": "Karen Red", + "supportedCurrencies": "JPY" + }, + "payeeParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payee009@example.com", + "partyName": "Louis Orange", + "supportedCurrencies": "USD" + }, + "quoteRequest": { + "quoteId": "quote5678", + "amountType": "SEND", + "amount": { + "currency": "JPY", + "amount": "1500.00" + }, + "fees": { + "currency": "JPY", + "amount": "30.00" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "USD", + "amount": "13.00" + }, + "payeeReceiveAmount": { + "currency": "USD", + "amount": "13.00" + }, + "payeeFspFee": { + "currency": "USD", + "amount": "1.00" + }, + "payeeFspCommission": { + "currency": "USD", + "amount": "0.50" + }, + "expiration": "2023-10-05T10:00:00Z", + "geoCode": { + "latitude": "+35.6895", + "longitude": "+139.6917" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "6f2b48ea-2d3b-4e82-b738-bb9b283d8d9b", + "conversionId": "b7c9c5d2-5401-451b-bc22-7dd383f0f1b8", + "conversionCommitRequestId": "6f2b48ea-2d3b-4e82-b738-bb9b283d8d9b", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-05T09:20:00Z", + "reason": "" + } + ], + "counterPartyFSP": "JPForex", + "conversionType": "Payer DFSP Conversion", + "conversionSettlementWindowId": "18440", + "conversionTerms": { + "conversionId": "b7c9c5d2-5401-451b-bc22-7dd383f0f1b8", + "determiningTransferId": "6f2b48ea-2d3b-4e82-b738-bb9b283d8d9b", + "initiatingFsp": "payerDFSP008", + "counterPartyFsp": "payeeDFSP009", + "amountType": "SEND", + "sourceAmount": { + "currency": "JPY", + "amount": "1500.00" + }, + "targetAmount": { + "currency": "USD", + "amount": "13.00" + }, + "expiration": "2023-10-05T10:00:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "JPY", + "amount": "30.00" + }, + "targetAmount": { + "currency": "USD", + "amount": "0.26" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "d45c0a58-324f-49e3-9814-bd4a467b7d38", + "transactionId": "a298dbb3-234a-4112-8c58-2c1f4fe2fdf9", + "sourceAmount": 500.0, + "sourceCurrency": "GBP", + "targetAmount": 700.0, + "targetCurrency": "EUR", + "createdAt": "2023-10-06T12:00:00Z", + "lastUpdated": "2023-10-06T12:10:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-06T12:05:00Z", + "reason": "Initial reservation" + } + ], + "transactionType": "TRANSFER", + "errorCode": null, + "transferSettlementWindowId": "18441", + "payerDFSP": "payerDFSP009", + "payerDFSPProxy": "UKBank", + "payeeDFSP": "payeeDFSP010", + "payeeDFSPProxy": "EuroBank", + "positionChanges": [ + { + "participantName": "payerDFSP009", + "currency": "GBP", + "ledgerType": "POSITION", + "dateTime": "2023-10-06T12:06:00Z", + "updatedPosition": "10000.00", + "change": "500.00" + } + ], + "payerParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payer009@example.com", + "partyName": "Alice Brown", + "supportedCurrencies": "GBP" + }, + "payeeParty": { + "partyIdType": "EMAIL", + "partyIdentifier": "payee010@example.com", + "partyName": "George White", + "supportedCurrencies": "EUR" + }, + "quoteRequest": { + "quoteId": "quote8910", + "amountType": "SEND", + "amount": { + "currency": "GBP", + "amount": "500.00" + }, + "fees": { + "currency": "GBP", + "amount": "10.00" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "EUR", + "amount": "700.00" + }, + "payeeReceiveAmount": { + "currency": "EUR", + "amount": "700.00" + }, + "payeeFspFee": { + "currency": "EUR", + "amount": "8.00" + }, + "payeeFspCommission": { + "currency": "EUR", + "amount": "5.00" + }, + "expiration": "2023-10-06T13:00:00Z", + "geoCode": { + "latitude": "+51.5074", + "longitude": "-0.1278" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "d45c0a58-324f-49e3-9814-bd4a467b7d38", + "conversionId": "a298dbb3-234a-4112-8c58-2c1f4fe2fdf9", + "conversionCommitRequestId": "d45c0a58-324f-49e3-9814-bd4a467b7d38", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-06T12:07:00Z", + "reason": "" + } + ], + "counterPartyFSP": "XYZForex", + "conversionType": "Payee DFSP Conversion", + "conversionSettlementWindowId": "18441", + "conversionTerms": { + "conversionId": "a298dbb3-234a-4112-8c58-2c1f4fe2fdf9", + "determiningTransferId": "d45c0a58-324f-49e3-9814-bd4a467b7d38", + "initiatingFsp": "payerDFSP009", + "counterPartyFsp": "payeeDFSP010", + "amountType": "SEND", + "sourceAmount": { + "currency": "GBP", + "amount": "500.00" + }, + "targetAmount": { + "currency": "EUR", + "amount": "700.00" + }, + "expiration": "2023-10-06T13:00:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "GBP", + "amount": "10.00" + }, + "targetAmount": { + "currency": "EUR", + "amount": "12.00" + } + } + ], + "ilpPacket": "pck" + } + } + ] + }, + { + "transferId": "2a5c607e-8435-4c08-8eeb-eef91a23b9c9", + "transactionId": "32fbf343-59b1-47e2-854d-6a593274bdb4", + "sourceAmount": 2000.0, + "sourceCurrency": "USD", + "targetAmount": 220000.0, + "targetCurrency": "KRW", + "createdAt": "2023-10-07T14:30:00Z", + "lastUpdated": "2023-10-07T14:40:00Z", + "transferState": "COMMITTED", + "transferStateChanges": [ + { + "transferState": "RESERVED", + "dateTime": "2023-10-07T14:35:00Z", + "reason": "Initial reservation" + } + ], + "transactionType": "PAYMENT", + "errorCode": null, + "transferSettlementWindowId": "18442", + "payerDFSP": "payerDFSP010", + "payerDFSPProxy": "USABank", + "payeeDFSP": "payeeDFSP011", + "payeeDFSPProxy": "KoreaBank", + "positionChanges": [ + { + "participantName": "payerDFSP010", + "currency": "USD", + "ledgerType": "POSITION", + "dateTime": "2023-10-07T14:36:00Z", + "updatedPosition": "250000.00", + "change": "2000.00" + } + ], + "payerParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+1234567890", + "partyName": "Brian Black", + "supportedCurrencies": "USD" + }, + "payeeParty": { + "partyIdType": "MSISDN", + "partyIdentifier": "+821012345678", + "partyName": "Yoon Min", + "supportedCurrencies": "KRW" + }, + "quoteRequest": { + "quoteId": "quote6543", + "amountType": "SEND", + "amount": { + "currency": "USD", + "amount": "2000.00" + }, + "fees": { + "currency": "USD", + "amount": "25.00" + } + }, + "transferTerms": { + "transferAmount": { + "currency": "KRW", + "amount": "220000.00" + }, + "payeeReceiveAmount": { + "currency": "KRW", + "amount": "220000.00" + }, + "payeeFspFee": { + "currency": "KRW", + "amount": "1500.00" + }, + "payeeFspCommission": { + "currency": "KRW", + "amount": "800.00" + }, + "expiration": "2023-10-07T15:30:00Z", + "geoCode": { + "latitude": "+37.5665", + "longitude": "+126.9780" + }, + "ilpPacket": "pck" + }, + "conversions": [ + { + "conversionRequestId": "2a5c607e-8435-4c08-8eeb-eef91a23b9c9", + "conversionId": "32fbf343-59b1-47e2-854d-6a593274bdb4", + "conversionCommitRequestId": "2a5c607e-8435-4c08-8eeb-eef91a23b9c9", + "conversionState": "COMMITTED", + "conversionStateChanges": [ + { + "conversionState": "RESERVED", + "dateTime": "2023-10-07T14:36:00Z", + "reason": "" + } + ], + "counterPartyFSP": "AsiaForex", + "conversionType": "Payer DFSP Conversion", + "conversionSettlementWindowId": "18442", + "conversionTerms": { + "conversionId": "32fbf343-59b1-47e2-854d-6a593274bdb4", + "determiningTransferId": "2a5c607e-8435-4c08-8eeb-eef91a23b9c9", + "initiatingFsp": "payerDFSP010", + "counterPartyFsp": "payeeDFSP011", + "amountType": "SEND", + "sourceAmount": { + "currency": "USD", + "amount": "2000.00" + }, + "targetAmount": { + "currency": "KRW", + "amount": "220000.00" + }, + "expiration": "2023-10-07T15:30:00Z", + "charges": [ + { + "chargeType": "FX Fee", + "sourceAmount": { + "currency": "USD", + "amount": "25.00" + }, + "targetAmount": { + "currency": "KRW", + "amount": "27500.00" + } + } + ], + "ilpPacket": "pck" + } + } + ] + } +] diff --git a/public/index.html b/public/index.html index 6c67c48..ca4b4cb 100644 --- a/public/index.html +++ b/public/index.html @@ -1,7 +1,7 @@ - - + + diff --git a/skaffold.yaml b/skaffold.yaml index f717a6a..5dc24c5 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -6,20 +6,20 @@ metadata: # profile, but we don't want to build the portal image if we're only interested in deploying the # backend. profiles: -- name: backend - deploy: - statusCheckDeadlineSeconds: 600 - kustomize: - paths: - - integration_test/manifests/backend -- name: integration-test - build: - artifacts: - - image: ghcr.io/mojaloop/reporting-hub-bop-trx-ui - docker: - dockerfile: Dockerfile - deploy: - statusCheckDeadlineSeconds: 600 - kustomize: - paths: - - integration_test + - name: backend + deploy: + statusCheckDeadlineSeconds: 600 + kustomize: + paths: + - integration_test/manifests/backend + - name: integration-test + build: + artifacts: + - image: ghcr.io/mojaloop/reporting-hub-bop-trx-ui + docker: + dockerfile: Dockerfile + deploy: + statusCheckDeadlineSeconds: 600 + kustomize: + paths: + - integration_test diff --git a/src/App/Transfers/Dashboard/ErrorSummary.tsx b/src/App/Transfers/Dashboard/ErrorSummary.tsx index 7b23ab7..a0a083f 100644 --- a/src/App/Transfers/Dashboard/ErrorSummary.tsx +++ b/src/App/Transfers/Dashboard/ErrorSummary.tsx @@ -35,6 +35,7 @@ const ErrorSummary: FC = ({ filtersModel }) => { endDate: filtersModel.to, }, }); + let content = null; if (error) { @@ -42,19 +43,15 @@ const ErrorSummary: FC = ({ filtersModel }) => { } else if (loading) { content = ; } else { + const totalTransactionCount = data.transferSummary.reduce( + (total: number, { count }: TransferSummary) => total + count, + 0, + ); const totalErrorCount = data.transferSummary - .filter((obj: TransferSummary) => { - return obj.errorCode !== null; - }) - .reduce((n: number, { count }: TransferSummary) => n + count, 0); - - let totalTransferCount = 0; - const totalTransfers = data.transferSummary.filter((obj: TransferSummary) => { - return obj.errorCode === null; - }); - if (totalTransfers.length > 0) { - totalTransferCount = totalTransfers[0].count; - } + .filter((obj: TransferSummary) => obj.group.errorCode !== null) + .reduce((total: number, { count }: TransferSummary) => total + count, 0); + const errorPercentage = + totalTransactionCount > 0 ? (totalErrorCount / totalTransactionCount) * 100 : 0; content = (
@@ -64,11 +61,14 @@ const ErrorSummary: FC = ({ filtersModel }) => { compactDisplay: 'short', }).format(totalErrorCount)} /> - {`${round((totalErrorCount / totalTransferCount) * 100, 2)}%`} + 50 ? 'red' : 'green' }}> + {round(errorPercentage, 2)}% + Total Errors
); } + return content; }; diff --git a/src/App/Transfers/Dashboard/ErrorsByErrorCodeChart.tsx b/src/App/Transfers/Dashboard/ErrorsByErrorCodeChart.tsx index 32df298..f1487c6 100644 --- a/src/App/Transfers/Dashboard/ErrorsByErrorCodeChart.tsx +++ b/src/App/Transfers/Dashboard/ErrorsByErrorCodeChart.tsx @@ -47,25 +47,28 @@ const ByCurrencyChart: FC = ({ filtersModel }) => { content = ; } else { const summary = data.transferSummary - .filter((obj: TransferSummary) => { - return obj.errorCode !== null; - }) - .slice() + .filter((obj: TransferSummary) => obj.group.errorCode !== null) + .map((obj: TransferSummary) => ({ + errorCode: obj.group.errorCode, + count: obj.count, + })) .sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); - const firstThree = summary.slice(0, 3); + + const topThree = summary.slice(0, 3); const remainingSummary = { errorCode: 'Other', - count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), + count: summary.slice(3).reduce((n: number, { count }: { count: number }) => n + count, 0), }; + if (remainingSummary.count > 0) { - firstThree.push(remainingSummary); + topThree.push(remainingSummary); } content = ( = ({ filtersModel }) => { content={renderRedLegend} /> = ({ filtersModel }) => { onMouseEnter={onPieEnter} onMouseLeave={onPieLeave} > - {firstThree.map((_entry: any, index: number) => ( + {topThree.map((_entry: any, index: number) => ( = ({ filtersModel }) => { ); } + return content; }; diff --git a/src/App/Transfers/Dashboard/ErrorsByPayeeChart.tsx b/src/App/Transfers/Dashboard/ErrorsByPayeeChart.tsx index 9ef57ea..f8f01fd 100644 --- a/src/App/Transfers/Dashboard/ErrorsByPayeeChart.tsx +++ b/src/App/Transfers/Dashboard/ErrorsByPayeeChart.tsx @@ -5,7 +5,7 @@ import React, { FC, useState } from 'react'; import { connect } from 'react-redux'; import { Cell, Legend, Pie, PieChart, Tooltip } from 'recharts'; import { ReduxContext, State, Dispatch } from 'store'; -import { GET_TRANSFER_SUMMARY_BY_PAYEE_DFSP } from 'apollo/query'; +import { GET_TRANSFER_SUMMARY_BY_PAYEE } from 'apollo/query'; import { map, groupBy, sumBy } from 'lodash'; import { FilterChangeValue, TransfersFilter } from '../types'; import { actions } from '../slice'; @@ -27,7 +27,7 @@ interface ConnectorProps { } const ByPayeeChart: FC = ({ filtersModel, onFilterChange }) => { - const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY_BY_PAYEE_DFSP, { + const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY_BY_PAYEE, { fetchPolicy: 'no-cache', variables: { startDate: filtersModel.from, @@ -51,33 +51,33 @@ const ByPayeeChart: FC = ({ filtersModel, onFilterChange }) => { } else if (loading) { content = ; } else { - const prunedSummary = data.transferSummary - .filter((obj: TransferSummary) => { - return obj.errorCode !== null; - }) - .slice(); - - const summary = map(groupBy(prunedSummary, 'payerDFSP'), (ts: any, payeeDFSP: string) => { - return { + const groupedSummary = map( + groupBy( + data.transferSummary.filter((obj: TransferSummary) => obj.group.errorCode !== null), + (item: TransferSummary) => item.group.payeeDFSP, + ), + (groupedItems, payeeDFSP) => ({ payeeDFSP, - count: sumBy(ts, 'count'), - }; - }).sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); + count: sumBy(groupedItems, 'count'), + }), + ); - const firstThree = summary.slice(0, 3); + const sortedSummary = groupedSummary.sort((a, b) => b.count - a.count); + const topThree = sortedSummary.slice(0, 3); const remainingSummary = { payeeDFSP: 'Other', - count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), + count: sortedSummary.slice(3).reduce((sum, { count }) => sum + count, 0), }; + if (remainingSummary.count > 0) { - firstThree.push(remainingSummary); + topThree.push(remainingSummary); } content = ( = ({ filtersModel, onFilterChange }) => { content={renderRedLegend} /> = ({ filtersModel, onFilterChange }) => { onMouseEnter={onPieEnter} onMouseLeave={onPieLeave} > - {firstThree.map((_entry: any, index: number) => ( + {topThree.map((_entry: any, index: number) => ( = ({ filtersModel, onFilterChange }) => { - const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY_BY_PAYER_DFSP, { + const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY_BY_PAYER, { fetchPolicy: 'no-cache', variables: { startDate: filtersModel.from, @@ -51,33 +51,33 @@ const ByPayerChart: FC = ({ filtersModel, onFilterChange }) => { } else if (loading) { content = ; } else { - const prunedSummary = data.transferSummary - .filter((obj: TransferSummary) => { - return obj.errorCode !== null; - }) - .slice(); - - const summary = map(groupBy(prunedSummary, 'payerDFSP'), (ts: any, payerDFSP: string) => { - return { + const groupedSummary = map( + groupBy( + data.transferSummary.filter((obj: TransferSummary) => obj.group.errorCode !== null), + (item: TransferSummary) => item.group.payerDFSP, + ), + (groupedItems, payerDFSP) => ({ payerDFSP, - count: sumBy(ts, 'count'), - }; - }).sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); + count: sumBy(groupedItems, 'count'), + }), + ); - const firstThree = summary.slice(0, 3); + const sortedSummary = groupedSummary.sort((a, b) => b.count - a.count); + const topThree = sortedSummary.slice(0, 3); const remainingSummary = { payerDFSP: 'Other', - count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), + count: sortedSummary.slice(3).reduce((sum, { count }) => sum + count, 0), }; + if (remainingSummary.count > 0) { - firstThree.push(remainingSummary); + topThree.push(remainingSummary); } content = ( = ({ filtersModel, onFilterChange }) => { content={renderRedLegend} /> = ({ filtersModel, onFilterChange }) => { onMouseEnter={onPieEnter} onMouseLeave={onPieLeave} > - {firstThree.map((_entry: any, index: number) => ( + {topThree.map((_entry, index) => ( ))} diff --git a/src/App/Transfers/Dashboard/ErrorsBySourceCurrencyChart.tsx b/src/App/Transfers/Dashboard/ErrorsBySourceCurrencyChart.tsx new file mode 100644 index 0000000..c428e4a --- /dev/null +++ b/src/App/Transfers/Dashboard/ErrorsBySourceCurrencyChart.tsx @@ -0,0 +1,95 @@ +import React, { FC, useState } from 'react'; +import { connect } from 'react-redux'; +import { Cell, Legend, Pie, PieChart, Tooltip } from 'recharts'; +import { ReduxContext, State } from 'store'; +import { MessageBox, Spinner } from 'components'; +import { useQuery } from '@apollo/client'; +import { TransferSummary } from 'apollo/types'; +import { GET_TRANSFER_SUMMARY } from 'apollo/query'; +import * as selectors from '../selectors'; +import { TransfersFilter } from '../types'; +import { RED_CHART_GRADIENT_COLORS, renderActiveShape, renderRedLegend } from './utils'; + +const stateProps = (state: State) => ({ + filtersModel: selectors.getTransfersFilter(state), +}); +const dispatchProps = () => ({}); +interface ConnectorProps { + filtersModel: TransfersFilter; +} +const BySourceCurrencyChart: FC = ({ filtersModel }) => { + const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY, { + fetchPolicy: 'no-cache', + variables: { + startDate: filtersModel.from, + endDate: filtersModel.to, + }, + }); + const [activeIndex, setActiveIndex] = useState(); + const onPieEnter = (_: any, index: number) => { + setActiveIndex(index); + }; + const onPieLeave = () => { + setActiveIndex(undefined); + }; + let content = null; + if (error) { + content = Error fetching transfers: {error.message}; + } else if (loading) { + content = ; + } else { + const summary = data.transferSummary + .filter((obj: TransferSummary) => { + return obj.errorCode !== null; + }) + .slice() + .sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); + const topThree = summary.slice(0, 3); + const remainingSummary = { + errorCode: 'Other', + count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), + }; + if (remainingSummary.count > 0) { + topThree.push(remainingSummary); + } + content = ( + + + + {topThree.map((_entry: any, index: number) => ( + + ))} + + + + ); + } + return content; +}; +export default connect(stateProps, dispatchProps, null, { context: ReduxContext })( + BySourceCurrencyChart, +); diff --git a/src/App/Transfers/Dashboard/ErrorsByTargetCurrencyChart.tsx b/src/App/Transfers/Dashboard/ErrorsByTargetCurrencyChart.tsx new file mode 100644 index 0000000..ca809bc --- /dev/null +++ b/src/App/Transfers/Dashboard/ErrorsByTargetCurrencyChart.tsx @@ -0,0 +1,96 @@ +import React, { FC, useState } from 'react'; +import { connect } from 'react-redux'; +import { Cell, Legend, Pie, PieChart, Tooltip } from 'recharts'; +import { ReduxContext, State } from 'store'; +import { MessageBox, Spinner } from 'components'; +import { useQuery } from '@apollo/client'; +import { TransferSummary } from 'apollo/types'; +import { GET_TRANSFER_SUMMARY } from 'apollo/query'; +import * as selectors from '../selectors'; +import { TransfersFilter } from '../types'; +import { RED_CHART_GRADIENT_COLORS, renderActiveShape, renderRedLegend } from './utils'; + +const stateProps = (state: State) => ({ + filtersModel: selectors.getTransfersFilter(state), +}); +const dispatchProps = () => ({}); +interface ConnectorProps { + filtersModel: TransfersFilter; +} +const BySourceCurrencyChart: FC = ({ filtersModel }) => { + const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY, { + fetchPolicy: 'no-cache', + variables: { + startDate: filtersModel.from, + endDate: filtersModel.to, + }, + }); + const [activeIndex, setActiveIndex] = useState(); + const onPieEnter = (_: any, index: number) => { + setActiveIndex(index); + }; + const onPieLeave = () => { + setActiveIndex(undefined); + }; + let content = null; + if (error) { + content = Error fetching transfers: {error.message}; + } else if (loading) { + content = ; + } else { + const summary = data.transferSummary + .filter((obj: TransferSummary) => { + return obj.errorCode !== null; + }) + .slice() + .sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); + const topThree = summary.slice(0, 3); + const remainingSummary = { + errorCode: 'Other', + count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), + }; + if (remainingSummary.count > 0) { + topThree.push(remainingSummary); + } + content = ( + + + + {topThree.map((_entry: any, index: number) => ( + + ))} + + + + ); + } + return content; +}; + +export default connect(stateProps, dispatchProps, null, { context: ReduxContext })( + BySourceCurrencyChart, +); diff --git a/src/App/Transfers/Dashboard/TransferTotalSummary.tsx b/src/App/Transfers/Dashboard/TransferTotalSummary.tsx index 774222c..36bb874 100644 --- a/src/App/Transfers/Dashboard/TransferTotalSummary.tsx +++ b/src/App/Transfers/Dashboard/TransferTotalSummary.tsx @@ -47,13 +47,12 @@ const TransferTotalSummary: FC = ({ filtersModel }) => { } else if (loading) { content = ; } else { - let totalTransferCount = 0; - const totalTransfers = data.transferSummary.filter((obj: TransferSummary) => { - return obj.errorCode === null; - }); - if (totalTransfers.length > 0) { - totalTransferCount = totalTransfers[0].count; - } + const successfulTransferGroup = data.transferSummary.find( + (obj: TransferSummary) => obj.group.errorCode === null, + ); + + const totalTransferCount = successfulTransferGroup ? successfulTransferGroup.count : 0; + content = (
({ filtersModel: selectors.getTransfersFilter(state), }); - -const dispatchProps = (dispatch: Dispatch) => ({ - onFilterChange: (field: string, value: FilterChangeValue | string) => - dispatch(actions.setTransferFinderFilter({ field, value })), -}); - +const dispatchProps = () => ({}); interface ConnectorProps { filtersModel: TransfersFilter; - onFilterChange: (field: string, value: FilterChangeValue | string) => void; } - -const ByCurrencyChart: FC = ({ filtersModel, onFilterChange }) => { - const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY_BY_CURRENCY, { +const BySourceCurrencyChart: FC = ({ filtersModel }) => { + const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY, { fetchPolicy: 'no-cache', variables: { startDate: filtersModel.from, endDate: filtersModel.to, }, }); - const [activeIndex, setActiveIndex] = useState(); - const onPieEnter = (_: any, index: number) => { setActiveIndex(index); }; - const onPieLeave = () => { setActiveIndex(undefined); }; - let content = null; if (error) { content = Error fetching transfers: {error.message}; @@ -53,53 +40,47 @@ const ByCurrencyChart: FC = ({ filtersModel, onFilterChange }) = } else { const summary = data.transferSummary .filter((obj: TransferSummary) => { - return obj.errorCode === null; + return obj.errorCode !== null; }) .slice() .sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); - const firstThree = summary.slice(0, 3); + const topThree = summary.slice(0, 3); const remainingSummary = { - currency: 'Other', + errorCode: 'Other', count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), }; if (remainingSummary.count > 0) { - firstThree.push(remainingSummary); + topThree.push(remainingSummary); } - content = ( - + { - if (value.name !== 'Other') { - onFilterChange('currency', value.name); - } - }} activeIndex={activeIndex} activeShape={renderActiveShape} onMouseEnter={onPieEnter} onMouseLeave={onPieLeave} > - {firstThree.map((_entry: any, index: number) => ( + {topThree.map((_entry: any, index: number) => ( ))} @@ -110,4 +91,6 @@ const ByCurrencyChart: FC = ({ filtersModel, onFilterChange }) = return content; }; -export default connect(stateProps, dispatchProps, null, { context: ReduxContext })(ByCurrencyChart); +export default connect(stateProps, dispatchProps, null, { context: ReduxContext })( + BySourceCurrencyChart, +); diff --git a/src/App/Transfers/Dashboard/TransfersByPayeeChart.tsx b/src/App/Transfers/Dashboard/TransfersByPayeeChart.tsx index b603bbd..bd7c555 100644 --- a/src/App/Transfers/Dashboard/TransfersByPayeeChart.tsx +++ b/src/App/Transfers/Dashboard/TransfersByPayeeChart.tsx @@ -6,9 +6,10 @@ import { connect } from 'react-redux'; import { Cell, Legend, Pie, PieChart, Tooltip } from 'recharts'; import { ReduxContext, State, Dispatch } from 'store'; import { GET_TRANSFER_SUMMARY_BY_PAYEE_DFSP } from 'apollo/query'; -import * as selectors from '../selectors'; +import { map, groupBy, sumBy } from 'lodash'; import { FilterChangeValue, TransfersFilter } from '../types'; import { actions } from '../slice'; +import * as selectors from '../selectors'; import { GREEN_CHART_GRADIENT_COLORS, renderActiveShape, renderGreenLegend } from './utils'; const stateProps = (state: State) => ({ @@ -36,7 +37,7 @@ const ByPayeeChart: FC = ({ filtersModel, onFilterChange }) => { const [activeIndex, setActiveIndex] = useState(); - const onPieEnter = (_: any, index: number) => { + const onPieEnter = (_pieData: any, index: number) => { setActiveIndex(index); }; @@ -50,26 +51,34 @@ const ByPayeeChart: FC = ({ filtersModel, onFilterChange }) => { } else if (loading) { content = ; } else { - const summary = data.transferSummary - .filter((obj: TransferSummary) => { - return obj.errorCode === null; - }) - .slice() - .sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); - const firstThree = summary.slice(0, 3); + const prunedSummary = data.transferSummary.filter( + (obj: TransferSummary) => obj.group.errorCode === null && obj.sum.targetAmount > 0, + ); + + const summary = map( + groupBy(prunedSummary, (ts: any) => ts.group.payeeDFSP), + (ts: any, payeeDFSP: string) => ({ + payeeDFSP, + targetAmount: sumBy(ts, (item: any) => item.sum.targetAmount), + }), + ).sort((a: any, b: any) => b.targetAmount - a.targetAmount); + + const topThree = summary.slice(0, 3); const remainingSummary = { payeeDFSP: 'Other', - count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), + targetAmount: summary + .slice(3) + .reduce((n: number, { targetAmount }: any) => n + targetAmount, 0), }; - if (remainingSummary.count > 0) { - firstThree.push(remainingSummary); + if (remainingSummary.targetAmount > 0) { + topThree.push(remainingSummary); } content = ( = ({ filtersModel, onFilterChange }) => { content={renderGreenLegend} /> = ({ filtersModel, onFilterChange }) => { onMouseEnter={onPieEnter} onMouseLeave={onPieLeave} > - {firstThree.map((_entry: any, index: number) => ( + {topThree.map((_entry: any, index: number) => ( = ({ filtersModel, onFilterChange }) => { ); } + return content; }; diff --git a/src/App/Transfers/Dashboard/TransfersByPayerChart.tsx b/src/App/Transfers/Dashboard/TransfersByPayerChart.tsx index e438fe2..857706b 100644 --- a/src/App/Transfers/Dashboard/TransfersByPayerChart.tsx +++ b/src/App/Transfers/Dashboard/TransfersByPayerChart.tsx @@ -6,6 +6,7 @@ import { connect } from 'react-redux'; import { Cell, Legend, Pie, PieChart, Tooltip } from 'recharts'; import { ReduxContext, Dispatch, State } from 'store'; import { TransferSummary } from 'apollo/types'; +import { map, groupBy, sumBy } from 'lodash'; import { FilterChangeValue, TransfersFilter } from '../types'; import { actions } from '../slice'; import * as selectors from '../selectors'; @@ -43,32 +44,42 @@ const ByPayerChart: FC = ({ filtersModel, onFilterChange }) => { const onPieLeave = () => { setActiveIndex(undefined); }; + let content = null; if (error) { content = Error fetching transfers: {error.message}; } else if (loading) { content = ; } else { - const summary = data.transferSummary - .filter((obj: TransferSummary) => { - return obj.errorCode === null; - }) - .slice() - .sort((a: TransferSummary, b: TransferSummary) => b.count - a.count); - const firstThree = summary.slice(0, 3); + const prunedSummary = data.transferSummary.filter( + (obj: TransferSummary) => obj.group.errorCode === null && obj.sum.sourceAmount > 0, + ); + + const summary = map( + groupBy(prunedSummary, (ts: any) => ts.group.payerDFSP), + (ts: any, payerDFSP: string) => ({ + payerDFSP, + sourceAmount: sumBy(ts, (t: any) => t.sum.sourceAmount), + }), + ).sort((a: any, b: any) => b.sourceAmount - a.sourceAmount); + + const topThree = summary.slice(0, 3); const remainingSummary = { payerDFSP: 'Other', - count: summary.slice(3).reduce((n: number, { count }: TransferSummary) => n + count, 0), + sourceAmount: summary + .slice(3) + .reduce((n: number, { sourceAmount }: any) => n + sourceAmount, 0), }; - if (remainingSummary.count > 0) { - firstThree.push(remainingSummary); + + if (remainingSummary.sourceAmount > 0) { + topThree.push(remainingSummary); } content = ( = ({ filtersModel, onFilterChange }) => { content={renderGreenLegend} /> = ({ filtersModel, onFilterChange }) => { onMouseEnter={onPieEnter} onMouseLeave={onPieLeave} > - {firstThree.map((_entry: any, index: number) => ( + {topThree.map((_entry: any, index: number) => ( ))} - + value.toFixed(2)} + labelFormatter={(label: string) => `${label}`} + /> ); } + return content; }; diff --git a/src/App/Transfers/Dashboard/TransfersBySourceCurrencyChart.tsx b/src/App/Transfers/Dashboard/TransfersBySourceCurrencyChart.tsx new file mode 100644 index 0000000..ca4815c --- /dev/null +++ b/src/App/Transfers/Dashboard/TransfersBySourceCurrencyChart.tsx @@ -0,0 +1,145 @@ +import { GET_TRANSFER_SUMMARY_BY_SOURCE_CURRENCY } from 'apollo/query'; +import React, { FC, useState } from 'react'; +import { connect } from 'react-redux'; +import { Cell, Legend, Pie, PieChart, Tooltip } from 'recharts'; +import { ReduxContext } from 'store'; +import { State, Dispatch } from 'store/types'; +import { MessageBox, Spinner } from 'components'; +import { useQuery } from '@apollo/client'; +import { TransferSummary } from 'apollo/types'; +import { FilterChangeValue, TransfersFilter } from '../types'; +import { actions } from '../slice'; +import * as selectors from '../selectors'; +import { GREEN_CHART_GRADIENT_COLORS, renderActiveShape, renderGreenLegend } from './utils'; + +const stateProps = (state: State) => ({ + filtersModel: selectors.getTransfersFilter(state), +}); + +const dispatchProps = (dispatch: Dispatch) => ({ + onFilterChange: (field: string, value: FilterChangeValue | string) => + dispatch(actions.setTransferFinderFilter({ field, value })), +}); + +interface ConnectorProps { + filtersModel: TransfersFilter; + onFilterChange: (field: string, value: FilterChangeValue | string) => void; +} + +const BySourceCurrencyChart: FC = ({ filtersModel, onFilterChange }) => { + const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY_BY_SOURCE_CURRENCY, { + fetchPolicy: 'no-cache', + variables: { + startDate: filtersModel.from, + endDate: filtersModel.to, + }, + }); + + const [activeIndex, setActiveIndex] = useState(); + + const onPieEnter = (_: any, index: number) => { + setActiveIndex(index); + }; + + const onPieLeave = () => { + setActiveIndex(undefined); + }; + + let content = null; + + if (error) { + content = Error fetching transfers: {error.message}; + } else if (loading) { + content = ; + } else { + const groupedSummary = data.transferSummary + .filter((obj: TransferSummary) => obj.group.errorCode === null) + .reduce( + (acc: Record, curr: TransferSummary) => { + const { sourceCurrency } = curr.group; + if (!sourceCurrency) return acc; + if (!acc[sourceCurrency]) { + acc[sourceCurrency] = { count: 0, sourceAmount: 0 }; + } + acc[sourceCurrency].count += curr.count; + acc[sourceCurrency].sourceAmount += curr.sum.sourceAmount; + return acc; + }, + {}, + ); + + const summaryArray = Object.entries(groupedSummary) + .map( + ([sourceCurrency, { count, sourceAmount }]: [ + string, + { count: number; sourceAmount: number }, + ]) => ({ + sourceCurrency, + count, + sourceAmount, + }), + ) + .sort((a, b) => b.count - a.count); + + const topThree = summaryArray.slice(0, 3); + const other = summaryArray.slice(3).reduce( + (acc, curr) => { + acc.count += curr.count; + acc.sourceAmount += curr.sourceAmount; + return acc; + }, + { sourceCurrency: 'Other', count: 0, sourceAmount: 0 }, + ); + + if (other.count > 0) { + topThree.push(other); + } + + content = ( + + + { + if (value.name !== 'Other') { + onFilterChange('sourceCurrency', value.name); + } + }} + activeIndex={activeIndex} + activeShape={renderActiveShape} + onMouseEnter={onPieEnter} + onMouseLeave={onPieLeave} + > + {topThree.map((_entry: any, index: number) => ( + + ))} + + + + ); + } + + return content; +}; + +export default connect(stateProps, dispatchProps, null, { context: ReduxContext })( + BySourceCurrencyChart, +); diff --git a/src/App/Transfers/Dashboard/TransfersByTargetCurrencyChart.tsx b/src/App/Transfers/Dashboard/TransfersByTargetCurrencyChart.tsx new file mode 100644 index 0000000..e793bdc --- /dev/null +++ b/src/App/Transfers/Dashboard/TransfersByTargetCurrencyChart.tsx @@ -0,0 +1,145 @@ +import { GET_TRANSFER_SUMMARY_BY_TARGET_CURRENCY } from 'apollo/query'; +import React, { FC, useState } from 'react'; +import { connect } from 'react-redux'; +import { Cell, Legend, Pie, PieChart, Tooltip } from 'recharts'; +import { ReduxContext } from 'store'; +import { State, Dispatch } from 'store/types'; +import { MessageBox, Spinner } from 'components'; +import { useQuery } from '@apollo/client'; +import { TransferSummary } from 'apollo/types'; +import { FilterChangeValue, TransfersFilter } from '../types'; +import { actions } from '../slice'; +import * as selectors from '../selectors'; +import { GREEN_CHART_GRADIENT_COLORS, renderActiveShape, renderGreenLegend } from './utils'; + +const stateProps = (state: State) => ({ + filtersModel: selectors.getTransfersFilter(state), +}); + +const dispatchProps = (dispatch: Dispatch) => ({ + onFilterChange: (field: string, value: FilterChangeValue | string) => + dispatch(actions.setTransferFinderFilter({ field, value })), +}); + +interface ConnectorProps { + filtersModel: TransfersFilter; + onFilterChange: (field: string, value: FilterChangeValue | string) => void; +} + +const ByTargetCurrencyChart: FC = ({ filtersModel, onFilterChange }) => { + const { loading, error, data } = useQuery(GET_TRANSFER_SUMMARY_BY_TARGET_CURRENCY, { + fetchPolicy: 'no-cache', + variables: { + startDate: filtersModel.from, + endDate: filtersModel.to, + }, + }); + + const [activeIndex, setActiveIndex] = useState(); + + const onPieEnter = (_: any, index: number) => { + setActiveIndex(index); + }; + + const onPieLeave = () => { + setActiveIndex(undefined); + }; + + let content = null; + + if (error) { + content = Error fetching transfers: {error.message}; + } else if (loading) { + content = ; + } else { + const groupedSummary = data.transferSummary + .filter((obj: TransferSummary) => obj.group.errorCode === null) + .reduce( + (acc: Record, curr: TransferSummary) => { + const { targetCurrency } = curr.group; + if (!targetCurrency) return acc; + if (!acc[targetCurrency]) { + acc[targetCurrency] = { count: 0, targetAmount: 0 }; + } + acc[targetCurrency].count += curr.count; + acc[targetCurrency].targetAmount += curr.sum.targetAmount; + return acc; + }, + {}, + ); + + const summaryArray = Object.entries(groupedSummary) + .map( + ([targetCurrency, { count, targetAmount }]: [ + string, + { count: number; targetAmount: number }, + ]) => ({ + targetCurrency, + count, + targetAmount, + }), + ) + .sort((a, b) => b.count - a.count); + + const topThree = summaryArray.slice(0, 3); + const other = summaryArray.slice(3).reduce( + (acc, curr) => { + acc.count += curr.count; + acc.targetAmount += curr.targetAmount; + return acc; + }, + { targetCurrency: 'Other', count: 0, targetAmount: 0 }, + ); + + if (other.count > 0) { + topThree.push(other); + } + + content = ( + + + { + if (value.name !== 'Other') { + onFilterChange('targetCurrency', value.name); + } + }} + activeIndex={activeIndex} + activeShape={renderActiveShape} + onMouseEnter={onPieEnter} + onMouseLeave={onPieLeave} + > + {topThree.map((_entry: any, index: number) => ( + + ))} + + + + ); + } + + return content; +}; + +export default connect(stateProps, dispatchProps, null, { context: ReduxContext })( + ByTargetCurrencyChart, +); diff --git a/src/App/Transfers/Dashboard/index.tsx b/src/App/Transfers/Dashboard/index.tsx index cfe0296..400525f 100644 --- a/src/App/Transfers/Dashboard/index.tsx +++ b/src/App/Transfers/Dashboard/index.tsx @@ -2,14 +2,15 @@ import React, { FC } from 'react'; import { connect } from 'react-redux'; import { ReduxContext } from 'store'; import { Row } from 'antd'; -import TransfersByCurrencyChart from './TransfersByCurrencyChart'; import ErrorsByPayeeChart from './ErrorsByPayeeChart'; import ErrorsByPayerChart from './ErrorsByPayerChart'; -import ErrorsByErrorCodeChart from './ErrorsByErrorCodeChart'; import TransfersByPayeeChart from './TransfersByPayeeChart'; import TransfersByPayerChart from './TransfersByPayerChart'; import TransferTotalSummary from './TransferTotalSummary'; +import TransfersBySourceCurrencyChart from './TransfersBySourceCurrencyChart'; +import TransfersByTargetCurrencyChart from './TransfersByTargetCurrencyChart'; import ErrorSummary from './ErrorSummary'; +import ErrorsByErrorCodeChart from './ErrorsByErrorCodeChart'; const stateProps = () => ({}); @@ -20,17 +21,22 @@ interface ConnectorProps {} const Dashboard: FC = () => { return (
- + - - + + +
+ +
- + - +
+ +
); diff --git a/src/App/Transfers/PartyModal/index.tsx b/src/App/Transfers/PartyModal/index.tsx index 3ba9d99..141c272 100644 --- a/src/App/Transfers/PartyModal/index.tsx +++ b/src/App/Transfers/PartyModal/index.tsx @@ -3,13 +3,15 @@ import { FormField, Modal } from 'components'; import { connect } from 'react-redux'; import { State, Dispatch } from 'store/types'; import { ReduxContext } from 'store'; -import moment from 'moment'; +import { Transfer } from 'apollo/types'; import { actions } from '../slice'; import * as selectors from '../selectors'; import { PartyModalData } from '../types'; +// import TransferDetails from '../TransferDetails'; const stateProps = (state: State) => ({ partyModalData: selectors.getSelectedPartyModalData(state), + transferDetails: selectors.getSelectedTransfer(state), }); const dispatchProps = (dispatch: Dispatch) => ({ @@ -18,10 +20,17 @@ const dispatchProps = (dispatch: Dispatch) => ({ interface ConnectorProps { partyModalData: PartyModalData; + transferDetails: Transfer; onModalCloseClick: () => void; } -const JsonModal: FC = ({ partyModalData, onModalCloseClick }) => { +const JsonModal: FC = ({ partyModalData, transferDetails, onModalCloseClick }) => { + // let fspId = partyModalData.type === 'Payer' ? transferDetails.payerDFSP?.toString() || '': transferDetails.payeeDFSP?.toString() || ''; + const fspId = + partyModalData.type === 'Payer' + ? transferDetails.payerDFSP?.toString() || '' + : transferDetails.payeeDFSP?.toString() || ''; + return ( = ({ partyModalData, onModalCloseClick }) => onClose={onModalCloseClick} > - - - - - - + ); diff --git a/src/App/Transfers/TransferDetails/index.tsx b/src/App/Transfers/TransferDetails/index.tsx index a2f5f9e..25ffdbe 100644 --- a/src/App/Transfers/TransferDetails/index.tsx +++ b/src/App/Transfers/TransferDetails/index.tsx @@ -1,4 +1,8 @@ -import React, { FC } from 'react'; +/* eslint-disable react/jsx-curly-newline */ +/* eslint-disable jsx-a11y/accessible-emoji */ +/* eslint-disable react/button-has-type */ +/* eslint-disable jsx-a11y/label-has-associated-control */ +import React, { FC, useState } from 'react'; import { Modal, Tabs, Tab, TabPanel, FormField, Button } from 'components'; import { connect } from 'react-redux'; import { State, Dispatch } from 'store/types'; @@ -32,6 +36,14 @@ const TransferDetails: FC = ({ onsetJsonModalData, onsetPartyModalData, }) => { + const [copyColor, setCopyColor] = useState('#acacac'); + + const handleCopy = (text: string) => { + navigator.clipboard.writeText(text); + setCopyColor('#4fc7e7'); + setTimeout(() => setCopyColor('#acacac'), 2000); + }; + let errorCodeField; if (transferDetails.errorCode) { errorCodeField = ( @@ -48,18 +60,160 @@ const TransferDetails: FC = ({ - +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {errorCodeField ||
} @@ -137,46 +291,119 @@ const TransferDetails: FC = ({ }); }} /> + +
+
- = ({ label="Transfer State" value={transferDetails.transferState || ''} /> - + + + - + + + + + + + + + + + +
+
+ ); + + const TransferTermsTab = ( + + + +
+ + +
+
+ + + + + + + + + + + + + + + + + +
+ + + +
+ Transfer Terms +
+ +
+ Transfer Amount +
+ +
+ +
+
+ + +
+ Payee Receive Amount +
+ +
+ +
+
+ + +
+ Payee DFSP Fee +
+ + +
+ +
+
+ + +
+ Payee DFSP Commission +
+ + +
+ +
+
+ + +
+ Expriry Date Time +
+ +
+
+
+ + + +
+ Conversion Terms +
+ + +
+ Source Amount +
+ + +
+ + +
+ Target Amount +
+ + +
+ +
+
+ + +
+ Source Charges +
+ + +
+ +
+
+ + +
+ Target Charges +
+ + +
+ +
+
+ + +
+ Expriry Date Time +
+ +
+
+
+
+
+ ); + + const TransferPartiesTab = ( + + + +
+ + +
+
+ + + + + + + + +
+ +
+
+ +
Payer Details
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
Payee Details
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
); @@ -239,9 +1088,13 @@ const TransferDetails: FC = ({
- Basic Information + Transfer Details + Transfer Terms + Transfer Parties Technical Details - {BasicInformationTab} + {TransferDetailsTab} + {TransferTermsTab} + {TransferPartiesTab} {TechnicalDetailsTab}
diff --git a/src/App/Transfers/Transfers.scss b/src/App/Transfers/Transfers.scss index 4b888d8..edb5266 100644 --- a/src/App/Transfers/Transfers.scss +++ b/src/App/Transfers/Transfers.scss @@ -8,11 +8,21 @@ margin: 4px; } +.transferPartiesTab .rc-formfield { + width: 300px; + margin: 4px; +} + .partyDetailsModal .rc-formfield { width: 300px; margin: 4px; } +.transferTermsTab .rc-formfield { + width: 150px; + margin: 4px; +} + .technicalDetailsTab .rc-button { width: 450px; margin: 4px; diff --git a/src/App/Transfers/slice.ts b/src/App/Transfers/slice.ts index cb33197..2f4acb4 100644 --- a/src/App/Transfers/slice.ts +++ b/src/App/Transfers/slice.ts @@ -15,9 +15,12 @@ const initialState: types.TransfersState = { payerIdValue: undefined, payeeIdType: undefined, payeeIdValue: undefined, - from: moment().subtract(1, 'day').toString(), - to: moment().toString(), - currency: undefined, + transactionType: undefined, + conversionState: undefined, + sourceCurrency: undefined, + targetCurrency: undefined, + from: moment().subtract(1, 'day').toISOString(), + to: moment().toISOString(), transferState: undefined, timeframeSelect: types.DateRanges.PastTwentyFour, }, diff --git a/src/App/Transfers/types.ts b/src/App/Transfers/types.ts index 0897c47..6ea1e65 100644 --- a/src/App/Transfers/types.ts +++ b/src/App/Transfers/types.ts @@ -10,8 +10,11 @@ export interface TransfersFilter { payeeIdValue: string | undefined; from: string | undefined; to: string | undefined; - currency: string | undefined; + targetCurrency: string | undefined; transferState: string | undefined; + transactionType: string | undefined; + conversionState: string | undefined; + sourceCurrency: string | undefined; timeframeSelect: string; } diff --git a/src/App/Transfers/views.tsx b/src/App/Transfers/views.tsx index b39ae45..f5f83a8 100644 --- a/src/App/Transfers/views.tsx +++ b/src/App/Transfers/views.tsx @@ -15,7 +15,7 @@ import { State, Dispatch } from 'store/types'; import { ReduxContext } from 'store'; import { useLazyQuery } from '@apollo/client'; import { GET_TRANSFER, GET_TRANSFERS_WITH_EVENTS } from 'apollo/query'; -import { DFSP, Party, Transfer } from 'apollo/types'; +import { Party, Transfer } from 'apollo/types'; import { Collapse } from 'antd'; import moment from 'moment'; import { TransfersFilter, FilterChangeValue, DateRanges } from './types'; @@ -33,6 +33,7 @@ const transfersColumns = [ { label: 'Transfer ID', key: 'transferId', + minWidth: 150, }, { label: 'State', @@ -43,36 +44,44 @@ const transfersColumns = [ key: 'transactionType', }, { - label: 'Currency', - key: 'currency', + label: 'Source Currency', + key: 'sourceCurrency', }, { - label: 'Amount', - key: 'amount', + label: 'Source Amount', + key: 'sourceAmount', + }, + { + label: 'Target Currency', + key: 'targetCurrency', + }, + { + label: 'Target Amount', + key: 'targetAmount', fn: (rawValue: Number) => { return `${rawValue ? rawValue.toString() : ''}`; }, sort: (lValue: Transfer, rValue: Transfer) => { - return (lValue.amount || 0) - (rValue.amount || 0); + return (lValue.sourceAmount || 0) - (rValue.sourceAmount || 0); }, }, { label: 'Payer DFSP', key: 'payerDFSP', - fn: (rawValue: DFSP) => { - return `${rawValue ? rawValue.name : ''}`; + fn: (rawValue: string) => { + return rawValue || ''; }, }, { label: 'Payee DFSP', key: 'payeeDFSP', - fn: (rawValue: DFSP) => { - return `${rawValue ? rawValue.name : ''}`; + fn: (rawValue: string) => { + return rawValue || ''; }, }, { label: 'Settlement Batch', - key: 'settlementId', + key: 'transferSettlementBatchId', fn: (rawValue: Number) => { return `${rawValue ? rawValue.toString() : ''}`; }, @@ -210,16 +219,16 @@ const DateFilters: FC = ({ model, onFilterChange, onClearFilte const Filters: FC = ({ model, onFilterChange, onFindTransfersClick }) => { return (
-
+
onFilterChange('payerFSPId', value)} /> = ({ model, onFilterChange, onFindTransf onChange={(value) => onFilterChange('payeeIdType', value as string)} /> onFilterChange('payeeIdValue', value)} /> + onFilterChange('conversionState', value)} + />
-
+
onFilterChange('sourceCurrency', value)} + /> + onFilterChange('currency', value)} + value={model?.targetCurrency} + onChange={(value) => onFilterChange('targetCurrency', value)} />