├── .earthlyignore ├── .editorconfig ├── .envrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── connector-request.md ├── actions │ ├── env │ │ └── action.yml │ ├── extract-in-tag │ │ ├── service.js │ │ ├── type.js │ │ └── version.js │ └── find-directory │ │ └── index.js ├── dependabot.yml ├── labeler.yml └── workflows │ ├── cron.yml │ ├── labeler.yml │ └── releases.yml ├── .gitignore ├── .local ├── docker-compose.yml ├── gateway │ └── Caddyfile ├── postgres │ └── multi-db.sh └── process-compose.yaml ├── CODEOWNERS ├── Earthfile ├── LICENSE ├── README.md ├── base.Dockerfile ├── codecov.yml ├── docker-compose.yml ├── go.mod ├── go.sum ├── libs ├── Earthfile └── events │ ├── .golangci.yml │ ├── Earthfile │ ├── README.md │ ├── base.yaml │ ├── events.go │ ├── generated │ ├── all.json │ ├── ledger │ │ ├── v1.0.0 │ │ │ ├── COMMITTED_TRANSACTIONS.json │ │ │ ├── REVERTED_TRANSACTION.json │ │ │ └── SAVED_METADATA.json │ │ └── v2.0.0 │ │ │ ├── COMMITTED_TRANSACTIONS.json │ │ │ ├── DELETED_METADATA.json │ │ │ ├── REVERTED_TRANSACTION.json │ │ │ └── SAVED_METADATA.json │ ├── orchestration │ │ └── v2.0.0 │ │ │ ├── FAILED_TRIGGER.json │ │ │ ├── FAILED_WORKFLOW.json │ │ │ ├── FAILED_WORKFLOW_STAGE.json │ │ │ ├── STARTED_WORKFLOW.json │ │ │ ├── STARTED_WORKFLOW_STAGE.json │ │ │ ├── SUCCEEDED_TRIGGER.json │ │ │ ├── SUCCEEDED_WORKFLOW.json │ │ │ └── SUCCEEDED_WORKFLOW_STAGE.json │ └── payments │ │ ├── v0.0.0 │ │ ├── CONNECTOR_RESET.json │ │ ├── SAVED_ACCOUNT.json │ │ ├── SAVED_BALANCE.json │ │ └── SAVED_PAYMENT.json │ │ ├── v2.0.0 │ │ ├── CONNECTOR_RESET.json │ │ ├── DELETED_POOL.json │ │ ├── DELETED_TRANSFER_INITIATION.json │ │ ├── SAVED_ACCOUNT.json │ │ ├── SAVED_BALANCE.json │ │ ├── SAVED_BANK_ACCOUNT.json │ │ ├── SAVED_PAYMENT.json │ │ ├── SAVED_POOL.json │ │ └── SAVED_TRANSFER_INITIATION.json │ │ └── v3.0.0 │ │ ├── CONNECTOR_RESET.json │ │ ├── DELETED_POOL.json │ │ ├── SAVED_ACCOUNT.json │ │ ├── SAVED_BALANCE.json │ │ ├── SAVED_BANK_ACCOUNT.json │ │ ├── SAVED_PAYMENT.json │ │ ├── SAVED_PAYMENT_INITIATION.json │ │ ├── SAVED_PAYMENT_INITIATION_ADJUSTMENT.json │ │ ├── SAVED_PAYMENT_INITIATION_RELATED_PAYMENT.json │ │ └── SAVED_POOL.json │ ├── go.mod │ ├── go.sum │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── services │ ├── ledger │ ├── v1.0.0 │ │ ├── COMMITTED_TRANSACTIONS.yaml │ │ ├── REVERTED_TRANSACTION.yaml │ │ └── SAVED_METADATA.yaml │ └── v2.0.0 │ │ ├── COMMITTED_TRANSACTIONS.yaml │ │ ├── DELETED_METADATA.yaml │ │ ├── REVERTED_TRANSACTION.yaml │ │ └── SAVED_METADATA.yaml │ ├── orchestration │ └── v2.0.0 │ │ ├── FAILED_TRIGGER.yaml │ │ ├── FAILED_WORKFLOW.yaml │ │ ├── FAILED_WORKFLOW_STAGE.yaml │ │ ├── STARTED_WORKFLOW.yaml │ │ ├── STARTED_WORKFLOW_STAGE.yaml │ │ ├── SUCCEEDED_TRIGGER.yaml │ │ ├── SUCCEEDED_WORKFLOW.yaml │ │ └── SUCCEEDED_WORKFLOW_STAGE.yaml │ └── payments │ ├── v0.0.0 │ ├── CONNECTOR_RESET.yaml │ ├── SAVED_ACCOUNT.yaml │ ├── SAVED_BALANCE.yaml │ └── SAVED_PAYMENT.yaml │ ├── v2.0.0 │ ├── CONNECTOR_RESET.yaml │ ├── DELETED_POOL.yaml │ ├── DELETED_TRANSFER_INITIATION.yaml │ ├── SAVED_ACCOUNT.yaml │ ├── SAVED_BALANCE.yaml │ ├── SAVED_BANK_ACCOUNT.yaml │ ├── SAVED_PAYMENT.yaml │ ├── SAVED_POOL.yaml │ └── SAVED_TRANSFER_INITIATION.yaml │ └── v3.0.0 │ ├── CONNECTOR_RESET.yaml │ ├── DELETED_POOL.yaml │ ├── SAVED_ACCOUNT.yaml │ ├── SAVED_BALANCE.yaml │ ├── SAVED_BANK_ACCOUNT.yaml │ ├── SAVED_PAYMENT.yaml │ ├── SAVED_PAYMENT_INITIATION.yaml │ ├── SAVED_PAYMENT_INITIATION_ADJUSTMENT.yaml │ ├── SAVED_PAYMENT_INITIATION_RELATED_PAYMENT.yaml │ └── SAVED_POOL.yaml ├── releases ├── .gitignore ├── base.yaml ├── openapi-merge.json ├── openapi-overlay.json ├── package-lock.json └── package.json └── tests ├── benchmarks ├── .gitignore ├── Earthfile ├── README.md ├── Taskfile.yaml ├── extension │ ├── extension.go │ ├── go.mod │ └── go.sum ├── scripts │ ├── .gitignore │ ├── benchs │ │ └── ledger-v2.js │ ├── package.json │ ├── simulates │ │ └── ingestion │ │ │ └── rate.js │ ├── src │ │ ├── generators │ │ │ ├── simple.numscript │ │ │ └── transactions.js │ │ ├── options.js │ │ └── steps │ │ │ ├── account.js │ │ │ ├── aggregateBalances.js │ │ │ ├── balances.js │ │ │ └── transactions.js │ ├── webpack.config.js │ └── yarn.lock └── tools │ ├── .env │ ├── docker-compose.yml │ ├── otel-collector-config.yaml │ ├── postgresql.conf │ ├── prometheus.yaml │ └── provisioning │ ├── dashboards │ ├── dashboards.yml │ └── default │ │ ├── k6.json │ │ ├── perf.json │ │ └── rps.json │ └── datasources │ └── datasource.yml ├── integration ├── Earthfile ├── Taskfile.yaml ├── doc.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── internal │ ├── config.go │ ├── env.go │ ├── events.go │ ├── gateway.go │ ├── helpers.go │ ├── init.go │ ├── module.go │ ├── modules │ │ ├── auth.go │ │ ├── ledger.go │ │ ├── orchestration.go │ │ ├── payments.go │ │ ├── reconciliation.go │ │ ├── search.go │ │ ├── wallets.go │ │ └── webhooks.go │ ├── service.go │ └── test.go ├── suite │ ├── orchestration-triggers.go │ ├── orchestration-workflows-create.go │ ├── orchestration-workflows-execute-with-error.go │ ├── orchestration-workflows-execute.go │ ├── orchestration-workflows-list.go │ ├── orchestration-workflows-soft-delete.go │ ├── reconciliation-policy-create-list.go │ ├── suite_test.go │ └── utils.go └── temporalite.Dockerfile └── loadtesting ├── .babelrc ├── .eslintrc.js ├── .gitignore ├── .yarnclean ├── Dockerfile ├── README.md ├── Taskfile.yaml ├── docker-compose.ledger.yml ├── libs ├── client │ ├── 1_3 │ │ ├── client.ts │ │ └── index.ts │ ├── 1_4 │ │ ├── client.ts │ │ └── index.ts │ ├── 1_5 │ │ ├── client.ts │ │ └── index.ts │ ├── 1_7 │ │ ├── client.ts │ │ └── index.ts │ ├── base │ │ └── index.ts │ └── index.ts ├── config │ └── index.ts └── core │ ├── expectations.ts │ ├── index.ts │ └── versions.ts ├── package.json ├── src └── ledger │ ├── accounts-test.ts │ ├── infos-test.ts │ ├── stats-test.ts │ ├── transactions-test.ts │ └── write-transactions-test.ts ├── tests ├── ledger-all.ts └── ledger-write.ts ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.earthlyignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | *.num 4 | dist/ 5 | autocomplete.* 6 | coverage.* 7 | completions 8 | .idea 9 | openapi/build/generate.json-e 10 | openapi/build/generate.json.bak 11 | coverage.out 12 | dist 13 | vendor 14 | .gitpod/_output 15 | .garden 16 | go.work 17 | go.work.sum 18 | worktrees 19 | 20 | **/*.out 21 | **/*.out 22 | **/*.test 23 | **/*.stats 24 | 25 | .direnv 26 | .cache 27 | .env 28 | 29 | 30 | # Earthly 31 | .tmp-earthly-out 32 | 33 | # Ignore SDKS 34 | sdks 35 | .DS_Store 36 | 37 | docs/node_modules 38 | docs/.docusaurus 39 | docs/build 40 | 41 | # Components 42 | agent 43 | auth 44 | gateway 45 | ledger 46 | operator 47 | orchestration 48 | payments 49 | search 50 | stargate 51 | wallets 52 | webhooks 53 | 54 | # Operator tooling 55 | components/operator/bin -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [Earthfile] 4 | end_of_line = lf 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | 9 | [*.tpl] 10 | end_of_line = lf 11 | charset = utf-8 12 | indent_style = space 13 | indent_size = 2 -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | export PATH=$PWD/.direnv:$PATH 2 | . .direnv/.env 3 | 4 | export EARTHLY_BUILDKIT_HOST=$FORMANCE_DEV_BUILDKITD_ADDRESS 5 | # Dev config 6 | export EARTHLY_SECRETS="SPEAKEASY_API_KEY=$SPEAKEASY_API_KEY" 7 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,GITHUB_TOKEN=$GITHUB_TOKEN" 8 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,GORELEASER_KEY=$GORELEASER_KEY" 9 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,FURY_TOKEN=toto" 10 | 11 | # vCluster Config 12 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,KUBE_TOKEN=$FORMANCE_DEV_KUBE_TOKEN" 13 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,KUBE_APISERVER=$FORMANCE_DEV_KUBE_API_SERVER_ADDRESS" 14 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,tld=$FORMANCE_DEV_TLD" 15 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,user=$FORMANCE_DEV_USER" 16 | export EARTHLY_SECRETS="$EARTHLY_SECRETS,awsKeyID=$FORMANCE_AWS_KEY_ID,awsSecretKey=$FORMANCE_AWS_SECRET_KEY" 17 | 18 | export EARTHLY_BUILD_ARGS="REPOSITORY=$FORMANCE_DEV_GHCR_REGISTRY" 19 | export EARTHLY_BUILD_ARGS="$EARTHLY_BUILD_ARGS,GOPROXY=$FORMANCE_DEV_GOPROXY" 20 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: FormanceHQ 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/connector-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Connector Request 3 | about: Suggest a payments processor connector 4 | title: "[Connector Request] Connector name" 5 | labels: connectors 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Name of the payment processor connector you'd like to see available** 11 | 12 | **Do you currently use this processor** 13 | 14 | **Important required processor features** 15 | 16 | **Additional context** 17 | -------------------------------------------------------------------------------- /.github/actions/env/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Env 2 | description: Setup Env for Linux x64 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Set up QEMU 7 | uses: docker/setup-qemu-action@v3 8 | - name: Set up Docker Buildx 9 | uses: docker/setup-buildx-action@v3 10 | - name: "Put back the git branch into git (Earthly uses it for tagging)" 11 | shell: bash 12 | run: | 13 | branch="" 14 | if [ -n "$GITHUB_HEAD_REF" ]; then 15 | branch="$GITHUB_HEAD_REF" 16 | else 17 | branch="${GITHUB_REF##*/}" 18 | fi 19 | git checkout -b "$branch" || true 20 | 21 | -------------------------------------------------------------------------------- /.github/actions/extract-in-tag/service.js: -------------------------------------------------------------------------------- 1 | const value = process.argv[2]; 2 | 3 | const getValue = (string) => { 4 | const value = string.match(/([a-z].*)\/([a-z].*)\/([a-z].*)/)[2]; 5 | return value; 6 | }; 7 | 8 | console.log(getValue(value)); 9 | -------------------------------------------------------------------------------- /.github/actions/extract-in-tag/type.js: -------------------------------------------------------------------------------- 1 | const value = process.argv[2]; 2 | 3 | const getValue = (string) => { 4 | const value = string.match(/([a-z].*)\/([a-z].*)\/([a-z].*)/)[1]; 5 | return value; 6 | }; 7 | 8 | console.log(getValue(value)); 9 | -------------------------------------------------------------------------------- /.github/actions/extract-in-tag/version.js: -------------------------------------------------------------------------------- 1 | const value = process.argv[2]; 2 | 3 | const getValue = (string) => { 4 | const value = string.match(/([a-z].*)\/([a-z].*)\/([a-z].*)/)[3]; 5 | return value; 6 | }; 7 | 8 | console.log(getValue(value)); 9 | -------------------------------------------------------------------------------- /.github/actions/find-directory/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | function findDockerFile(dir) { 5 | let results = []; 6 | const list = fs.readdirSync(dir); 7 | list.forEach(file => { 8 | file = path.resolve(dir, file); 9 | const stat = fs.statSync(file); 10 | if (stat && stat.isDirectory()) { 11 | /* if it is a directory, recurse */ 12 | results = results.concat(findDockerFile(file)); 13 | } else { 14 | if (path.basename(file) === ".goreleaser.yml") { 15 | results.push(path.relative(".", path.dirname(file))); 16 | } 17 | } 18 | }); 19 | return results; 20 | } 21 | 22 | const dataComponents = findDockerFile("./components"); 23 | const dataEe = findDockerFile("./ee"); 24 | const data = dataComponents.concat(dataEe); 25 | console.log(JSON.stringify(data, null, 0)); -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/components/**" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 40 8 | labels: [ "dependencies" ] 9 | # Groups are updated together in one pull request 10 | groups: 11 | otel: 12 | patterns: 13 | - "go.opentelemetry.io/otel*" 14 | otel-collector: 15 | patterns: 16 | - "go.opentelemetry.io/collector*" 17 | - "github.com/open-telemetry/o*-collector-contrib/*" 18 | otel-instrumentation: 19 | patterns: 20 | - "go.opentelemetry.io/contrib/instrumentation/*" 21 | go-openapi: 22 | patterns: 23 | - "github.com/go-openapi/*" 24 | 25 | - package-ecosystem: gomod 26 | directory: "/ee/**" 27 | schedule: 28 | interval: daily 29 | open-pull-requests-limit: 40 30 | labels: [ "dependencies" ] 31 | # Groups are updated together in one pull request 32 | groups: 33 | otel: 34 | patterns: 35 | - "go.opentelemetry.io/otel*" 36 | otel-collector: 37 | patterns: 38 | - "go.opentelemetry.io/collector*" 39 | - "github.com/open-telemetry/o*-collector-contrib/*" 40 | otel-instrumentation: 41 | patterns: 42 | - "go.opentelemetry.io/contrib/instrumentation/*" 43 | go-openapi: 44 | patterns: 45 | - "github.com/go-openapi/*" 46 | 47 | - package-ecosystem: "github-actions" 48 | directory: "/" 49 | schedule: 50 | interval: "weekly" 51 | labels: [ "dependencies" ] 52 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | libs: 2 | - changed-files: 3 | - any-glob-to-any-file: "libs/*" 4 | openapi: 5 | - changed-files: 6 | - any-glob-to-any-file: "openapi/*" 7 | sdks: 8 | - changed-files: 9 | - any-glob-to-any-file: "sdks/*" 10 | github: 11 | - changed-files: 12 | - any-glob-to-any-file: ".github/*" 13 | 14 | # Apps 15 | 'components/ledger': 16 | - changed-files: 17 | - any-glob-to-any-file: "components/ledger/*" 18 | 'components/operator': 19 | - changed-files: 20 | - any-glob-to-any-file: "components/operator/*" 21 | 'components/payments': 22 | - changed-files: 23 | - any-glob-to-any-file: "components/payments/*" 24 | 25 | # EE 26 | 'ee/agent': 27 | - changed-files: 28 | - any-glob-to-any-file: "ee/agent/*" 29 | 'ee/auth': 30 | - changed-files: 31 | - any-glob-to-any-file: "ee/auth/*" 32 | 'ee/gateway': 33 | - changed-files: 34 | - any-glob-to-any-file: "ee/gateway/*" 35 | 'ee/orchestration': 36 | - changed-files: 37 | - any-glob-to-any-file: "ee/orchestration/*" 38 | 'ee/search': 39 | - changed-files: 40 | - any-glob-to-any-file: "ee/search/*" 41 | 'ee/stargate': 42 | - changed-files: 43 | - any-glob-to-any-file: "ee/stargate/*" 44 | 'ee/wallets': 45 | - changed-files: 46 | - any-glob-to-any-file: "ee/wallets/*" 47 | 'ee/webhooks': 48 | - changed-files: 49 | - any-glob-to-any-file: "ee/webhooks/*" 50 | 'ee/reconciliation': 51 | - changed-files: 52 | - any-glob-to-any-file: "ee/reconciliation/*" 53 | -------------------------------------------------------------------------------- /.github/workflows/cron.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | schedule: 4 | - cron: '30 4 * * *' 5 | 6 | jobs: 7 | Base: 8 | runs-on: "ubuntu-latest" 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | - name: Set up QEMU 13 | uses: docker/setup-qemu-action@v3 14 | - name: Set up Docker Buildx 15 | uses: docker/setup-buildx-action@v3 16 | - name: Login to GitHub Container Registry 17 | uses: docker/login-action@v3 18 | with: 19 | registry: ghcr.io 20 | username: "NumaryBot" 21 | password: ${{ secrets.NUMARY_GITHUB_TOKEN }} 22 | - name: Build and Push Base 23 | uses: docker/build-push-action@v6 24 | with: 25 | platforms: linux/amd64,linux/arm64 26 | push: true 27 | context: . 28 | file: ./base.Dockerfile 29 | target: base 30 | tags: ghcr.io/formancehq/base:22.04 31 | - name: Build and Push Scratch 32 | uses: docker/build-push-action@v6 33 | with: 34 | platforms: linux/amd64,linux/arm64 35 | push: true 36 | context: . 37 | file: ./base.Dockerfile 38 | target: scratch 39 | tags: ghcr.io/formancehq/base:scratch 40 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Labeler" 2 | on: 3 | pull_request_target: 4 | types: 5 | - opened 6 | - reopened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | Triage: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | pull-requests: write 16 | steps: 17 | - uses: actions/labeler@v5 18 | with: 19 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 20 | 21 | PR: 22 | name: Check PR Title 23 | runs-on: ubuntu-latest 24 | permissions: 25 | statuses: write 26 | steps: 27 | - uses: amannn/action-semantic-pull-request@v5 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/releases.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - 'v*.*.*' 6 | permissions: 7 | contents: write 8 | 9 | jobs: 10 | OpenAPI: 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - uses: earthly/actions-setup@v1 14 | with: 15 | github-token: ${{ secrets.GITHUB_TOKEN }} 16 | version: "latest" 17 | - uses: 'actions/checkout@v4' 18 | with: 19 | fetch-depth: 0 20 | - name: Setup Env 21 | uses: ./.github/actions/env 22 | - name: Generate OpenAPI final specification 23 | run: > 24 | earthly +build-final-spec --version=${{github.ref_name}} 25 | - name: Create Release 26 | run: gh release create ${{github.ref_name}} --generate-notes 27 | env: 28 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | - name: Add the OpenAPI file to the release assets 30 | run: > 31 | gh release upload ${{github.ref_name}} ./releases/build/generate.json#openapi.json 32 | env: 33 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | openapi/build/generate.json-e 3 | openapi/build/generate.json.bak 4 | coverage.out 5 | dist 6 | vendor 7 | .gitpod/_output 8 | .garden 9 | go.work 10 | go.work.sum 11 | worktrees 12 | 13 | **/*.out 14 | **/*.out 15 | **/*.test 16 | **/*.stats 17 | .direnv 18 | 19 | # moon 20 | .moon/cache 21 | .moon/docker 22 | .moon/bin 23 | .moon/go 24 | 25 | .devbox 26 | .cache 27 | .env 28 | 29 | 30 | # Earthly 31 | .tmp-earthly-out 32 | .DS_Store 33 | openapi/node_modules 34 | 35 | 36 | .kubeconfig 37 | 38 | .aws 39 | 40 | .kube 41 | 42 | # Helm 43 | *.tgz -------------------------------------------------------------------------------- /.local/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | volumes: 3 | postgres_data: 4 | services: 5 | postgres: 6 | platform: linux/x86_64 7 | image: "postgres:15-alpine" 8 | restart: always 9 | healthcheck: 10 | test: [ "CMD-SHELL", "pg_isready -U ledger" ] 11 | interval: 10s 12 | timeout: 5s 13 | retries: 5 14 | ports: 15 | - "${FORMANCE_POSTGRES_PORT:-5432}:5432" 16 | environment: 17 | POSTGRES_USER: "formance" 18 | POSTGRES_PASSWORD: "formance" 19 | POSTGRES_MULTIPLE_DATABASES: "ledger,payments" 20 | PGDATA: "/data/postgres" 21 | volumes: 22 | - postgres_data:/data/postgres 23 | - ./postgres:/docker-entrypoint-initdb.d -------------------------------------------------------------------------------- /.local/gateway/Caddyfile: -------------------------------------------------------------------------------- 1 | (cors) { 2 | header { 3 | Access-Control-Allow-Methods "GET,OPTIONS,PUT,POST,DELETE,HEAD,PATCH" 4 | Access-Control-Allow-Headers content-type 5 | Access-Control-Max-Age 100 6 | Access-Control-Allow-Origin * 7 | } 8 | } 9 | 10 | (handle_path_route_without_auth) { 11 | # handle_path automatically strips the prefix from the request path 12 | handle_path {args.0}* { 13 | reverse_proxy {args.1} 14 | 15 | import cors 16 | } 17 | } 18 | 19 | (handle_route_without_auth) { 20 | # handle_path automatically strips the prefix from the request path 21 | handle {args.0} { 22 | reverse_proxy {args.2} 23 | uri strip_prefix {args.1} 24 | 25 | import cors 26 | } 27 | } 28 | 29 | (payments) { 30 | @transferinitiationwritermatcher { 31 | path {args.0}/transfer-initiations* 32 | method POST DELETE 33 | } 34 | 35 | @transferinitiationreadermatcher { 36 | path {args.0}/transfer-initiation* 37 | method GET 38 | } 39 | 40 | @bankaccountswritermatcher { 41 | path {args.0}/bank-accounts* 42 | method POST 43 | } 44 | 45 | @bankaccountsreadermatcher { 46 | path {args.0}/bank-accounts* 47 | method GET 48 | } 49 | 50 | @connectorsmatcher { 51 | path {args.0}/connectors* 52 | } 53 | 54 | @configmatcher { 55 | path {args.0}/configs* 56 | } 57 | 58 | @accountsmatcher { 59 | path {args.0}/accounts* 60 | } 61 | 62 | import handle_route_without_auth @transferinitiationreadermatcher {args.0} {args.1} 63 | import handle_route_without_auth @bankaccountsreadermatcher {args.0} {args.1} 64 | import handle_route_without_auth @accountsmatcher {args.0} {args.1} 65 | 66 | import handle_route_without_auth @bankaccountswritermatcher {args.0} {args.2} 67 | import handle_route_without_auth @transferinitiationwritermatcher {args.0} {args.2} 68 | import handle_route_without_auth @connectorsmatcher {args.0} {args.2} 69 | import handle_route_without_auth @configmatcher {args.0} {args.2} 70 | 71 | # All other requests on the api 72 | import handle_path_route_without_auth {args.0} {args.1} 73 | } 74 | 75 | { 76 | # Local env dev config 77 | debug 78 | } 79 | 80 | localhost:80 { 81 | 82 | import handle_path_route_without_auth "/api/ledger" "ledger:3068" 83 | import payments "/api/payments" "payments-api:8080" "payments-connectors:8080" 84 | 85 | # handle all other requests 86 | handle { 87 | respond "Bad Gateway" 502 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /.local/postgres/multi-db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -u 5 | 6 | function create_user_and_database() { 7 | local database=$1 8 | echo " Creating user and database '$database'" 9 | psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL 10 | CREATE USER $database; 11 | CREATE DATABASE $database; 12 | GRANT ALL PRIVILEGES ON DATABASE $database TO $database; 13 | EOSQL 14 | } 15 | 16 | if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then 17 | echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" 18 | for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do 19 | create_user_and_database $db 20 | done 21 | echo "Multiple databases created" 22 | fi 23 | -------------------------------------------------------------------------------- /.local/process-compose.yaml: -------------------------------------------------------------------------------- 1 | version: 0.5 2 | 3 | environment: 4 | - "OTEL_TRACES=true" 5 | - "OTEL_TRACES_EXPORTER=otlp" 6 | - "OTEL_TRACES_ENDPOINT=jaeger" 7 | - "OTEL_TRACES_PORT=4317" 8 | - "OTEL_TRACES_EXPORTER_OTLP_INSECURE=true" 9 | - "OTEL_TRACES_EXPORTER_OTLP_MODE=grpc" 10 | - "OTEL_TRACES_EXPORTER_OTLP_ENDPOINT=jaeger:4317" 11 | - "DEBUG=true" 12 | 13 | processes: 14 | gateway: 15 | command: gateway run --config /etc/formance/gateway/Caddyfile --adapter caddyfile 16 | availability: 17 | restart: always 18 | environment: 19 | - "OTEL_SERVICE_NAME=gateway" 20 | - "OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317" 21 | - "OTEL_EXPORTER_OTLP_INSECURE=true" 22 | - "OTEL_EXPORTER_OTLP_PROTOCOL=grpc" 23 | 24 | ledger: 25 | command: ledger server start --storage.driver=postgres --storage.postgres.conn_string postgresql://formance:formance@postgres/ledger?sslmode=disable --server.http.bind_address 0.0.0.0:3068 --publisher-nats-enabled true --publisher-nats-url nats:4222 --publisher-topic-mapping *:ledger 26 | availability: 27 | restart: always 28 | environment: 29 | - "OTEL_SERVICE_NAME=ledger" 30 | 31 | wallets: 32 | command: wallets server --listen 0.0.0.0:8081 --stack-url http://localhost --stack-client-id wallets --stack-client-secret wallets 33 | availability: 34 | restart: always 35 | environment: 36 | - "OTEL_SERVICE_NAME=wallets" 37 | 38 | paymentsapi: 39 | command: payments api server --listen 0.0.0.0:8082 --publisher-nats-enabled true --publisher-nats-url nats:4222 --publisher-topic-mapping *:payments --postgres-uri postgresql://formance:formance@postgres/payments?sslmode=disable --config-encryption-key mysuperencryptionkey 40 | availability: 41 | restart: always 42 | environment: 43 | - "OTEL_SERVICE_NAME=paymentsapi" 44 | 45 | paymentsconnectors: 46 | command: payments connectors server --listen 0.0.0.0:8087 --publisher-nats-enabled true --publisher-nats-url nats:4222 --publisher-topic-mapping *:payments --postgres-uri postgresql://formance:formance@postgres/payments?sslmode=disable --config-encryption-key mysuperencryptionkey 47 | availability: 48 | restart: always 49 | environment: 50 | - "OTEL_SERVICE_NAME=paymentsconnectors" 51 | 52 | payments-migrate: 53 | command: payments migrate up --postgres-uri postgresql://formance:formance@postgres/payments?sslmode=disable 54 | availability: 55 | restart: never -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @formancehq/backend 2 | -------------------------------------------------------------------------------- /Earthfile: -------------------------------------------------------------------------------- 1 | VERSION 0.8 2 | PROJECT FormanceHQ/stack 3 | 4 | IMPORT github.com/formancehq/earthly:tags/v0.15.0 AS core 5 | IMPORT github.com/formancehq/ledger:v2.2.45 AS ledger 6 | IMPORT github.com/formancehq/payments:v3.0.15 AS payments 7 | IMPORT github.com/formancehq/wallets:v2.1.5 AS wallets 8 | IMPORT github.com/formancehq/webhooks:v2.1.0 AS webhooks 9 | IMPORT github.com/formancehq/auth:v2.1.1 AS auth 10 | IMPORT github.com/formancehq/search:v2.1.0 AS search 11 | IMPORT github.com/formancehq/stargate:v2.1.0 AS stargate 12 | IMPORT github.com/formancehq/flows:v2.1.0 AS orchestration 13 | IMPORT github.com/formancehq/reconciliation:v2.1.0 AS reconciliation 14 | IMPORT github.com/formancehq/gateway:v2.1.0 AS gateway 15 | 16 | sources: 17 | FROM core+base-image 18 | ARG --required LOCATION 19 | COPY ${LOCATION} out 20 | SAVE ARTIFACT out 21 | 22 | build-final-spec: 23 | FROM core+base-image 24 | RUN apk update && apk add yarn nodejs npm jq 25 | WORKDIR /src/releases 26 | COPY releases/package.* . 27 | RUN npm install 28 | 29 | WORKDIR /src/releases 30 | COPY releases/base.yaml . 31 | COPY releases/openapi-overlay.json . 32 | COPY releases/openapi-merge.json . 33 | RUN mkdir ./build 34 | 35 | COPY (ledger+openapi/openapi.yaml) /src/components/ledger/ 36 | COPY (payments+openapi/openapi.yaml) /src/components/payments/ 37 | COPY (gateway+openapi/openapi.yaml) /src/ee/gateway/ 38 | COPY (auth+openapi/openapi.yaml) /src/ee/auth/ 39 | COPY (search+openapi/openapi.yaml) /src/ee/search/ 40 | COPY (webhooks+openapi/openapi.yaml) /src/ee/webhooks/ 41 | COPY (wallets+openapi/openapi.yaml) /src/ee/wallets/ 42 | COPY (reconciliation+openapi/openapi.yaml) /src/ee/reconciliation/ 43 | COPY (orchestration+openapi/openapi.yaml) /src/ee/orchestration/ 44 | 45 | RUN npm run build 46 | RUN jq -s '.[0] * .[1]' build/generate.json openapi-overlay.json > build/latest.json 47 | ARG version=v0.0.0 48 | IF [ "$version" = "v0.0.0" ] 49 | RUN sed -i 's/SDK_VERSION/v0.0.0/g' build/latest.json 50 | SAVE ARTIFACT build/latest.json AS LOCAL releases/build/latest.json 51 | ELSE 52 | RUN sed -i 's/SDK_VERSION/'$version'/g' build/latest.json 53 | SAVE ARTIFACT build/latest.json AS LOCAL releases/build/generate.json 54 | END 55 | SAVE ARTIFACT build/latest.json 56 | 57 | pre-commit: # Generate the final spec and run all the pre-commit hooks 58 | LOCALLY 59 | BUILD +build-final-spec 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-present Formance Solutions 2 | 3 | Portions of the Formance software are licensed as follows: 4 | 5 | - If you are accessing or using any component of the software that resides under an "ee/" directory, then you are deemed to be using our “Enterprise Edition” of the software and you understand and agree that the software is not licensed under the "MIT" license as set forth below but instead, all the software you access is licensed under the license defined in "./ee/LICENSE", unless (a) you or the company you represent has signed an alternative agreement referencing this code, then such signed agreement applies or (b) you are using the software in connection with a subscription to our cloud offering, then the terms of the agreement relevant to the cloud offering which you have assented to apply and the software licenses included in that agreement shall apply. 6 | - If (a) you are not accessing or using the software that resides under an “ee/” directory and therefore you are only accessing or using our “Community Edition” of the Software and (b) you have no registered account on our cloud offering, then we are providing you the software under the "MIT " license as set forth below. 7 | 8 | MIT License 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -------------------------------------------------------------------------------- /base.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 AS base 2 | RUN apt update && apt install -y ca-certificates curl && rm -rf /var/lib/apt/lists/* 3 | 4 | FROM alpine:latest as certs 5 | RUN apk --update add ca-certificates 6 | 7 | FROM scratch AS scratch 8 | COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "components/ledger/pkg/machine/script/parser" 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | include: 3 | - .local/docker-compose.yml 4 | services: 5 | gateway: 6 | image: "caddy:2.7.5-alpine" 7 | ports: 8 | - "80:80" 9 | - "443:443" 10 | volumes: 11 | - ".local/gateway/Caddyfile:/etc/caddy/Caddyfile" 12 | 13 | ledger: 14 | image: "ghcr.io/formancehq/ledger:v2.2.0" 15 | healthcheck: 16 | test: [ "CMD", "curl", "-f", "http://127.0.0.1:3068/_healthcheck" ] 17 | interval: 10s 18 | timeout: 5s 19 | retries: 5 20 | depends_on: 21 | postgres: 22 | condition: service_healthy 23 | environment: 24 | POSTGRES_URI: "postgresql://formance:formance@postgres:${FORMANCE_POSTGRES_PORT:-5432}/ledger?sslmode=disable" 25 | 26 | payments-migrate: 27 | image: "ghcr.io/formancehq/payments:v3.0.1" 28 | command: migrate up 29 | depends_on: 30 | postgres: 31 | condition: service_healthy 32 | environment: 33 | POSTGRES_URI: "postgres://formance:formance@postgres:${FORMANCE_POSTGRES_PORT:-5432}/payments?sslmode=disable" 34 | 35 | payments-api: 36 | image: "ghcr.io/formancehq/payments:v3.0.1" 37 | command: api server 38 | healthcheck: 39 | test: [ "CMD", "curl", "-f", "http://127.0.0.1:8080/_live" ] 40 | interval: 10s 41 | timeout: 5s 42 | retries: 5 43 | depends_on: 44 | postgres: 45 | condition: service_healthy 46 | payments-migrate: 47 | condition: service_completed_successfully 48 | environment: 49 | DEBUG: ${DEBUG:-"true"} 50 | POSTGRES_URI: "postgres://formance:formance@postgres:${FORMANCE_POSTGRES_PORT:-5432}/payments?sslmode=disable" 51 | CONFIG_ENCRYPTION_KEY: mysuperencryptionkey 52 | 53 | payments-connectors: 54 | image: "ghcr.io/formancehq/payments:v3.0.1" 55 | command: connectors server 56 | healthcheck: 57 | test: [ "CMD", "curl", "-f", "http://127.0.0.1:8080/_live" ] 58 | interval: 10s 59 | timeout: 5s 60 | retries: 5 61 | depends_on: 62 | postgres: 63 | condition: service_healthy 64 | payments-migrate: 65 | condition: service_completed_successfully 66 | environment: 67 | DEBUG: ${DEBUG:-"true"} 68 | POSTGRES_URI: "postgres://formance:formance@postgres:${FORMANCE_POSTGRES_PORT:-5432}/payments?sslmode=disable" 69 | CONFIG_ENCRYPTION_KEY: mysuperencryptionkey 70 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/formancehq/stack 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.22.7 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/formancehq/stack/1c5d03ee73a8255f3e7c48531de43ffb7649d96f/go.sum -------------------------------------------------------------------------------- /libs/Earthfile: -------------------------------------------------------------------------------- 1 | VERSION 0.8 2 | 3 | IMPORT github.com/formancehq/earthly:tags/v0.15.0 AS core 4 | IMPORT .. AS stack 5 | 6 | run: 7 | LOCALLY 8 | ARG --required TARGET 9 | BUILD ./events+$TARGET 10 | -------------------------------------------------------------------------------- /libs/events/.golangci.yml: -------------------------------------------------------------------------------- 1 | allow-parallel-runners: true 2 | run: 3 | timeout: 5m 4 | linters: 5 | disable-all: true 6 | enable: 7 | - gofmt 8 | - goimports 9 | - unused 10 | - gosec 11 | linters-settings: 12 | gosec: 13 | # To select a subset of rules to run. 14 | # Available rules: https://github.com/securego/gosec#available-rules 15 | includes: 16 | - G103 # Audit the use of unsafe block 17 | - G104 # Audit errors not checked 18 | - G106 # Audit the use of ssh.InsecureIgnoreHostKey 19 | - G108 # Profiling endpoint automatically exposed on /debug/pprof 20 | - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32 21 | - G110 # Potential DoS vulnerability via decompression bomb 22 | - G111 # Potential directory traversal 23 | - G112 # Potential slowloris attack 24 | # - G113 # Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772) 25 | - G201 # SQL query construction using format string 26 | - G202 # SQL query construction using string concatenation 27 | - G203 # Use of unescaped data in HTML templates 28 | - G204 # Audit use of command execution 29 | - G301 # Poor file permissions used when creating a directory 30 | - G302 # Poor file permissions used with chmod 31 | - G303 # Creating tempfile using a predictable path 32 | - G304 # File path provided as taint input 33 | - G305 # File traversal when extracting zip/tar archive 34 | - G306 # Poor file permissions used when writing to a new file 35 | - G307 # Poor file permissions used when creating a file with os.Create 36 | - G401 # Detect the usage of DES, RC4, MD5 or SHA1 37 | - G403 # Ensure minimum RSA key length of 2048 bits 38 | - G501 # Import blocklist: crypto/md5 39 | - G502 # Import blocklist: crypto/des 40 | - G503 # Import blocklist: crypto/rc4 41 | - G504 # Import blocklist: net/http/cgi 42 | - G505 # Import blocklist: crypto/sha1 43 | - G602 # Slice access out of bounds -------------------------------------------------------------------------------- /libs/events/Earthfile: -------------------------------------------------------------------------------- 1 | VERSION 0.8 2 | 3 | IMPORT github.com/formancehq/earthly:tags/v0.15.0 AS core 4 | IMPORT ../.. AS stack 5 | 6 | FROM core+base-image 7 | 8 | go-sources: 9 | COPY events.go go.* base.yaml /src/ 10 | COPY --dir services /src/ 11 | WORKDIR /src 12 | SAVE ARTIFACT /src 13 | 14 | tidy: 15 | FROM core+builder-image 16 | COPY (+go-sources/*) /src 17 | WORKDIR /src 18 | DO --pass-args stack+GO_TIDY 19 | SAVE ARTIFACT go.* AS LOCAL ./ 20 | 21 | lint: 22 | FROM core+builder-image 23 | COPY (+go-sources/*) /src 24 | WORKDIR /src 25 | COPY --pass-args +tidy/go.* . 26 | DO --pass-args stack+GO_LINT 27 | SAVE ARTIFACT * AS LOCAL ./ 28 | 29 | generate: 30 | RUN apk add nodejs npm 31 | WORKDIR /src 32 | COPY package* . 33 | RUN npm install 34 | RUN mkdir generated 35 | COPY index.js base.yaml . 36 | COPY --dir services . 37 | RUN node index.js 38 | SAVE ARTIFACT generated AS LOCAL ./generated 39 | 40 | pre-commit: 41 | WAIT 42 | BUILD --pass-args +tidy 43 | END 44 | BUILD --pass-args +lint 45 | BUILD --pass-args +generate -------------------------------------------------------------------------------- /libs/events/README.md: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | This repository centralizes event schemas across the Formance Stack. For each stack version, a repository named "vX" contains all related events. 4 | 5 | Each "vX" folder contains a "base" folder that contains the base event format, which is common to all services. This base event includes a "type" property and an "app" property, which denote the format of the "payload" property. 6 | 7 | For example, an event with "type" == "SAVED_PAYMENT" and "app" == "payments" must have a payload matching schema in the file "payments/SAVED_PAYMENT.yaml". 8 | 9 | ## Payments Versions 10 | 11 | We decided to go with stack releases starting at v2.0.x. 12 | 13 | Before that, the last payments version was v0.9.7. 14 | 15 | This is why we do not have a v1.0.0 directory with events, since payments 16 | was never released with a v1.x version. -------------------------------------------------------------------------------- /libs/events/base.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | type: object 3 | properties: 4 | app: 5 | type: string 6 | version: 7 | type: string 8 | date: 9 | type: string 10 | format: date-time 11 | type: 12 | type: string 13 | ledger: 14 | type: string 15 | payload: 16 | type: object 17 | additionalProperties: true 18 | required: 19 | - date 20 | - app 21 | - version 22 | - type 23 | - payload 24 | -------------------------------------------------------------------------------- /libs/events/events.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "embed" 8 | 9 | "github.com/pkg/errors" 10 | "github.com/xeipuuv/gojsonschema" 11 | "golang.org/x/mod/semver" 12 | "gopkg.in/yaml.v3" 13 | ) 14 | 15 | //go:embed base.yaml 16 | var baseEvent string 17 | 18 | //go:embed services 19 | var services embed.FS 20 | 21 | func ComputeSchema(serviceName, eventName string) (*gojsonschema.Schema, error) { 22 | base := map[string]any{} 23 | if err := yaml.Unmarshal([]byte(baseEvent), &base); err != nil { 24 | return nil, err 25 | } 26 | 27 | ls, err := services.ReadDir(filepath.Join("services", serviceName)) 28 | if err != nil { 29 | return nil, errors.Wrapf(err, "reading events directory for service '%s'", serviceName) 30 | } 31 | 32 | var moreRecent string 33 | for _, directory := range ls { 34 | if moreRecent == "" || semver.Compare(directory.Name(), moreRecent) > 0 { 35 | moreRecent = directory.Name() 36 | } 37 | } 38 | 39 | if moreRecent == "" { 40 | return nil, fmt.Errorf("error retrieving more recent version directory for service '%s'", serviceName) 41 | } 42 | 43 | eventData, err := services.ReadFile(fmt.Sprintf("services/%s/%s/%s.yaml", serviceName, moreRecent, eventName)) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | event := map[string]any{} 49 | if err := yaml.Unmarshal(eventData, &event); err != nil { 50 | return nil, err 51 | } 52 | 53 | base["properties"].(map[string]any)["payload"] = event 54 | 55 | loader := gojsonschema.NewGoLoader(base) 56 | return gojsonschema.NewSchema(loader) 57 | } 58 | 59 | func Check(data []byte, serviceName, eventName string) error { 60 | schema, err := ComputeSchema(serviceName, eventName) 61 | if err != nil { 62 | return errors.Wrap(err, "computing schema") 63 | } 64 | result, err := schema.Validate(gojsonschema.NewStringLoader(string(data))) 65 | if err != nil { 66 | return errors.Wrap(err, "validating schema") 67 | } 68 | if len(result.Errors()) > 0 { 69 | ret := "" 70 | for _, resultError := range result.Errors() { 71 | ret += resultError.String() + "\r\n" 72 | } 73 | return errors.New(ret) 74 | } 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /libs/events/generated/ledger/v1.0.0/COMMITTED_TRANSACTIONS.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "ledger": { 24 | "type": "string" 25 | }, 26 | "transactions": { 27 | "type": "array", 28 | "items": { 29 | "type": "object", 30 | "properties": { 31 | "postings": { 32 | "type": "array", 33 | "items": { 34 | "type": "object", 35 | "properties": { 36 | "source": { 37 | "type": "string" 38 | }, 39 | "destination": { 40 | "type": "string" 41 | }, 42 | "amount": { 43 | "type": "number" 44 | }, 45 | "asset": { 46 | "type": "string" 47 | } 48 | }, 49 | "required": [ 50 | "source", 51 | "destination", 52 | "amount", 53 | "asset" 54 | ] 55 | } 56 | }, 57 | "reference": { 58 | "type": "string" 59 | }, 60 | "metadata": { 61 | "type": "object", 62 | "properties": {}, 63 | "required": [] 64 | }, 65 | "txid": { 66 | "type": "number" 67 | }, 68 | "timestamp": { 69 | "type": "string" 70 | } 71 | }, 72 | "required": [ 73 | "postings", 74 | "reference", 75 | "metadata", 76 | "txid", 77 | "timestamp" 78 | ] 79 | } 80 | } 81 | }, 82 | "required": [ 83 | "ledger", 84 | "transactions" 85 | ] 86 | } 87 | }, 88 | "required": [ 89 | "date", 90 | "app", 91 | "version", 92 | "type", 93 | "payload" 94 | ] 95 | } -------------------------------------------------------------------------------- /libs/events/generated/ledger/v1.0.0/SAVED_METADATA.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "ledger": { 24 | "type": "string" 25 | }, 26 | "targetType": { 27 | "type": "string" 28 | }, 29 | "targetId": { 30 | "type": "string" 31 | }, 32 | "metadata": { 33 | "type": "object", 34 | "additionalProperties": {} 35 | } 36 | }, 37 | "required": [ 38 | "ledger", 39 | "targetType", 40 | "targetId", 41 | "metadata" 42 | ] 43 | } 44 | }, 45 | "required": [ 46 | "date", 47 | "app", 48 | "version", 49 | "type", 50 | "payload" 51 | ] 52 | } -------------------------------------------------------------------------------- /libs/events/generated/ledger/v2.0.0/COMMITTED_TRANSACTIONS.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "ledger": { 24 | "type": "string" 25 | }, 26 | "transactions": { 27 | "type": "array", 28 | "items": { 29 | "type": "object", 30 | "properties": { 31 | "postings": { 32 | "type": "array", 33 | "items": { 34 | "type": "object", 35 | "properties": { 36 | "source": { 37 | "type": "string" 38 | }, 39 | "destination": { 40 | "type": "string" 41 | }, 42 | "amount": { 43 | "type": "number" 44 | }, 45 | "asset": { 46 | "type": "string" 47 | } 48 | }, 49 | "required": [ 50 | "source", 51 | "destination", 52 | "amount", 53 | "asset" 54 | ] 55 | } 56 | }, 57 | "reference": { 58 | "type": "string" 59 | }, 60 | "metadata": { 61 | "type": "object", 62 | "properties": {}, 63 | "required": [] 64 | }, 65 | "id": { 66 | "type": "number" 67 | }, 68 | "timestamp": { 69 | "type": "string" 70 | }, 71 | "reverted": { 72 | "type": "boolean" 73 | } 74 | }, 75 | "required": [ 76 | "postings", 77 | "metadata", 78 | "id", 79 | "timestamp", 80 | "reverted" 81 | ] 82 | } 83 | } 84 | }, 85 | "required": [ 86 | "ledger", 87 | "transactions" 88 | ] 89 | } 90 | }, 91 | "required": [ 92 | "date", 93 | "app", 94 | "version", 95 | "type", 96 | "payload" 97 | ] 98 | } -------------------------------------------------------------------------------- /libs/events/generated/ledger/v2.0.0/DELETED_METADATA.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "ledger": { 24 | "type": "string" 25 | }, 26 | "targetType": { 27 | "type": "string" 28 | }, 29 | "targetId": { 30 | "type": "string" 31 | }, 32 | "key": { 33 | "type": "string" 34 | } 35 | }, 36 | "required": [ 37 | "ledger", 38 | "targetType", 39 | "targetId", 40 | "key" 41 | ] 42 | } 43 | }, 44 | "required": [ 45 | "date", 46 | "app", 47 | "version", 48 | "type", 49 | "payload" 50 | ] 51 | } -------------------------------------------------------------------------------- /libs/events/generated/ledger/v2.0.0/SAVED_METADATA.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "ledger": { 24 | "type": "string" 25 | }, 26 | "targetType": { 27 | "type": "string" 28 | }, 29 | "targetId": { 30 | "type": "string" 31 | }, 32 | "metadata": { 33 | "type": "object", 34 | "additionalProperties": {} 35 | } 36 | }, 37 | "required": [ 38 | "ledger", 39 | "targetType", 40 | "targetId", 41 | "metadata" 42 | ] 43 | } 44 | }, 45 | "required": [ 46 | "date", 47 | "app", 48 | "version", 49 | "type", 50 | "payload" 51 | ] 52 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/FAILED_TRIGGER.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "triggerID": { 28 | "type": "string", 29 | "minLength": 1 30 | }, 31 | "error": { 32 | "type": "string", 33 | "minLength": 1 34 | } 35 | }, 36 | "required": [ 37 | "id", 38 | "triggerID", 39 | "error" 40 | ] 41 | } 42 | }, 43 | "required": [ 44 | "date", 45 | "app", 46 | "version", 47 | "type", 48 | "payload" 49 | ] 50 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/FAILED_WORKFLOW.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "instanceID": { 28 | "type": "string", 29 | "minLength": 1 30 | }, 31 | "error": { 32 | "type": "string", 33 | "minLength": 1 34 | } 35 | }, 36 | "required": [ 37 | "id", 38 | "instanceID", 39 | "error" 40 | ] 41 | } 42 | }, 43 | "required": [ 44 | "date", 45 | "app", 46 | "version", 47 | "type", 48 | "payload" 49 | ] 50 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/FAILED_WORKFLOW_STAGE.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "instanceID": { 28 | "type": "string", 29 | "minLength": 1 30 | }, 31 | "number": { 32 | "type": "integer", 33 | "minLength": 1 34 | }, 35 | "error": { 36 | "type": "string", 37 | "minLength": 1 38 | } 39 | }, 40 | "required": [ 41 | "id", 42 | "instanceID", 43 | "number", 44 | "error" 45 | ] 46 | } 47 | }, 48 | "required": [ 49 | "date", 50 | "app", 51 | "version", 52 | "type", 53 | "payload" 54 | ] 55 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/STARTED_WORKFLOW.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "instanceID": { 28 | "type": "string", 29 | "minLength": 1 30 | } 31 | }, 32 | "required": [ 33 | "id", 34 | "instanceID" 35 | ] 36 | } 37 | }, 38 | "required": [ 39 | "date", 40 | "app", 41 | "version", 42 | "type", 43 | "payload" 44 | ] 45 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/STARTED_WORKFLOW_STAGE.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "instanceID": { 28 | "type": "string", 29 | "minLength": 1 30 | }, 31 | "number": { 32 | "type": "integer", 33 | "minLength": 1 34 | } 35 | }, 36 | "required": [ 37 | "id", 38 | "instanceID", 39 | "number" 40 | ] 41 | } 42 | }, 43 | "required": [ 44 | "date", 45 | "app", 46 | "version", 47 | "type", 48 | "payload" 49 | ] 50 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/SUCCEEDED_TRIGGER.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "triggerID": { 28 | "type": "string", 29 | "minLength": 1 30 | } 31 | }, 32 | "required": [ 33 | "id", 34 | "triggerID", 35 | "workflowInstanceID" 36 | ] 37 | } 38 | }, 39 | "required": [ 40 | "date", 41 | "app", 42 | "version", 43 | "type", 44 | "payload" 45 | ] 46 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/SUCCEEDED_WORKFLOW.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "instanceID": { 28 | "type": "string", 29 | "minLength": 1 30 | } 31 | }, 32 | "required": [ 33 | "id", 34 | "instanceID" 35 | ] 36 | } 37 | }, 38 | "required": [ 39 | "date", 40 | "app", 41 | "version", 42 | "type", 43 | "payload" 44 | ] 45 | } -------------------------------------------------------------------------------- /libs/events/generated/orchestration/v2.0.0/SUCCEEDED_WORKFLOW_STAGE.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "id": { 24 | "type": "string", 25 | "minLength": 1 26 | }, 27 | "instanceID": { 28 | "type": "string", 29 | "minLength": 1 30 | }, 31 | "number": { 32 | "type": "integer", 33 | "minLength": 1 34 | } 35 | }, 36 | "required": [ 37 | "id", 38 | "instanceID", 39 | "number" 40 | ] 41 | } 42 | }, 43 | "required": [ 44 | "date", 45 | "app", 46 | "version", 47 | "type", 48 | "payload" 49 | ] 50 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v0.0.0/CONNECTOR_RESET.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "properties": { 23 | "createdAt": { 24 | "type": "string" 25 | }, 26 | "connector": { 27 | "type": "string" 28 | } 29 | }, 30 | "required": [ 31 | "createdAt", 32 | "connector" 33 | ] 34 | } 35 | }, 36 | "required": [ 37 | "date", 38 | "app", 39 | "version", 40 | "type", 41 | "payload" 42 | ] 43 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v0.0.0/SAVED_ACCOUNT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "reference", 25 | "createdAt", 26 | "provider", 27 | "defaultAsset", 28 | "accountName", 29 | "type" 30 | ], 31 | "properties": { 32 | "id": { 33 | "type": "string" 34 | }, 35 | "reference": { 36 | "type": "string" 37 | }, 38 | "createdAt": { 39 | "type": "string", 40 | "format": "date-time" 41 | }, 42 | "provider": { 43 | "type": "string" 44 | }, 45 | "defaultAsset": { 46 | "type": "string" 47 | }, 48 | "accountName": { 49 | "type": "string" 50 | }, 51 | "type": { 52 | "type": "string", 53 | "enum": [ 54 | "UNKNOWN", 55 | "INTERNAL", 56 | "EXTERNAL" 57 | ] 58 | } 59 | } 60 | } 61 | }, 62 | "required": [ 63 | "date", 64 | "app", 65 | "version", 66 | "type", 67 | "payload" 68 | ] 69 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v0.0.0/SAVED_BALANCE.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "accountID", 24 | "createdAt", 25 | "asset", 26 | "balance" 27 | ], 28 | "properties": { 29 | "accountID": { 30 | "type": "string" 31 | }, 32 | "createdAt": { 33 | "type": "string", 34 | "format": "date-time" 35 | }, 36 | "asset": { 37 | "type": "string" 38 | }, 39 | "balance": { 40 | "type": "number", 41 | "format": "bigint" 42 | } 43 | } 44 | } 45 | }, 46 | "required": [ 47 | "date", 48 | "app", 49 | "version", 50 | "type", 51 | "payload" 52 | ] 53 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v0.0.0/SAVED_PAYMENT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "reference", 25 | "createdAt", 26 | "provider", 27 | "type", 28 | "status", 29 | "scheme", 30 | "asset", 31 | "amount", 32 | "initialAmount" 33 | ], 34 | "properties": { 35 | "id": { 36 | "type": "string" 37 | }, 38 | "reference": { 39 | "type": "string" 40 | }, 41 | "createdAt": { 42 | "type": "string", 43 | "format": "date-time" 44 | }, 45 | "provider": { 46 | "type": "string" 47 | }, 48 | "type": { 49 | "type": "string", 50 | "enum": [ 51 | "PAY-IN", 52 | "PAYOUT", 53 | "TRANSFER", 54 | "OTHER" 55 | ] 56 | }, 57 | "status": { 58 | "type": "string" 59 | }, 60 | "scheme": { 61 | "type": "string", 62 | "enum": [ 63 | "unknown", 64 | "other", 65 | "visa", 66 | "mastercard", 67 | "amex", 68 | "diners", 69 | "discover", 70 | "jcb", 71 | "unionpay", 72 | "alipay", 73 | "cup", 74 | "sepa debit", 75 | "sepa credit", 76 | "sepa", 77 | "apple pay", 78 | "google pay", 79 | "doku", 80 | "dragonpay", 81 | "maestro", 82 | "molpay", 83 | "a2a", 84 | "ach debit", 85 | "ach", 86 | "rtp" 87 | ] 88 | }, 89 | "asset": { 90 | "type": "string" 91 | }, 92 | "amount": { 93 | "type": "number", 94 | "format": "bigint" 95 | }, 96 | "initialAmount": { 97 | "type": "number", 98 | "format": "bigint" 99 | }, 100 | "sourceAccountId": { 101 | "type": "string" 102 | }, 103 | "destinationAccountId": { 104 | "type": "string" 105 | }, 106 | "links": { 107 | "type": [ 108 | "array", 109 | "null" 110 | ], 111 | "items": { 112 | "type": "object", 113 | "properties": { 114 | "name": { 115 | "type": "string" 116 | }, 117 | "uri": { 118 | "type": "string" 119 | } 120 | } 121 | } 122 | }, 123 | "rawData": { 124 | "type": "object" 125 | }, 126 | "metadata": { 127 | "type": "object", 128 | "additionalProperties": { 129 | "type": "string" 130 | } 131 | } 132 | } 133 | } 134 | }, 135 | "required": [ 136 | "date", 137 | "app", 138 | "version", 139 | "type", 140 | "payload" 141 | ] 142 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/CONNECTOR_RESET.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "createdAt", 24 | "connectorID" 25 | ], 26 | "properties": { 27 | "createdAt": { 28 | "type": "string" 29 | }, 30 | "connectorID": { 31 | "type": "string" 32 | } 33 | } 34 | } 35 | }, 36 | "required": [ 37 | "date", 38 | "app", 39 | "version", 40 | "type", 41 | "payload" 42 | ] 43 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/DELETED_POOL.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "createdAt" 25 | ], 26 | "properties": { 27 | "id": { 28 | "type": "string" 29 | }, 30 | "createdAt": { 31 | "type": "string", 32 | "format": "date-time" 33 | } 34 | } 35 | } 36 | }, 37 | "required": [ 38 | "date", 39 | "app", 40 | "version", 41 | "type", 42 | "payload" 43 | ] 44 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/DELETED_TRANSFER_INITIATION.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "createdAt" 25 | ], 26 | "properties": { 27 | "id": { 28 | "type": "string" 29 | }, 30 | "createdAt": { 31 | "type": "string", 32 | "format": "date-time" 33 | } 34 | } 35 | } 36 | }, 37 | "required": [ 38 | "date", 39 | "app", 40 | "version", 41 | "type", 42 | "payload" 43 | ] 44 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/SAVED_ACCOUNT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "reference", 25 | "createdAt", 26 | "connectorId", 27 | "provider", 28 | "defaultAsset", 29 | "accountName", 30 | "type" 31 | ], 32 | "properties": { 33 | "id": { 34 | "type": "string" 35 | }, 36 | "reference": { 37 | "type": "string" 38 | }, 39 | "createdAt": { 40 | "type": "string", 41 | "format": "date-time" 42 | }, 43 | "connectorId": { 44 | "type": "string" 45 | }, 46 | "provider": { 47 | "type": "string" 48 | }, 49 | "defaultAsset": { 50 | "type": "string" 51 | }, 52 | "accountName": { 53 | "type": "string" 54 | }, 55 | "type": { 56 | "type": "string", 57 | "enum": [ 58 | "UNKNOWN", 59 | "INTERNAL", 60 | "EXTERNAL" 61 | ] 62 | } 63 | } 64 | } 65 | }, 66 | "required": [ 67 | "date", 68 | "app", 69 | "version", 70 | "type", 71 | "payload" 72 | ] 73 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/SAVED_BALANCE.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "accountID", 24 | "connectorId", 25 | "createdAt", 26 | "asset", 27 | "balance" 28 | ], 29 | "properties": { 30 | "accountID": { 31 | "type": "string" 32 | }, 33 | "connectorId": { 34 | "type": "string" 35 | }, 36 | "createdAt": { 37 | "type": "string", 38 | "format": "date-time" 39 | }, 40 | "asset": { 41 | "type": "string" 42 | }, 43 | "balance": { 44 | "type": "number", 45 | "format": "bigint" 46 | } 47 | } 48 | } 49 | }, 50 | "required": [ 51 | "date", 52 | "app", 53 | "version", 54 | "type", 55 | "payload" 56 | ] 57 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/SAVED_BANK_ACCOUNT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "createdAt", 25 | "name", 26 | "country" 27 | ], 28 | "properties": { 29 | "id": { 30 | "type": "string" 31 | }, 32 | "createdAt": { 33 | "type": "string", 34 | "format": "date-time" 35 | }, 36 | "name": { 37 | "type": "string" 38 | }, 39 | "country": { 40 | "type": "string" 41 | }, 42 | "accountNumber": { 43 | "type": "string" 44 | }, 45 | "iban": { 46 | "type": "string" 47 | }, 48 | "swiftBicCode": { 49 | "type": "string" 50 | }, 51 | "adjustments": { 52 | "type": [ 53 | "array", 54 | "null" 55 | ], 56 | "items": { 57 | "type": "object", 58 | "required": [ 59 | "id", 60 | "createdAt", 61 | "accountID", 62 | "connectorID", 63 | "provider" 64 | ], 65 | "properties": { 66 | "id": { 67 | "type": "string" 68 | }, 69 | "createdAt": { 70 | "type": "string", 71 | "format": "date-time" 72 | }, 73 | "accountID": { 74 | "type": "string" 75 | }, 76 | "connectorID": { 77 | "type": "string" 78 | }, 79 | "provider": { 80 | "type": "string" 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | }, 88 | "required": [ 89 | "date", 90 | "app", 91 | "version", 92 | "type", 93 | "payload" 94 | ] 95 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/SAVED_PAYMENT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "reference", 25 | "createdAt", 26 | "connectorId", 27 | "provider", 28 | "type", 29 | "status", 30 | "scheme", 31 | "asset", 32 | "amount", 33 | "initialAmount" 34 | ], 35 | "properties": { 36 | "id": { 37 | "type": "string" 38 | }, 39 | "reference": { 40 | "type": "string" 41 | }, 42 | "createdAt": { 43 | "type": "string", 44 | "format": "date-time" 45 | }, 46 | "connectorId": { 47 | "type": "string" 48 | }, 49 | "provider": { 50 | "type": "string" 51 | }, 52 | "type": { 53 | "type": "string", 54 | "enum": [ 55 | "PAY-IN", 56 | "PAYOUT", 57 | "TRANSFER", 58 | "OTHER" 59 | ] 60 | }, 61 | "status": { 62 | "type": "string" 63 | }, 64 | "scheme": { 65 | "type": "string", 66 | "enum": [ 67 | "unknown", 68 | "other", 69 | "visa", 70 | "mastercard", 71 | "amex", 72 | "diners", 73 | "discover", 74 | "jcb", 75 | "unionpay", 76 | "alipay", 77 | "cup", 78 | "sepa debit", 79 | "sepa credit", 80 | "sepa", 81 | "apple pay", 82 | "google pay", 83 | "doku", 84 | "dragonpay", 85 | "maestro", 86 | "molpay", 87 | "a2a", 88 | "ach debit", 89 | "ach", 90 | "rtp" 91 | ] 92 | }, 93 | "asset": { 94 | "type": "string" 95 | }, 96 | "amount": { 97 | "type": "number", 98 | "format": "bigint" 99 | }, 100 | "initialAmount": { 101 | "type": "number", 102 | "format": "bigint" 103 | }, 104 | "sourceAccountId": { 105 | "type": "string" 106 | }, 107 | "destinationAccountId": { 108 | "type": "string" 109 | }, 110 | "links": { 111 | "type": [ 112 | "array", 113 | "null" 114 | ], 115 | "items": { 116 | "type": "object", 117 | "properties": { 118 | "name": { 119 | "type": "string" 120 | }, 121 | "uri": { 122 | "type": "string" 123 | } 124 | } 125 | } 126 | }, 127 | "rawData": { 128 | "type": "object" 129 | }, 130 | "metadata": { 131 | "type": "object", 132 | "additionalProperties": { 133 | "type": "string" 134 | } 135 | } 136 | } 137 | } 138 | }, 139 | "required": [ 140 | "date", 141 | "app", 142 | "version", 143 | "type", 144 | "payload" 145 | ] 146 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v2.0.0/SAVED_POOL.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "name", 25 | "createdAt", 26 | "accountIDs" 27 | ], 28 | "properties": { 29 | "id": { 30 | "type": "string" 31 | }, 32 | "name": { 33 | "type": "string" 34 | }, 35 | "createdAt": { 36 | "type": "string", 37 | "format": "date-time" 38 | }, 39 | "accountIDs": { 40 | "type": "array", 41 | "items": { 42 | "type": "string" 43 | } 44 | } 45 | } 46 | } 47 | }, 48 | "required": [ 49 | "date", 50 | "app", 51 | "version", 52 | "type", 53 | "payload" 54 | ] 55 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/CONNECTOR_RESET.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "createdAt", 24 | "connectorID" 25 | ], 26 | "properties": { 27 | "createdAt": { 28 | "type": "string" 29 | }, 30 | "connectorID": { 31 | "type": "string" 32 | } 33 | } 34 | } 35 | }, 36 | "required": [ 37 | "date", 38 | "app", 39 | "version", 40 | "type", 41 | "payload" 42 | ] 43 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/DELETED_POOL.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "createdAt" 25 | ], 26 | "properties": { 27 | "id": { 28 | "type": "string" 29 | }, 30 | "createdAt": { 31 | "type": "string", 32 | "format": "date-time" 33 | } 34 | } 35 | } 36 | }, 37 | "required": [ 38 | "date", 39 | "app", 40 | "version", 41 | "type", 42 | "payload" 43 | ] 44 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_ACCOUNT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "provider", 25 | "connectorID", 26 | "createdAt", 27 | "reference", 28 | "type" 29 | ], 30 | "properties": { 31 | "id": { 32 | "type": "string" 33 | }, 34 | "provider": { 35 | "type": "string" 36 | }, 37 | "connectorID": { 38 | "type": "string" 39 | }, 40 | "createdAt": { 41 | "type": "string", 42 | "format": "date-time" 43 | }, 44 | "reference": { 45 | "type": "string" 46 | }, 47 | "type": { 48 | "type": "string" 49 | }, 50 | "rawData": { 51 | "type": "object" 52 | }, 53 | "defaultAsset": { 54 | "type": "string" 55 | }, 56 | "name": { 57 | "type": "string" 58 | }, 59 | "metadata": { 60 | "type": "object", 61 | "additionalProperties": { 62 | "type": "string" 63 | } 64 | } 65 | } 66 | } 67 | }, 68 | "required": [ 69 | "date", 70 | "app", 71 | "version", 72 | "type", 73 | "payload" 74 | ] 75 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_BALANCE.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "accountID", 24 | "connectorID", 25 | "provider", 26 | "createdAt", 27 | "lastUpdatedAt", 28 | "asset", 29 | "balance" 30 | ], 31 | "properties": { 32 | "accountID": { 33 | "type": "string" 34 | }, 35 | "connectorID": { 36 | "type": "string" 37 | }, 38 | "provider": { 39 | "type": "string" 40 | }, 41 | "createdAt": { 42 | "type": "string", 43 | "format": "date-time" 44 | }, 45 | "lastUpdatedAt": { 46 | "type": "string", 47 | "format": "date-time" 48 | }, 49 | "asset": { 50 | "type": "string" 51 | }, 52 | "balance": { 53 | "type": "number", 54 | "format": "bigint" 55 | } 56 | } 57 | } 58 | }, 59 | "required": [ 60 | "date", 61 | "app", 62 | "version", 63 | "type", 64 | "payload" 65 | ] 66 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_BANK_ACCOUNT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "createdAt", 25 | "name" 26 | ], 27 | "properties": { 28 | "id": { 29 | "type": "string" 30 | }, 31 | "createdAt": { 32 | "type": "string", 33 | "format": "date-time" 34 | }, 35 | "name": { 36 | "type": "string" 37 | }, 38 | "country": { 39 | "type": "string" 40 | }, 41 | "accountNumber": { 42 | "type": "string" 43 | }, 44 | "iban": { 45 | "type": "string" 46 | }, 47 | "swiftBicCode": { 48 | "type": "string" 49 | }, 50 | "metadata": { 51 | "type": "object", 52 | "additionalProperties": { 53 | "type": "string" 54 | } 55 | }, 56 | "relatedAccounts": { 57 | "type": [ 58 | "array", 59 | "null" 60 | ], 61 | "items": { 62 | "type": "object", 63 | "required": [ 64 | "createdAt", 65 | "accountID", 66 | "connectorID", 67 | "provider" 68 | ], 69 | "properties": { 70 | "createdAt": { 71 | "type": "string", 72 | "format": "date-time" 73 | }, 74 | "accountID": { 75 | "type": "string" 76 | }, 77 | "connectorID": { 78 | "type": "string" 79 | }, 80 | "provider": { 81 | "type": "string" 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | }, 89 | "required": [ 90 | "date", 91 | "app", 92 | "version", 93 | "type", 94 | "payload" 95 | ] 96 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_PAYMENT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "connectorID", 25 | "provider", 26 | "reference", 27 | "createdAt", 28 | "type", 29 | "status", 30 | "scheme", 31 | "asset", 32 | "amount", 33 | "initialAmount" 34 | ], 35 | "properties": { 36 | "id": { 37 | "type": "string" 38 | }, 39 | "connectorID": { 40 | "type": "string" 41 | }, 42 | "provider": { 43 | "type": "string" 44 | }, 45 | "reference": { 46 | "type": "string" 47 | }, 48 | "createdAt": { 49 | "type": "string", 50 | "format": "date-time" 51 | }, 52 | "type": { 53 | "type": "string" 54 | }, 55 | "status": { 56 | "type": "string" 57 | }, 58 | "scheme": { 59 | "type": "string" 60 | }, 61 | "asset": { 62 | "type": "string" 63 | }, 64 | "amount": { 65 | "type": "number", 66 | "format": "bigint" 67 | }, 68 | "initialAmount": { 69 | "type": "number", 70 | "format": "bigint" 71 | }, 72 | "sourceAccountID": { 73 | "type": "string" 74 | }, 75 | "destinationAccountID": { 76 | "type": "string" 77 | }, 78 | "links": { 79 | "type": [ 80 | "array", 81 | "null" 82 | ], 83 | "items": { 84 | "type": "object", 85 | "properties": { 86 | "name": { 87 | "type": "string" 88 | }, 89 | "uri": { 90 | "type": "string" 91 | } 92 | } 93 | } 94 | }, 95 | "rawData": { 96 | "type": "object" 97 | }, 98 | "metadata": { 99 | "type": "object", 100 | "additionalProperties": { 101 | "type": "string" 102 | } 103 | } 104 | } 105 | } 106 | }, 107 | "required": [ 108 | "date", 109 | "app", 110 | "version", 111 | "type", 112 | "payload" 113 | ] 114 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_PAYMENT_INITIATION.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "connectorID", 25 | "provider", 26 | "reference", 27 | "createdAt", 28 | "scheduledAt", 29 | "description", 30 | "type", 31 | "amount", 32 | "asset" 33 | ], 34 | "properties": { 35 | "id": { 36 | "type": "string" 37 | }, 38 | "connectorID": { 39 | "type": "string" 40 | }, 41 | "provider": { 42 | "type": "string" 43 | }, 44 | "reference": { 45 | "type": "string" 46 | }, 47 | "createdAt": { 48 | "type": "string", 49 | "format": "date-time" 50 | }, 51 | "scheduledAt": { 52 | "type": "string", 53 | "format": "date-time" 54 | }, 55 | "description": { 56 | "type": "string" 57 | }, 58 | "type": { 59 | "type": "string" 60 | }, 61 | "amount": { 62 | "type": "number", 63 | "format": "bigint" 64 | }, 65 | "asset": { 66 | "type": "string" 67 | }, 68 | "sourceAccountID": { 69 | "type": "string" 70 | }, 71 | "destinationAccountID": { 72 | "type": "string" 73 | }, 74 | "metadata": { 75 | "type": "object", 76 | "additionalProperties": { 77 | "type": "string" 78 | } 79 | } 80 | } 81 | } 82 | }, 83 | "required": [ 84 | "date", 85 | "app", 86 | "version", 87 | "type", 88 | "payload" 89 | ] 90 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_PAYMENT_INITIATION_ADJUSTMENT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "paymentInitiationID", 25 | "status" 26 | ], 27 | "properties": { 28 | "id": { 29 | "type": "string" 30 | }, 31 | "paymentInitiationID": { 32 | "type": "string" 33 | }, 34 | "status": { 35 | "type": "string" 36 | }, 37 | "amount": { 38 | "type": "number", 39 | "format": "bigint" 40 | }, 41 | "asset": { 42 | "type": "string" 43 | }, 44 | "error": { 45 | "type": "object" 46 | }, 47 | "metadata": { 48 | "type": "object", 49 | "additionalProperties": { 50 | "type": "string" 51 | } 52 | } 53 | } 54 | } 55 | }, 56 | "required": [ 57 | "date", 58 | "app", 59 | "version", 60 | "type", 61 | "payload" 62 | ] 63 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_PAYMENT_INITIATION_RELATED_PAYMENT.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "paymentInitiationID", 24 | "paymentID" 25 | ], 26 | "properties": { 27 | "paymentInitiationID": { 28 | "type": "string" 29 | }, 30 | "paymentID": { 31 | "type": "string" 32 | } 33 | } 34 | } 35 | }, 36 | "required": [ 37 | "date", 38 | "app", 39 | "version", 40 | "type", 41 | "payload" 42 | ] 43 | } -------------------------------------------------------------------------------- /libs/events/generated/payments/v3.0.0/SAVED_POOL.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "app": { 5 | "type": "string" 6 | }, 7 | "version": { 8 | "type": "string" 9 | }, 10 | "date": { 11 | "type": "string", 12 | "format": "date-time" 13 | }, 14 | "type": { 15 | "type": "string" 16 | }, 17 | "ledger": { 18 | "type": "string" 19 | }, 20 | "payload": { 21 | "type": "object", 22 | "required": [ 23 | "id", 24 | "name", 25 | "createdAt", 26 | "accountIDs" 27 | ], 28 | "properties": { 29 | "id": { 30 | "type": "string" 31 | }, 32 | "name": { 33 | "type": "string" 34 | }, 35 | "createdAt": { 36 | "type": "string", 37 | "format": "date-time" 38 | }, 39 | "accountIDs": { 40 | "type": "array", 41 | "items": { 42 | "type": "string" 43 | } 44 | } 45 | } 46 | } 47 | }, 48 | "required": [ 49 | "date", 50 | "app", 51 | "version", 52 | "type", 53 | "payload" 54 | ] 55 | } -------------------------------------------------------------------------------- /libs/events/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/formancehq/stacks/libs/events 2 | 3 | go 1.21.6 4 | 5 | require ( 6 | github.com/pkg/errors v0.9.1 7 | github.com/xeipuuv/gojsonschema v1.2.0 8 | golang.org/x/mod v0.15.0 9 | gopkg.in/yaml.v3 v3.0.1 10 | ) 11 | 12 | require ( 13 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect 14 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /libs/events/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 4 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 8 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 11 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 12 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 13 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 14 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 15 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 16 | golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= 17 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 21 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 22 | -------------------------------------------------------------------------------- /libs/events/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs/promises"); 2 | const yaml = require('yaml'); 3 | 4 | (async () => { 5 | 6 | const rawBase = await fs.readFile("./base.yaml", { encoding: 'utf8' }); 7 | const aggregated = {}; 8 | 9 | for(const service of await fs.readdir("services")) { 10 | aggregated[service] = {}; 11 | for(const version of await fs.readdir('services/' + service)) { 12 | aggregated[service][version] = {}; 13 | for(const event of await fs.readdir('services/' + service + '/' + version)) { 14 | const rawEventData = await fs.readFile('services/' + service + '/' + version + '/' + event, { encoding: 'utf8' }); 15 | const base = yaml.parse(rawBase); 16 | base.properties.payload = yaml.parse(rawEventData); 17 | const directory = 'generated/' + service + '/' + version + '/'; 18 | await fs.mkdir(directory, { recursive: true }); 19 | await fs.writeFile(directory + event.replace('.yaml', '.json'), JSON.stringify(base, null, 2)); 20 | 21 | aggregated[service][version][event.replace('.yaml', '')] = base; 22 | } 23 | } 24 | } 25 | 26 | console.log(aggregated); 27 | await fs.writeFile('generated/all.json', JSON.stringify(aggregated, null, 2)); 28 | })(); -------------------------------------------------------------------------------- /libs/events/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "formancehq-events", 3 | "version": "1.0.0", 4 | "description": "This repository centralizes event schemas across the Formance Stack. For each stack version, a repository named \"vX\" contains all related events.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "json-schema-static-docs": "^0.24.1", 13 | "yaml": "^2.3.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /libs/events/services/ledger/v1.0.0/COMMITTED_TRANSACTIONS.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | ledger: 4 | type: string 5 | transactions: 6 | type: array 7 | items: 8 | type: object 9 | properties: 10 | postings: 11 | type: array 12 | items: 13 | type: object 14 | properties: 15 | source: 16 | type: string 17 | destination: 18 | type: string 19 | amount: 20 | type: number 21 | asset: 22 | type: string 23 | required: 24 | - source 25 | - destination 26 | - amount 27 | - asset 28 | reference: 29 | type: string 30 | metadata: 31 | type: object 32 | properties: {} 33 | required: [] 34 | txid: 35 | type: number 36 | timestamp: 37 | type: string 38 | required: 39 | - postings 40 | - reference 41 | - metadata 42 | - txid 43 | - timestamp 44 | required: 45 | - ledger 46 | - transactions 47 | -------------------------------------------------------------------------------- /libs/events/services/ledger/v1.0.0/REVERTED_TRANSACTION.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | ledger: 4 | type: string 5 | revertedTransaction: 6 | type: object 7 | properties: 8 | postings: 9 | type: array 10 | items: 11 | type: object 12 | properties: 13 | source: 14 | type: string 15 | destination: 16 | type: string 17 | amount: 18 | type: number 19 | asset: 20 | type: string 21 | required: 22 | - source 23 | - destination 24 | - amount 25 | - asset 26 | reference: 27 | type: string 28 | metadata: 29 | type: object 30 | properties: { } 31 | required: [ ] 32 | txid: 33 | type: number 34 | timestamp: 35 | type: string 36 | required: 37 | - postings 38 | - reference 39 | - metadata 40 | - txid 41 | - timestamp 42 | revertTransaction: 43 | type: object 44 | properties: 45 | postings: 46 | type: array 47 | items: 48 | type: object 49 | properties: 50 | source: 51 | type: string 52 | destination: 53 | type: string 54 | amount: 55 | type: number 56 | asset: 57 | type: string 58 | required: 59 | - source 60 | - destination 61 | - amount 62 | - asset 63 | reference: 64 | type: string 65 | metadata: 66 | type: object 67 | properties: { } 68 | required: [ ] 69 | txid: 70 | type: number 71 | timestamp: 72 | type: string 73 | required: 74 | - postings 75 | - reference 76 | - metadata 77 | - txid 78 | - timestamp 79 | required: 80 | - ledger 81 | - revertedTransaction 82 | - revertTransaction 83 | -------------------------------------------------------------------------------- /libs/events/services/ledger/v1.0.0/SAVED_METADATA.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | ledger: 4 | type: string 5 | targetType: 6 | type: string 7 | targetId: 8 | type: string 9 | metadata: 10 | type: object 11 | additionalProperties: {} 12 | required: 13 | - ledger 14 | - targetType 15 | - targetId 16 | - metadata 17 | -------------------------------------------------------------------------------- /libs/events/services/ledger/v2.0.0/COMMITTED_TRANSACTIONS.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | ledger: 4 | type: string 5 | transactions: 6 | type: array 7 | items: 8 | type: object 9 | properties: 10 | postings: 11 | type: array 12 | items: 13 | type: object 14 | properties: 15 | source: 16 | type: string 17 | destination: 18 | type: string 19 | amount: 20 | type: number 21 | asset: 22 | type: string 23 | required: 24 | - source 25 | - destination 26 | - amount 27 | - asset 28 | reference: 29 | type: string 30 | metadata: 31 | type: object 32 | properties: {} 33 | required: [] 34 | id: 35 | type: number 36 | timestamp: 37 | type: string 38 | reverted: 39 | type: boolean 40 | required: 41 | - postings 42 | - metadata 43 | - id 44 | - timestamp 45 | - reverted 46 | required: 47 | - ledger 48 | - transactions 49 | -------------------------------------------------------------------------------- /libs/events/services/ledger/v2.0.0/DELETED_METADATA.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | ledger: 4 | type: string 5 | targetType: 6 | type: string 7 | targetId: 8 | type: string 9 | key: 10 | type: string 11 | required: 12 | - ledger 13 | - targetType 14 | - targetId 15 | - key 16 | -------------------------------------------------------------------------------- /libs/events/services/ledger/v2.0.0/REVERTED_TRANSACTION.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | ledger: 4 | type: string 5 | revertedTransaction: 6 | type: object 7 | properties: 8 | postings: 9 | type: array 10 | items: 11 | type: object 12 | properties: 13 | source: 14 | type: string 15 | destination: 16 | type: string 17 | amount: 18 | type: number 19 | asset: 20 | type: string 21 | required: 22 | - source 23 | - destination 24 | - amount 25 | - asset 26 | reference: 27 | type: string 28 | metadata: 29 | type: object 30 | properties: { } 31 | required: [ ] 32 | id: 33 | type: number 34 | timestamp: 35 | type: string 36 | reverted: 37 | type: boolean 38 | required: 39 | - postings 40 | - metadata 41 | - id 42 | - timestamp 43 | - reverted 44 | revertTransaction: 45 | type: object 46 | properties: 47 | postings: 48 | type: array 49 | items: 50 | type: object 51 | properties: 52 | source: 53 | type: string 54 | destination: 55 | type: string 56 | amount: 57 | type: number 58 | asset: 59 | type: string 60 | required: 61 | - source 62 | - destination 63 | - amount 64 | - asset 65 | reference: 66 | type: string 67 | metadata: 68 | type: object 69 | properties: { } 70 | required: [ ] 71 | id: 72 | type: number 73 | timestamp: 74 | type: string 75 | required: 76 | - postings 77 | - metadata 78 | - id 79 | - timestamp 80 | required: 81 | - ledger 82 | - revertedTransaction 83 | - revertTransaction 84 | -------------------------------------------------------------------------------- /libs/events/services/ledger/v2.0.0/SAVED_METADATA.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | ledger: 4 | type: string 5 | targetType: 6 | type: string 7 | targetId: 8 | type: string 9 | metadata: 10 | type: object 11 | additionalProperties: {} 12 | required: 13 | - ledger 14 | - targetType 15 | - targetId 16 | - metadata 17 | -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/FAILED_TRIGGER.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | triggerID: 7 | type: string 8 | minLength: 1 9 | error: 10 | type: string 11 | minLength: 1 12 | required: 13 | - id 14 | - triggerID 15 | - error -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/FAILED_WORKFLOW.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | instanceID: 7 | type: string 8 | minLength: 1 9 | error: 10 | type: string 11 | minLength: 1 12 | required: 13 | - id 14 | - instanceID 15 | - error -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/FAILED_WORKFLOW_STAGE.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | instanceID: 7 | type: string 8 | minLength: 1 9 | number: 10 | type: integer 11 | minLength: 1 12 | error: 13 | type: string 14 | minLength: 1 15 | required: 16 | - id 17 | - instanceID 18 | - number 19 | - error -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/STARTED_WORKFLOW.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | instanceID: 7 | type: string 8 | minLength: 1 9 | required: 10 | - id 11 | - instanceID -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/STARTED_WORKFLOW_STAGE.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | instanceID: 7 | type: string 8 | minLength: 1 9 | number: 10 | type: integer 11 | minLength: 1 12 | required: 13 | - id 14 | - instanceID 15 | - number -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/SUCCEEDED_TRIGGER.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | triggerID: 7 | type: string 8 | minLength: 1 9 | required: 10 | - id 11 | - triggerID 12 | - workflowInstanceID -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/SUCCEEDED_WORKFLOW.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | instanceID: 7 | type: string 8 | minLength: 1 9 | required: 10 | - id 11 | - instanceID -------------------------------------------------------------------------------- /libs/events/services/orchestration/v2.0.0/SUCCEEDED_WORKFLOW_STAGE.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | id: 4 | type: string 5 | minLength: 1 6 | instanceID: 7 | type: string 8 | minLength: 1 9 | number: 10 | type: integer 11 | minLength: 1 12 | required: 13 | - id 14 | - instanceID 15 | - number -------------------------------------------------------------------------------- /libs/events/services/payments/v0.0.0/CONNECTOR_RESET.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | properties: 3 | createdAt: 4 | type: string 5 | connector: 6 | type: string 7 | required: 8 | - createdAt 9 | - connector 10 | -------------------------------------------------------------------------------- /libs/events/services/payments/v0.0.0/SAVED_ACCOUNT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - reference 5 | - createdAt 6 | - provider 7 | - defaultAsset 8 | - accountName 9 | - type 10 | properties: 11 | id: 12 | type: string 13 | reference: 14 | type: string 15 | createdAt: 16 | type: string 17 | format: date-time 18 | provider: 19 | type: string 20 | defaultAsset: 21 | type: string 22 | accountName: 23 | type: string 24 | type: 25 | type: string 26 | enum: 27 | - UNKNOWN 28 | - INTERNAL 29 | - EXTERNAL -------------------------------------------------------------------------------- /libs/events/services/payments/v0.0.0/SAVED_BALANCE.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - accountID 4 | - createdAt 5 | - asset 6 | - balance 7 | properties: 8 | accountID: 9 | type: string 10 | createdAt: 11 | type: string 12 | format: date-time 13 | asset: 14 | type: string 15 | balance: 16 | type: number 17 | format: bigint -------------------------------------------------------------------------------- /libs/events/services/payments/v0.0.0/SAVED_PAYMENT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - reference 5 | - createdAt 6 | - provider 7 | - type 8 | - status 9 | - scheme 10 | - asset 11 | - amount 12 | - initialAmount 13 | properties: 14 | id: 15 | type: string 16 | reference: 17 | type: string 18 | createdAt: 19 | type: string 20 | format: date-time 21 | provider: 22 | type: string 23 | type: 24 | type: string 25 | enum: 26 | - PAY-IN 27 | - PAYOUT 28 | - TRANSFER 29 | - OTHER 30 | status: 31 | type: string 32 | scheme: 33 | type: string 34 | enum: 35 | - unknown 36 | - other 37 | - visa 38 | - mastercard 39 | - amex 40 | - diners 41 | - discover 42 | - jcb 43 | - unionpay 44 | - alipay 45 | - cup 46 | - sepa debit 47 | - sepa credit 48 | - sepa 49 | - apple pay 50 | - google pay 51 | - doku 52 | - dragonpay 53 | - maestro 54 | - molpay 55 | - a2a 56 | - ach debit 57 | - ach 58 | - rtp 59 | asset: 60 | type: string 61 | amount: 62 | type: number 63 | format: bigint 64 | initialAmount: 65 | type: number 66 | format: bigint 67 | sourceAccountId: 68 | type: string 69 | destinationAccountId: 70 | type: string 71 | links: 72 | type: ["array", "null"] 73 | items: 74 | type: object 75 | properties: 76 | name: 77 | type: string 78 | uri: 79 | type: string 80 | rawData: 81 | type: object 82 | metadata: 83 | type: object 84 | additionalProperties: 85 | type: string 86 | -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/CONNECTOR_RESET.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - createdAt 4 | - connectorID 5 | properties: 6 | createdAt: 7 | type: string 8 | connectorID: 9 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/DELETED_POOL.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - createdAt 5 | properties: 6 | id: 7 | type: string 8 | createdAt: 9 | type: string 10 | format: date-time -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/DELETED_TRANSFER_INITIATION.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - createdAt 5 | properties: 6 | id: 7 | type: string 8 | createdAt: 9 | type: string 10 | format: date-time -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/SAVED_ACCOUNT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - reference 5 | - createdAt 6 | - connectorId 7 | - provider 8 | - defaultAsset 9 | - accountName 10 | - type 11 | properties: 12 | id: 13 | type: string 14 | reference: 15 | type: string 16 | createdAt: 17 | type: string 18 | format: date-time 19 | connectorId: 20 | type: string 21 | provider: 22 | type: string 23 | defaultAsset: 24 | type: string 25 | accountName: 26 | type: string 27 | type: 28 | type: string 29 | enum: 30 | - UNKNOWN 31 | - INTERNAL 32 | - EXTERNAL -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/SAVED_BALANCE.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - accountID 4 | - connectorId 5 | - createdAt 6 | - asset 7 | - balance 8 | properties: 9 | accountID: 10 | type: string 11 | connectorId: 12 | type: string 13 | createdAt: 14 | type: string 15 | format: date-time 16 | asset: 17 | type: string 18 | balance: 19 | type: number 20 | format: bigint -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/SAVED_BANK_ACCOUNT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - createdAt 5 | - name 6 | - country 7 | properties: 8 | id: 9 | type: string 10 | createdAt: 11 | type: string 12 | format: date-time 13 | name: 14 | type: string 15 | country: 16 | type: string 17 | accountNumber: 18 | type: string 19 | iban: 20 | type: string 21 | swiftBicCode: 22 | type: string 23 | adjustments: 24 | type: ["array", "null"] 25 | items: 26 | type: object 27 | required: 28 | - id 29 | - createdAt 30 | - accountID 31 | - connectorID 32 | - provider 33 | properties: 34 | id: 35 | type: string 36 | createdAt: 37 | type: string 38 | format: date-time 39 | accountID: 40 | type: string 41 | connectorID: 42 | type: string 43 | provider: 44 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/SAVED_PAYMENT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - reference 5 | - createdAt 6 | - connectorId 7 | - provider 8 | - type 9 | - status 10 | - scheme 11 | - asset 12 | - amount 13 | - initialAmount 14 | properties: 15 | id: 16 | type: string 17 | reference: 18 | type: string 19 | createdAt: 20 | type: string 21 | format: date-time 22 | connectorId: 23 | type: string 24 | provider: 25 | type: string 26 | type: 27 | type: string 28 | enum: 29 | - PAY-IN 30 | - PAYOUT 31 | - TRANSFER 32 | - OTHER 33 | status: 34 | type: string 35 | scheme: 36 | type: string 37 | enum: 38 | - unknown 39 | - other 40 | - visa 41 | - mastercard 42 | - amex 43 | - diners 44 | - discover 45 | - jcb 46 | - unionpay 47 | - alipay 48 | - cup 49 | - sepa debit 50 | - sepa credit 51 | - sepa 52 | - apple pay 53 | - google pay 54 | - doku 55 | - dragonpay 56 | - maestro 57 | - molpay 58 | - a2a 59 | - ach debit 60 | - ach 61 | - rtp 62 | asset: 63 | type: string 64 | amount: 65 | type: number 66 | format: bigint 67 | initialAmount: 68 | type: number 69 | format: bigint 70 | sourceAccountId: 71 | type: string 72 | destinationAccountId: 73 | type: string 74 | links: 75 | type: ["array", "null"] 76 | items: 77 | type: object 78 | properties: 79 | name: 80 | type: string 81 | uri: 82 | type: string 83 | rawData: 84 | type: object 85 | metadata: 86 | type: object 87 | additionalProperties: 88 | type: string 89 | -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/SAVED_POOL.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - name 5 | - createdAt 6 | - accountIDs 7 | properties: 8 | id: 9 | type: string 10 | name: 11 | type: string 12 | createdAt: 13 | type: string 14 | format: date-time 15 | accountIDs: 16 | type: array 17 | items: 18 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v2.0.0/SAVED_TRANSFER_INITIATION.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - createdAt 5 | - scheduledAt 6 | - connectorId 7 | - provider 8 | - description 9 | - type 10 | - destinationAccountId 11 | - amount 12 | - asset 13 | - attempts 14 | - status 15 | - error 16 | properties: 17 | id: 18 | type: string 19 | createdAt: 20 | type: string 21 | format: date-time 22 | scheduledAt: 23 | type: string 24 | format: date-time 25 | connectorId: 26 | type: string 27 | provider: 28 | type: string 29 | description: 30 | type: string 31 | type: 32 | type: string 33 | enum: 34 | - TRANSFER 35 | - PAYOUT 36 | sourceAccountId: 37 | type: string 38 | destinationAccountId: 39 | type: string 40 | amount: 41 | type: number 42 | format: bigint 43 | asset: 44 | type: string 45 | attempts: 46 | type: number 47 | status: 48 | type: string 49 | enum: 50 | - WAITING_FOR_VALIDATION 51 | - PROCESSING 52 | - PROCESSED 53 | - FAILED 54 | - REJECTED 55 | - VALIDATED 56 | - ASK_RETRIED 57 | - ASK_REVERSED 58 | - REVERSE_PROCESSING 59 | - REVERSE_FAILED 60 | - PARTIALLY_REVERSED 61 | - REVERSED 62 | error: 63 | type: string 64 | relatedPayments: 65 | type: ["array", "null"] 66 | items: 67 | type: object 68 | required: 69 | - transferInitiationId 70 | - paymentId 71 | - createdAt 72 | - status 73 | - error 74 | properties: 75 | transferInitiationId: 76 | type: string 77 | paymentId: 78 | type: string 79 | createdAt: 80 | type: string 81 | format: date-time 82 | status: 83 | type: string 84 | error: 85 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/CONNECTOR_RESET.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - createdAt 4 | - connectorID 5 | properties: 6 | createdAt: 7 | type: string 8 | connectorID: 9 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/DELETED_POOL.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - createdAt 5 | properties: 6 | id: 7 | type: string 8 | createdAt: 9 | type: string 10 | format: date-time -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_ACCOUNT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - provider 5 | - connectorID 6 | - createdAt 7 | - reference 8 | - type 9 | properties: 10 | id: 11 | type: string 12 | provider: 13 | type: string 14 | connectorID: 15 | type: string 16 | createdAt: 17 | type: string 18 | format: date-time 19 | reference: 20 | type: string 21 | type: 22 | type: string 23 | rawData: 24 | type: object 25 | defaultAsset: 26 | type: string 27 | name: 28 | type: string 29 | metadata: 30 | type: object 31 | additionalProperties: 32 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_BALANCE.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - accountID 4 | - connectorID 5 | - provider 6 | - createdAt 7 | - lastUpdatedAt 8 | - asset 9 | - balance 10 | properties: 11 | accountID: 12 | type: string 13 | connectorID: 14 | type: string 15 | provider: 16 | type: string 17 | createdAt: 18 | type: string 19 | format: date-time 20 | lastUpdatedAt: 21 | type: string 22 | format: date-time 23 | asset: 24 | type: string 25 | balance: 26 | type: number 27 | format: bigint -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_BANK_ACCOUNT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - createdAt 5 | - name 6 | properties: 7 | id: 8 | type: string 9 | createdAt: 10 | type: string 11 | format: date-time 12 | name: 13 | type: string 14 | country: 15 | type: string 16 | accountNumber: 17 | type: string 18 | iban: 19 | type: string 20 | swiftBicCode: 21 | type: string 22 | metadata: 23 | type: object 24 | additionalProperties: 25 | type: string 26 | relatedAccounts: 27 | type: ["array", "null"] 28 | items: 29 | type: object 30 | required: 31 | - createdAt 32 | - accountID 33 | - connectorID 34 | - provider 35 | properties: 36 | createdAt: 37 | type: string 38 | format: date-time 39 | accountID: 40 | type: string 41 | connectorID: 42 | type: string 43 | provider: 44 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_PAYMENT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - connectorID 5 | - provider 6 | - reference 7 | - createdAt 8 | - type 9 | - status 10 | - scheme 11 | - asset 12 | - amount 13 | - initialAmount 14 | properties: 15 | id: 16 | type: string 17 | connectorID: 18 | type: string 19 | provider: 20 | type: string 21 | reference: 22 | type: string 23 | createdAt: 24 | type: string 25 | format: date-time 26 | type: 27 | type: string 28 | status: 29 | type: string 30 | scheme: 31 | type: string 32 | asset: 33 | type: string 34 | amount: 35 | type: number 36 | format: bigint 37 | initialAmount: 38 | type: number 39 | format: bigint 40 | sourceAccountID: 41 | type: string 42 | destinationAccountID: 43 | type: string 44 | links: 45 | type: ["array", "null"] 46 | items: 47 | type: object 48 | properties: 49 | name: 50 | type: string 51 | uri: 52 | type: string 53 | rawData: 54 | type: object 55 | metadata: 56 | type: object 57 | additionalProperties: 58 | type: string 59 | -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_PAYMENT_INITIATION.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - connectorID 5 | - provider 6 | - reference 7 | - createdAt 8 | - scheduledAt 9 | - description 10 | - type 11 | - amount 12 | - asset 13 | properties: 14 | id: 15 | type: string 16 | connectorID: 17 | type: string 18 | provider: 19 | type: string 20 | reference: 21 | type: string 22 | createdAt: 23 | type: string 24 | format: date-time 25 | scheduledAt: 26 | type: string 27 | format: date-time 28 | description: 29 | type: string 30 | type: 31 | type: string 32 | amount: 33 | type: number 34 | format: bigint 35 | asset: 36 | type: string 37 | sourceAccountID: 38 | type: string 39 | destinationAccountID: 40 | type: string 41 | metadata: 42 | type: object 43 | additionalProperties: 44 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_PAYMENT_INITIATION_ADJUSTMENT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - paymentInitiationID 5 | - status 6 | properties: 7 | id: 8 | type: string 9 | paymentInitiationID: 10 | type: string 11 | status: 12 | type: string 13 | amount: 14 | type: number 15 | format: bigint 16 | asset: 17 | type: string 18 | error: 19 | type: object 20 | metadata: 21 | type: object 22 | additionalProperties: 23 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_PAYMENT_INITIATION_RELATED_PAYMENT.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - paymentInitiationID 4 | - paymentID 5 | properties: 6 | paymentInitiationID: 7 | type: string 8 | paymentID: 9 | type: string -------------------------------------------------------------------------------- /libs/events/services/payments/v3.0.0/SAVED_POOL.yaml: -------------------------------------------------------------------------------- 1 | type: object 2 | required: 3 | - id 4 | - name 5 | - createdAt 6 | - accountIDs 7 | properties: 8 | id: 9 | type: string 10 | name: 11 | type: string 12 | createdAt: 13 | type: string 14 | format: date-time 15 | accountIDs: 16 | type: array 17 | items: 18 | type: string -------------------------------------------------------------------------------- /releases/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | sdks/php 3 | sdks/ruby 4 | sdks/typescript 5 | sdks/java 6 | sdks/python -------------------------------------------------------------------------------- /releases/base.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Formance Stack API 4 | description: | 5 | Open, modular foundation for unique payments flows 6 | 7 | # Introduction 8 | This API is documented in **OpenAPI format**. 9 | 10 | # Authentication 11 | Formance Stack offers one forms of authentication: 12 | - OAuth2 13 | OAuth2 - an open protocol to allow secure authorization in a simple 14 | and standard method from web, mobile and desktop applications. 15 | 16 | contact: 17 | name: Formance 18 | url: https://www.formance.com 19 | email: support@formance.com 20 | x-logo: 21 | url: https://avatars.githubusercontent.com/u/84325077?s=200&v=4 22 | altText: Formance 23 | version: "SDK_VERSION" 24 | 25 | servers: 26 | - url: http://localhost 27 | description: local server 28 | - url: https://{organization}.{environment}.formance.cloud 29 | description: A per-organization and per-environment API 30 | variables: 31 | organization: 32 | description: The organization name. Defaults to a generic organization. 33 | default: orgID-stackID 34 | environment: 35 | description: The environment name. Defaults to the production environment. 36 | default: eu.sandbox 37 | enum: 38 | - eu.sandbox 39 | - sandbox 40 | - eu-west-1 41 | - us-east-1 42 | 43 | tags: 44 | - name: ledger.v1 45 | - name: ledger.v2 46 | - name: payments.v1 47 | - name: payments.v3 48 | - name: auth.v1 49 | - name: orchestration.v1 50 | - name: orchestration.v2 51 | - name: reconciliation.v1 52 | - name: search.v1 53 | - name: webhooks.v1 54 | - name: wallets.v1 55 | 56 | components: 57 | securitySchemes: 58 | Authorization: 59 | type: oauth2 60 | flows: 61 | clientCredentials: 62 | tokenUrl: '/api/auth/oauth/token' 63 | refreshUrl: '/api/auth/oauth/token' 64 | scopes: {} 65 | NoAuthorization: 66 | type: oauth2 67 | flows: 68 | clientCredentials: 69 | tokenUrl: '/api/auth/oauth/token' 70 | refreshUrl: '/api/auth/oauth/token' 71 | scopes: {} 72 | 73 | x-tagGroups: 74 | - name: Auth 75 | tags: 76 | - auth.v1 77 | - name: Ledger 78 | tags: 79 | - ledger.v1 80 | - ledger.v2 81 | - name: Payments 82 | tags: 83 | - payments.v1 84 | - payments.v3 85 | - name: Search 86 | tags: 87 | - search.v1 88 | - name: Wallets 89 | tags: 90 | - wallets.v1 91 | - name: Webhooks 92 | tags: 93 | - webhooks.v1 94 | - name: Flows 95 | tags: 96 | - orchestration.v1 97 | - orchestration.v2 98 | - name: Reconciliation 99 | tags: 100 | - reconciliation.v1 -------------------------------------------------------------------------------- /releases/openapi-merge.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputs": [ 3 | { 4 | "inputFile": "./base.yaml" 5 | }, 6 | { 7 | "inputFile": "./../ee/auth/openapi.yaml", 8 | "pathModification":{ 9 | "prepend": "/api/auth" 10 | }, 11 | "dispute": { 12 | "prefix": "auth" 13 | } 14 | }, 15 | { 16 | "inputFile": "./../ee/gateway/openapi.yaml", 17 | "dispute": { 18 | "prefix": "gateway" 19 | } 20 | }, 21 | { 22 | "inputFile": "./../components/ledger/openapi.yaml", 23 | "pathModification":{ 24 | "prepend": "/api/ledger" 25 | }, 26 | "dispute": { 27 | "prefix": "ledger" 28 | } 29 | }, 30 | { 31 | "inputFile": "./../components/payments/openapi.yaml", 32 | "pathModification":{ 33 | "prepend": "/api/payments" 34 | }, 35 | "dispute": { 36 | "prefix": "payments" 37 | } 38 | }, 39 | { 40 | "inputFile": "./../ee/search/openapi.yaml", 41 | "pathModification":{ 42 | "prepend": "/api/search" 43 | }, 44 | "dispute": { 45 | "prefix": "search" 46 | } 47 | }, 48 | { 49 | "inputFile": "./../ee/webhooks/openapi.yaml", 50 | "pathModification":{ 51 | "prepend": "/api/webhooks" 52 | }, 53 | "operationSelection": { 54 | "excludeTags": ["Health"] 55 | }, 56 | "dispute": { 57 | "prefix": "webhooks" 58 | } 59 | }, 60 | { 61 | "inputFile": "./../ee/wallets/openapi.yaml", 62 | "pathModification":{ 63 | "prepend": "/api/wallets" 64 | }, 65 | "operationSelection": { 66 | "excludeTags": ["Health"] 67 | }, 68 | "dispute": { 69 | "prefix": "wallets" 70 | } 71 | }, 72 | { 73 | "inputFile": "./../ee/orchestration/openapi.yaml", 74 | "pathModification":{ 75 | "prepend": "/api/orchestration" 76 | }, 77 | "operationSelection": { 78 | "excludeTags": ["Health"] 79 | }, 80 | "dispute": { 81 | "prefix": "orchestration" 82 | } 83 | }, 84 | { 85 | "inputFile": "./../ee/reconciliation/openapi.yaml", 86 | "pathModification":{ 87 | "prepend": "/api/reconciliation" 88 | }, 89 | "operationSelection": { 90 | "excludeTags": ["Health"] 91 | }, 92 | "dispute": { 93 | "prefix": "reconciliation" 94 | } 95 | } 96 | ], 97 | "output": "./build/generate.json" 98 | } 99 | -------------------------------------------------------------------------------- /releases/openapi-overlay.json: -------------------------------------------------------------------------------- 1 | { 2 | "paths": { 3 | "x-speakeasy-errors": { 4 | "statusCodes": ["default"] 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /releases/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openapi", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "openapi-merge-cli": "^1.3.1" 6 | }, 7 | "private": true, 8 | "scripts": { 9 | "build": "openapi-merge-cli" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/benchmarks/.gitignore: -------------------------------------------------------------------------------- 1 | k6 2 | results 3 | .env 4 | .env 5 | -------------------------------------------------------------------------------- /tests/benchmarks/Earthfile: -------------------------------------------------------------------------------- 1 | VERSION 0.8 2 | 3 | IMPORT github.com/formancehq/earthly:tags/v0.15.0 AS core 4 | 5 | IMPORT ../.. AS stack 6 | 7 | FROM core+base-image 8 | 9 | k6: 10 | FROM core+builder-image 11 | DO --pass-args core+GO_INSTALL --package=go.k6.io/xk6/cmd/xk6@latest 12 | COPY extension extension 13 | ARG GOPROXY 14 | RUN --mount type=cache,id=gopkgcache,target=$GOPATH/pkg/mod \ 15 | --mount type=cache,id=gobuild,target=/root/.cache/go-build \ 16 | xk6 build --with extension=/extension --with github.com/grafana/xk6-output-prometheus-remote@v0.2.3 17 | SAVE ARTIFACT /k6 18 | 19 | run: 20 | RUN apk update && apk add nodejs npm 21 | COPY --pass-args +k6/k6 /bin/k6 22 | COPY --dir scripts . 23 | WORKDIR ./scripts 24 | RUN npm install 25 | RUN npm run build 26 | WORKDIR / 27 | COPY --dir tools . 28 | WITH DOCKER \ 29 | --compose tools/docker-compose.yml \ 30 | --load ghcr.io/formancehq/ledger:latest=../../components/ledger+build-image 31 | RUN TEST_ID=$(date +%s)-v2 \ 32 | K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true \ 33 | K6_PROMETHEUS_RW_SERVER_URL=http://127.0.0.1:9090/api/v1/write \ 34 | POSTGRES_DSN=postgresql://ledger:ledger@postgres:5432/ledger?sslmode=disable \ 35 | DOCKER_NETWORK=default_benchmarks \ 36 | LEDGER_VERSION=latest \ 37 | sh -c 'sleep 10s && k6 run --summary-trend-stats="avg,min,med,max,p(90),p(95),p(99)" --out xk6-prometheus-rw scripts/dist/ledger-v2.js' 38 | END -------------------------------------------------------------------------------- /tests/benchmarks/README.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | 3 | ## Deps 4 | - earthly 5 | 6 | ## Launch benchmarks 7 | 8 | Will run benchmarks against actual branch 9 | 10 | ```bash 11 | earthly +run 12 | ``` -------------------------------------------------------------------------------- /tests/benchmarks/Taskfile.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | tools: 5 | dir: ./tools 6 | cmds: 7 | - docker compose up -d 8 | - sleep 20 9 | 10 | build:scripts: 11 | dir: ./scripts 12 | cmds: 13 | - npm run build 14 | 15 | build:k6: 16 | cmds: 17 | - xk6 build --with extension=$(pwd)/extension --with github.com/grafana/xk6-output-prometheus-remote@v0.2.3 18 | 19 | run: 20 | deps: 21 | - tools 22 | - build:scripts 23 | - build:k6 24 | cmds: 25 | - PGPASSWORD=ledger psql -h 127.0.0.1 -U ledger -c "DROP DATABASE IF EXISTS ledgerv2;" 26 | - PGPASSWORD=ledger psql -h 127.0.0.1 -U ledger -c "CREATE DATABASE ledgerv2;" 27 | - TEST_ID=$(date +%s)-v2 K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true K6_PROMETHEUS_RW_SERVER_URL=http://127.0.0.1:9090/api/v1/write ./k6 run --summary-trend-stats="avg,min,med,max,p(90),p(95),p(99)" --out xk6-prometheus-rw scripts/dist/ledger-v2.js 28 | env: 29 | POSTGRES_DSN: postgresql://ledger:ledger@postgres:5432/ledgerv2?sslmode=disable 30 | DOCKER_NETWORK: tools_benchmarks 31 | 32 | cleanup: 33 | dir: ./tools 34 | cmds: 35 | - docker compose down -v 36 | -------------------------------------------------------------------------------- /tests/benchmarks/extension/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/formancehq/stack/tests/benchmarks/extension 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2 8 | github.com/ory/dockertest/v3 v3.10.0 9 | github.com/sirupsen/logrus v1.9.3 10 | go.k6.io/k6 v0.45.1-0.20230719100510-ac9c6bc85d13 11 | ) 12 | 13 | require ( 14 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect 15 | github.com/Microsoft/go-winio v0.6.1 // indirect 16 | github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect 17 | github.com/cenkalti/backoff/v4 v4.1.3 // indirect 18 | github.com/containerd/continuity v0.4.1 // indirect 19 | github.com/dlclark/regexp2 v1.9.0 // indirect 20 | github.com/docker/cli v24.0.4+incompatible // indirect 21 | github.com/docker/docker v25.0.6+incompatible // indirect 22 | github.com/docker/go-connections v0.4.0 // indirect 23 | github.com/docker/go-units v0.5.0 // indirect 24 | github.com/dop251/goja v0.0.0-20230621100801-7749907a8a20 // indirect 25 | github.com/fatih/color v1.15.0 // indirect 26 | github.com/go-sourcemap/sourcemap v2.1.4-0.20211119122758-180fcef48034+incompatible // indirect 27 | github.com/gogo/protobuf v1.3.2 // indirect 28 | github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect 29 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect 30 | github.com/imdario/mergo v0.3.16 // indirect 31 | github.com/josharian/intern v1.0.0 // indirect 32 | github.com/mailru/easyjson v0.7.7 // indirect 33 | github.com/mattn/go-colorable v0.1.13 // indirect 34 | github.com/mattn/go-isatty v0.0.19 // indirect 35 | github.com/mitchellh/mapstructure v1.5.0 // indirect 36 | github.com/moby/term v0.5.0 // indirect 37 | github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd // indirect 38 | github.com/onsi/ginkgo v1.16.5 // indirect 39 | github.com/onsi/gomega v1.27.8 // indirect 40 | github.com/opencontainers/go-digest v1.0.0 // indirect 41 | github.com/opencontainers/image-spec v1.0.2 // indirect 42 | github.com/opencontainers/runc v1.1.14 // indirect 43 | github.com/pkg/errors v0.9.1 // indirect 44 | github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e // indirect 45 | github.com/spf13/afero v1.1.2 // indirect 46 | github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect 47 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect 48 | github.com/xeipuuv/gojsonschema v1.2.0 // indirect 49 | golang.org/x/crypto v0.31.0 // indirect 50 | golang.org/x/mod v0.17.0 // indirect 51 | golang.org/x/sync v0.10.0 // indirect 52 | golang.org/x/sys v0.28.0 // indirect 53 | golang.org/x/text v0.21.0 // indirect 54 | golang.org/x/time v0.3.0 // indirect 55 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 56 | gopkg.in/guregu/null.v3 v3.3.0 // indirect 57 | gopkg.in/yaml.v2 v2.4.0 // indirect 58 | ) 59 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | results -------------------------------------------------------------------------------- /tests/benchmarks/scripts/benchs/ledger-v2.js: -------------------------------------------------------------------------------- 1 | import {ReadTransactions, WriteTransactions} from "../src/steps/transactions"; 2 | import {ReadAccounts} from "../src/steps/account"; 3 | import {ReadBalances} from "../src/steps/balances"; 4 | import {ReadAggregateBalances} from "../src/steps/aggregateBalances"; 5 | import {K6Options} from "../src/options"; 6 | import {startLedger, stopLedger} from 'k6/x/formancehq/benchmarks'; 7 | import exec from 'k6/execution'; 8 | 9 | export function setup() { 10 | return startLedger({ 11 | version: 'latest', 12 | }); 13 | } 14 | 15 | export let options = K6Options(); 16 | 17 | const ledgerName = `/tests`; 18 | 19 | export function readTransactions(ledger) { 20 | const url = ledger.url + ledgerName 21 | ReadTransactions(url); 22 | } 23 | export function readAccounts(ledger) { 24 | const url = ledger.url + ledgerName 25 | ReadAccounts(url); 26 | } 27 | export function readBalances(ledger) { 28 | const url = ledger.url + ledgerName 29 | ReadBalances(url); 30 | } 31 | export function readAggregatedBalances(ledger) { 32 | const url = ledger.url + ledgerName 33 | ReadAggregateBalances(url); 34 | } 35 | 36 | export function write(ledger) { 37 | const url = ledger.url + ledgerName 38 | 39 | WriteTransactions(url, exec.scenario.iterationInTest); 40 | } 41 | 42 | export function teardown(data) { 43 | stopLedger(); 44 | } 45 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "k6", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "", 7 | "license": "ISC", 8 | "dependencies": { 9 | "@faker-js/faker": "^9.0.0", 10 | "intl": "^1.2.5", 11 | "k6": "^0.0.0" 12 | }, 13 | "devDependencies": { 14 | "@babel/core": "7.23.3", 15 | "@babel/plugin-proposal-class-properties": "7.18.6", 16 | "@babel/plugin-proposal-object-rest-spread": "7.20.7", 17 | "@babel/preset-env": "7.23.3", 18 | "@babel/preset-typescript": "7.23.3", 19 | "@types/k6": "~0.47.3", 20 | "@types/semver": "^7.3.10", 21 | "@types/uniqid": "^5.3.2", 22 | "@types/webpack": "5.28.5", 23 | "@typescript-eslint/eslint-plugin": "^6.12.0", 24 | "@typescript-eslint/parser": "^6.12.0", 25 | "babel-loader": "9.1.3", 26 | "clean-webpack-plugin": "4.0.0", 27 | "copy-webpack-plugin": "^11.0.0", 28 | "core-js": "^3.23.2", 29 | "eslint": "^8.33.0", 30 | "eslint-config-prettier": "^9.0.0", 31 | "eslint-plugin-jsdoc": "^46.9.0", 32 | "eslint-plugin-prefer-arrow": "^1.2.3", 33 | "semver": "^7.3.7", 34 | "typescript": "5.3.2", 35 | "uniqid": "^5.4.0", 36 | "webpack": "5.94.0", 37 | "webpack-cli": "5.1.4", 38 | "webpack-glob-entries": "^1.0.1" 39 | }, 40 | "scripts": { 41 | "build": "webpack" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/simulates/ingestion/rate.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/formancehq/stack/1c5d03ee73a8255f3e7c48531de43ffb7649d96f/tests/benchmarks/scripts/simulates/ingestion/rate.js -------------------------------------------------------------------------------- /tests/benchmarks/scripts/src/generators/simple.numscript: -------------------------------------------------------------------------------- 1 | vars { 2 | account $payment_in 3 | } 4 | send [EUR/2 10000] ( 5 | source = @world 6 | destination = $payment_in 7 | ) 8 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/src/generators/transactions.js: -------------------------------------------------------------------------------- 1 | const num = open('./../src/generators/simple.numscript', 'r'); 2 | 3 | export function generateTransactions(iterationInTest) { 4 | const paymentIn = `payments:in:${iterationInTest}`; 5 | const wallet = `wallets:${iterationInTest}:main`; 6 | return { 7 | postings: [ 8 | { 9 | amount: 100e2, 10 | asset: 'EUR/2', 11 | source: `world`, 12 | destination: paymentIn, 13 | }, 14 | { 15 | amount: 100e2, 16 | asset: 'EUR/2', 17 | source: paymentIn, 18 | destination: wallet, 19 | }, 20 | ], 21 | } 22 | } 23 | 24 | export function generateTransactionsNumscript(iterationInTest) { 25 | const paymentIn = `payments:in:${iterationInTest}`; 26 | return { 27 | script: { 28 | plain: num, 29 | vars: { 30 | "payment_in": paymentIn, 31 | } 32 | }, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/src/options.js: -------------------------------------------------------------------------------- 1 | import {readAccounts, readAggregatedBalances, readBalances} from "../benchs/ledger-v2"; 2 | 3 | export function K6Options() { 4 | return { 5 | discardResponseBodies: true, 6 | scenarios: { 7 | write_constant: { 8 | executor: 'ramping-vus', 9 | startVUs: 0, 10 | stages: [ 11 | { duration: '10s', target: 20 }, 12 | { duration: '5m', target: 20 }, 13 | ], 14 | gracefulRampDown: '0s', 15 | exec: 'write', 16 | tags: { testid: __ENV.TEST_ID} 17 | }, 18 | read_transactions_constant: { 19 | executor: 'ramping-vus', 20 | startVUs: 0, 21 | stages: [ 22 | { duration: '6m', target: 0 }, 23 | { duration: '10s', target: 10 }, 24 | { duration: '5m', target: 10 }, 25 | ], 26 | gracefulRampDown: '0s', 27 | exec: 'readTransactions', 28 | tags: { testid: __ENV.TEST_ID} 29 | }, 30 | read_accounts_constant: { 31 | executor: 'ramping-vus', 32 | startVUs: 0, 33 | stages: [ 34 | { duration: '6m', target: 0 }, 35 | { duration: '10s', target: 10 }, 36 | { duration: '5m', target: 10 }, 37 | ], 38 | gracefulRampDown: '0s', 39 | exec: 'readAccounts', 40 | tags: { testid: __ENV.TEST_ID} 41 | }, 42 | read_balances_constant: { 43 | executor: 'ramping-vus', 44 | startVUs: 0, 45 | stages: [ 46 | { duration: '6m', target: 0 }, 47 | { duration: '10s', target: 10 }, 48 | { duration: '5m', target: 10 }, 49 | ], 50 | gracefulRampDown: '0s', 51 | exec: 'readBalances', 52 | tags: { testid: __ENV.TEST_ID} 53 | }, 54 | read_aggregatebalances_constant: { 55 | executor: 'ramping-vus', 56 | startVUs: 0, 57 | stages: [ 58 | { duration: '6m', target: 0 }, 59 | { duration: '10s', target: 10 }, 60 | { duration: '5m', target: 10 }, 61 | ], 62 | gracefulRampDown: '0s', 63 | exec: 'readAggregatedBalances', 64 | tags: { testid: __ENV.TEST_ID} 65 | }, 66 | } 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/src/steps/account.js: -------------------------------------------------------------------------------- 1 | import {Trend} from "k6/metrics"; 2 | import {check, group} from "k6"; 3 | import http from "k6/http"; 4 | import { URL } from 'https://jslib.k6.io/url/1.0.0/index.js'; 5 | 6 | const readAccountsWaitingTime = new Trend('waiting_time_accounts_read'); 7 | const WALLET = "wallets:99:main"; 8 | 9 | export function ReadAccounts(BASE_URL) { 10 | const url = new URL(`${BASE_URL}/accounts`); 11 | 12 | group('Accounts - NoParams (READ)', function () { 13 | const res = http.get(url.toString()); 14 | readAccountsWaitingTime.add(res.timings.waiting); 15 | check(res, { 16 | 'is status 200': (r) => r.status === 200, 17 | }); 18 | }); 19 | group('Accounts - filter by address (READ)', function () { 20 | url.searchParams.append('address', WALLET); 21 | 22 | const res = http.get(url.toString()); 23 | readAccountsWaitingTime.add(res.timings.waiting); 24 | check(res, { 25 | 'is status 200': (r) => r.status === 200, 26 | }); 27 | }); 28 | group('Accounts - filter by address pattern (READ)', function () { 29 | url.searchParams.append('address', 'wallet::main'); 30 | 31 | const res = http.get(url.toString()); 32 | readAccountsWaitingTime.add(res.timings.waiting); 33 | check(res, { 34 | 'is status 200': (r) => r.status === 200, 35 | }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/src/steps/aggregateBalances.js: -------------------------------------------------------------------------------- 1 | import {Trend} from "k6/metrics"; 2 | import {check, group} from "k6"; 3 | import http from "k6/http"; 4 | import { URL } from 'https://jslib.k6.io/url/1.0.0/index.js'; 5 | 6 | const readAggregateBalancesWaitingTime = new Trend('waiting_time_aggregateBalances_read'); 7 | const WALLET = "wallets:99:main"; 8 | 9 | export function ReadAggregateBalances(BASE_URL) { 10 | const url = new URL(`${BASE_URL}/aggregate/balances`); 11 | group('AggregateBalances - NoParams (READ)', function () { 12 | const res = http.get(url.toString()); 13 | readAggregateBalancesWaitingTime.add(res.timings.waiting); 14 | 15 | check(res, { 16 | 'is status 200': (r) => r.status === 200, 17 | }); 18 | }); 19 | group('AggregateBalances - filter by address (READ)', function () { 20 | url.searchParams.append('address', WALLET); 21 | 22 | const res = http.get(url.toString()); 23 | readAggregateBalancesWaitingTime.add(res.timings.waiting); 24 | check(res, { 25 | 'is status 200': (r) => r.status === 200, 26 | }); 27 | }); 28 | group('AggregateBalances - filter by address pattern (READ)', function () { 29 | url.searchParams.append('address', 'wallet::main'); 30 | 31 | const res = http.get(url.toString()); 32 | readAggregateBalancesWaitingTime.add(res.timings.waiting); 33 | check(res, { 34 | 'is status 200': (r) => r.status === 200, 35 | }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/src/steps/balances.js: -------------------------------------------------------------------------------- 1 | import {Rate, Trend} from "k6/metrics"; 2 | import {check, group} from "k6"; 3 | import http from "k6/http"; 4 | import { URL } from 'https://jslib.k6.io/url/1.0.0/index.js'; 5 | 6 | const readBalancesWaitingTime = new Trend('waiting_time_balances_read'); 7 | const WALLET = "wallets:99:main"; 8 | 9 | export function ReadBalances(BASE_URL) { 10 | const url = new URL(`${BASE_URL}/balances`); 11 | 12 | group('Balances - NoParams (READ)', function () { 13 | const res = http.get(url.toString()); 14 | readBalancesWaitingTime.add(res.timings.waiting); 15 | check(res, { 16 | 'is status 200': (r) => r.status === 200, 17 | }); 18 | }); 19 | group('Balances - filter by address (READ)', function () { 20 | url.searchParams.append('address', WALLET); 21 | 22 | const res = http.get(url.toString()); 23 | readBalancesWaitingTime.add(res.timings.waiting); 24 | check(res, { 25 | 'is status 200': (r) => r.status === 200, 26 | }); 27 | }); 28 | group('Balances - filter by address pattern (READ)', function () { 29 | url.searchParams.append('address', 'wallet::main'); 30 | 31 | const res = http.get(url.toString()); 32 | readBalancesWaitingTime.add(res.timings.waiting); 33 | check(res, { 34 | 'is status 200': (r) => r.status === 200, 35 | }); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /tests/benchmarks/scripts/src/steps/transactions.js: -------------------------------------------------------------------------------- 1 | import {Trend} from "k6/metrics"; 2 | import {check, group} from "k6"; 3 | import http from "k6/http"; 4 | import { URL } from 'https://jslib.k6.io/url/1.0.0/index.js'; 5 | import {generateTransactions, generateTransactionsNumscript} from "../generators/transactions"; 6 | 7 | const writeTransactionsWaitingTime = new Trend('waiting_time_transactions_write'); 8 | const readTransactionsWaitingTime = new Trend('waiting_time_transactions_read'); 9 | 10 | 11 | export function WriteTransactions(BASE_URL, iterationInTest) { 12 | const url = new URL(`${BASE_URL}/transactions`); 13 | url.searchParams.append('async', "true"); 14 | 15 | group('Transactions (WRITE)', function () { 16 | const res = http.post(url.toString(), JSON.stringify(generateTransactions(iterationInTest)), { 17 | headers: { 'Content-Type': 'application/json' }, 18 | }); 19 | writeTransactionsWaitingTime.add(res.timings.waiting); 20 | 21 | check(res, { 22 | 'is status 200': (r) => r.status === 200, 23 | }); 24 | }); 25 | 26 | group('Transactions - Numscript (WRITE)', function () { 27 | const res = http.post(url.toString(), JSON.stringify(generateTransactionsNumscript(iterationInTest)), { 28 | headers: { 'Content-Type': 'application/json' }, 29 | }); 30 | writeTransactionsWaitingTime.add(res.timings.waiting); 31 | 32 | check(res, { 33 | 'is status 200': (r) => r.status === 200, 34 | }); 35 | }); 36 | } 37 | 38 | export function ReadTransactions(BASE_URL) { 39 | const url = new URL(`${BASE_URL}/transactions`); 40 | const WALLET = "wallets:99:main"; 41 | 42 | group('Transactions - NoParams (READ)', function () { 43 | const res = http.get(url.toString()); 44 | readTransactionsWaitingTime.add(res.timings.waiting); 45 | 46 | check(res, { 47 | 'is status 200': (r) => r.status === 200, 48 | }); 49 | }); 50 | group('Transactions - Specific Account (READ)', function () { 51 | url.searchParams.append('account', WALLET); 52 | 53 | const res = http.get(url.toString()); 54 | readTransactionsWaitingTime.add(res.timings.waiting); 55 | check(res, { 56 | 'is status 200': (r) => r.status === 200, 57 | }); 58 | }); 59 | group('Transactions - Specific Source (READ)', function () { 60 | url.searchParams.append('source', WALLET); 61 | 62 | const res = http.get(url.toString()); 63 | readTransactionsWaitingTime.add(res.timings.waiting); 64 | check(res, { 65 | 'is status 200': (r) => r.status === 200, 66 | }); 67 | }); 68 | group('Transactions - Specific Destination (READ)', function () { 69 | url.searchParams.append('destination', WALLET); 70 | 71 | const res = http.get(url.toString()); 72 | readTransactionsWaitingTime.add(res.timings.waiting); 73 | check(res, { 74 | 'is status 200': (r) => r.status === 200, 75 | }); 76 | }); 77 | group('Transactions - Pattern Account (READ)', function () { 78 | url.searchParams.append('account', 'wallet:*:main'); 79 | 80 | const res = http.get(url.toString()); 81 | readTransactionsWaitingTime.add(res.timings.waiting); 82 | check(res, { 83 | 'is status 200': (r) => r.status === 200, 84 | }); 85 | }); 86 | } -------------------------------------------------------------------------------- /tests/benchmarks/scripts/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | const CopyPlugin = require('copy-webpack-plugin'); 4 | const GlobEntries = require('webpack-glob-entries'); 5 | 6 | module.exports = { 7 | mode: 'production', 8 | entry: GlobEntries('./benchs/*.js'), 9 | output: { 10 | path: path.join(__dirname, 'dist'), 11 | libraryTarget: 'commonjs', 12 | filename: '[name].js', 13 | }, 14 | resolve: { 15 | extensions: ['.ts', '.js'], 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.ts$/, 21 | use: 'babel-loader', 22 | exclude: /node_modules/, 23 | }, 24 | ], 25 | }, 26 | target: 'web', 27 | externals: /^(k6|https?\:\/\/)(\/.*)?/, 28 | // Generate map files for compiled scripts 29 | devtool: "source-map", 30 | stats: { 31 | colors: true, 32 | }, 33 | plugins: [ 34 | new CleanWebpackPlugin(), 35 | ], 36 | optimization: { 37 | // Don't minimize, as it's not used in the browser 38 | minimize: false, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /tests/benchmarks/tools/.env: -------------------------------------------------------------------------------- 1 | POSTGRES_CPU_COUNT=2 2 | -------------------------------------------------------------------------------- /tests/benchmarks/tools/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | networks: 3 | benchmarks: {} 4 | 5 | services: 6 | postgres: 7 | image: "postgres:15-alpine" 8 | environment: 9 | POSTGRES_USER: "ledger" 10 | POSTGRES_PASSWORD: "ledger" 11 | POSTGRES_DB: "ledger" 12 | ports: 13 | - 5432:5432 14 | volumes: 15 | - ./postgresql.conf:/etc/postgresql/postgresql.conf 16 | command: 17 | - -c 18 | - 'config_file=/etc/postgresql/postgresql.conf' 19 | healthcheck: 20 | test: [ "CMD-SHELL", "pg_isready -U ledger" ] 21 | interval: 10s 22 | timeout: 5s 23 | retries: 5 24 | networks: 25 | - benchmarks 26 | deploy: 27 | replicas: 1 28 | resources: 29 | limits: 30 | cpus: "${POSTGRES_CPU_COUNT:-2}" 31 | memory: 32G 32 | 33 | jaeger: 34 | image: jaegertracing/all-in-one:latest 35 | ports: 36 | - "16686:16686" 37 | - "14268:14268" 38 | environment: 39 | - METRICS_STORAGE_TYPE=prometheus 40 | - PROMETHEUS_SERVER_URL=http://prometheus:9090 41 | networks: 42 | - benchmarks 43 | 44 | prometheus: 45 | image: prom/prometheus:latest 46 | restart: always 47 | volumes: 48 | - ./prometheus.yaml:/etc/prometheus/prometheus.yml 49 | command: 50 | - --config.file=/etc/prometheus/prometheus.yml 51 | - --storage.tsdb.path=/prometheus 52 | - --web.console.libraries=/usr/share/prometheus/console_libraries 53 | - --web.console.templates=/usr/share/prometheus/consoles 54 | - --web.enable-remote-write-receiver 55 | - --enable-feature=native-histograms 56 | ports: 57 | - "9090:9090" 58 | networks: 59 | - benchmarks 60 | deploy: 61 | replicas: 1 62 | resources: 63 | limits: 64 | cpus: "1" 65 | memory: 512M 66 | 67 | grafana: 68 | image: grafana/grafana 69 | restart: unless-stopped 70 | networks: 71 | - benchmarks 72 | deploy: 73 | replicas: 1 74 | resources: 75 | limits: 76 | cpus: "1" 77 | memory: 512M 78 | ports: 79 | - '3000:3000' 80 | environment: 81 | - GF_SECURITY_ADMIN_USER=admin 82 | - GF_SECURITY_ADMIN_PASSWORD=admin 83 | - GF_SECURITY_ALLOW_EMBEDDING=true 84 | - GF_SECURITY_DISABLE_GRAVATAR=true 85 | - GF_USERS_ALLOW_SIGN_UP=false 86 | volumes: 87 | - './provisioning/datasources:/etc/grafana/provisioning/datasources' 88 | - './provisioning/dashboards:/etc/grafana/provisioning/dashboards' 89 | 90 | cadvisor: 91 | image: gcr.io/cadvisor/cadvisor:latest 92 | volumes: 93 | - /:/rootfs:ro 94 | - /var/run:/var/run:rw 95 | - /sys:/sys:ro 96 | - /var/lib/docker/:/var/lib/docker:ro 97 | networks: 98 | - benchmarks 99 | deploy: 100 | replicas: 1 101 | resources: 102 | limits: 103 | cpus: "1" 104 | memory: 512M 105 | 106 | volumes: 107 | grafana_storage: {} 108 | -------------------------------------------------------------------------------- /tests/benchmarks/tools/otel-collector-config.yaml: -------------------------------------------------------------------------------- 1 | receivers: 2 | otlp: 3 | protocols: 4 | grpc: 5 | 6 | exporters: 7 | prometheusremotewrite: 8 | endpoint: http://prometheus:9090/api/v1/write 9 | resource_to_telemetry_conversion: 10 | enabled: true 11 | logging: 12 | jaeger: 13 | endpoint: "jaeger:14250" # using the docker-compose name of the jaeger container 14 | tls: 15 | insecure: true 16 | 17 | processors: 18 | batch: 19 | send_batch_size: 10000 20 | timeout: 10s 21 | 22 | extensions: 23 | health_check: 24 | pprof: 25 | endpoint: :1888 26 | zpages: 27 | endpoint: :55679 28 | 29 | connectors: 30 | spanmetrics: 31 | namespace: span.metrics 32 | histogram: 33 | explicit: 34 | buckets: [ 100us, 1ms, 2ms, 6ms, 10ms, 100ms, 250ms ] 35 | dimensions: 36 | - name: http.status_code 37 | - name: http.method 38 | 39 | service: 40 | telemetry: 41 | logs: 42 | level: "debug" 43 | extensions: [pprof, zpages, health_check] 44 | pipelines: 45 | traces: 46 | receivers: [otlp] 47 | processors: [batch] 48 | exporters: [spanmetrics, jaeger] 49 | metrics: 50 | receivers: [otlp, spanmetrics] 51 | # processors: [batch] 52 | exporters: [prometheusremotewrite] 53 | -------------------------------------------------------------------------------- /tests/benchmarks/tools/prometheus.yaml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 1s # Set the scrape interval to every 5 seconds. Default is every 1 minute. 4 | evaluation_interval: 1s # Evaluate rules every 5 seconds. The default is every 1 minute. 5 | 6 | scrape_configs: 7 | - job_name: cadvisor 8 | scrape_interval: 1s 9 | static_configs: 10 | - targets: 11 | - cadvisor:8080 -------------------------------------------------------------------------------- /tests/benchmarks/tools/provisioning/dashboards/dashboards.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'default' 5 | orgId: 1 6 | folder: Default 7 | folderUid: Nr4ofiDZk 8 | type: file 9 | disableDeletion: false 10 | editable: true 11 | updateIntervalSeconds: 86400 12 | options: 13 | path: /etc/grafana/provisioning/dashboards/default 14 | -------------------------------------------------------------------------------- /tests/benchmarks/tools/provisioning/datasources/datasource.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: prometheus 5 | type: prometheus 6 | access: proxy 7 | url: http://prometheus:9090 8 | version: 1 9 | editable: true 10 | -------------------------------------------------------------------------------- /tests/integration/Taskfile.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | vars: 4 | PKG: "./suite" 5 | RUN: ".*" 6 | VERBOSE: "false" 7 | PARALLEL: "true" 8 | FAILFAST: "false" 9 | DEBUG: "false" 10 | REPEAT: 0 11 | 12 | tasks: 13 | default: 14 | cmds: 15 | - task: lint 16 | - task: tests 17 | 18 | lint: 19 | cmds: 20 | - golangci-lint run --fix --allow-parallel-runners --config ./../../.golangci.yml 21 | 22 | tests: 23 | deps: [run-docker-compose] 24 | cmds: 25 | - > 26 | ginkgo --vet "" 27 | {{if eq .VERBOSE "true"}}-v{{end}} 28 | {{if eq .PARALLEL "true"}}-p{{end}} 29 | {{if eq .FAILFAST "true"}}--fail-fast{{end}} 30 | --timeout=10m 31 | --focus-file "{{.RUN}}" 32 | --repeat {{.REPEAT}} 33 | {{if eq .DEBUG "true"}}--output-interceptor-mode=none{{end}} 34 | {{.PKG}} 35 | 36 | run-docker-compose: 37 | cmds: 38 | - docker compose up -d --remove-orphans 39 | 40 | cleanup: 41 | cmds: 42 | - docker compose down -v 43 | -------------------------------------------------------------------------------- /tests/integration/doc.go: -------------------------------------------------------------------------------- 1 | package integration 2 | -------------------------------------------------------------------------------- /tests/integration/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '3.9' 3 | 4 | services: 5 | postgres: 6 | image: "postgres:15-alpine" 7 | command: 8 | - -c 9 | - max_connections=500 10 | ports: 11 | - "5432:5432" 12 | environment: 13 | POSTGRES_USER: "formance" 14 | POSTGRES_PASSWORD: "formance" 15 | 16 | opensearch: 17 | image: "opensearchproject/opensearch:2.2.1" 18 | restart: always 19 | environment: 20 | discovery.type: single-node 21 | plugins.security.disabled: "true" 22 | ES_JAVA_OPTS: "-Xms1g -Xmx1g" 23 | ports: 24 | - "9200:9200" 25 | 26 | nats: 27 | image: nats 28 | ports: 29 | - "4222:4222" 30 | command: 31 | - -js 32 | 33 | temporalite: 34 | container_name: temporalite 35 | build: 36 | context: . 37 | dockerfile: temporalite.Dockerfile 38 | image: temporalite 39 | ports: 40 | - 7233:7233 41 | - 8233:8233 42 | stdin_open: true 43 | tty: true 44 | -------------------------------------------------------------------------------- /tests/integration/internal/config.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "os" 4 | 5 | func GetPostgresDSNString() string { 6 | if fromEnv := os.Getenv("POSTGRES_DSN"); fromEnv != "" { 7 | return fromEnv 8 | } 9 | return "postgres://formance:formance@localhost:5432/formance?sslmode=disable" 10 | } 11 | 12 | func GetNatsAddress() string { 13 | return "localhost:4222" // TODO: Make configurable 14 | } 15 | 16 | func GetOpenSearchUrl() string { 17 | if fromEnv := os.Getenv("OPENSEARCH_URL"); fromEnv != "" { 18 | return fromEnv 19 | } 20 | return "localhost:9200" 21 | } 22 | 23 | func GetTemporalAddress() string { 24 | if fromEnv := os.Getenv("TEMPORAL_ADDRESS"); fromEnv != "" { 25 | return fromEnv 26 | } 27 | return "localhost:7233" 28 | } 29 | 30 | func GetDockerEndpoint() string { 31 | host := os.Getenv("DOCKER_HOSTNAME") 32 | if host == "" { 33 | host = "host.docker.internal" 34 | } 35 | return host 36 | } 37 | -------------------------------------------------------------------------------- /tests/integration/internal/env.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "context" 5 | "github.com/pkg/errors" 6 | "io" 7 | "os" 8 | 9 | "github.com/docker/docker/client" 10 | "github.com/formancehq/go-libs/collectionutils" 11 | "github.com/google/uuid" 12 | "github.com/jackc/pgx/v5" 13 | "github.com/ory/dockertest/v3" 14 | ) 15 | 16 | type Env struct { 17 | sqlConn *pgx.Conn 18 | writer io.Writer 19 | dockerPool *dockertest.Pool 20 | dockerClient *client.Client 21 | workdir string 22 | } 23 | 24 | func (e *Env) Setup(ctx context.Context) error { 25 | 26 | var err error 27 | e.workdir, err = os.Getwd() 28 | if err != nil { 29 | return err 30 | } 31 | 32 | e.sqlConn, err = pgx.Connect(ctx, GetPostgresDSNString()) 33 | if err != nil { 34 | return errors.Wrapf(err, "connecting to database '%s'", GetPostgresDSNString()) 35 | } 36 | 37 | e.dockerPool, err = dockertest.NewPool("") 38 | if err != nil { 39 | return err 40 | } 41 | 42 | e.dockerClient, err = client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | // uses pool to try to connect to Docker 48 | err = e.dockerPool.Client.Ping() 49 | if err != nil { 50 | return err 51 | } 52 | 53 | return nil 54 | } 55 | 56 | func (e *Env) Teardown(ctx context.Context) error { 57 | if e.dockerClient != nil { 58 | if err := e.dockerClient.Close(); err != nil { 59 | return err 60 | } 61 | } 62 | 63 | // TODO: Purge docker pool resources 64 | if e.sqlConn != nil { 65 | return e.sqlConn.Close(ctx) 66 | } 67 | return nil 68 | } 69 | 70 | func (e *Env) Database() *pgx.Conn { 71 | return e.sqlConn 72 | } 73 | 74 | func (e *Env) newTest() *Test { 75 | return &Test{ 76 | env: e, 77 | id: uuid.NewString(), 78 | loadedModules: collectionutils.NewSet[string](), 79 | servicesToRoute: make(map[string][]routing), 80 | } 81 | } 82 | 83 | func newEnv(w io.Writer) *Env { 84 | return &Env{ 85 | writer: w, 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/integration/internal/events.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/formancehq/stack/libs/events" 7 | "github.com/formancehq/go-libs/publish" 8 | "github.com/nats-io/nats.go" 9 | . "github.com/onsi/gomega" 10 | "github.com/onsi/gomega/types" 11 | ) 12 | 13 | func NatsClient() *nats.Conn { 14 | natsConn, err := nats.Connect(GetNatsAddress()) 15 | Expect(err).ToNot(HaveOccurred()) 16 | return natsConn 17 | } 18 | 19 | func Subscribe(service string) (func(), chan *nats.Msg) { 20 | msgs := make(chan *nats.Msg) 21 | subscription, err := NatsClient().Subscribe(fmt.Sprintf("%s-%s", currentTest.id, service), func(msg *nats.Msg) { 22 | msgs <- msg 23 | }) 24 | Expect(err).ToNot(HaveOccurred()) 25 | return func() { 26 | Expect(subscription.Unsubscribe()).To(Succeed()) 27 | }, msgs 28 | } 29 | 30 | func SubscribeLedger() (func(), chan *nats.Msg) { 31 | return Subscribe("ledger") 32 | } 33 | 34 | func SubscribePayments() (func(), chan *nats.Msg) { 35 | return Subscribe("payments") 36 | } 37 | 38 | func SubscribeOrchestration() (func(), chan *nats.Msg) { 39 | return Subscribe("orchestration") 40 | } 41 | 42 | func PublishPayments(message publish.EventMessage) { 43 | data, err := json.Marshal(message) 44 | Expect(err).WithOffset(1).To(BeNil()) 45 | 46 | err = NatsClient().Publish(fmt.Sprintf("%s-payments", currentTest.id), data) 47 | Expect(err).To(BeNil()) 48 | } 49 | 50 | func Topic(service string) string { 51 | return fmt.Sprintf("%s-%s", currentTest.id, service) 52 | } 53 | 54 | func CreateTopic(service string) { 55 | js, err := NatsClient().JetStream() 56 | Expect(err).To(Succeed()) 57 | 58 | _, err = js.AddStream(&nats.StreamConfig{ 59 | Name: Topic(service), 60 | Subjects: []string{Topic(service)}, 61 | Retention: nats.LimitsPolicy, 62 | }) 63 | Expect(err).To(Succeed()) 64 | } 65 | 66 | func PublishLedger(message publish.EventMessage) { 67 | data, err := json.Marshal(message) 68 | Expect(err).WithOffset(1).To(BeNil()) 69 | 70 | err = NatsClient().Publish(Topic("ledger"), data) 71 | Expect(err).To(BeNil()) 72 | } 73 | 74 | type receiveEventMatcher struct { 75 | eventName string 76 | serviceName string 77 | err error 78 | } 79 | 80 | func (r *receiveEventMatcher) Match(actual interface{}) (success bool, err error) { 81 | 82 | var data []byte 83 | // Handle different types of channels and extract data accordingly. 84 | switch v := actual.(type) { 85 | case chan *nats.Msg: 86 | select { 87 | case msg := <-v: 88 | data = msg.Data 89 | default: 90 | return false, nil 91 | } 92 | case chan []byte: 93 | select { 94 | case msg := <-v: 95 | data = msg 96 | default: 97 | return false, nil 98 | } 99 | default: 100 | return false, fmt.Errorf("expected chan *nats.Msg or chan []uint8, got %T", actual) 101 | } 102 | 103 | r.err = events.Check(data, r.serviceName, r.eventName) 104 | return r.err == nil, nil 105 | } 106 | 107 | func (r *receiveEventMatcher) FailureMessage(actual interface{}) (message string) { 108 | return fmt.Sprintf("Expected event to match schema: \r\n%s", r.err) 109 | } 110 | 111 | func (r *receiveEventMatcher) NegatedFailureMessage(actual interface{}) (message string) { 112 | return fmt.Sprintf("Expected event not to match schema: \n%s", r.err) 113 | } 114 | 115 | var _ types.GomegaMatcher = (*receiveEventMatcher)(nil) 116 | 117 | func ReceiveEvent(serviceName, eventName string) *receiveEventMatcher { 118 | return &receiveEventMatcher{ 119 | eventName: eventName, 120 | serviceName: serviceName, 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /tests/integration/internal/gateway.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "net/http/httputil" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | type serviceInfo struct { 13 | Name string `json:"name"` 14 | // We do not want to omit empty values in the json response 15 | Version string `json:"version"` 16 | Health bool `json:"health"` 17 | } 18 | 19 | type versionsResponse struct { 20 | Versions []*serviceInfo `json:"versions"` 21 | } 22 | 23 | type gateway struct { 24 | test *Test 25 | } 26 | 27 | func (g gateway) ServeHTTP(w http.ResponseWriter, r *http.Request) { 28 | if r.URL.Path == "/versions" { 29 | res := versionsResponse{ 30 | Versions: []*serviceInfo{ 31 | { 32 | Name: "ledger", 33 | Version: "v2.0.0", 34 | }, 35 | // If needed, add other services version 36 | }, 37 | } 38 | w.Header().Set("Content-Type", "application/json") 39 | if err := json.NewEncoder(w).Encode(res); err != nil { 40 | panic(err) 41 | } 42 | return 43 | } 44 | 45 | if !strings.HasPrefix(r.URL.Path, "/api/") { 46 | w.WriteHeader(http.StatusNotFound) 47 | return 48 | } 49 | 50 | paths := strings.Split(r.URL.Path, "/") 51 | service := paths[2] 52 | routings, ok := g.test.servicesToRoute[service] 53 | if !ok { 54 | w.WriteHeader(http.StatusNotFound) 55 | return 56 | } 57 | 58 | found := false 59 | var port uint16 60 | for _, routing := range routings { 61 | if routing.routingFunc == nil { 62 | port = routing.port 63 | found = true 64 | break 65 | } 66 | 67 | ok = routing.routingFunc("/"+strings.Join(paths[3:], "/"), r.Method) 68 | if ok { 69 | port = routing.port 70 | found = true 71 | break 72 | } 73 | } 74 | if !found { 75 | w.WriteHeader(http.StatusNotFound) 76 | return 77 | } 78 | 79 | url, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", port)) 80 | proxy := httputil.NewSingleHostReverseProxy(url) 81 | 82 | http.StripPrefix("/api/"+service, proxy).ServeHTTP(w, r) 83 | } 84 | 85 | var _ http.Handler = &gateway{} 86 | 87 | func newGateway(test *Test) *gateway { 88 | return &gateway{ 89 | test: test, 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/integration/internal/helpers.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | "time" 7 | ) 8 | 9 | func Given(text string, body func()) bool { 10 | return Context(text, body) 11 | } 12 | 13 | func With(text string, body func()) bool { 14 | return Context(text, body) 15 | } 16 | 17 | func Then(text string, body func()) bool { 18 | return Context(text, body) 19 | } 20 | 21 | func NeedModule(m *Module) { 22 | BeforeEach(func() { 23 | Expect(currentTest.loadModule(ctx, m)).To(Succeed()) 24 | }) 25 | AfterEach(func() { 26 | Expect(currentTest.unloadModule(ctx, m)).To(Succeed()) 27 | }) 28 | } 29 | 30 | func WithModules(modules []*Module, callback func()) bool { 31 | Context("with modules", func() { 32 | for _, m := range modules { 33 | NeedModule(m) 34 | } 35 | callback() 36 | }) 37 | return true 38 | } 39 | 40 | func WaitOnChanWithTimeout[T any](ch chan T, timeout time.Duration) T { 41 | select { 42 | case t := <-ch: 43 | return t 44 | case <-time.After(timeout): 45 | Fail("should have received an event", 1) 46 | } 47 | panic("cannot happen") 48 | } 49 | 50 | func ChanClosed[T any](ch chan T) func() bool { 51 | return func() bool { 52 | select { 53 | case _, alive := <-ch: 54 | return !alive 55 | default: 56 | return false 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/integration/internal/init.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "context" 5 | formance "github.com/formancehq/formance-sdk-go/v3" 6 | "github.com/formancehq/go-libs/logging" 7 | "github.com/oauth2-proxy/mockoidc" 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | "github.com/sirupsen/logrus" 11 | "net/http" 12 | "net/url" 13 | "time" 14 | ) 15 | 16 | var ( 17 | ctx context.Context 18 | currentEnv *Env 19 | currentTest *Test 20 | mockOIDC *mockoidc.MockOIDC 21 | ) 22 | 23 | func CurrentEnv() *Env { 24 | return currentEnv 25 | } 26 | 27 | func CurrentTest() *Test { 28 | return currentTest 29 | } 30 | 31 | func TestContext() context.Context { 32 | return ctx 33 | } 34 | 35 | func Client(options ...formance.SDKOption) *formance.Formance { 36 | gatewayUrl, err := url.Parse(currentTest.httpServer.URL) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | Expect(err).To(BeNil()) 42 | 43 | options = append([]formance.SDKOption{ 44 | formance.WithServerURL(gatewayUrl.String()), 45 | formance.WithClient( 46 | &http.Client{ 47 | Transport: currentTest.httpTransport, 48 | }, 49 | ), 50 | }, options...) 51 | 52 | return formance.New(options...) 53 | } 54 | 55 | func GatewayURL() string { 56 | return currentTest.GatewayURL() 57 | } 58 | 59 | func HTTPClient() *http.Client { 60 | return &http.Client{ 61 | Transport: currentTest.httpTransport, 62 | } 63 | } 64 | 65 | func OIDCServer() *mockoidc.MockOIDC { 66 | return mockOIDC 67 | } 68 | 69 | var _ = BeforeSuite(func() { 70 | ctx = context.TODO() 71 | 72 | l := logrus.New() 73 | l.Out = GinkgoWriter 74 | l.Level = logrus.DebugLevel 75 | ctx = logging.ContextWithLogger(ctx, logging.NewLogrus(l)) 76 | 77 | var err error 78 | mockOIDC, err = mockoidc.Run() 79 | Expect(err).To(BeNil()) 80 | 81 | // Some defaults 82 | SetDefaultEventuallyTimeout(10 * time.Second) 83 | SetDefaultEventuallyPollingInterval(500 * time.Millisecond) 84 | 85 | Eventually(func() error { 86 | _, err := http.Get("http://" + GetOpenSearchUrl()) 87 | return err 88 | }).Should(Succeed()) 89 | 90 | currentEnv = newEnv(GinkgoWriter) 91 | Expect(currentEnv.Setup(ctx)).To(Succeed()) 92 | 93 | Eventually(func() error { 94 | _, err := http.Get("http://" + GetOpenSearchUrl()) 95 | return err 96 | }).Should(Succeed()) 97 | }) 98 | 99 | var _ = AfterSuite(func() { 100 | Expect(currentEnv.Teardown(ctx)).To(Succeed()) 101 | }) 102 | 103 | var _ = BeforeEach(func() { 104 | currentTest = currentEnv.newTest() 105 | Expect(currentTest.setup()).To(Succeed()) 106 | }) 107 | 108 | var _ = AfterEach(func() { 109 | Expect(currentTest.tearDown()).To(Succeed()) 110 | }) 111 | -------------------------------------------------------------------------------- /tests/integration/internal/module.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | type Module struct { 4 | name string 5 | createDatabase bool 6 | services []service 7 | } 8 | 9 | func (m *Module) WithCreateDatabase() *Module { 10 | m.createDatabase = true 11 | return m 12 | } 13 | 14 | func (m *Module) WithServices(services ...service) *Module { 15 | for _, service := range services { 16 | m.services = append(m.services, service) 17 | } 18 | return m 19 | } 20 | 21 | func NewModule(name string) *Module { 22 | return &Module{ 23 | name: name, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/auth.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "github.com/formancehq/auth/cmd" 5 | auth "github.com/formancehq/auth/pkg" 6 | "github.com/formancehq/stack/tests/integration/internal" 7 | "github.com/google/uuid" 8 | "gopkg.in/yaml.v3" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | const AuthIssuer = "http://localhost/api/auth" 14 | 15 | var Auth = internal.NewModule("auth"). 16 | WithCreateDatabase(). 17 | WithServices( 18 | internal.NewCommandService("auth", cmd.NewRootCommand). 19 | WithArgs(func(test *internal.Test) []string { 20 | 21 | authDir := filepath.Join(os.TempDir(), uuid.NewString()) 22 | if err := os.MkdirAll(authDir, 0o777); err != nil { 23 | panic(err) 24 | } 25 | type configuration struct { 26 | Clients []auth.StaticClient `yaml:"clients"` 27 | } 28 | cfg := &configuration{ 29 | Clients: []auth.StaticClient{{ 30 | ClientOptions: auth.ClientOptions{ 31 | Name: "global", 32 | Id: "global", 33 | Trusted: true, 34 | }, 35 | Secrets: []string{"global"}, 36 | }}, 37 | } 38 | configFile := filepath.Join(authDir, "config.yaml") 39 | f, err := os.Create(configFile) 40 | if err != nil { 41 | panic(err) 42 | } 43 | if err := yaml.NewEncoder(f).Encode(cfg); err != nil { 44 | panic(err) 45 | } 46 | 47 | if err := os.Setenv("CAOS_OIDC_DEV", "1"); err != nil { 48 | panic(err) 49 | } 50 | 51 | args := make([]string, 0) 52 | args = append(args) 53 | 54 | return []string{ 55 | "serve", 56 | "--config=" + configFile, 57 | "--postgres-uri=" + test.GetDatabaseSourceName("auth"), 58 | "--delegated-client-id=" + internal.OIDCServer().ClientID, 59 | "--delegated-client-secret=" + internal.OIDCServer().ClientSecret, 60 | "--delegated-issuer=" + internal.OIDCServer().Issuer(), 61 | "--base-url=" + AuthIssuer, 62 | "--listen=0.0.0.0:0", 63 | } 64 | }), 65 | ) 66 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/ledger.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/formancehq/ledger/cmd" 7 | "github.com/formancehq/stack/tests/integration/internal" 8 | ) 9 | 10 | var Ledger = internal.NewModule("ledger"). 11 | WithCreateDatabase(). 12 | WithServices( 13 | internal.NewCommandService("ledger", cmd.NewRootCommand). 14 | WithArgs(func(test *internal.Test) []string { 15 | return []string{ 16 | "serve", 17 | "--auth-enabled=false", 18 | "--publisher-nats-enabled", 19 | "--publisher-nats-client-id=ledger", 20 | "--publisher-nats-url=" + internal.GetNatsAddress(), 21 | fmt.Sprintf("--publisher-topic-mapping=*:%s-ledger", test.ID()), 22 | "--postgres-uri=" + test.GetDatabaseSourceName("ledger"), 23 | "--json-formatting-logger=false", 24 | "--bind=0.0.0.0:0", // Random port 25 | "--debug", 26 | } 27 | }), 28 | ) 29 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/orchestration.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/formancehq/orchestration/cmd" 7 | "github.com/formancehq/stack/tests/integration/internal" 8 | ) 9 | 10 | var Orchestration = internal.NewModule("orchestration"). 11 | WithCreateDatabase(). 12 | WithServices( 13 | internal.NewCommandService("orchestration", cmd.NewRootCommand). 14 | WithArgs(func(test *internal.Test) []string { 15 | return []string{ 16 | "serve", 17 | "--auth-enabled=false", 18 | "--listen=0.0.0.0:0", 19 | "--postgres-uri=" + test.GetDatabaseSourceName("orchestration"), 20 | "--stack-client-id=global", 21 | "--stack-client-secret=global", 22 | "--stack-url=" + test.GatewayURL(), 23 | "--temporal-address=" + internal.GetTemporalAddress(), 24 | "--temporal-task-queue=" + test.ID(), 25 | "--temporal-init-search-attributes", 26 | "--worker", 27 | "--publisher-nats-enabled", 28 | "--publisher-nats-client-id=orchestration", 29 | "--publisher-nats-url=" + internal.GetNatsAddress(), 30 | fmt.Sprintf("--publisher-topic-mapping=*:%s-orchestration", test.ID()), 31 | fmt.Sprintf("--topics=%s-ledger", test.ID()), 32 | fmt.Sprintf("--topics=%s-payments", test.ID()), 33 | "--debug", 34 | } 35 | }), 36 | ) 37 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/payments.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | 8 | "github.com/formancehq/payments/cmd" 9 | "github.com/formancehq/stack/tests/integration/internal" 10 | ) 11 | 12 | var Payments = internal.NewModule("payments"). 13 | WithCreateDatabase(). 14 | WithServices( 15 | internal.NewCommandService("paymentsapi", cmd.NewRootCommand). 16 | WithArgs(func(test *internal.Test) []string { 17 | return []string{ 18 | "api", 19 | "serve", 20 | "--auth-enabled=false", 21 | "--postgres-uri=" + test.GetDatabaseSourceName("payments"), 22 | "--config-encryption-key=encryption-key", 23 | "--publisher-nats-enabled", 24 | "--publisher-nats-client-id=payments", 25 | "--publisher-nats-url=" + internal.GetNatsAddress(), 26 | fmt.Sprintf("--publisher-topic-mapping=*:%s-payments", test.ID()), 27 | "--listen=0.0.0.0:0", 28 | "--auto-migrate", 29 | } 30 | }).WithRoutingFunc("payments", func(path, method string) bool { 31 | switch { 32 | case strings.HasPrefix(path, "/payments"): 33 | return true 34 | case strings.HasPrefix(path, "/accounts"): 35 | return method == http.MethodGet || method == http.MethodPost 36 | case strings.HasPrefix(path, "/bank-accounts"), 37 | strings.HasPrefix(path, "/transfer-initiations"): 38 | return method == http.MethodGet 39 | default: 40 | return false 41 | } 42 | }), 43 | internal.NewCommandService("paymentsconnectors", cmd.NewRootCommand). 44 | WithArgs(func(test *internal.Test) []string { 45 | return []string{ 46 | "connectors", 47 | "serve", 48 | "--auth-enabled=false", 49 | "--postgres-uri=" + test.GetDatabaseSourceName("payments"), 50 | "--config-encryption-key=encryption-key", 51 | "--publisher-nats-enabled", 52 | "--publisher-nats-client-id=payments", 53 | "--publisher-nats-url=" + internal.GetNatsAddress(), 54 | fmt.Sprintf("--publisher-topic-mapping=*:%s-payments", test.ID()), 55 | "--listen=0.0.0.0:0", 56 | "--auto-migrate", 57 | } 58 | }).WithRoutingFunc("payments", func(path, method string) bool { 59 | switch { 60 | case strings.HasPrefix(path, "/bank-accounts"): 61 | return method == http.MethodPost 62 | case strings.HasPrefix(path, "/transfer-initiations"): 63 | return method == http.MethodPost || method == http.MethodDelete 64 | case strings.HasPrefix(path, "/connectors"): 65 | return true 66 | default: 67 | return false 68 | } 69 | }), 70 | ) 71 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/reconciliation.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "github.com/formancehq/reconciliation/cmd" 5 | "github.com/formancehq/stack/tests/integration/internal" 6 | ) 7 | 8 | var Reconciliation = internal.NewModule("reconciliation"). 9 | WithCreateDatabase(). 10 | WithServices( 11 | internal.NewCommandService("reconciliation", cmd.NewRootCommand). 12 | WithArgs(func(test *internal.Test) []string { 13 | return []string{ 14 | "serve", 15 | "--auth-enabled=false", 16 | "--stack-client-id=global", 17 | "--stack-client-secret=global", 18 | "--stack-url=" + test.GatewayURL(), 19 | "--listen=0.0.0.0:0", 20 | "--debug", 21 | "--postgres-uri=" + test.GetDatabaseSourceName("reconciliation"), 22 | "--auto-migrate", 23 | } 24 | }), 25 | ) 26 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/search.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/formancehq/search/cmd" 8 | "github.com/formancehq/stack/tests/integration/internal" 9 | "github.com/ory/dockertest/v3" 10 | ) 11 | 12 | var Search = internal.NewModule("search"). 13 | WithServices( 14 | internal.NewCommandService("search", cmd.NewRootCommand). 15 | WithArgs(func(test *internal.Test) []string { 16 | return []string{ 17 | "serve", 18 | "--auth-enabled=false", 19 | "--open-search-service=" + internal.GetOpenSearchUrl(), 20 | "--open-search-scheme=http", 21 | "--open-search-username=admin", 22 | "--open-search-password=admin", 23 | "--bind=0.0.0.0:0", 24 | "--stack=" + test.ID(), 25 | fmt.Sprintf("--es-indices=%s", test.ID()), 26 | //"--mapping-init-disabled", 27 | } 28 | }), 29 | internal.NewDockerService("public.ecr.aws/formance-internal/jeffail/benthos", "v4.23.1-es"). 30 | WithEntrypoint([]string{ 31 | "/benthos", 32 | "-c", "/config/config.yml", 33 | "-t", "/config/templates/*.yaml", 34 | "-r", "/config/resources/*.yaml", 35 | "streams", 36 | "/config/streams/ledger/*.yaml", 37 | "/config/streams/payments/*.yaml", 38 | }). 39 | WithEnv(func(test *internal.Test) []string { 40 | return []string{ 41 | fmt.Sprintf("OPENSEARCH_URL=http://%s:9200", internal.GetDockerEndpoint()), 42 | "BASIC_AUTH_ENABLED=true", 43 | "BASIC_AUTH_USERNAME=admin", 44 | "BASIC_AUTH_PASSWORD=admin", 45 | fmt.Sprintf("OPENSEARCH_INDEX=%s", test.ID()), 46 | fmt.Sprintf("NATS_URL=nats://%s:4222", internal.GetDockerEndpoint()), 47 | fmt.Sprintf("STACK=%s", test.ID()), 48 | fmt.Sprintf("TOPIC_PREFIX=%s-", test.ID()), 49 | } 50 | }). 51 | WithMounts(func(test *internal.Test) []string { 52 | return []string{ 53 | test.Workdir() + "/../../../ee/search/benthos:/config", 54 | } 55 | }). 56 | WithHealthCheck(func(test *internal.Test, resource *dockertest.Resource) bool { 57 | rsp, err := http.Get("http://localhost:" + resource.GetPort("4195/tcp") + "/ping") 58 | if err != nil { 59 | return false 60 | } 61 | 62 | return rsp.StatusCode == http.StatusOK 63 | }), 64 | ) 65 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/wallets.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "github.com/formancehq/stack/tests/integration/internal" 5 | "github.com/formancehq/wallets/cmd" 6 | ) 7 | 8 | var Wallets = internal.NewModule("wallets"). 9 | WithServices( 10 | internal.NewCommandService("wallets", cmd.NewRootCommand). 11 | WithArgs(func(test *internal.Test) []string { 12 | return []string{ 13 | "serve", 14 | "--auth-enabled=false", 15 | "--stack-client-id=global", 16 | "--stack-client-secret=global", 17 | "--stack-url=" + test.GatewayURL(), 18 | "--listen=0.0.0.0:0", 19 | } 20 | }), 21 | ) 22 | -------------------------------------------------------------------------------- /tests/integration/internal/modules/webhooks.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/formancehq/stack/tests/integration/internal" 7 | "github.com/formancehq/webhooks/cmd" 8 | ) 9 | 10 | var Webhooks = internal.NewModule("webhooks"). 11 | WithCreateDatabase(). 12 | WithServices( 13 | internal.NewCommandService("webhooks", cmd.NewRootCommand). 14 | WithArgs(func(test *internal.Test) []string { 15 | return []string{ 16 | "serve", 17 | "--auth-enabled=false", 18 | "--postgres-uri=" + test.GetDatabaseSourceName("webhooks"), 19 | "--listen=0.0.0.0:0", 20 | "--worker", 21 | "--publisher-nats-enabled", 22 | "--publisher-nats-client-id=webhooks", 23 | "--publisher-nats-url=" + internal.GetNatsAddress(), 24 | fmt.Sprintf("--kafka-topics=%s-ledger", test.ID()), 25 | fmt.Sprintf("--kafka-topics=%s-payments", test.ID()), 26 | "--retry-period=1s", 27 | "--min-backoff-delay=1s", 28 | "--abort-after=3s", 29 | "--auto-migrate=true", 30 | } 31 | }), 32 | ) 33 | -------------------------------------------------------------------------------- /tests/integration/internal/test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/formancehq/go-libs/collectionutils" 7 | "github.com/formancehq/go-libs/httpclient" 8 | "github.com/xo/dburl" 9 | "net/http" 10 | "net/http/httptest" 11 | "os" 12 | ) 13 | 14 | type routing struct { 15 | port uint16 16 | routingFunc func(path, method string) bool 17 | } 18 | 19 | type Test struct { 20 | env *Env 21 | id string 22 | loadedModules collectionutils.Set[string] 23 | servicesToRoute map[string][]routing 24 | httpServer *httptest.Server 25 | httpTransport http.RoundTripper 26 | } 27 | 28 | func (test *Test) setup() error { 29 | gateway := newGateway(test) 30 | test.httpServer = httptest.NewServer(gateway) 31 | test.httpTransport = httpclient.NewDebugHTTPTransport(http.DefaultTransport) 32 | 33 | return nil 34 | } 35 | 36 | func (test *Test) tearDown() error { 37 | if test.httpServer != nil { 38 | test.httpServer.Close() 39 | } 40 | return nil 41 | } 42 | 43 | func (test *Test) loadModule(ctx context.Context, m *Module) error { 44 | if test.loadedModules.Contains(m.name) { 45 | return nil 46 | } 47 | 48 | if m.createDatabase { 49 | if err := test.createDatabase(ctx, m.name); err != nil { 50 | return err 51 | } 52 | } 53 | 54 | for _, s := range m.services { 55 | if err := s.load(ctx, test); err != nil { 56 | return err 57 | } 58 | } 59 | 60 | test.loadedModules.Put(m.name) 61 | 62 | return nil 63 | } 64 | 65 | func (test *Test) unloadModule(ctx context.Context, m *Module) error { 66 | 67 | for _, s := range m.services { 68 | if err := s.unload(ctx, test); err != nil { 69 | return err 70 | } 71 | } 72 | 73 | if m.createDatabase { 74 | if err := test.dropDatabase(ctx, m.name); err != nil { 75 | return err 76 | } 77 | } 78 | return nil 79 | } 80 | 81 | func (test *Test) createDatabase(ctx context.Context, name string) error { 82 | _, err := test.env.sqlConn.Exec(ctx, fmt.Sprintf(`CREATE DATABASE "%s-%s";`, test.id, name)) 83 | return err 84 | } 85 | 86 | func (test *Test) dropDatabase(ctx context.Context, name string) error { 87 | if os.Getenv("NO_CLEANUP") != "true" { 88 | _, err := test.env.sqlConn.Exec(ctx, fmt.Sprintf(`DROP DATABASE "%s-%s";`, test.id, name)) 89 | return err 90 | } 91 | return nil 92 | } 93 | 94 | func (test *Test) registerServiceToRoute(name string, routing routing) { 95 | test.servicesToRoute[name] = append(test.servicesToRoute[name], routing) 96 | } 97 | 98 | func (test *Test) GetDatabaseSourceName(name string) string { 99 | dsn, err := dburl.Parse(GetPostgresDSNString()) 100 | if err != nil { 101 | panic(err) 102 | } 103 | dsn.Path = fmt.Sprintf("%s-%s", test.id, name) 104 | 105 | return dsn.String() 106 | } 107 | 108 | func (test *Test) ID() string { 109 | return test.id 110 | } 111 | 112 | func (test *Test) Workdir() string { 113 | return test.env.workdir 114 | } 115 | 116 | func (test *Test) GatewayURL() string { 117 | return test.httpServer.URL 118 | } 119 | -------------------------------------------------------------------------------- /tests/integration/suite/orchestration-workflows-create.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" 5 | . "github.com/formancehq/stack/tests/integration/internal" 6 | "github.com/formancehq/stack/tests/integration/internal/modules" 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | "github.com/pborman/uuid" 10 | ) 11 | 12 | var _ = WithModules([]*Module{modules.Auth, modules.Orchestration}, func() { 13 | When("creating a new workflow", func() { 14 | var ( 15 | createWorkflowResponse *shared.V2CreateWorkflowResponse 16 | ) 17 | BeforeEach(func() { 18 | response, err := Client().Orchestration.V2.CreateWorkflow( 19 | TestContext(), 20 | &shared.V2CreateWorkflowRequest{ 21 | Name: ptr(uuid.New()), 22 | Stages: []map[string]interface{}{ 23 | { 24 | "send": map[string]any{ 25 | "source": map[string]any{ 26 | "account": map[string]any{ 27 | "id": "world", 28 | "ledger": "default", 29 | }, 30 | }, 31 | "destination": map[string]any{ 32 | "account": map[string]any{ 33 | "id": "bank", 34 | "ledger": "default", 35 | }, 36 | }, 37 | "amount": map[string]any{ 38 | "amount": 100, 39 | "asset": "EUR/2", 40 | }, 41 | }, 42 | }, 43 | }, 44 | }, 45 | ) 46 | Expect(err).ToNot(HaveOccurred()) 47 | Expect(response.StatusCode).To(Equal(201)) 48 | 49 | createWorkflowResponse = response.V2CreateWorkflowResponse 50 | }) 51 | It("should be ok", func() { 52 | Expect(createWorkflowResponse.Data.ID).NotTo(BeEmpty()) 53 | }) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /tests/integration/suite/orchestration-workflows-list.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" 5 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" 6 | . "github.com/formancehq/stack/tests/integration/internal" 7 | "github.com/formancehq/stack/tests/integration/internal/modules" 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | "github.com/pborman/uuid" 11 | ) 12 | 13 | var _ = WithModules([]*Module{modules.Auth, modules.Orchestration}, func() { 14 | var ( 15 | workflow shared.V2Workflow 16 | list []shared.V2Workflow 17 | ) 18 | When("first listing workflows", func() { 19 | BeforeEach(func() { 20 | response, err := Client().Orchestration.V2.ListWorkflows( 21 | TestContext(), 22 | operations.V2ListWorkflowsRequest{}, 23 | ) 24 | Expect(err).ToNot(HaveOccurred()) 25 | Expect(response.StatusCode).To(Equal(200)) 26 | 27 | list = response.V2ListWorkflowsResponse.Cursor.Data 28 | }) 29 | It("should respond with an empty list", func() { 30 | Expect(list).To(BeEmpty()) 31 | }) 32 | }) 33 | When("populating 1 workflow", func() { 34 | BeforeEach(func() { 35 | response, err := Client().Orchestration.V2.CreateWorkflow( 36 | TestContext(), 37 | &shared.V2CreateWorkflowRequest{ 38 | Name: ptr(uuid.New()), 39 | Stages: []map[string]interface{}{ 40 | { 41 | "send": map[string]any{ 42 | "source": map[string]any{ 43 | "account": map[string]any{ 44 | "id": "world", 45 | "ledger": "default", 46 | }, 47 | }, 48 | "destination": map[string]any{ 49 | "account": map[string]any{ 50 | "id": "bank", 51 | "ledger": "default", 52 | }, 53 | }, 54 | "amount": map[string]any{ 55 | "amount": 100, 56 | "asset": "EUR/2", 57 | }, 58 | }, 59 | }, 60 | }, 61 | }, 62 | ) 63 | 64 | Expect(err).ToNot(HaveOccurred()) 65 | Expect(response.StatusCode).To(Equal(201)) 66 | 67 | workflow = response.V2CreateWorkflowResponse.Data 68 | }) 69 | It("should be ok", func() { 70 | Expect(workflow.ID).NotTo(BeEmpty()) 71 | }) 72 | JustBeforeEach(func() { 73 | response, err := Client().Orchestration.V2.ListWorkflows( 74 | TestContext(), 75 | operations.V2ListWorkflowsRequest{}, 76 | ) 77 | Expect(err).ToNot(HaveOccurred()) 78 | Expect(response.StatusCode).To(Equal(200)) 79 | 80 | list = response.V2ListWorkflowsResponse.Cursor.Data 81 | }) 82 | It("should respond with a list of 1 elements", func() { 83 | Expect(list).ToNot(BeEmpty()) 84 | Expect(list).Should(HaveLen(1)) 85 | }) 86 | When("Deleting a workflow", func() { 87 | JustBeforeEach(func() { 88 | response, err := Client().Orchestration.V2.DeleteWorkflow( 89 | TestContext(), 90 | operations.V2DeleteWorkflowRequest{ 91 | FlowID: workflow.ID, 92 | }, 93 | ) 94 | Expect(err).ToNot(HaveOccurred()) 95 | Expect(response.StatusCode).To(Equal(204)) 96 | 97 | }) 98 | JustBeforeEach(func() { 99 | response, err := Client().Orchestration.V2.ListWorkflows( 100 | TestContext(), 101 | operations.V2ListWorkflowsRequest{}, 102 | ) 103 | Expect(err).ToNot(HaveOccurred()) 104 | Expect(response.StatusCode).To(Equal(200)) 105 | 106 | list = response.V2ListWorkflowsResponse.Cursor.Data 107 | 108 | }) 109 | It("should have a list of 0 element", func() { 110 | Expect(list).To(BeEmpty()) 111 | }) 112 | }) 113 | }) 114 | }) 115 | -------------------------------------------------------------------------------- /tests/integration/suite/orchestration-workflows-soft-delete.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" 5 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/sdkerrors" 6 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" 7 | . "github.com/formancehq/stack/tests/integration/internal" 8 | "github.com/formancehq/stack/tests/integration/internal/modules" 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | "github.com/pborman/uuid" 12 | ) 13 | 14 | var _ = WithModules([]*Module{modules.Auth, modules.Orchestration}, func() { 15 | When("populating 1 workflow", func() { 16 | var ( 17 | workflow *shared.V2Workflow 18 | ) 19 | BeforeEach(func() { 20 | response, err := Client().Orchestration.V2.CreateWorkflow( 21 | TestContext(), 22 | &shared.V2CreateWorkflowRequest{ 23 | Name: ptr(uuid.New()), 24 | Stages: []map[string]interface{}{ 25 | { 26 | "send": map[string]any{ 27 | "source": map[string]any{ 28 | "account": map[string]any{ 29 | "id": "world", 30 | "ledger": "default", 31 | }, 32 | }, 33 | "destination": map[string]any{ 34 | "account": map[string]any{ 35 | "id": "bank", 36 | "ledger": "default", 37 | }, 38 | }, 39 | "amount": map[string]any{ 40 | "amount": 100, 41 | "asset": "EUR/2", 42 | }, 43 | }, 44 | }, 45 | }, 46 | }, 47 | ) 48 | 49 | Expect(err).ToNot(HaveOccurred()) 50 | Expect(response.StatusCode).To(Equal(201)) 51 | 52 | workflow = &response.V2CreateWorkflowResponse.Data 53 | }) 54 | It("should be ok", func() { 55 | Expect(workflow.ID).NotTo(BeEmpty()) 56 | }) 57 | It("should delete the workflow", func() { 58 | response, err := Client().Orchestration.V2.DeleteWorkflow( 59 | TestContext(), 60 | operations.V2DeleteWorkflowRequest{ 61 | FlowID: workflow.ID, 62 | }, 63 | ) 64 | Expect(err).ToNot(HaveOccurred()) 65 | Expect(response.StatusCode).To(Equal(204)) 66 | 67 | }) 68 | }) 69 | 70 | When("deleting a non-uuid workflow", func() { 71 | It("should return 400 with unknown", func() { 72 | _, err := Client().Orchestration.V2.DeleteWorkflow( 73 | TestContext(), 74 | operations.V2DeleteWorkflowRequest{ 75 | FlowID: "unknown", 76 | }, 77 | ) 78 | Expect(err).To(HaveOccurred()) 79 | Expect(err.(*sdkerrors.V2Error).ErrorCode).To(Equal(sdkerrors.SchemasErrorCodeValidation)) 80 | }) 81 | }) 82 | When("deleting a non-existing workflow", func() { 83 | It("should return 404", func() { 84 | _, err := Client().Orchestration.V2.DeleteWorkflow( 85 | TestContext(), 86 | operations.V2DeleteWorkflowRequest{ 87 | FlowID: uuid.New(), 88 | }, 89 | ) 90 | Expect(err).To(HaveOccurred()) 91 | Expect(err.(*sdkerrors.V2Error).ErrorCode).To(Equal(sdkerrors.SchemasErrorCodeNotFound)) 92 | }) 93 | }) 94 | }) 95 | -------------------------------------------------------------------------------- /tests/integration/suite/reconciliation-policy-create-list.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/operations" 5 | "github.com/formancehq/formance-sdk-go/v3/pkg/models/shared" 6 | . "github.com/formancehq/stack/tests/integration/internal" 7 | "github.com/formancehq/stack/tests/integration/internal/modules" 8 | "github.com/google/uuid" 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | var _ = WithModules([]*Module{modules.Auth, modules.Ledger, modules.Payments, modules.Reconciliation}, func() { 14 | When("1 - reconciliation list policies", func() { 15 | var ( 16 | policies []shared.Policy 17 | ) 18 | JustBeforeEach(func() { 19 | policiesResponse, err := Client().Reconciliation.V1.ListPolicies( 20 | TestContext(), 21 | operations.ListPoliciesRequest{}, 22 | ) 23 | Expect(err).ToNot(HaveOccurred()) 24 | Expect(policiesResponse.StatusCode).To(Equal(200)) 25 | 26 | policies = policiesResponse.PoliciesCursorResponse.Cursor.Data 27 | }) 28 | It("should respond with empty lists", func() { 29 | Expect(policies).To(BeEmpty()) 30 | }) 31 | }) 32 | When("2 - reconciliation create 2 policies", func() { 33 | JustBeforeEach(func() { 34 | response, err := Client().Reconciliation.V1.CreatePolicy( 35 | TestContext(), 36 | shared.PolicyRequest{ 37 | LedgerName: "default", 38 | LedgerQuery: map[string]interface{}{}, 39 | Name: "test 1", 40 | PaymentsPoolID: uuid.New().String(), 41 | }, 42 | ) 43 | Expect(err).ToNot(HaveOccurred()) 44 | Expect(response.StatusCode).To(Equal(201)) 45 | 46 | response, err = Client().Reconciliation.V1.CreatePolicy( 47 | TestContext(), 48 | shared.PolicyRequest{ 49 | LedgerName: "default", 50 | LedgerQuery: map[string]interface{}{}, 51 | Name: "test 2", 52 | PaymentsPoolID: uuid.New().String(), 53 | }, 54 | ) 55 | Expect(err).ToNot(HaveOccurred()) 56 | Expect(response.StatusCode).To(Equal(201)) 57 | }) 58 | Then("should list 2 policies", func() { 59 | var ( 60 | policies []shared.Policy 61 | ) 62 | JustBeforeEach(func() { 63 | policiesResponse, err := Client().Reconciliation.V1.ListPolicies( 64 | TestContext(), 65 | operations.ListPoliciesRequest{}, 66 | ) 67 | Expect(err).ToNot(HaveOccurred()) 68 | Expect(policiesResponse.StatusCode).To(Equal(200)) 69 | 70 | policies = policiesResponse.PoliciesCursorResponse.Cursor.Data 71 | }) 72 | It("should return 2 items", func() { 73 | Expect(policies).To(HaveLen(2)) 74 | }) 75 | }) 76 | }) 77 | }) 78 | -------------------------------------------------------------------------------- /tests/integration/suite/suite_test.go: -------------------------------------------------------------------------------- 1 | package suite_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestExamples(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Examples Testing Suite") 13 | } 14 | -------------------------------------------------------------------------------- /tests/integration/suite/utils.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | func ptr[T any](t T) *T { 4 | return &t 5 | } 6 | -------------------------------------------------------------------------------- /tests/integration/temporalite.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19 AS builder 2 | 3 | RUN mkdir -p ${GOPATH:-/go}/src/temporalite && \ 4 | git clone https://github.com/temporalio/temporalite.git ${GOPATH:-/go}/src/temporalite && \ 5 | cd ${GOPATH:-/go}/src/temporalite && \ 6 | git switch -c tmp v0.2.0 && \ 7 | go mod download && \ 8 | go get -d -v ./... && \ 9 | go build -o ${GOPATH:-/go}/bin/ ${GOPATH:-/go}/src/temporalite/cmd/temporalite 10 | 11 | FROM debian:stable-slim 12 | 13 | COPY --from=builder ${GOPATH:-/go}/bin/temporalite /bin 14 | EXPOSE 7233 8233 15 | 16 | ENTRYPOINT ["/bin/temporalite", "start", "--ephemeral", "-n", "default", "--ip" , "0.0.0.0"] 17 | -------------------------------------------------------------------------------- /tests/loadtesting/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/typescript"], 3 | "plugins": [ 4 | "@babel/proposal-class-properties", 5 | 6 | "@babel/proposal-object-rest-spread" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /tests/loadtesting/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn-error.log 3 | dist 4 | .idea 5 | .env 6 | .terraform 7 | k6 8 | results 9 | -------------------------------------------------------------------------------- /tests/loadtesting/.yarnclean: -------------------------------------------------------------------------------- 1 | # test directories 2 | __tests__ 3 | test 4 | tests 5 | powered-test 6 | 7 | # asset directories 8 | docs 9 | doc 10 | website 11 | images 12 | assets 13 | 14 | # examples 15 | example 16 | examples 17 | 18 | # code coverage directories 19 | coverage 20 | .nyc_output 21 | 22 | # build scripts 23 | Makefile 24 | Gulpfile.js 25 | Gruntfile.js 26 | 27 | # configs 28 | appveyor.yml 29 | circle.yml 30 | codeship-services.yml 31 | codeship-steps.yml 32 | wercker.yml 33 | .tern-project 34 | .gitattributes 35 | .editorconfig 36 | .*ignore 37 | .eslintrc 38 | .jshintrc 39 | .flowconfig 40 | .documentup.json 41 | .yarn-metadata.json 42 | .travis.yml 43 | 44 | # misc 45 | *.md 46 | -------------------------------------------------------------------------------- /tests/loadtesting/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-buster 2 | # Install ca-certificates 3 | RUN apt update && apt install -y ca-certificates curl && rm -rf /var/lib/apt/lists/* 4 | # Install k6 5 | ADD https://github.com/formancehq/xk6-extension/releases/download/v0.1.1/k6 /usr/bin/k6 6 | RUN chmod +x /usr/bin/k6 7 | # Build deps 8 | WORKDIR /k6 9 | COPY . . 10 | RUN yarn install 11 | RUN yarn run build 12 | ENTRYPOINT ["/usr/bin/k6"] 13 | -------------------------------------------------------------------------------- /tests/loadtesting/README.md: -------------------------------------------------------------------------------- 1 | # Load testing 2 | 3 | ## Structure 4 | 5 | Tests are located in tests folder. 6 | Libraries are located in libs folder. 7 | 8 | A task file help to start tests. 9 | 10 | ## Run a test 11 | 12 | ``` 13 | task tests 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /tests/loadtesting/Taskfile.yaml: -------------------------------------------------------------------------------- 1 | # https://taskfile.dev 2 | 3 | version: '3' 4 | 5 | vars: 6 | LEDGER_COMPOSE_FILE: -f docker-compose.ledger.yml 7 | 8 | tasks: 9 | tests: 10 | cmds: 11 | - docker compose {{.LEDGER_COMPOSE_FILE}} up --abort-on-container-exit --exit-code-from k6 --build 12 | - docker compose {{.LEDGER_COMPOSE_FILE}} down 13 | -------------------------------------------------------------------------------- /tests/loadtesting/docker-compose.ledger.yml: -------------------------------------------------------------------------------- 1 | volumes: 2 | postgres: 3 | services: 4 | k6: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | command: run /k6/dist/ledger-write.js 9 | depends_on: 10 | ledger: 11 | condition: service_healthy 12 | environment: 13 | LEDGER_URL: "http://ledger:3068" 14 | 15 | postgres: 16 | image: "postgres:13-alpine" 17 | healthcheck: 18 | test: [ "CMD-SHELL", "pg_isready -U ledger" ] 19 | interval: 10s 20 | timeout: 5s 21 | retries: 5 22 | ports: 23 | - "5432:5432" 24 | environment: 25 | POSTGRES_USER: "ledger" 26 | POSTGRES_PASSWORD: "ledger" 27 | POSTGRES_DB: "ledger" 28 | PGDATA: /data/postgres 29 | volumes: 30 | - postgres:/data/postgres 31 | 32 | ledger: 33 | image: "ghcr.io/formancehq/ledger:v1.10.4" 34 | healthcheck: 35 | test: [ "CMD", "wget", "http://127.0.0.1:3068/_info", "-O", "-", "-q" ] 36 | interval: 10s 37 | timeout: 5s 38 | retries: 5 39 | depends_on: 40 | postgres: 41 | condition: service_healthy 42 | environment: 43 | NUMARY_STORAGE_DRIVER: "postgres" 44 | NUMARY_STORAGE_POSTGRES_CONN_STRING: "postgresql://ledger:ledger@postgres/ledger" 45 | NUMARY_SERVER_HTTP_BIND_ADDRESS: "0.0.0.0:3068" 46 | # deploy: 47 | # resources: 48 | # limits: 49 | # cpus: "0.10" 50 | # memory: "25M" 51 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/1_3/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client'; 2 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/1_4/client.ts: -------------------------------------------------------------------------------- 1 | import {Client as Client1_3, Config, Headers} from "../1_3"; 2 | import {loadConfig} from "../../config"; 3 | 4 | export class Client extends Client1_3 { 5 | 6 | version = '1.4'; 7 | 8 | constructor( 9 | config: Config 10 | ) { 11 | super(config); 12 | } 13 | } 14 | 15 | export const newClient = () => { 16 | const config = loadConfig(); 17 | const headers: Headers = {}; 18 | return new Client({ 19 | endpoint: config.ledgerUrl, 20 | headers 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/1_4/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client'; 2 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/1_5/client.ts: -------------------------------------------------------------------------------- 1 | import {Account, Config, Headers, ListTransactionQuery as ListTransactionQuery1_3, Transaction} from "../1_3"; 2 | import {Client as Client1_4} from '../1_4'; 3 | import {loadConfig} from "../../config"; 4 | 5 | export interface ListTransactionQuery extends ListTransactionQuery1_3 { 6 | start_time: Date; 7 | end_time: Date; 8 | } 9 | 10 | export class Client extends Client1_4 { 11 | 12 | version = '1.5'; 13 | 14 | constructor( 15 | config: Config 16 | ) { 17 | super(config); 18 | } 19 | 20 | listTransactions(ledger: string, query?: Partial, tags?: { [name: string]: string }): Transaction[] { 21 | return super.listTransactions(ledger, { 22 | ...query, 23 | // @ts-ignore 24 | end_time: query?.end_time ? query.end_time.toISOString() : '', 25 | start_time: query?.start_time ? query.start_time.toISOString() : '', 26 | }, tags); 27 | } 28 | 29 | countTransactions(ledger: string, query?: Partial, tags?: { [p: string]: string }): number { 30 | return super.countTransactions(ledger, { 31 | ...query, 32 | // @ts-ignore 33 | end_time: query?.end_time ? query.end_time.toISOString() : '', 34 | start_time: query?.start_time ? query.start_time.toISOString() : '', 35 | }, tags); 36 | } 37 | 38 | listAccounts(ledger: string): Account[] { 39 | return super.listAccounts(ledger); 40 | } 41 | } 42 | 43 | export const newClient = () => { 44 | const config = loadConfig(); 45 | const headers: Headers = {}; 46 | return new Client({ 47 | endpoint: config.ledgerUrl, 48 | headers 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/1_5/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client'; 2 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/1_7/client.ts: -------------------------------------------------------------------------------- 1 | import {Account, Config, Headers, ListTransactionQuery as ListTransactionQuery1_3, Transaction} from "../1_3"; 2 | import {Client as Client1_4} from '../1_4'; 3 | import {loadConfig} from "../../config"; 4 | 5 | export interface ListTransactionQuery extends ListTransactionQuery1_3 { 6 | start_time: Date; 7 | end_time: Date; 8 | } 9 | 10 | export class Client extends Client1_4 { 11 | 12 | version = '1.7'; 13 | 14 | constructor( 15 | config: Config 16 | ) { 17 | super(config); 18 | } 19 | 20 | listTransactions(ledger: string, query?: Partial, tags?: { [name: string]: string }): Transaction[] { 21 | return super.listTransactions(ledger, { 22 | ...query, 23 | // @ts-ignore 24 | end_time: query?.end_time ? query.end_time.toISOString() : '', 25 | start_time: query?.start_time ? query.start_time.toISOString() : '', 26 | }, tags); 27 | } 28 | 29 | countTransactions(ledger: string, query?: Partial, tags?: { [p: string]: string }): number { 30 | return super.countTransactions(ledger, { 31 | ...query, 32 | // @ts-ignore 33 | end_time: query?.end_time ? query.end_time.toISOString() : '', 34 | start_time: query?.start_time ? query.start_time.toISOString() : '', 35 | }, tags); 36 | } 37 | 38 | listAccounts(ledger: string): Account[] { 39 | return super.listAccounts(ledger); 40 | } 41 | } 42 | 43 | export const newClient = () => { 44 | const config = loadConfig(); 45 | const headers: Headers = {}; 46 | return new Client({ 47 | endpoint: config.ledgerUrl, 48 | headers 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/1_7/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client'; 2 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/base/index.ts: -------------------------------------------------------------------------------- 1 | import http from "k6/http"; 2 | import {parseVersion, Version} from "../../core"; 3 | import {loadConfig} from "../../config"; 4 | 5 | export interface BaseClient { 6 | readonly version: string; 7 | } 8 | 9 | export const discoverServerVersion = (): Version => { 10 | const config = loadConfig(); 11 | const ret = http.get(config.ledgerUrl + '/_info'); 12 | 13 | return parseVersion(ret.json("data.version") as string); 14 | }; 15 | 16 | export const fromServerVersion = (version: string, callback: () => void) => { 17 | const serverVersion = discoverServerVersion(); 18 | if (serverVersion.lt(parseVersion(version))) { 19 | console.info(`Skip test, support is after version: ${version}`); 20 | return; 21 | } 22 | callback(); 23 | }; 24 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/client/index.ts: -------------------------------------------------------------------------------- 1 | import {Client as Client1_7, newClient as newClient1_7} from "./1_7"; 2 | import {Client as Client1_5, newClient as newClient1_5} from "./1_5"; 3 | import {Client as Client1_4, newClient as newClient1_4} from "./1_4"; 4 | import {Client as Client1_3, newClient as newClient1_3} from "./1_3"; 5 | 6 | export type ClientMapping = { 7 | '1.3': Client1_3, 8 | '1.4': Client1_4, 9 | '1.5': Client1_5, 10 | '1.7': Client1_7, 11 | }; 12 | 13 | export const withClient = (version: V, callback: (client: ClientMapping[V]) => any) => { 14 | let client: ClientMapping[V]; 15 | switch(version) { 16 | case "1.3": 17 | client = newClient1_3(); 18 | break; 19 | case "1.4": 20 | client = newClient1_4(); 21 | break; 22 | case "1.5": 23 | client = newClient1_5(); 24 | break; 25 | case "1.7": 26 | client = newClient1_7(); 27 | break; 28 | default: 29 | throw new Error("Invalid ledger version"); 30 | } 31 | return callback(client); 32 | }; 33 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/config/index.ts: -------------------------------------------------------------------------------- 1 | export const loadConfig = (): Index => { 2 | return { 3 | ledgerUrl: __ENV.LEDGER_URL, 4 | }; 5 | }; 6 | 7 | export interface Index { 8 | ledgerUrl: string; 9 | } 10 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/core/expectations.ts: -------------------------------------------------------------------------------- 1 | export const count = (v: number) => (t: any[]) => { 2 | return t.length === v; 3 | }; 4 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/core/index.ts: -------------------------------------------------------------------------------- 1 | import {ClientMapping, withClient} from "../client"; 2 | import {fromServerVersion} from "../client/base"; 3 | 4 | export * from './versions'; 5 | export * from './expectations'; 6 | 7 | export interface MatrixTest { 8 | serverVersion: string; 9 | clientVersion: keyof ClientMapping; 10 | } 11 | 12 | export const withMatrix = ( 13 | tests: T[], 14 | callback: (t: T, client: ClientMapping[T['clientVersion']]) => void, 15 | ) => { 16 | for (const test of tests) { 17 | fromServerVersion(test.serverVersion, () => { 18 | withClient(test.clientVersion, client => { 19 | callback(test, client); 20 | }); 21 | }); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /tests/loadtesting/libs/core/versions.ts: -------------------------------------------------------------------------------- 1 | const semverRegexp = /^v?(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:-(?(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; 2 | 3 | export class Version { 4 | constructor( 5 | public readonly major: number, 6 | public readonly minor: number, 7 | public readonly patch: number, 8 | ) { 9 | } 10 | 11 | lt(v: Version) { 12 | return this.major < v.major || 13 | this.minor < v.minor || 14 | this.patch < v.patch; 15 | } 16 | } 17 | 18 | export const parseVersion = (v: string) => { 19 | if (!semverRegexp.test(v)) { 20 | return new Version(1, 7, 4); 21 | // throw new Error(`Invalid version ${v}, not matching semver`); 22 | } 23 | const ret = semverRegexp.exec(v); 24 | const major = ret!.groups!.Major as string; 25 | const minor = ret!.groups!.Minor; 26 | const patch = ret!.groups!.Patch; 27 | 28 | return new Version( 29 | parseInt(major, 10), 30 | minor ? parseInt(minor, 10) : 0, 31 | patch ? parseInt(patch, 10) : 0 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /tests/loadtesting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "load-testing", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "@babel/core": "7.23.3", 8 | "@babel/plugin-proposal-class-properties": "7.18.6", 9 | "@babel/plugin-proposal-object-rest-spread": "7.20.7", 10 | "@babel/preset-env": "7.23.3", 11 | "@babel/preset-typescript": "7.23.3", 12 | "@types/k6": "~0.47.3", 13 | "@types/semver": "^7.5.6", 14 | "@types/uniqid": "^5.3.4", 15 | "@types/webpack": "5.28.5", 16 | "@typescript-eslint/eslint-plugin": "^6.12.0", 17 | "@typescript-eslint/parser": "^6.12.0", 18 | "babel-loader": "9.1.3", 19 | "clean-webpack-plugin": "4.0.0", 20 | "copy-webpack-plugin": "^11.0.0", 21 | "core-js": "^3.33.3", 22 | "eslint": "^8.54.0", 23 | "eslint-config-prettier": "^9.0.0", 24 | "eslint-plugin-jsdoc": "^46.9.0", 25 | "eslint-plugin-prefer-arrow": "^1.2.3", 26 | "semver": "^7.5.4", 27 | "typescript": "5.3.2", 28 | "uniqid": "^5.4.0", 29 | "webpack": "5.94.0", 30 | "webpack-cli": "5.1.4", 31 | "webpack-glob-entries": "^1.0.1" 32 | }, 33 | "scripts": { 34 | "build": "webpack" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/loadtesting/src/ledger/infos-test.ts: -------------------------------------------------------------------------------- 1 | import {withClient} from "../../libs/client"; 2 | 3 | export default () => { 4 | withClient('1.3', client => { 5 | client.getInfo(); 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /tests/loadtesting/src/ledger/stats-test.ts: -------------------------------------------------------------------------------- 1 | import {withClient} from "../../libs/client"; 2 | 3 | export default () => { 4 | withClient('1.3', client => { 5 | client.getStats(__ENV.LEDGER); 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /tests/loadtesting/tests/ledger-all.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import {Options} from 'k6/options'; 3 | import testInfo from "./../src/ledger/infos-test"; 4 | import testAccounts from "./../src/ledger/accounts-test"; 5 | import testTransactions from "./../src/ledger/transactions-test"; 6 | import testWriteTransactions from "./../src/ledger/write-transactions-test"; 7 | 8 | export let options: Options = { 9 | vus: 10, 10 | duration: '1m', 11 | }; 12 | 13 | export default () => { 14 | testInfo(); 15 | testAccounts(); 16 | testTransactions(); 17 | testWriteTransactions(); 18 | }; 19 | -------------------------------------------------------------------------------- /tests/loadtesting/tests/ledger-write.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import {Options} from 'k6/options'; 3 | import testInfo from "./../src/ledger/infos-test"; 4 | import testWriteTransactions from "./../src/ledger/write-transactions-test"; 5 | 6 | export let options: Options = { 7 | vus: 20, 8 | duration: '5m', 9 | }; 10 | 11 | export default () => { 12 | testInfo(); 13 | testWriteTransactions(); 14 | }; 15 | -------------------------------------------------------------------------------- /tests/loadtesting/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "noEmit": true, 7 | "allowJs": true, 8 | "removeComments": false, 9 | 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "noImplicitThis": true, 13 | 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | 19 | "allowSyntheticDefaultImports": true, 20 | "esModuleInterop": true, 21 | "experimentalDecorators": true, 22 | "emitDecoratorMetadata": true, 23 | 24 | "skipLibCheck": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/loadtesting/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | const CopyPlugin = require('copy-webpack-plugin'); 4 | const GlobEntries = require('webpack-glob-entries'); 5 | 6 | module.exports = { 7 | mode: 'production', 8 | entry: GlobEntries('./tests/*.ts'), // Generates multiple entry for each test 9 | output: { 10 | path: path.join(__dirname, 'dist'), 11 | libraryTarget: 'commonjs', 12 | filename: '[name].js', 13 | }, 14 | resolve: { 15 | extensions: ['.ts', '.js'], 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.ts$/, 21 | use: 'babel-loader', 22 | exclude: /node_modules/, 23 | }, 24 | ], 25 | }, 26 | target: 'web', 27 | externals: /^(k6|https?\:\/\/)(\/.*)?/, 28 | // Generate map files for compiled scripts 29 | devtool: "source-map", 30 | stats: { 31 | colors: true, 32 | }, 33 | plugins: [ 34 | new CleanWebpackPlugin(), 35 | // Copy assets to the destination folder 36 | // see `src/post-file-test.ts` for an test example using an asset 37 | new CopyPlugin({ 38 | patterns: [{ 39 | from: path.resolve(__dirname, 'assets'), 40 | noErrorOnMissing: true 41 | }], 42 | }), 43 | ], 44 | optimization: { 45 | // Don't minimize, as it's not used in the browser 46 | minimize: false, 47 | }, 48 | }; 49 | --------------------------------------------------------------------------------