├── .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 |
--------------------------------------------------------------------------------