9 Commits

Author SHA1 Message Date
f940f5a0f9 adding FIX stuff 2026-03-12 12:52:18 -03:00
ac285e662b fixes 2026-03-12 12:31:05 -03:00
0424e2ed79 makefile 2026-03-12 12:25:10 -03:00
072d44f3c3 changes 2026-03-12 12:14:57 -03:00
dcb1916c39 update library imports 2026-03-12 12:14:13 -03:00
c09a1fd21a adding quickfix library 2026-03-12 12:08:34 -03:00
9e55c5c562 changes 2026-03-11 11:04:53 -03:00
b45efdb297 FIX xml 2026-03-11 10:59:47 -03:00
aa0525a78c first commit 2026-03-11 10:54:11 -03:00
1397 changed files with 1903114 additions and 0 deletions

115
.gitignore vendored Normal file
View File

@ -0,0 +1,115 @@
# IDEA IDE
.idea/
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
# MAC cache files
.DS_Store
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# vscode config
.vscode
# JIRA plugin
atlassian-ide-plugin.xml
#-----------------------------------
# Golang
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
# GO compiled
/pkg/
# GO binaries
/bin/
# GO vendoring
/vendor/
# Project autogenerated documentation
/doc/
#-----------------------------------
# General
out.log
# generated binary
/main
# Autogenerated by go-bindata
# bindata.go
# Autogenerated strings by stringer for const values (enums)
/src/**/*_string.go
# Autogenerated files by go-enum for enum values.
/src/**/*_enum.go
# Binary tools
tools/bin/
# Build folders and files
/build/
todo.md
/logs/
conf.toml
src/client/api/rest/docs
.gitlab-ci-local
*conf.toml
diffs
.linter_version.txt
multiDBLogs/

View File

@ -0,0 +1,3 @@
---
MULTIDB_ACCESS_TOKEN: <ACCESS_TOKEN>
USERNAME: <USERNAME>

99
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,99 @@
# Official framework image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/node/tags/
image: golang:1.24
default:
before_script:
- go clean -modcache
- git config --global url."https://$USERNAME:$MULTIDB_ACCESS_TOKEN@gitlab.com/quantex-exchange".insteadOf "https://gitlab.com/quantex-exchange"
- export GOPRIVATE="quantex.com.ar/multidb"
- go get quantex.com.ar/multidb@v1.2.2
- make gogenerate
- make swag
- go mod vendor
artifacts:
when: always
paths:
- qfixpt.gz
stages:
- lint
- build
regular:
stage: build
needs: []
except:
- master
- develop
- demo
- open-demo
script:
- go clean -cache
- make gogenerate
- make swag
- make test
- make check
- make build
- mv build/out/distribution/qfixpt.gz .
develop:
stage: build
needs: []
only:
- develop
script:
- make gogenerate
- make swag
- make test
- make only-build
- mv build/out/distribution/qfixpt.gz .
environment:
name: dev
url: https://dev.quantex.com.ar
master:
stage: build
needs: []
only:
- master
script:
- go clean -cache
- make gogenerate
- make swag
- make test
- make build
- mv build/out/distribution/qfixpt.gz .
environment:
name: prod
url: https://app.quantex.com.ar
demo:
stage: build
needs: []
only:
- demo
script:
- make test
- make only-build
- apt-get update
- apt-get install sshpass
- mv build/out/distribution/qfixpt.gz .
environment:
name: demo
url: https://demo.quantex.com.ar
open-demo:
stage: build
needs: []
only:
- open-demo
script:
- make test
- make only-build
- apt-get update
- apt-get install sshpass
- mv build/out/distribution/qfixpt.gz .
environment:
name: open-demo
url: https://open-demo.quantex.com.ar

View File

@ -0,0 +1,26 @@
## Issue Type
- [ ] feature
- [ ] hotfix
- [ ] refactor
- [ ] improvement
- [ ] doc
- [ ] fix
## ❌ Issue Description
Summary_of_what_is_the_issue_and_include_screenshots_and_links_of_slack_to_follow_the_problem
## ✅ Expected Behaviuor
Explain_little_about_how_it_should_be_working
## 📋 Steps to Reproduce
What_steps_should_someone_take_to_reproduce_the_issue_include_screenshots_and_links
## 🐛 Possible Impacts
What_other_areas_in_our_code_might_be_affected_or_is_it_possible_that_something_else_broke_with_these_changes

View File

@ -0,0 +1,30 @@
## 🐛 Reason to Be
Double_click_to_replace_with_a_brief_summary_of_what_this_MR_does_including_a_summary_of_the_original_issue_and_include_screenshots_and_links_to_designs_if_this_MR_has_a_UI_component
Resolves #Issue_number
Related #Issue_number
## ✅ Expected Behaviuor
Explain_little_about_how_is_it_working_in_the_current_MR
## 📋 How To Test
What_steps_should_someone_take_to_test_these_changes_include_screenshots_and_links_to_mocks_for_any_ui_work
## ⚠️ Check List
1. [ ] Title format _**SKL-33: Implement Logging using Qapix Endpoints**_ (PREFIX-Issue#: Message)
2. [ ] Run `make ci` at your end
3. [ ] Check the pipeline is **🟢 SUCCESS**
4. [ ] Set the Assignee and Reviewer
5. [ ] Create 2 PRs for ⚡ HOTFIX in master and develop

24
AGENTS.md Normal file
View File

@ -0,0 +1,24 @@
# Repository Guidelines
## Project Structure & Module Organization
The service boots from `main.go`, and all Go packages live under `src`. Use `src/app` for orchestration, `src/domain` for entities and ports, `src/common` for shared helpers, `src/client/api/rest` for HTTP handlers and Swagger assets, and `src/cmd` for runnable entrypoints. Configuration samples live in `conf.toml`, `my_global_conf.toml`, and `res/`, while automation scripts sit in `build/` and `tools/`; vendor-locked dependencies remain in `vendor/` for reproducible builds.
## Build, Test, and Development Commands
- `make dev` runs the contributor checklist plus lint gate before coding.
- `make check` installs toolchains and executes golangci-lint, gosec, revive, errcheck, and dependency audits.
- `make test` calls `go test ./...`; use `go test ./src/... -run TestName` for a focused run.
- `make build e=dev` compiles via `tools/build.sh`; switch `e` to `prod`, `demo`, or `open-demo`.
- `QUANTEX_ENVIRONMENT="dev" go run main.go -c -run service` starts the service with text logs (omit `-c` for JSON).
- `make deploy e=<alias>` pushes a build using the target defined in `build/deploy.sh`.
## Coding Style & Naming Conventions
Run `make fmt` to apply gofumpt, gci, and goimports; Gos formatter enforces tab indentation and canonical spacing. Follow idiomatic naming—PascalCase for exported symbols, camelCase for internals, lowercase filenames—and keep package paths under `quantex.com/qfixpt/<module>`. Group imports as standard library, third-party, then `quantex.com`, and avoid formatting-only commits.
## Testing Guidelines
Keep `*_test.go` files beside the code and prefer table-driven cases. Guard integration tests with `QUANTEX_ENVIRONMENT` so they hit the intended backend, and regenerate Swagger with `make swag` whenever REST contracts change. Spot-check concurrency with `go test -race ./src/<package>` and store fixtures under package-level `testdata/` directories.
## Commit & Pull Request Guidelines
Use `make branch t=<type> u=<user> n=<issue> m="summary"` to produce names such as `feature/jdoe/SKL-123/add_logger`. Write commit subjects in the imperative mood, referencing `SKL-###` when relevant. Pull requests must include a concise summary, linked issue, confirmation of `make test` and `make check`, and evidence (logs or screenshots) when altering responses or logging; call out configuration changes explicitly.
## Configuration & Deployment Tips
Treat `conf.toml` and `my_global_conf.toml` as templates and override secrets via environment variables. Confirm build metadata with `make print-version` before deploying, and promote builds through `make deploy e=<alias>`. When exploring logging formats pass `-f text|json|tint1|tint2`, and follow the `README.md` guidance for multi-DB runs that require elevated permissions.

138
Makefile Normal file
View File

@ -0,0 +1,138 @@
# Copyright 2019 PingCAP, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.
include Makefile.common
.PHONY: help all clean dev check checklist tidy
vendor:
go mod vendor
push: vendor ci check-dirty ## Push the current branch to remote server
git push origin $$(git rev-parse --abbrev-ref HEAD)
branch: ## Create a new branch and checkout it. Usage: make branch (interactive)
@./tools/branch.sh
dev: checklist check
# Note from Fede:
# See https://github.com/golang/go/issues/44129
# If issues run: go env -w GOFLAGS=-mod=mod
# Run swag just in case
ci: swag fmt tidy check ## Run formatters (could modify the code), checks and linters
check: check-static dependencies-check ## Run checks and linters
download-versions:
curl --url "https://app.quantex.com.ar/linter_version.txt" -o $(linter_version_file)
test: ## Run all the tests
go test ./...
VALID_ENV_TYPES := prod dev demo open-demo
check-env:
@if [ "$(filter $(e),$(VALID_ENV_TYPES))" != "$(e)" ]; then \
echo "\033[31mError: Invalid environment type $(e). Types can be: prod, dev, demo, open-demo"; \
exit 1; \
fi;
# set dev as default
$(eval e := dev)
print-version: check-env ## Print the version of the latest build
OUT_PATH=$(DEFAULT_OUT_PATH) tools/print-version.sh $(e)
build: check-env swag vendor only-build # Build a native version. Set e=environment: prod, dev, demo, open-demo
only-build: check-env
@echo "Building for $(e) environment..."
env OUT_PATH=$(DEFAULT_OUT_PATH) GOARCH=amd64 GOOS=linux tools/build.sh $(e)
linux-build: check-env swag # Build a linux version for prod environment. Set e=environment: prod, dev, demo, open-demo
env OUT_PATH=$(DEFAULT_OUT_PATH) GOARCH=amd64 GOOS=linux tools/build.sh $(e)
deploy: check-env # Deploy to remote server. Set e=environment: prod, dev, demo, open-demo; s=serverName; i=instance; e.g. make deploy e=dev s=nonprodFix i=qfixpt
make build e=$(e) && qscp build/out/distribution/qfixdpl.gz $(s):/home/quantex/qfixtb/$(i)/
fmt: download-versions # Apply the Go formatter to the code
cd tools/check; unset GOPATH; GOBIN=$$PWD/../bin go install mvdan.cc/gofumpt@$(call get_version,gofumpt);
@echo "running fmt..."
@echo $(GIT_TREE_STATE)
@echo "> running gofumpt..."
@tools/bin/gofumpt -l -w main.go 2>&1
@tools/bin/gofumpt -l -w $(FILES) 2>&1
swag: download-versions
@echo "installing swag..."
cd tools/check; unset GOPATH; GOBIN=$$PWD/../bin go install github.com/swaggo/swag/cmd/swag@$(call get_version,swag);
@echo "running swag..."
rm -f src/client/api/rest/docs/swagger.json
rm -f src/client/api/rest/docs/swagger.yaml
rm -f src/client/api/rest/docs/docs.go
mkdir -p src/client/api/rest/docs
echo "package docs" > src/client/api/rest/docs/docs.go # This empty file is needed for the initial build. Then is overwritten by Swag.
tools/bin/swag init --parseDependency -g server.go -d src/client/api/rest -o src/client/api/rest/docs/
check-static: download-versions
@echo "installing golangci-lint..."
cd tools/check; unset GOPATH; GOBIN=$$PWD/../bin go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(call get_version,golangci-lint);
@echo "running golangci-lint..."
@echo $$($(PACKAGE_DIRECTORIES))
tools/bin/golangci-lint run -v -c tools/check/golangci.yml --timeout 5m0s ./...
check-dirty:
$(if $(filter dirty,$(GIT_TREE_STATE)), $(error git state is not clean <$(GIT_TREE_STATE)>))
dependencies-check: download-versions
@echo "installing govulncheck..."
cd tools/check; unset GOPATH; GOBIN=$$PWD/../bin go install golang.org/x/vuln/cmd/govulncheck@$(call get_version,govulncheck);
@echo "running govulncheck..."
@tools/bin/govulncheck -show verbose ./...
gogenerate: download-versions
@echo "installing go-enum..."
cd tools/check; unset GOPATH; GOBIN=$$PWD/../bin go install github.com/abice/go-enum@$(call get_version,go-enum);
@echo "running go generate ./.."
./tools/check/check-gogenerate.sh
tidy:
@echo "running go mod tidy..."
./tools/check/check-tidy.sh
server_coverage:
ifeq ($(TARGET), "")
$(GOBUILDCOVERAGE) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(COVERAGE_SERVER_LDFLAGS) $(CHECK_FLAG)' -o ../bin/bitlab-coverage
else
$(GOBUILDCOVERAGE) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(COVERAGE_SERVER_LDFLAGS) $(CHECK_FLAG)' -o '$(TARGET)'
endif
checklist:
cat checklist.md
linter_version_file = .linter_version.txt
define get_version
$(shell grep "^$(1):" $(linter_version_file) | cut -d: -f2 | tr -d ' ')
endef
init: ## Install dependencies to run the project
make swag
make gogenerate
go install ./...
# Absolutely awesome: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
.DEFAULT_GOAL := help

62
Makefile.common Normal file
View File

@ -0,0 +1,62 @@
# Copyright 2020 PingCAP, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.
##################################
# Set this values for each project
PROJECT := qfixpt
DOMAIN := quantex.com
##################################
ISSUE_PREFIX := QPT
GOPATH ?= $(shell go env GOPATH)
P=8
GIT_TREE_STATE=$(shell (git status --porcelain | grep -q .) && echo dirty || echo clean)
# Ensure GOPATH is set before running build process.
ifeq "$(GOPATH)" ""
$(error Please set the environment variable GOPATH before running `make`)
endif
FAIL_ON_STDOUT := awk '{ print } END { if (NR > 0) { exit 1 } }'
CURDIR := $(shell pwd)
path_to_add := $(addsuffix /bin,$(subst :,/bin:,$(GOPATH))):$(PWD)/tools/bin
export PATH := $(path_to_add):$(PATH)
GOBUILD := go build $(BUILD_FLAG) -tags codes
GOBUILDCOVERAGE := GOPATH=$(GOPATH) cd tidb-server; $(GO) test -coverpkg="../..." -c .
GOTEST := go test -p $(P)
LINUX := "Linux"
MAC := "Darwin"
# PACKAGE_LIST := go list ./...| grep -vE "cmd|$(DOMAIN)\/$(PROJECT)\/tests"
PACKAGE_LIST := go list ./...| grep -vE "$(DOMAIN)\/$(PROJECT)\/tests"
PACKAGES ?= $$($(PACKAGE_LIST))
PACKAGE_DIRECTORIES := $(PACKAGE_LIST) | sed 's|$(DOMAIN)/$(PROJECT)/||' | sed 's|$(DOMAIN)/$(PROJECT)||'
FILES := $$(find $$($(PACKAGE_DIRECTORIES)) -name "*.go")
DEFAULT_OUT_PATH := "./build/out/distribution"
RACE_FLAG =
ifeq ("$(WITH_RACE)", "1")
RACE_FLAG = -race
GOBUILD = GOPATH=$(GOPATH) $(GO) build
endif
CHECK_FLAG =
ifeq ("$(WITH_CHECK)", "1")
CHECK_FLAG = $(TEST_LDFLAGS)
endif

31
checklist.md Normal file
View File

@ -0,0 +1,31 @@
# Following the checklist saves the reviewers' time and gets your PR reviewed faster.
# Self Review
Have you reviewed every line of your changes by yourself?
# Test
Have you added enough test cases to cover the new feature or bug fix? Also, add comments to describe your test cases.
# Naming
Do function names keep consistent with its behavior? Is it easy to infer the function's behavior by its name?
# Comment
Is there any code that confuses the reviewer? Add comments on them! You'll be asked to do so anyway. Make sure there is
no syntax or spelling error in your comments. Some online syntax checking tools like Grammarly may be helpful.
# Refactor
Is there any way to refactor the code to make it more readable? If the refactoring touches a lot of existing code, send
another PR to do it.
# Single Purpose
Make sure the PR does only one thing and nothing else.
# Diff Size
Make sure the diff size is no more than 500, split it into small PRs if it is too large.

View File

@ -0,0 +1,417 @@
# JWT Service-to-Service Authentication
## Overview
Services authenticate with each other using JWT tokens. The system uses **two-factor authentication**: JWT signature validation + user credential validation against the database.
---
## How It Works
### Outgoing Request (Skeleton → QApix)
```
1. Skeleton wants to call QApix
2. Creates JWT with:
- iss: "Skeleton"
- token: "franco:bpa31kniubroq28rpvg010" (from External.QApix.Token in config)
- iat: current timestamp
3. Signs JWT with QAPIX_QUANTEX_SECRET_KEY
4. Sends request with header: Authorization: {JWT}
```
### Incoming Request (QApix → Skeleton)
```
1. Skeleton receives JWT from QApix
2. Validates JWT signature with SKELETON_QUANTEX_SECRET_KEY
3. Checks "QApix" is in AuthorizedServices config
4. Checks QApix has required permissions (currently FullAccess)
5. Extracts token claim: "franco:bpa31kniubroq28rpvg010"
6. Looks up user "franco" in database
7. Verifies password "bpa31kniubroq28rpvg010" matches
8. ✅ Request allowed
```
---
## Configuration
### conf.toml
```toml
# Enable/disable JWT authentication
EnableJWTAuth = true # Set to true to enable JWT authentication checks
# Who we call (outgoing requests)
[External.QApix]
Name = "QApix"
Token = "franco:bpa31kniubroq28rpvg010" # Credentials we use to authenticate
Host = "https://qapix.example.com"
Port = "5001"
# Who can call us (incoming requests)
[AuthorizedServices.QApix]
Name = "QApix"
Permissions = ["FullAccess"] # What QApix is allowed to do
# Token validated against user DB - not stored in config
```
**Configuration Options:**
- `EnableJWTAuth` (bool): Controls whether JWT authentication is enabled
- `true`: Enables JWT authentication and health check validation
- `false`: Disables JWT authentication features (default for local development)
### Environment Variables
```bash
# On Skeleton
export SKELETON_QUANTEX_SECRET_KEY="..." # Validates incoming JWTs
export QAPIX_QUANTEX_SECRET_KEY="..." # Signs outgoing JWTs to QApix
# On QApix
export QAPIX_QUANTEX_SECRET_KEY="..." # Validates incoming JWTs
export SKELETON_QUANTEX_SECRET_KEY="..." # Signs outgoing JWTs to Skeleton
```
**Important**: Secret keys must match across services:
- `SKELETON_QUANTEX_SECRET_KEY` on Skeleton = `SKELETON_QUANTEX_SECRET_KEY` on QApix
- `QAPIX_QUANTEX_SECRET_KEY` on Skeleton = `QAPIX_QUANTEX_SECRET_KEY` on QApix
---
## Security Layers
| Layer | Description | Purpose |
|-------|-------------|---------|
| 1. JWT Signature | Validates HS256 signature | Proves caller has secret key |
| 2. Issuer Whitelist | Checks AuthorizedServices | Only known services allowed |
| 3. Permission Check | Validates service permissions | ReadOnly/ReadWrite/FullAccess |
| 4. User Validation | Checks token against user DB | Real credentials required |
---
## Code Flow
### Creating JWT
**Location**: `src/common/jwttoken/jwt.go`
```go
func Encrypt(auth app.ExtAuth) (string, error)
```
**Usage**:
```go
auth := app.ExtAuth{
Name: "QApix",
Token: "franco:bpa31kniubroq28rpvg010",
}
jwt, err := jwttoken.Encrypt(auth)
// Returns signed JWT token
```
### Validating JWT
**Location**: `src/common/jwttoken/jwt.go`
```go
func Validate(token string, authServices map[string]AuthorizedService) (*AuthorizedService, error)
```
**Usage**:
```go
service, err := jwttoken.Validate(jwtToken, config.AuthorizedServices)
// Returns authorized service with token and permissions
```
### Middleware
**Location**: `src/client/api/rest/midlewares.go`
```go
func (cont *Controller) JWTTokenAuth(reqToken string, ctx *gin.Context)
```
**Flow**:
1. Receives JWT from Authorization header
2. Calls `jwttoken.Validate()` to verify JWT and check permissions
3. Calls `validateServiceToken(token)` to verify user credentials in database
4. Sets `authorized_service` in Gin context
5. Proceeds to next handler
---
## JWT Token Structure
### Claims
```json
{
"iss": "Skeleton", // Issuer (who created the token)
"token": "franco:bpa31kniubroq28rpvg010", // User credentials
"iat": 1704481490 // Issued at (Unix timestamp)
}
```
### Header
```json
{
"alg": "HS256", // Algorithm (HMAC SHA-256)
"typ": "JWT" // Type
}
```
---
## Service Permissions
Defined in `src/app/model.go`:
```go
type ServicePermission int
const (
ServicePermissionReadOnly // Read-only access
ServicePermissionReadWrite // Read and write access
ServicePermissionFullAccess // Full access to all operations
ServicePermissionUndefined // No permissions
)
```
**Permission Check**:
```go
service.HasPermissions(app.ServicePermissionFullAccess) // Returns bool
```
---
## Key Benefits
-**No credentials in config** - Token validated against user database
-**Recipient controls access** - Permissions defined by recipient, not sender
-**Two-factor auth** - Secret key + user credentials
-**Easy rotation** - Change user password → JWT auth automatically updates
-**Audit trail** - Know which service user made each request
-**Single source of truth** - User credentials only in database
-**Configurable** - Enable/disable JWT auth per environment via `EnableJWTAuth`
---
## Testing
### Generate JWT Token
```bash
# Set environment variable
export SKELETON_QUANTEX_SECRET_KEY="your-secret-key"
# Run generator
cd tools
./generate-jwt.sh
# Follow prompts:
# Issuer: Skeleton
# Service: SKELETON
# Token: franco:bpa31kniubroq28rpvg010
```
### Test with curl
```bash
# Store generated token
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# Test endpoint
curl -H "Authorization: $TOKEN" \
http://localhost:8097/api/v1/endpoint
# Expected: 200 OK (if token valid and user exists)
# Expected: 401 Unauthorized (if token invalid or user not found)
```
### Test Scenarios
| Scenario | Expected Result |
|----------|-----------------|
| Valid JWT + Valid user | ✅ 200 OK |
| Valid JWT + Invalid user | ❌ 401 Unauthorized |
| Invalid JWT signature | ❌ 401 Unauthorized |
| Unknown issuer | ❌ 401 Unauthorized |
| Insufficient permissions | ❌ 403 Forbidden |
---
## Authentication Flow Diagram
```
┌─────────────┐ ┌─────────────┐
│ Skeleton │ │ QApix │
└──────┬──────┘ └──────┬──────┘
│ │
│ 1. Create JWT │
│ - iss: "Skeleton" │
│ - token: "franco:password" │
│ │
│ 2. Sign with QAPIX_QUANTEX_SECRET_KEY │
│ │
│ 3. HTTP Request │
│ Authorization: {JWT} │
├─────────────────────────────────────────────────>│
│ │
│ 4. Decrypt JWT│
│ (QAPIX_QUANTEX_SECRET_KEY) │
│ │
│ 5. Validate claims │
│ (iss, token) │
│ │
│ 6. Check AuthorizedServices│
│ ("Skeleton" allowed?)│
│ │
│ 7. Check permissions │
│ (FullAccess?) │
│ │
│ 8. Validate user in DB │
│ (franco:password exists?)│
│ │
│ 9. Response (200 OK or 401/403) │
│<─────────────────────────────────────────────────┤
│ │
```
---
## Troubleshooting
### Common Issues
**"Invalid JWT"**
- Check secret key environment variable is set
- Verify secret keys match on both services
**"Service not authorized"**
- Add service to `AuthorizedServices` in conf.toml
- Verify `Name` matches JWT `iss` claim
**"Invalid credentials"**
- Check user exists in database
- Verify token format is `email:password`
- Ensure password matches user record
**"Insufficient permissions"**
- Check service has required permission in `AuthorizedServices`
- Update `Permissions` array in conf.toml
---
## Files Reference
| File | Purpose |
|------|---------|
| `src/common/jwttoken/jwt.go` | JWT encryption, decryption, validation |
| `src/client/api/rest/midlewares.go` | HTTP middleware for JWT authentication |
| `src/client/api/rest/controller.go` | REST controllers including health check with JWT validation |
| `src/app/model.go` | AuthorizedService, ServicePermission types, EnableJWTAuth config |
| `src/client/api/rest/server.go` | REST API server configuration |
| `src/cmd/service/service.go` | Service runner that initializes JWT config |
| `conf.toml` | Service configuration |
| `tools/generate-jwt.sh` | JWT token generator for testing |
---
## Migration Notes
### From Old System
**Before** (self-declared permissions):
```toml
[External.QApix]
Token = "franco:password"
Permissions = ["QApixAccess"] # ❌ Sender declares own permissions
```
**After** (recipient-controlled):
```toml
[External.QApix]
Token = "franco:password"
# ✅ No permissions - recipient decides
[AuthorizedServices.QApix]
Permissions = ["FullAccess"] # ✅ Recipient controls access
```
### Deployment Steps
1. Update `conf.toml` on both services
- Set `EnableJWTAuth = true` for production environments
- Set `EnableJWTAuth = false` for local development
- Configure `External` and `AuthorizedServices` sections
2. Set environment variables for secret keys
3. Restart services
4. Test with `generate-jwt.sh`
5. Verify health endpoint shows `jwtAuthentications: "ok"`
6. Monitor logs for authentication errors
---
## Security Best Practices
1. **Rotate Secret Keys Regularly** - Update environment variables periodically
2. **Use Strong Secrets** - Minimum 32 bytes, cryptographically random
3. **HTTPS Only** - Never send JWTs over unencrypted connections
4. **Monitor Auth Logs** - Track failed authentication attempts
5. **Principle of Least Privilege** - Use ReadOnly when possible
6. **Separate Service Users** - Don't share user credentials across services
---
## Health Check Endpoint
The `/health` endpoint provides service health status and optionally includes JWT authentication validation when enabled.
### Response Structure
**When `EnableJWTAuth = false`** (default for local development):
```json
{
"status": "ok",
"build": "feature-branch",
"sha": "abc123def"
}
```
**When `EnableJWTAuth = true`** (production):
```json
{
"status": "ok",
"build": "feature-branch",
"sha": "abc123def",
"jwtAuthentications": "ok"
}
```
**When JWT validation fails** (`EnableJWTAuth = true`):
```json
{
"status": "degraded",
"build": "feature-branch",
"sha": "abc123def",
"jwtAuthentications": "error"
}
```
### Health Check Validation
When `EnableJWTAuth = true`, the health endpoint validates JWT authentication by:
1. Attempting to fetch a test user from the database
2. Verifying the database connection is working
3. Returning `jwtAuthentications: "ok"` if successful
4. Returning `jwtAuthentications: "error"` and `status: "degraded"` if validation fails
**Location**: `src/client/api/rest/controller.go:178`
---
## Additional Resources
- [JWT RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)
- [golang-jwt/jwt](https://github.com/golang-jwt/jwt)
- Internal: `tools/README-JWT-GENERATOR.md`

125
go.mod Normal file
View File

@ -0,0 +1,125 @@
module quantex.com/qfixpt
go 1.24.6
require (
github.com/BurntSushi/toml v1.4.0
github.com/eclipse/paho.mqtt.golang v1.5.1
github.com/gin-gonic/gin v1.10.0
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/gomodule/redigo v1.9.2
github.com/mattn/go-sqlite3 v1.14.34
github.com/mitchellh/panicwrap v1.0.0
github.com/pires/go-proxyproto v0.11.0
github.com/pkg/errors v0.9.1
github.com/quagmt/udecimal v1.10.0
github.com/rs/zerolog v1.15.0
github.com/sasha-s/go-deadlock v0.3.5
github.com/satori/go.uuid v1.2.0
github.com/shopspring/decimal v1.4.0
github.com/stretchr/testify v1.9.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.4
go.mongodb.org/mongo-driver v1.17.9
golang.org/x/net v0.47.0
quantex.com.ar/multidb v1.2.2
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 // indirect
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.12.4 // indirect
github.com/bytedance/sonic/loader v0.2.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/codenotary/immudb v1.9.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.3 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgtype v1.14.4 // indirect
github.com/jackc/pgx/v4 v4.18.3 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/o1egl/paseto v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/petermattis/goid v0.0.0-20241025130422-66cb2e6d7274 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.12.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/tools v0.38.0 // indirect
google.golang.org/genproto v0.0.0-20241113154021-e0fbfb71d213 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241113154021-e0fbfb71d213 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113154021-e0fbfb71d213 // indirect
google.golang.org/grpc v1.68.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

532
go.sum Normal file
View File

@ -0,0 +1,532 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU=
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 h1:1DcvRPZOdbQRg5nAHt2jrc5QbV0AGuhDdfQI6gXjiFE=
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k=
github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/codenotary/immudb v1.9.5 h1:z4DdsDLalQFVcuOktLw2H95BjFnvFL4TxY2tkYwOkkk=
github.com/codenotary/immudb v1.9.5/go.mod h1:+Sex0kDu5F1hE+ydm9p+mpZixjlSeBqrgUZUjNayrNg=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8=
github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/panicwrap v1.0.0 h1:67zIyVakCIvcs69A0FGfZjBdPleaonSgGlXRSRlb6fE=
github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/o1egl/paseto v1.0.0 h1:bwpvPu2au176w4IBlhbyUv/S5VPptERIA99Oap5qUd0=
github.com/o1egl/paseto v1.0.0/go.mod h1:5HxsZPmw/3RI2pAwGo1HhOOwSdvBpcuVzO7uDkm+CLU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/petermattis/goid v0.0.0-20241025130422-66cb2e6d7274 h1:qli3BGQK0tYDkSEvZ/FzZTi9ZrOX86Q6CIhKLGc489A=
github.com/petermattis/goid v0.0.0-20241025130422-66cb2e6d7274/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/quagmt/udecimal v1.10.0 h1:r8VgL1ns0HruRrTX4aRRIE4J5FGdjd1sGRwG7eZGx7A=
github.com/quagmt/udecimal v1.10.0/go.mod h1:ScmJ/xTGZcEoYiyMMzgDLn79PEJHcMBiJ4NNRT3FirA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20241113154021-e0fbfb71d213 h1:aK1CSq3+WIXZj6XcWhK0fAb303nF8sta2eca0DSARwQ=
google.golang.org/genproto v0.0.0-20241113154021-e0fbfb71d213/go.mod h1:Q5m6g8b5KaFFzsQFIGdJkSJDGeJiybVenoYFMMa3ohI=
google.golang.org/genproto/googleapis/api v0.0.0-20241113154021-e0fbfb71d213 h1:cNftAhx0Q32f3Fz2+BLargfsMD6pGINE+/mUZneTMyk=
google.golang.org/genproto/googleapis/api v0.0.0-20241113154021-e0fbfb71d213/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113154021-e0fbfb71d213 h1:L+WcQXqkyf5MX6g7AudgYEuJjmYbqSRkTmJqGfAPw+Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113154021-e0fbfb71d213/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
quantex.com.ar/multidb v1.2.2 h1:vYZkOI/srWFlfUk8ZsbmcNp7OWiPWflBnaLYaPTPgAs=
quantex.com.ar/multidb v1.2.2/go.mod h1:aIxLGHeesPkmSocw/VSZBc+pGDmxx7QRUSd+TxjAFFc=

1
last_update_info.txt Normal file
View File

@ -0,0 +1 @@
Thu Feb 19 05:35:16 2026 -0600

12
linter_version.txt Normal file
View File

@ -0,0 +1,12 @@
golangci-lint: v1.63.4
revive: v1.5.1
gosec: v2.22.0
errcheck: v1.8.0
unconvert: latest
goimports: v0.29.0
gci: v0.13.5
gofumpt: v0.7.0
staticcheck: v0.5.1
swag: v1.16.4
go-enum: v0.6.0
govulncheck: latest

206
main.go Normal file
View File

@ -0,0 +1,206 @@
// package qfixpt is a Micro service quantex base project
package main
import (
"embed"
"flag"
"fmt"
"log/slog"
"os"
"time"
"github.com/mitchellh/panicwrap"
"quantex.com/qfixpt/src/app"
"quantex.com/qfixpt/src/app/mode"
"quantex.com/qfixpt/src/app/version"
"quantex.com/qfixpt/src/client/config"
googlechat "quantex.com/qfixpt/src/client/notify/google"
"quantex.com/qfixpt/src/client/res"
"quantex.com/qfixpt/src/cmd"
"quantex.com/qfixpt/src/cmd/example"
"quantex.com/qfixpt/src/cmd/service"
"quantex.com/qfixpt/src/common/logger"
"quantex.com/qfixpt/src/common/logger/tint"
"quantex.com/qfixpt/src/domain"
)
// Embed the entire directory.
//go:embed res
var resources embed.FS
type flagsType struct {
runner, globalCfg, serviceCfg, logLevel, logFormat string
debug, version bool
}
func main() {
flags := parseFlags()
if len(os.Args) == 1 {
flag.Usage()
return
}
if flags.version {
fmt.Printf("%s version %s", version.AppName, version.Info())
return
}
cfg, err := config.Read([]string{flags.globalCfg, flags.serviceCfg})
if err != nil {
panic(fmt.Sprintf("Something went wrong reading the config. %s", err))
}
notify := googlechat.New(cfg.Notify.Google)
exitStatus, err := panicwrap.BasicWrap(func(output string) {
notify.SendMsg(domain.MessageChannelPanic, output, domain.MessageStatusStopper, nil)
os.Exit(1)
})
if err != nil {
// Something went wrong setting up the panic wrapper.
panic(fmt.Sprintf("Something went wrong setting up the panic wrapper. %s", err))
}
// If exitStatus >= 0, then we're the parent process and the panicwrap
// re-executed ourselves and completed. Just exit with the proper status.
if exitStatus >= 0 {
os.Exit(exitStatus)
}
handler, err := newLogHandler(flags)
if err != nil {
panic(err)
}
hook := logger.NewQuantexHandler(handler, []slog.Level{slog.LevelError, slog.LevelWarn})
slog.SetDefault(slog.New(hook))
slog.Info("New Run ----------------------------------------------------------------------------")
slog.Debug(fmt.Sprintf("flags: %+v", flags))
v := version.AppName + " version " + version.Info()
fmt.Println(v)
slog.Info(v)
setDebugMode(flags.debug)
startRunner(flags.runner, flags.globalCfg, flags.serviceCfg)
}
func parseFlags() (f flagsType) {
fmt.Println(f.runner)
flag.StringVar(&f.runner, "run", "", "run the specified service")
flag.StringVar(&f.globalCfg, "global-cfg", "../global_conf.toml", "set the config global file name")
flag.StringVar(&f.serviceCfg, "service-cfg", "conf.toml", "set the config service file name")
const u0 = "set the logs output format: text, json, tint1 (one line), tint2 (two lines)"
flag.StringVar(&f.logFormat, "logs-format", "json", u0)
flag.StringVar(&f.logFormat, "f", "json", u0+" (shorthand)")
const u1 = "set the debug mode"
flag.BoolVar(&f.debug, "debug", false, u1)
flag.BoolVar(&f.debug, "d", false, u1+" (shorthand)")
const u2 = "show the program version"
flag.BoolVar(&f.version, "version", false, u2)
flag.BoolVar(&f.version, "v", false, u2+" (shorthand)")
const u3 = "set the log level: debug, info, warn, error"
flag.StringVar(&f.logLevel, "level", "info", u3)
flag.StringVar(&f.logLevel, "l", "info", u3+" (shorthand)")
flag.Parse()
return f
}
func newLogHandler(flags flagsType) (slog.Handler, error) {
logLevel, err := parseLogLevel(flags.logLevel)
if err != nil {
return nil, err
}
switch flags.logFormat {
case "json":
return logger.NewJSONHandler(logLevel), nil
case "text":
return logger.NewTextHandler(logLevel), nil
case "tint1":
return logger.NewTextHandlerTint(logLevel, tint.OneLine), nil
case "tint2":
return logger.NewTextHandlerTint(logLevel, tint.TwoLines), nil
default:
flag.PrintDefaults()
return nil, fmt.Errorf("invalid log format option: \"%s\"", flags.logFormat)
}
}
func setDebugMode(debug bool) {
mode.Debug = debug
var msg string
if mode.Debug {
res.Set(os.DirFS("./"))
msg = fmt.Sprintf("Running %s in DEBUG mode!", version.AppName)
} else {
res.Set(resources)
msg = fmt.Sprintf("Running %s!", version.AppName)
}
slog.Info(msg)
}
func parseLogLevel(level string) (slog.Level, error) {
switch level {
case "debug":
return slog.LevelDebug, nil
case "info":
return slog.LevelInfo, nil
case "warn":
return slog.LevelWarn, nil
case "error":
return slog.LevelError, nil
default:
return 0, fmt.Errorf("invalid log level: %s", level)
}
}
func startRunner(runner, globalCfg, serviceCfg string) {
var fn func(cfg app.Config) error
if runner == "" {
runner = "service"
}
switch runner {
case "service":
fn = service.Runner
case "async":
fn = example.AsyncRunner
case "external":
fn = example.ExternalRunner
case "tracerr":
fn = example.TracerrRunner
case "logger":
fn = example.LogsRunner
default:
panic("Invalid runner option: \"" + runner + "\"")
}
if err := cmd.NewRunner(fn)(globalCfg, serviceCfg); err != nil {
slog.Error(err.Error())
// Time guard to allow to notify to send the message
time.Sleep(2 * time.Second)
}
}

399
quickfix/CHANGELOG.md Normal file
View File

@ -0,0 +1,399 @@
## 0.9.10 (August 8, 2025)
### BUG FIXES
* Send Reset if the ResetTime elapsed in between checks [#725](https://quantex.com/qfixpt/quickfix/pull/725)
## 0.9.9 (July 31, 2025)
### BUG FIXES
* Adds missing tz for resetseqtime without starttime endtime #723 [#723](https://quantex.com/qfixpt/quickfix/pull/723)
## 0.9.8 (July 21, 2025)
### ENHANCEMENTS
* Add DB name configuration [#711](https://quantex.com/qfixpt/quickfix/pull/711)
* Change ResetSeqTime to time.Time based on the config's timeZone [#712](https://quantex.com/qfixpt/quickfix/pull/712)
### BUG FIXES
* Block Sends when Resend Request is active [#715](https://quantex.com/qfixpt/quickfix/pull/715)
* Fix the issue of incorrect time range calculation across days [#718](https://quantex.com/qfixpt/quickfix/pull/718)
## 0.9.7 (April 23, 2025)
### FEATURES
* Adds SQL, MongoDB and Composite FIX Log and LogFactory implementations, see `config/configuration.go` for details [#672](https://quantex.com/qfixpt/quickfix/pull/672)
* Adds convenience getters for session log and store [#675](https://quantex.com/qfixpt/quickfix/pull/675)
* Adds config option for ResetSeqTime [#705](https://quantex.com/qfixpt/quickfix/pull/705)
### ENHANCEMENTS
* File store uses files exclusively [#680](https://quantex.com/qfixpt/quickfix/pull/680)
* Protect concurrent usage of filestore [#688](https://quantex.com/qfixpt/quickfix/pull/688)
* Support udecimal library in code generation [#700](https://quantex.com/qfixpt/quickfix/pull/700)
### BUG FIXES
* Avoid unkeyed fields usage for exported struct in generated code [#683](https://quantex.com/qfixpt/quickfix/pull/683)
* Iterate messages in filestore opens a separate file to avoid deadlock [#703](https://quantex.com/qfixpt/quickfix/pull/703)
* Correct ordering in message trailer [#707](https://quantex.com/qfixpt/quickfix/pull/707)
## 0.9.6 (September 20, 2024)
### ENHANCEMENTS
* Allow the clients of acceptor to specify their own tls.Config https://quantex.com/qfixpt/quickfix/pull/667
* Adds NextExpectedSeqNum setting https://quantex.com/qfixpt/quickfix/pull/668
### BUG FIXES
* Reinit stop sync to prevent deadlock on sequential start/stops https://quantex.com/qfixpt/quickfix/pull/669
* Check logon auth before resetting store https://quantex.com/qfixpt/quickfix/pull/670
* Reverts ToAdmin call sequencing https://quantex.com/qfixpt/quickfix/pull/674
## 0.9.5 (August 14, 2024)
### ENHANCEMENTS
* Introduce message iterator to avoid loading all messages into memory at once upon resend request https://quantex.com/qfixpt/quickfix/pull/659
* Only lock fieldmap once during message parsing https://quantex.com/qfixpt/quickfix/pull/658
* Optimize tag value parsing https://quantex.com/qfixpt/quickfix/pull/657
* Use bytes.Count to count the number of message fields https://quantex.com/qfixpt/quickfix/pull/655
* Port config documentation into proper go doc format https://quantex.com/qfixpt/quickfix/pull/649
* Support TLS configuration as raw bytes https://quantex.com/qfixpt/quickfix/pull/647
### BUG FIXES
* Use the Go generated file convention https://quantex.com/qfixpt/quickfix/pull/660
* Fix stuck call to Dial when calling Stop on the Initiator https://quantex.com/qfixpt/quickfix/pull/654
* Do not increment NextTargetMsgSeqNum for out of sequence Logout and Test Requests https://quantex.com/qfixpt/quickfix/pull/645
## 0.9.4 (May 29, 2024)
### ENHANCEMENTS
* Adds log to readLoop just like writeLoop https://quantex.com/qfixpt/quickfix/pull/642
### BUG FIXES
* Maintain repeating group field order when parsing messages https://quantex.com/qfixpt/quickfix/pull/636
## 0.9.3 (May 9, 2024)
### BUG FIXES
* Change filestore.offsets from map[int]msgDef to sync.Map https://quantex.com/qfixpt/quickfix/pull/639
* Unregister sessions on stop https://quantex.com/qfixpt/quickfix/pull/637
* Corrects ResetOnLogon behavior for initiators https://quantex.com/qfixpt/quickfix/pull/635
### FEATURES
* Add AllowUnknownMessageFields & CheckUserDefinedFields settings as included in QuickFIX/J https://quantex.com/qfixpt/quickfix/pull/632
## 0.9.2 (April 23, 2024)
### BUG FIXES
* Prevent message queue blocking in the case of network connection trouble https://quantex.com/qfixpt/quickfix/pull/615 https://quantex.com/qfixpt/quickfix/pull/628
* Corrects validation of multiple repeating groups with different fields https://quantex.com/qfixpt/quickfix/pull/623
## 0.9.1 (April 15, 2024)
### BUG FIXES
* Preserve original body when resending https://quantex.com/qfixpt/quickfix/pull/624
## 0.9.0 (November 13, 2023)
### FEATURES
* Add Weekdays config setting as included in QuickFIX/J https://quantex.com/qfixpt/quickfix/pull/590
* `MessageStore` Refactor
The message store types external to a quickfix-go application have been refactored into individual sub-packages within `quickfix`. The benefit of this is that the dependencies for these specific store types are no longer included in the quickfix package itself, so many projects depending on the quickfix package will no longer be bloated with large indirect dependencies if they are not specifically implemented in your application. This applies to the `mongo` (MongoDB), `file` (A file on-disk), and `sql` (Any db accessed with a go sql driver interface). The `memorystore` (in-memory message store) syntax remains unchanged. The minor drawback to this is that with some re-packaging came some minor syntax changes. See https://quantex.com/qfixpt/quickfix/issues/547 and https://quantex.com/qfixpt/quickfix/pull/592 for more information. The relevant examples are below.
MONGO
```go
import "quantex.com/qfixpt/quickfix"
...
acceptor, err = quickfix.NewAcceptor(app, quickfix.NewMongoStoreFactory(appSettings), appSettings, fileLogFactory)
```
becomes
```go
import (
"quantex.com/qfixpt/quickfix"
"quantex.com/qfixpt/quickfix/store/mongo"
)
...
acceptor, err = quickfix.NewAcceptor(app, mongo.NewStoreFactory(appSettings), appSettings, fileLogFactory)
```
FILE
```go
import "quantex.com/qfixpt/quickfix"
...
acceptor, err = quickfix.NewAcceptor(app, quickfix.NewFileStoreFactory(appSettings), appSettings, fileLogFactory)
```
becomes
```go
import (
"quantex.com/qfixpt/quickfix"
"quantex.com/qfixpt/quickfix/store/file"
)
...
acceptor, err = quickfix.NewAcceptor(app, file.NewStoreFactory(appSettings), appSettings, fileLogFactory)
```
SQL
```go
import "quantex.com/qfixpt/quickfix"
...
acceptor, err = quickfix.NewAcceptor(app, quickfix.NewSQLStoreFactory(appSettings), appSettings, fileLogFactory)
```
becomes
```go
import (
"quantex.com/qfixpt/quickfix"
"quantex.com/qfixpt/quickfix/store/sql"
)
...
acceptor, err = quickfix.NewAcceptor(app, sql.NewStoreFactory(appSettings), appSettings, fileLogFactory)
```
### ENHANCEMENTS
* Acceptance suite store type expansions https://quantex.com/qfixpt/quickfix/pull/596 and https://quantex.com/qfixpt/quickfix/pull/591
* Support Go v1.21 https://quantex.com/qfixpt/quickfix/pull/589
### BUG FIXES
* Resolves outstanding issues with postgres db creation syntax and `pgx` driver https://quantex.com/qfixpt/quickfix/pull/598
* Fix sequence number bug when storage fails https://quantex.com/qfixpt/quickfix/pull/432
## 0.8.1 (October 27, 2023)
BUG FIXES
* Remove initiator wait [GH 587]
* for xml charset and bug of "Incorrect NumInGroup" [GH 368, 363, 365, 366]
* Allow time.Duration or int for timeouts [GH 477]
* Trim extra non-ascii characters that can arise from manually editing [GH 463, 464]
## 0.8.0 (October 25, 2023)
ENHANCEMENTS
* Remove tag from field map [GH 544]
* Add message.Bytes() to avoid string conversion [GH 546]
* Check RejectInvalidMessage on FIXT validation [GH 572]
BUG FIXES
* Fix repeating group read tags lost [GH 462]
* Acceptance test result must be predictable [GH 578]
* Makes event timer stop idempotent [GH 580, 581]
* Added WaitGroup Wait in Initiator [GH 584]
## 0.7.0 (January 2, 2023)
FEATURES
* PersistMessages Config [GH 297]
* MaxLatency [GH 242]
* ResetOnDisconnect Configuration [GH 68]
* Support for High Precision Timestamps [GH 288]
* LogonTimeout [GH 295]
* LogoutTimeout [GH 296]
* Socks Proxy [GH 375]
ENHANCEMENTS
* Add SocketUseSSL parameter to allow SSL/TLS without client certs [GH 311]
* Support for RejectInvalidMessage configuration [GH 336]
* Add deep copy for Messages [GH 338]
* Add Go Module support [GH 340]
* Support timeout on ssl connection [GH 347, 349]
* Dynamic Sessions [GH 521]
* Upgrade Mongo Driver to support transactions [GH 527]
BUG FIXES
* header and trailer templates use rootpath [GH 302]
* Initiator stop panic if stop chan's already closed [GH 359]
* Connection closed when inbound logon has a too-low sequence number [GH 369]
* TLS server name config [GH 384]
* Fix concurrent map write [GH 436]
* Race condition during bilateral initial resend request [GH 439]
* Deadlock when disconnecting dynamic session [GH 524]
* Align session's ticker with round second [GH 533]
* Seqnum persist and increment fix [GH 528]
## 0.6.0 (August 14, 2017)
FEATURES
* CheckLatency [GH 241, 266]
* ResendRequestChunkSize [GH 243, 245]
* EnableLastMsgSeqNumProcessed [GH 253, 256]
ENHANCEMENTS
* config.SocketTLSForTesting(bool) [GH 235]
* API Interface Enhancements [GH 251, 252, 254, 255, 257, 258, 259]
* Misc. Performance Optimizations [GH 260, 261, 263, 264, 265, 268, 270, 271, 272, 273, 274, 275]
* TLS Configuration [GH 279, 280]
* Use data dictionary for parsing fix messages [GH 281]
BUG FIXES
* Resend logon fix [GH 244]
* PossDup messages with seqnum too low should not be sent to FromCallbacks [GH 246, 247]
* Router should not reject admin messages or business rejects [GH 249, 250]
* Fixes file log output for incoming, outgoing [GH 262]
* message.String() returns rawMessage if set, builds otherwise [GH 269]
* Use timestamp with time zone for postgres sql [GH 286]
## 0.5.0 (September 1, 2016)
FEATURES
* Session Scheduling [GH 31, 195, 196, 197, 198, 200, 201, 202, 203, 204, 205, 211, 212, 218, 220]
* TimeZone configuration [GH 206]
* StartDay, EndDay for week long sessions [GH 207, 239]
* Support for connection over SSL [GH 63, 193]
* SocketConnectHost/Port<n> [GH 65, 217]
* ResetOnLogout Configuration enhancement [GH 67, 192]
* SocketAcceptAddress, ipv6 support [GH 83, 215]
* RefreshOnLogon [GH 214]
ENHANCEMENTS
* Reject Logon Support [GH 57, 225]
* FIX Decimal [GH 114, 223, 224, 227, 228]
* test refactoring, leveraging testify mock assertions [GH 186]
* KISS on registry, session management [GH 208]
* moving and renaming test jigs [GH 210]
* Config Enhancements [GH 216]
* travis uses go 1.7 [GH 219]
* adds 'generated.go' suffix to generated source [GH 221]
* vendored deps GH [GH 222, 226]
* renames SQLStore config settings [GH 229]
* Add FieldMap.{SetInt, SetString} [GH 236]
BUG FIXES
* DefaultApplVerID Configuration not translating enum names [GH 89, 213]
* Dropped issues in logout state [GH 187, 188]
* correctly increments in target seq num on logout for in session state [GH 189]
* SendToTarget should return an error if toApp returns DoNotSend [GH 190, 191]
* Logon fix [GH 194]
* onlogout calls depend on session state [GH 199]
* fixes bug in resend state where resend response is processed incomplete [GH 230]
* fixes logic around logon message with sequence number too high [GH 231]
* SequenceReset, Resent Messages not hitting ToAdmin/ToApp [GH 232, 233]
* Next MsgSeqNo after received ResetSeqNumFlag=Y [GH 238, 240]
## 0.4.0 (August 1, 2016)
FEATURES
* ReconnectInterval Initiator Configuration [GH 66, 161]
ENHANCEMENTS
* Generate Getters for messages [GH 118]
* Drop generate-* apps into cmd/ [GH 125, 150]
* Misc field type refactoring [GH 145]
* Cmd gen [GH 146, 147]
* refactoring enum generation [GH 148]
* pipelining generation [GH 149]
* Sending a message to an unlogged-in session, results an error [GH 173, 182]
* adds event logging related to session events [GH 175]
* Error handling around session code enhancement [GH 176]
* refactoring of session code [GH 183]
* Gen header beginstring [GH 184]
BUG FIXES
* Do not incr target seq num when seq num too high [GH 151]
* can't parse securitylist message [GH 153, 155]
* Concurrent map operations on Acceptor.Stop() [GH 156]
* Return requiredConfigurationMissing when "FileStorePath" not found [GH 157]
* Checks around HeartBtInt configuration [GH 158]
* Removed complexity around closing Initiator sessions [GH 159]
* Proper FIX logout sequence [GH 160]
* Session logic doesn't account for a failure when calling messageStore methods (e.g. persisting a message) [GH 162]
* Session event loop [GH 164]
* Session event loop follow up [GH 165]
* Handle OnLogon/Logout callbacks in user space [GH 167]
* Session deadlocks if both initiator and acceptor enter the resend state [GH 169, 179]
* Initiator.Stop() does not wait for Acceptor's logout response, causing a resend on next logon [GH 170, 172]
* Reset peer timer after logon [GH 171]
* Ensure OnLogon is called even if seq num too high [GH 174]
* increment target seq num on logout [GH 177]
* fixes bogus resend logic [GH 178]
* Session forgets it is in resend state [GH 180, 181]
* fixes donotsend logic [GH 185]
## 0.3.0 (June 3, 2016)
FEATURES
* ValidateFieldsOutOfOrder Configuration Option [GH 107]
ENHANCEMENTS
* Datadictionary tests [GH 108]
* Proposal to add public method to convert raw fix message to quickfix.Message [GH 110]
* Make gen package public [GH 113]
* Make gen import path relative [GH 119]
* Validator interface [GH 120]
* Expose IncorrectDataFormat [GH 121]
* Spelling, fmtness [GH 123]
* More verbose error text on conditionally required field BMR [GH 131]
* better error handling in gen package [GH 134]
* Include timestamp in messages file log [GH 135]
* extracts repeating group interface [GH 137]
* Header, Body, Trailer FieldMap types [GH 138]
* Datadictionary/Gen refactor [GH 140]
* Gen value timestamp [GH 141]
* Slight gen revert [GH 142]
* replaced regex with faster impl for float parsing [GH 143]
* Expose sql.DB's SetConnMaxLifetime() in settings [GH 144, 139]
BUG FIXES
* Routing Incorrect for FIXT [GH 101]
* Validation Error for component tag [GH 102]
* Unmarshal error for repeating Group [GH 103]
* Marshaler/Reflector tries to convert time.Time struct into fix format [GH 109]
* nil pointer panic if no config.DataDictionary specified [GH 127]
* fixes required group validation error [GH 129]
* RefTagID is not known to BusinessMessageReject type [GH 130]
## 0.2.0 (April 19, 2016)
FEATURES
* Persisted Store [GH 32]
* Initializer Constructors for Generated Messages [GH 69]
* Field Setters for generated messages [GH 70]
* Support for optional components, component setters [GH 79]
* Setters for repeating groups in components [GH 87]
* DB Store [GH 88]
ENHANCEMENTS
* Gen refactor [GH 78]
* Refactoring data dictionary pkg [GH 93]
BUG FIXES
* Initiator panic if connection closed [GH 59]
* New logs overrides old ones [GH 72]
* Session sending message timeout [GH 80]
* Potential FIX50 Market Data marshaling bug [GH 91]
* Allow settings values to contain equals signs [GH 97]
* Error when trying to unmarshal FIX message (FIX 5.0) [GH 99]
## 0.1.0 (February 21, 2016)
* Initial release

18
quickfix/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,18 @@
# Contribution Guidelines
We welcome pull requests, bug fixes and issue reports.
Please direct questions about using the library to the [Mailing List](https://groups.google.com/forum/#!forum/quickfixgo).
Before proposing a large change, please discuss your change by raising an issue.
## Submitting Changes
* Push your changes to a topic branch in your fork of the repository
* Submit a pull request to the repository in the QuickFIXGo Organization
## Notes
* If you report a bug and do not include a fix, please include a failing test
* Generally, contributions without tests will not be accepted
* Contributions that fail the automated build will not be accepted

46
quickfix/LICENSE Normal file
View File

@ -0,0 +1,46 @@
The QuickFIX Software License, Version 1.0
Copyright (c) 2001- quickfixengine.org All rights
reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. The end-user documentation included with the redistribution,
if any, must include the following acknowledgment:
"This product includes software developed by
quickfixengine.org (http://www.quickfixengine.org/)."
Alternately, this acknowledgment may appear in the software itself,
if and wherever such third-party acknowledgments normally appear.
4. The names "QuickFIX" and "quickfixengine.org" must
not be used to endorse or promote products derived from this
software without prior written permission. For written
permission, please contact ask@quickfixengine.org
5. Products derived from this software may not be called "QuickFIX",
nor may "QuickFIX" appear in their name, without prior written
permission of quickfixengine.org
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL QUICKFIXENGINE.ORG OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

92
quickfix/Makefile Normal file
View File

@ -0,0 +1,92 @@
all: vet test
clean:
rm -rf gen
generate: clean
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -pkg-root=quantex.com/qfixpt/quickfix/gen ../spec/*.xml
generate-udecimal: clean
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -use-udecimal=true -pkg-root=quantex.com/qfixpt/quickfix/gen ../spec/*.xml
fmt:
gofmt -l -w -s $(shell find . -type f -name '*.go')
vet:
go vet `go list ./... | grep -v quickfix/gen`
test:
MONGODB_TEST_CXN=mongodb://db:27017 go test -v -cover `go list ./... | grep -v quickfix/gen`
linters-install:
@golangci-lint --version >/dev/null 2>&1 || { \
echo "installing linting tools..."; \
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.64.6; \
}
lint: linters-install
golangci-lint run
# An easy way to run the linter without going through the install process -
# docker run -t --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.64.6 golangci-lint run -v
# See https://golangci-lint.run/welcome/install/ for more details.
# ---------------------------------------------------------------
# Targets related to running acceptance tests -
ifdef STORE_TYPE
STORE := $(STORE_TYPE)
else
STORE := memory
endif
ifdef ACCEPTANCE_SET
TEST_SET := $(ACCEPTANCE_SET)
else
TEST_SET := server
endif
build-test-srv:
cd _test; go build -v -o echo_server ./test-server/
fix40:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5001 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
fix41:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5002 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
fix42:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5003 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
fix43:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5004 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
fix44:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5005 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
fix50:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5006 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
fix50sp1:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5007 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
fix50sp2:
cd _test; ./runat.sh cfg/$(TEST_SET)/$@.cfg 5008 $(STORE) "definitions/$(TEST_SET)/$@/*.def"
ACCEPT_SUITE=fix40 fix41 fix42 fix43 fix44 fix50 fix50sp1 fix50sp2
accept: $(ACCEPT_SUITE)
.PHONY: test $(ACCEPT_SUITE)
# ---------------------------------------------------------------
# ---------------------------------------------------------------
# These targets are specific to the Github CI Runner -
build-src:
go build -v `go list ./...`
build: build-src build-test-srv
test-ci:
go test -v -cover `go list ./... | grep -v quickfix/gen`
generate-ci: clean
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -pkg-root=quantex.com/qfixpt/quickfix/gen ../spec/$(shell echo $(FIX_TEST) | tr '[:lower:]' '[:upper:]').xml;
generate-ci-udecimal: clean
mkdir -p gen; cd gen; go run ../cmd/generate-fix/generate-fix.go -use-udecimal=true -pkg-root=quantex.com/qfixpt/quickfix/gen ../spec/$(shell echo $(FIX_TEST) | tr '[:lower:]' '[:upper:]').xml;
# ---------------------------------------------------------------

156
quickfix/README.md Normal file
View File

@ -0,0 +1,156 @@
# QuickFIX/Go
[![Build Status](https://quantex.com/qfixpt/quickfix/workflows/CI/badge.svg)](https://quantex.com/qfixpt/quickfix/actions) [![GoDoc](https://godoc.org/quantex.com/qfixpt/quickfix?status.png)](https://godoc.org/quantex.com/qfixpt/quickfix) [![Go Report Card](https://goreportcard.com/badge/quantex.com/qfixpt/quickfix)](https://goreportcard.com/report/quantex.com/qfixpt/quickfix)
Open Source [FIX Protocol](http://www.fixprotocol.org/) library implemented in Go
### Looking for help with `MessageStore` syntax changes?
See v0.9.0 release notes [here](https://quantex.com/qfixpt/quickfix/releases/tag/v0.9.0)
## About
<p>QuickFIX/Go is a <a href="https://www.fixtrading.org/">FIX Protocol Community</a> implementation for the <a href="https://golang.org">Go programming language</a>.</p>
<ul>
<li>100% free and open source with a liberal <a href="https://quantex.com/qfixpt/quickfix/blob/master/LICENSE.txt">license</a></li>
<li>Supports FIX versions 4.0 - 5.0SP2</li>
<li>Runs on any hardware and operating system supported by Go (1.21+ required)</li>
<li>Spec driven run-time message validation</li>
<li>Spec driven code generation of type-safe FIX messages, fields, and repeating groups</li>
<li>Support for protocol customizations</li>
<li>Session state storage options: SQL, MongoDB, On-disk, or In-memory</li>
<li>Logging options: File, Screen</li>
<li>Failover and High Availability</li>
<li>Daily and weekly scheduling of session connections</li>
<li>Integrated support for SSL communicaitons</li>
<li>Automated unit and acceptance tests</li>
<li><a href="https://www.connamara.com/">Commercial Support available</a></li>
</ul>
<br>
<img width="208" alt="Sponsored by Connamara" src="https://user-images.githubusercontent.com/3065126/282546730-16220337-4960-48ae-8c2f-760fbaedb135.png">
## Installation
With [Go module](https://github.com/golang/go/wiki/Modules) support, simply add the following import
```
import "quantex.com/qfixpt/quickfix"
```
to your code, and then `go [build|run|test]` will automatically fetch the necessary dependencies.
Otherwise, run the following Go command to install the `quickfix` package:
```sh
go get -u quantex.com/qfixpt/quickfix
```
## Getting Started
* [QuickFIX User Manual](https://quickfixengine.org/go/documentation/)
* [Go API Documentation](https://godoc.org/quantex.com/qfixpt/quickfix)
* See [examples](https://github.com/quickfixgo/examples) for some simple examples of using QuickFIX/Go.
## FIX Messaging Model
To send and receive messages, your application will need a few additional packages.
QuickFIX/Go maintains separate packages for tags, fields, enums, messages, and message components auto-generated from the FIX 4.0 - FIX5.0SP2 XML specifications-
* [Tag](https://github.com/quickfixgo/tag)
* [Field](https://github.com/quickfixgo/field)
* [Enum](https://github.com/quickfixgo/enum)
* [FIX 4.0](https://github.com/quickfixgo/fix40)
* [FIX 4.1](https://github.com/quickfixgo/fix41)
* [FIX 4.2](https://github.com/quickfixgo/fix42)
* [FIX 4.3](https://github.com/quickfixgo/fix43)
* [FIX 4.4](https://github.com/quickfixgo/fix44)
* [FIX 5.0](https://github.com/quickfixgo/fix50)
* [FIX 5.0 SP1](https://github.com/quickfixgo/fix50sp1)
* [FIX 5.0 SP2](https://github.com/quickfixgo/fix50sp2)
* [FIXT 1.1](https://github.com/quickfixgo/fixt11)
For most FIX applications, these generated resources are sufficient. Custom FIX applications may generate source specific to the FIX spec of that application using the `generate-fix` tool included with QuickFIX/Go.
Following installation, `generate-fix` is installed to `$GOPATH/bin/generate-fix`. Run `$GOPATH/bin/generate-fix --help` for usage instructions.
## General Support
<h3>Github Discussions</h3>
<p>Our <a href="https://quantex.com/qfixpt/quickfix/discussions/categories/q-a">Github Discussions Board</a> is free, public, and easily searchable. Its the preferred method of user support from the QuickFIX/Go team.
<p>Please provide as much detail as you can when asking a question, and include relevant configurations and code snippets.</p>
<h3>FIX Protocol</h3>
<p>More information about the FIX protocol can be found at the <a href="http://fixtradingcommunity.org">FIX Protocol website</a>.
<h3>Bugs and Issues</h3>
<p>Bugs and issues can be submitted by anyone through our GitHub repository issues list.</p>
<p><strong>Note:</strong> Please do not submit questions or help requests to the issues list. It is for bugs and issues. If you need help, please use the Discussions board as described above and youll be able to send your question to the entire community.</p>
<p><a href="https://quantex.com/qfixpt/quickfix/issues">GitHub Issues</a></p>
<p>Please provide sample code, logs, and a description of the problem when the issue is submitted.</p>
<p>We will try to address new issues as quickly as possible, and we welcome contributions for bug fixes and new features!</p>
## Commercial Support
<p><a href="https://connamara.com">Connamara Systems</a> offers commercial support for developers who are integrating any of the QuickFIX implementations (Go, C++, Java, .NET). The support is offered in 10-hour bundles and grants developers access, via telephone or email, to the team that created QuickFIX/Go, QuickFIX/n, and are maintainers of QuickFIX.</p>
<p>In addition to offering QuickFIX support, Connamara delivers Made-To-Measure Trading Solutions by bridging the gap between buy and build. By using internally developed trading platform components, Connamara delivers the best of off-the-shelf ISV solutions and custom application development. Coupled with Connamaras unique licensing model, trading firms can get the best of both build and buy.</p>
## Contributing
If you wish to work on QuickFIX/Go itself, you will need [Docker](https://docs.docker.com/get-docker/) and [VSCode](https://code.visualstudio.com/download) on your machine.
* Clone the repo and open it with VSCode with Docker running
* This repo comes with vscode devcontainer configs in `./.devcontainer/`
* Click the pop-up to re-open the project in the Dev Container
* This opens the project in a docker container pre-configured with everything you need
### Build and Test
The default make target runs [go vet](https://godoc.org/golang.org/x/tools/cmd/vet) and unit tests.
```sh
make
```
If this exits with exit status 0, then everything is working!
### Generated Code
Generated code from the FIX40-FIX50SP2 specs are available as separate repos under the [QuickFIX/Go organization](https://github.com/quickfixgo). The source specifications for this generated code is located in `spec/`. Generated code can be identified by the `.generated.go` suffix. Any changes to generated code must be captured by changes to source in `cmd/generate-fix`. After making changes to the code generator source, run the following to re-generate the source
```sh
make generate
```
If you are making changes to the generated code, please create Pull Requests for these changes for the affected repos.
### Acceptance Tests
QuickFIX/Go has a comprehensive acceptance test suite covering the FIX protocol. These are the same tests used across all QuickFIX implementations.
QuickFIX/Go acceptance tests depend on ruby in path, if you are using the dev container, it is already installed
To run acceptance tests,
```sh
# generate code locally
make generate
# build acceptance test rig
make build-test-srv
# run acceptance tests
make accept
```
## Licensing
This software is available under the QuickFIX Software License. Please see the [LICENSE.txt](https://quantex.com/qfixpt/quickfix/blob/main/LICENSE.txt) for the terms specified by the QuickFIX Software License.

6
quickfix/_sql/embed.go Normal file
View File

@ -0,0 +1,6 @@
package sql
import "embed"
//go:embed mssql mysql oracle postgresql sqlite3
var FS embed.FS

View File

@ -0,0 +1 @@
osql -U sa -P -i quickfix_database.sql

View File

@ -0,0 +1,65 @@
DROP DATABASE quickfix;
CREATE DATABASE quickfix;
USE quickfix;
CREATE TABLE sessions (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
creation_time DATETIME NOT NULL,
incoming_seqnum INT NOT NULL,
outgoing_seqnum INT NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier)
);
CREATE TABLE messages (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
msgseqnum INT NOT NULL,
message TEXT NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier,
msgseqnum)
);
CREATE TABLE event_log (
id INT NOT NULL IDENTITY,
time DATETIME NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
text TEXT NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE messages_log (
id INT NOT NULL IDENTITY,
time DATETIME NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
text TEXT NOT NULL,
PRIMARY KEY (id)
);

View File

@ -0,0 +1 @@
mysql -u root --execute="source mysql.sql";

View File

@ -0,0 +1 @@
mysql -u root --execute="source mysql.sql";

View File

@ -0,0 +1,18 @@
USE quickfix;
DROP TABLE IF EXISTS event_log;
CREATE TABLE event_log (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
time DATETIME NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64),
text TEXT NOT NULL,
PRIMARY KEY (id)
);

View File

@ -0,0 +1,18 @@
USE quickfix;
DROP TABLE IF EXISTS messages_log;
CREATE TABLE messages_log (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
time DATETIME NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
text TEXT NOT NULL,
PRIMARY KEY (id)
);

View File

@ -0,0 +1,19 @@
USE quickfix;
DROP TABLE IF EXISTS messages;
CREATE TABLE messages (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
msgseqnum INT NOT NULL,
message TEXT NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier,
msgseqnum)
);

View File

@ -0,0 +1,5 @@
source quickfix_database.sql;
source sessions_table.sql;
source messages_table.sql;
source messages_log_table.sql;
source event_log_table.sql;

View File

@ -0,0 +1,2 @@
DROP DATABASE IF EXISTS quickfix;
CREATE DATABASE quickfix;

View File

@ -0,0 +1,19 @@
USE quickfix;
DROP TABLE IF EXISTS sessions;
CREATE TABLE sessions (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
creation_time DATETIME NOT NULL,
incoming_seqnum INT NOT NULL,
outgoing_seqnum INT NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier)
);

View File

@ -0,0 +1,14 @@
CREATE TABLE messages (
beginstring VARCHAR2(8) NOT NULL,
sendercompid VARCHAR2(64) NOT NULL,
sendersubid VARCHAR2(64) NOT NULL,
senderlocid VARCHAR2(64) NOT NULL,
targetcompid VARCHAR2(64) NOT NULL,
targetsubid VARCHAR2(64) NOT NULL,
targetlocid VARCHAR2(64) NOT NULL,
session_qualifier VARCHAR2(64) NOT NULL,
msgseqnum INTEGER NOT NULL,
message VARCHAR2(4000) NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier, msgseqnum)
);

View File

@ -0,0 +1,15 @@
CREATE TABLE sessions (
beginstring VARCHAR2(8) NOT NULL,
sendercompid VARCHAR2(64) NOT NULL,
sendersubid VARCHAR2(64) NOT NULL,
senderlocid VARCHAR2(64) NOT NULL,
targetcompid VARCHAR2(64) NOT NULL,
targetsubid VARCHAR2(64) NOT NULL,
targetlocid VARCHAR2(64) NOT NULL,
session_qualifier VARCHAR2(64) NOT NULL,
creation_time TIMESTAMP NOT NULL,
incoming_seqnum INTEGER NOT NULL,
outgoing_seqnum INTEGER NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier)
);

View File

@ -0,0 +1,3 @@
dropdb -U postgres --if-exists quickfix
createdb -U postgres quickfix
psql -U postgres -d quickfix -f postgresql.sql

View File

@ -0,0 +1,3 @@
dropdb -U postgres --if-exists quickfix
createdb -U postgres quickfix
psql -U postgres -d quickfix -f postgresql.sql

View File

@ -0,0 +1,16 @@
CREATE SEQUENCE event_log_sequence;
CREATE TABLE event_log (
id INTEGER DEFAULT NEXTVAL('event_log_sequence'),
time TIMESTAMP WITH TIME ZONE NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64),
text TEXT NOT NULL,
PRIMARY KEY (id)
);

View File

@ -0,0 +1,16 @@
CREATE SEQUENCE messages_log_sequence;
CREATE TABLE messages_log (
id INTEGER DEFAULT NEXTVAL('messages_log_sequence'),
time TIMESTAMP WITH TIME ZONE NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64),
text TEXT NOT NULL,
PRIMARY KEY (id)
);

View File

@ -0,0 +1,15 @@
CREATE TABLE messages (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
msgseqnum INTEGER NOT NULL,
message TEXT NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier,
msgseqnum)
);

View File

@ -0,0 +1,4 @@
\i sessions_table.sql;
\i messages_table.sql;
\i messages_log_table.sql;
\i event_log_table.sql;

View File

@ -0,0 +1,15 @@
CREATE TABLE sessions (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
creation_time TIMESTAMP WITH TIME ZONE NOT NULL,
incoming_seqnum INTEGER NOT NULL,
outgoing_seqnum INTEGER NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier)
);

View File

@ -0,0 +1,15 @@
DROP TABLE IF EXISTS event_log;
CREATE TABLE event_log (
id INTEGER PRIMARY KEY NOT NULL,
time DATETIME NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64),
text TEXT NOT NULL
);

View File

@ -0,0 +1,15 @@
DROP TABLE IF EXISTS messages_log;
CREATE TABLE messages_log (
id INTEGER PRIMARY KEY NOT NULL,
time DATETIME NOT NULL,
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
text TEXT NOT NULL
);

View File

@ -0,0 +1,17 @@
DROP TABLE IF EXISTS messages;
CREATE TABLE messages (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
msgseqnum INT NOT NULL,
message TEXT NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier,
msgseqnum)
);

View File

@ -0,0 +1,17 @@
DROP TABLE IF EXISTS sessions;
CREATE TABLE sessions (
beginstring CHAR(8) NOT NULL,
sendercompid VARCHAR(64) NOT NULL,
sendersubid VARCHAR(64) NOT NULL,
senderlocid VARCHAR(64) NOT NULL,
targetcompid VARCHAR(64) NOT NULL,
targetsubid VARCHAR(64) NOT NULL,
targetlocid VARCHAR(64) NOT NULL,
session_qualifier VARCHAR(64) NOT NULL,
creation_time DATETIME NOT NULL,
incoming_seqnum INT NOT NULL,
outgoing_seqnum INT NOT NULL,
PRIMARY KEY (beginstring, sendercompid, sendersubid, senderlocid,
targetcompid, targetsubid, targetlocid, session_qualifier)
);

View File

@ -0,0 +1,72 @@
#****************************************************************************
# Copyright (c) quickfixengine.org All rights reserved.
#
# This file is part of the QuickFIX FIX Engine
#
# This file may be distributed under the terms of the quickfixengine.org
# license as defined by quickfixengine.org and appearing in the file
# LICENSE included in the packaging of this file.
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# See http://www.quickfixengine.org/LICENSE for licensing information.
#
# Contact ask@quickfixengine.org if any conditions of this licensing are
# not clear to you.
#****************************************************************************
class Comparator < Hash
def initialize(patterns)
patterns.each_line do
| line |
line.chomp!
array = line.split("=")
num = array[0].to_i
regex = Regexp.new(array[1])
self[num] = regex;
end
end
def compare(left, right)
@reason = nil
left_array = left.split("\001")
right_array = right.split("\001")
# check for number of fields
if left_array.size != right_array.size
@reason = "Number of fields do not match"
return false
end
left_array.each_index do
| index |
left_field = left_array[index].split("=")
right_field = right_array[index].split("=")
# check if field is in same order
if left_field[0] != right_field[0]
@reason = "Expected field (" + left_field[0] + ") but found field (" + right_field[0] + ")"
return false
end
regexp = self[left_field[0].to_i]
# do a straight comparison or regex comparison
if regexp == nil
if left_field[1] != right_field[1]
@reason = "Value in field (" + left_field[0] + ") should be (" + left_field[1] + ") but was (" + right_field[1] + ")"
return false
end
else
if !(regexp === right_field[1])
@reason = "Field (" + left_field[0] + ") does not match pattern"
return false
end
end
end
return true
end
def reason()
return @reason
end
end

View File

@ -0,0 +1,37 @@
require 'Comparator'
require 'runit/testcase'
class ComparatorTestCase < RUNIT::TestCase
def test_parsePatterns
patterns = "10=\\d{1,3}\n52=\\d{8}-\\d2:\\d2:\\d2\n"
comp = Comparator.new(patterns)
assert_equals(/\d{1,3}/, comp[10])
assert_equals(/\d{8}-\d2:\d2:\d2/, comp[52])
end
def test_compare
patterns = "10=\\d{1,3}\n52=\\d{8}-\\d{2}:\\d{2}:\\d{2}\n"
comp = Comparator.new(patterns)
# matching fields
assert(comp.compare("1=hello\0012=goodbye\001", "1=hello\0012=goodbye\001"))
assert(comp.reason == nil)
# non-matching field
assert(!comp.compare("1=helloo\0012=goodbye\001", "1=hello\0012=goodbye\001"))
assert(comp.reason == "Value in field (1) should be (helloo) but was (hello)")
# out of order fields
assert(!comp.compare("2=hello\0011=goodbye\001", "1=hello\0012=goodbye\001"))
assert(comp.reason == "Expected field (2) but found field (1)")
# different number of fields
assert(!comp.compare("1=hello\001", "1=hello\0012=goodbye\001"))
assert(comp.reason == "Number of fields do not match")
# mathing non-deterministic field
assert(comp.compare(
"1=hello\0012=goodbye\00152=12345678-12:23:34\001", "1=hello\0012=goodbye\00152=87654321-98:87:76\001"))
# non-matching non-deterministic field
assert(!comp.compare(
"1=hello\0012=goodbye\00152=12345678-12:23:34\001", "1=hello\0012=goodbye\00152=7654321-98:87:76\001"))
assert(comp.reason == "Field (52) does not match pattern")
end
end

View File

@ -0,0 +1,53 @@
#****************************************************************************
# Copyright (c) quickfixengine.org All rights reserved.
#
# This file is part of the QuickFIX FIX Engine
#
# This file may be distributed under the terms of the quickfixengine.org
# license as defined by quickfixengine.org and appearing in the file
# LICENSE included in the packaging of this file.
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# See http://www.quickfixengine.org/LICENSE for licensing information.
#
# Contact ask@quickfixengine.org if any conditions of this licensing are
# not clear to you.
#****************************************************************************
require "socket"
class FixParser
def initialize(io)
@io = io
end
def readFixMessage()
if(@io.eof?)
raise "Was disconnected, expected data"
end
m = ""
# read to begining of MsgLen field
m = @io.gets("\0019=")
# read contents of MsgLen field
length = @io.gets("\001")
m += length
length.chop!
# regex checks to make sure length is an integer
# if it isn't there is nothing we can do so
# close the connection
if( (/^\d*$/ === length) == nil )
@io.close
end
# read body
m += @io.read(Integer(length))
# read CheckSum
m += @io.gets("\001")
return m
end
end

View File

@ -0,0 +1,46 @@
require 'FixParser'
require 'runit/testcase'
require "thread"
require 'SocketServer'
class FixParserTestCase < RUNIT::TestCase
def test_readFixMessage
fixMsg1 = "8=FIX.4.2\0019=12\00135=A\001108=30\00110=31\001"
fixMsg2 = "8=FIX.4.2\0019=17\00135=4\00136=88\001123=Y\00110=34\001"
server = SocketServer.new
def server.message=(m)
@message = m
end
def server.connectAction(s)
end
def server.receiveAction(s)
s.write(@message)
end
def server.disconnectAction(s)
end
server.message = fixMsg1 + fixMsg2
Thread.start do
server.listen(RUNIT::TestCase.port)
end
server.wait
s = TCPSocket.open("localhost", RUNIT::TestCase.port)
parser = FixParser.new(s)
begin
assert_equals(fixMsg1, parser.readFixMessage)
assert_equals(fixMsg2, parser.readFixMessage)
rescue IOError
# I have no idea why this is being thrown
end
s.close
server.stop()
end
end

150
quickfix/_test/Reflector.rb Normal file
View File

@ -0,0 +1,150 @@
#****************************************************************************
# Copyright (c) quickfixengine.org All rights reserved.
#
# This file is part of the QuickFIX FIX Engine
#
# This file may be distributed under the terms of the quickfixengine.org
# license as defined by quickfixengine.org and appearing in the file
# LICENSE included in the packaging of this file.
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# See http://www.quickfixengine.org/LICENSE for licensing information.
#
# Contact ask@quickfixengine.org if any conditions of this licensing are
# not clear to you.
#****************************************************************************
class Reflector < Array
def identifyMessage(message)
if [?I, ?E, ?R, ?i, ?e].include?(message[0])
return message[0]
else
return ?X
end
end
def processFile(messages)
lineNum = 0
messages.each_line do
| line |
lineNum += 1
line.chomp!
if line.empty? then
elsif (/^[IEie]\d{1},/ === line) then
cid = line[1].to_i - 48
body = fixify!(timify!(line[3, line.length]))
else
cid = 1
body = fixify!(timify!(line[1, line.length]))
end
begin
processLine(lineNum, line, body, cid)
rescue
errorAction(lineNum, line);
end
end
end
def processLine(lineNum, line, body, cid)
if line.empty?
elsif line[0] == ?\#
elsif identifyMessage(line) == ?I
initiateAction(body, cid)
elsif identifyMessage(line) == ?E
expectedAction(body, cid)
elsif identifyMessage(line) == ?i
if body == "CONNECT"
connectAction(cid)
elsif body == "DISCONNECT"
disconnectAction(cid)
elsif body.index("SET_SESSION") == 0
setSeqnum(body)
else
raise "Syntax error: " + body
end
elsif identifyMessage(line) == ?e
if body == "CONNECT"
waitConnectAction(cid)
elsif body == "DISCONNECT"
waitDisconnectAction(cid)
else
raise "Syntax error: " + body
end
else
raise "Syntax error: " + body
end
end
def fixify!(message)
hasLength = (message =~ /[\001]9=.*?[\001]/)
length = ""
head = message.slice!(/^8=.*?[\001]/)
if head == nil
return message
end
checksum = message.slice(/[\001]10=.*[\001]$/)
if(checksum != nil)
message.slice!(/[\001]10=.*[\001]$/)
end
message.chomp!
if hasLength == nil
length = "9=" + message.length.to_s + "\001"
end
if checksum == nil
checksumStr = sprintf("%03d", (head + length + message).sum(8));
checksum = "10=" + checksumStr + "\001"
end
message.replace(head + length + message + checksum)
return message
end
def timify!(message)
copy = ""
copy.replace(message)
t = getTime
strtime = t.strftime("%Y%m%d-%H:%M:%S")
message.sub!("<TIME>", strtime)
if( message != copy )
return timify!(message)
end
pos1 = /\<TIME[+-]\d+\>/ =~ message
pos2 = /\>/ =~ message
if( pos1 != nil )
op = message[pos1 + 5]
num = message.slice(pos1+6..pos2-1)
if( op == ?+ )
t += num.to_i
else
t -= num.to_i
end
strtime = t.strftime("%Y%m%d-%H:%M:%S")
exp = Regexp.compile("<TIME[" + op.chr + "]" + num + ">")
message.sub!(exp, strtime)
if( message != copy )
return timify!(message)
end
end
return message
end
def getTime
t = Time.new
t = t.gmtime
return t
end
end

View File

@ -0,0 +1,135 @@
#****************************************************************************
# Copyright (c) quickfixengine.org All rights reserved.
#
# This file is part of the QuickFIX FIX Engine
#
# This file may be distributed under the terms of the quickfixengine.org
# license as defined by quickfixengine.org and appearing in the file
# LICENSE included in the packaging of this file.
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# See http://www.quickfixengine.org/LICENSE for licensing information.
#
# Contact ask@quickfixengine.org if any conditions of this licensing are
# not clear to you.
#****************************************************************************
require 'Reflector'
require 'FixParser'
require "socket"
require 'uri'
require 'net/http'
class ReflectorClient
def initialize(messages, address, port)
@reflector = Reflector.new
@messages = messages
@sockets = Hash.new(nil)
@parsers = Hash.new(nil)
def @reflector.address=(a)
@address = a
end
def @reflector.port=(p)
@port = p
end
def @reflector.client=(c)
@client = c
end
def @reflector.sockets=(s)
@sockets = s
end
def @reflector.parsers=(p)
@parsers = p
end
def @reflector.connectAction(cid)
socket = TCPSocket.open(@address, @port)
if socket == nil
raise IOError("failed to connect")
end
@sockets[cid] = socket
@parsers[cid] = FixParser.new(socket)
end
def @reflector.disconnectAction(cid)
@sockets[cid].close
@sockets.delete(cid)
@parsers.delete(cid)
end
def @reflector.setSeqnum(body)
left_array = body.split(" ")
uri = URI('http://localhost:8095/seqnum?SESSION='+left_array[1]+'&'+left_array[2])
Net::HTTP.get_response(uri)
end
def @reflector.waitConnectAction(cid)
end
def @reflector.waitDisconnectAction(cid)
begin
socket = @sockets[cid]
if IO.select([socket], nil, nil, 10) == nil then
raise "Connection hangs after ten seconds."
elsif !socket.eof? then
raise "Expected disconnection, got data"
end
rescue Errno::ECONNRESET
# Ignore, server has already disconnected the socket
end
end
def @reflector.initiateAction(msg, cid)
if( @sockets[cid] == nil )
raise "Unable to send message because connection was dropped"
end
@sockets[cid].write(msg)
@client.initiateAction(msg, cid)
end
def @reflector.expectedAction(msg, cid)
m = @parsers[cid].readFixMessage
@client.expectedAction(msg, cid)
@client.compareAction(msg, m)
end
def @reflector.errorAction(lineNum, line)
@client.errorAction(lineNum, line)
end
@reflector.client = self
@reflector.address = address
@reflector.port = port
@reflector.sockets = @sockets
@reflector.parsers = @parsers
end
def start()
@reflector.processFile(@messages)
end
def stop()
@sockets.each do
|cid, s|
begin
s.close
rescue
end
end
end
def initiateAction(msg, cid)
end
def expectedAction(msg, cid)
end
def responseAction(msg, cid)
end
end

View File

@ -0,0 +1,55 @@
require 'ReflectorClient'
require 'runit/testcase'
require 'ReflectorServer'
require "thread"
class ReflectorClientTestCase < RUNIT::TestCase
def test_reflectMessages
serverMessages = "E8=FIX.4.2\0019=13\00135=AC\001108=30\00110=31\001\n"
serverMessages += "I8=FIX.4.2\0019=13\00135=AS\001108=10\00110=31\001\n"
serverMessages += "E8=FIX.4.2\0019=13\00135=BC\001108=25\00110=31\001\n"
serverMessages += "I8=FIX.4.2\0019=13\00135=BS\001108=15\00110=31\001\n"
clientMessages = "iCONNECT\n"
clientMessages += "I8=FIX.4.2\0019=13\00135=AC\001108=30\00110=31\001\n"
clientMessages += "E8=FIX.4.2\0019=13\00135=AS\001108=10\00110=31\001\n"
clientMessages += "I8=FIX.4.2\0019=13\00135=BC\001108=25\00110=31\001\n"
clientMessages += "E8=FIX.4.2\0019=13\00135=BS\001108=15\00110=31\001\n"
clientMessages += "iDISCONNECT\n"
server = ReflectorServer.new(serverMessages)
client = ReflectorClient.new(clientMessages, "localhost", RUNIT::TestCase.port)
def client.next
begin
return @queue.pop 0
rescue
return nil
end
end
def client.compareAction(e, a)
if !defined? @queue
@queue = Queue.new
end
@queue.push(e)
@queue.push(a)
end
Thread.start do
server.listen(RUNIT::TestCase.port)
end
server.wait
client.start()
assert_equals("8=FIX.4.2\0019=13\00135=AS\001108=10\00110=31\001", client.next)
assert_equals("8=FIX.4.2\0019=13\00135=AS\001108=10\00110=31\001", client.next)
assert_equals("8=FIX.4.2\0019=13\00135=BS\001108=15\00110=31\001", client.next)
assert_equals("8=FIX.4.2\0019=13\00135=BS\001108=15\00110=31\001", client.next)
assert_equals(nil, client.next)
server.stop
end
end

View File

@ -0,0 +1,111 @@
#****************************************************************************
# Copyright (c) quickfixengine.org All rights reserved.
#
# This file is part of the QuickFIX FIX Engine
#
# This file may be distributed under the terms of the quickfixengine.org
# license as defined by quickfixengine.org and appearing in the file
# LICENSE included in the packaging of this file.
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# See http://www.quickfixengine.org/LICENSE for licensing information.
#
# Contact ask@quickfixengine.org if any conditions of this licensing are
# not clear to you.
#****************************************************************************
require 'SocketServer'
require 'Reflector'
class ReflectorServer < SocketServer
def initialize(messages, address = "localhost", port = 5000)
@reflector = Reflector.new
@parser = nil
@socket = nil
@messages = messages
@port = port
def @reflector.socket=(s)
@socket = s
end
def @reflector.socket
return @socket
end
def @reflector.server=(s)
@server = s
end
def @reflector.parser=(s)
@parser = s
end
def @reflector.expectedAction(msg, cid)
m = @parser.readFixMessage
@server.compareAction(msg, m)
end
def @reflector.initiateAction(msg, cid)
@socket.write(msg)
end
def @reflector.waitConnectAction(cid)
@server.waitConnectAction
end
def @reflector.waitDisconnectAction(cid)
begin
if IO.select([@socket], nil, nil, 10) == nil then
raise "Connection hangs after five seconds."
elsif !@socket.eof? then
raise "Expected disconnection, got data"
end
rescue Errno::ECONNRESET
# Ignore, client has already disconnected the socket
end
end
end
def connectAction(s)
if @socket == nil
@socket = s
@reflector.socket = s
@reflector.server = self
else
s.close
end
end
def waitConnectAction
gs = TCPServer.open(@port)
addr = gs.addr
addr.shift
if IO.select([gs], nil, nil, 10) == nil then
raise "Connection hangs after ten seconds."
end
@socket = gs.accept
@parser = FixParser.new(@socket)
@reflector.parser = @parser
@reflector.socket = @socket
end
def disconnectAction(s)
end
def receiveAction(s)
end
def compareAction(e, a)
end
def start
@reflector.server = self
@reflector.processFile(@messages)
end
end

View File

@ -0,0 +1,33 @@
require 'ReflectorServer'
require 'runit/testcase'
require "thread"
class ReflectorServerTestCase < RUNIT::TestCase
def test_reflectMessages
messages = "E8=FIX.4.2\0019=12\00135=A\001108=30\00110=31\001\n"
messages += "R8=FIX.4.2\0019=12\00135=A\001108=10\00110=31\001\n"
messages += "E8=FIX.4.2\0019=12\00135=A\001108=25\00110=31\001\n"
messages += "R8=FIX.4.2\0019=12\00135=A\001108=15\00110=31\001\n"
server = ReflectorServer.new(messages)
Thread.start do
server.listen(RUNIT::TestCase.port)
end
server.wait
s = TCPSocket.open("localhost", RUNIT::TestCase.port)
parser = FixParser.new(s)
begin
s.write("8=FIX.4.2\0019=12\00135=A\001108=30\00110=31\001")
assert_equals("8=FIX.4.2\0019=12\00135=A\001108=10\00110=31\001", parser.readFixMessage)
s.write("8=FIX.4.2\0019=12\00135=A\001108=30\00125=31\001")
assert_equals("8=FIX.4.2\0019=12\00135=A\001108=15\00110=31\001", parser.readFixMessage)
rescue IOError
end
s.close
server.stop
end
end

View File

@ -0,0 +1,208 @@
require 'Reflector'
class MockReflector < Reflector
def getTime
t = Time.gm(2000, "jan", 1, 20, 15, 1)
return t
end
end
require 'runit/testcase'
class ReflectorTestCase < RUNIT::TestCase
def test_identifyMessage
reflector = Reflector.new
message = "I8=FIX42"
assert(reflector.identifyMessage(message) == ?I)
message = "E8=FIX42"
assert(reflector.identifyMessage(message) == ?E)
message = "R8=FIX42"
assert(reflector.identifyMessage(message) == ?R)
message = "8=FIX42"
assert(reflector.identifyMessage(message) == ?X)
message = "iACTION"
assert(reflector.identifyMessage(message) == ?i)
message = "eACTION"
assert(reflector.identifyMessage(message) == ?e)
end
def test_timify_and_fixify
reflector = MockReflector.new
str = reflector.fixify!(reflector.timify!("8=FIX.4.235=D34=249=PATS52=<TIME>56=RCG1=acct111=121=138=240=154=155=ESU260=<TIME>167=FUT204=1207=CME9701=omni19702=19706=E9707=1239708=G"))
assert_equals("8=FIX.4.29=17135=D34=249=PATS52=20000101-20:15:0156=RCG1=acct111=121=138=240=154=155=ESU260=20000101-20:15:01167=FUT204=1207=CME9701=omni19702=19706=E9707=1239708=G10=121", str)
str = reflector.fixify!(reflector.timify!("8=FIX.4.235=D34=249=PATS52=<TIME>56=RCG1=acct111=121=138=240=154=155=ESU260=<TIME>167=FUT204=1207=CME9701=omni19702=19706=E9707=1239708=G9709=PEA"))
assert_equals("8=FIX.4.29=18035=D34=249=PATS52=20000101-20:15:0156=RCG1=acct111=121=138=240=154=155=ESU260=20000101-20:15:01167=FUT204=1207=CME9701=omni19702=19706=E9707=1239708=G9709=PEA10=102", str)
end
def test_fixify_bang
reflector = Reflector.new
str = "8=FIX.4.235=A34=149=TW52=20000426-12:05:06" +
"56=ISLD98=0108=30"
reflector.fixify!(str)
assert_equals("8=FIX.4.29=5735=A34=149=TW52=20000426-12:05:0656=ISLD98=0108=3010=005", str)
str = "8=FIX.4.29=5735=A34=149=TW52=20000426-12:05:06" +
"56=ISLD98=0108=3010=005"
reflector.fixify!(str)
assert_equals("8=FIX.4.29=5735=A34=149=TW52=20000426-12:05:0656=ISLD98=0108=3010=005", str)
end
def test_timify_bang
reflector = Reflector.new
str = "8=FIX.4.29=5735=A34=149=TW52=20011010-10:10:1056=ISLD98=0108=3010=005"
reflector.timify!(str)
assert_equals("8=FIX.4.29=5735=A34=149=TW52=20011010-10:10:1056=ISLD98=0108=3010=005", str)
str = "8=FIX.4.29=5735=A34=149=TW52=<TIME>56=ISLD98=0" +
"108=3010=005"
reflector.timify!(str)
match = (/8=FIX.4.29=5735=A34=149=TW52=\d{8}-\d{2}:\d{2}:\d{2}56=ISLD98=0108=3010=005/ === str)
assert(match != nil)
str = "8=FIX.4.29=5735=A34=149=TW52=<TIME>56=ISLD" +
"122=<TIME>98=0108=3010=005"
reflector.timify!(str)
match = (/8=FIX.4.29=5735=A34=149=TW52=\d{8}-\d{2}:\d{2}:\d{2}56=ISLD122=\d{8}-\d{2}:\d{2}:\d{2}98=0108=3010=005/ === str)
assert(match != nil)
str = "8=FIX.4.29=5735=A34=149=TW52=<TIME+9>56=ISLD98=0" +
"108=3010=005"
reflector.timify!(str)
match = (/8=FIX.4.29=5735=A34=149=TW52=\d{8}-\d{2}:\d{2}:\d{2}56=ISLD98=0108=3010=005/ === str)
assert(match != nil)
str = "8=FIX.4.29=5735=A34=149=TW52=<TIME>56=ISLD98=0" +
"108=3060=<TIME>10=005"
reflector.timify!(str)
match = (/8=FIX.4.29=5735=A34=149=TW52=\d{8}-\d{2}:\d{2}:\d{2}56=ISLD98=0108=3060=\d{8}-\d{2}:\d{2}:\d{2}10=005/ === str)
assert(match != nil)
str = "8=FIX.4.235=D34=249=PATS52=<TIME>56=RCG1=acct111=121=138=240=154=155=ESU2" +
"60=<TIME>167=FUT204=1207=CME9701=omni19702=19706=E9707=1239708=G9710=PEA"
reflector.timify!(str)
match = (/8=FIX.4.235=D34=249=PATS52=\d{8}-\d{2}:\d{2}:\d{2}56=RCG1=acct111=121=138=240=154=155=ESU260=\d{8}-\d{2}:\d{2}:\d{2}167=FUT204=1207=CME9701=omni19702=19706=E9707=1239708=G9710=PEA/ === str)
assert(match != nil)
end
def test_identifyFile
reflector = Reflector.new
messages = "E8=1\nI8=2\n\nI8=3\nE8=4\n#foo\nE8=5\nE8=6\nI8=7\niCONNECT\neDISCONNECT\neCONNECT\niDISCONNECT\nE2,8=8\n"
cum = ""
def reflector.ini=(i)
@ini = i
end
def reflector.ini
return @ini
end
def reflector.cum=(c)
@cum = c
end
def reflector.cum
return @cum
end
def reflector.exp=(e)
@exp = e
end
def reflector.exp
return @exp
end
def reflector.icon=(i)
@icon = i
end
def reflector.icon
return @icon
end
def reflector.idis=(i)
@idis = i
end
def reflector.idis
return @idis
end
def reflector.econ=(e)
@econ = e
end
def reflector.econ
return @econ
end
def reflector.edis=(e)
@edis = e
end
def reflector.edis
return @edis
end
def reflector.err=(e)
@err = e
end
def reflector.err
return @err
end
reflector.ini = ""; reflector.cum = "";
reflector.exp = ""; reflector.icon = "";
reflector.idis = ""; reflector.econ = "";
reflector.edis = ""; reflector.err = "";
def reflector.initiateAction(msg, cid)
@cum += cid.to_s + "," + msg + "|"
@ini += cid.to_s + "," + msg + "|"
end
def reflector.expectedAction(msg, cid)
@cum += cid.to_s + "," + msg + "|"
@exp += cid.to_s + "," + msg + "|"
end
def reflector.connectAction(cid)
@cum += cid.to_s + "," + "iCONNECT" + "|"
@icon += cid.to_s + "," + "iCONNECT" + "|"
end
def reflector.disconnectAction(cid)
@cum += cid.to_s + "," + "iDISCONNECT" + "|"
@idis += cid.to_s + "," + "iDISCONNECT" + "|"
end
def reflector.waitConnectAction(cid)
@cum += cid.to_s + "," + "eCONNECT" + "|"
@econ += cid.to_s + "," + "eCONNECT" + "|"
end
def reflector.waitDisconnectAction(cid)
@cum += cid.to_s + "," + "eDISCONNECT" + "|"
@edis += cid.to_s + "," + "eDISCONNECT" + "|"
end
def reflector.errorAction(lineNum, msg)
@cum += msg + "|"
@err += msg + "|"
end
reflector.processFile(messages)
#messages = "E8=1\nI8=2\n\nI8=3\nE8=4\n#foo\nE8=5\nE8=6\nI8=7\niCONNECT\neDISCONNECT\neCONNECT\niDISCONNECT\nE2,8=8\n"
assert_equals("1,8=1|1,8=2|1,8=3|1,8=4|1,8=5|1,8=6|1,8=7|" +
"1,iCONNECT|1,eDISCONNECT|1,eCONNECT|1,iDISCONNECT|2,8=8|",
reflector.cum)
assert_equals("1,8=2|1,8=3|1,8=7|", reflector.ini)
assert_equals("1,8=1|1,8=4|1,8=5|1,8=6|2,8=8|", reflector.exp)
assert_equals("1,iCONNECT|", reflector.icon)
assert_equals("1,iDISCONNECT|", reflector.idis)
assert_equals("1,eCONNECT|", reflector.econ)
assert_equals("1,eDISCONNECT|", reflector.edis)
end
end

144
quickfix/_test/Runner.rb Normal file
View File

@ -0,0 +1,144 @@
#****************************************************************************
# Copyright (c) quickfixengine.org All rights reserved.
#
# This file is part of the QuickFIX FIX Engine
#
# This file may be distributed under the terms of the quickfixengine.org
# license as defined by quickfixengine.org and appearing in the file
# LICENSE included in the packaging of this file.
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# See http://www.quickfixengine.org/LICENSE for licensing information.
#
# Contact ask@quickfixengine.org if any conditions of this licensing are
# not clear to you.
#****************************************************************************
require 'ReflectorClient'
require 'Comparator'
def extendProcess(c)
def c.errorAction(lineNum, line)
report = " " + $!.to_s + "\n"
report += " <line>" + lineNum.to_s + "</line>\n"
raise report
end
def c.compareAction(e, a)
if( !defined? @patterns )
@patterns = "10=\\d{1,3}\n52=\\d{8}-\\d{2}:\\d{2}:\\d{2}\n";
end
if( !defined? @comp )
@comp = Comparator.new(@patterns)
end
if( !@comp.compare(e,a) )
e.tr!("\001", "*")
a.tr!("\001", "*")
report = @comp.reason + "\n"
report += " <expected><![CDATA[" + e + "]]></expected>\n"
report += " <received><![CDATA[" + a + "]]></received>"
raise report
end
end
def c.patterns=(p)
@patterns = p
end
c.patterns = File.open("definitions/fields.fmt", "r")
end
def printResult(test, exception)
print "<test name='", test, "' result='"
if exception == nil then
print "success'/>\n"
else
print "failure' >\n"
print " <message>\n", $!, " </message>\n"
#print " <trace><![CDATA["
#print $!.backtrace.join("]]></trace>\n <trace><![CDATA[")
#print " ]]></trace>\n"
print "</test>\n"
end
STDOUT.flush
end
def createProcess(file, address, port)
newarray = [1,2,3,4,5,6,7,8,9,10]
newarray.each do
| num |
begin
socket = TCPSocket.open(address, port);
rescue
end
if socket == nil
sleep 3
next
else
socket.close
break
end
end
file.each_line do
| line |
if line =~ /^i\d*,?CONNECT/ then
return ReflectorClient.new(file, address, port)
elsif line =~ /^e\d*,?CONNECT/ then
return ReflectorServer.new(file)
end
end
return nil
end
i = 0
newarray = ARGV[2, ARGV.length-2]
exitValue = 0
total = 0
failures = 0
begin
print "<at>\n"
newarray.each do
| v |
file = File.open(v, "r")
process = createProcess(file, ARGV[0], ARGV[1])
if process.nil? then
print " <test name='", v, "' result='", "failure' >\n"
print " <message><![CDATA[Test definition did "
print "not contain iCONNECT or eCONNECT]]></message>\n"
print " </test>\n"
exitValue += 1
next
end
file.rewind
extendProcess(process)
sleep(0.1)
total += 1
begin
process.start
printResult(v, nil)
process.stop
rescue
failures += 1
exitValue += 1
printResult(v, $!)
process.stop
end
end
print "\n<results total='", total, "' failures='", failures, "'/>\n"
print "</at>\n"
rescue
print " ",$!,"\n"
print "</at>\n"
end
exit exitValue
if not Object.respond_to?("is_testing") or not Object.is_testing then
end

View File

@ -0,0 +1,21 @@
require 'Runner'
require 'runit/testcase'
class RunnerTestCase < RUNIT::TestCase
def test_createProcess
process = createProcess("foo\nbar\niCONNECT\nbaz", "localhost", 39333)
assert process.kind_of?(ReflectorClient)
process = createProcess("iCONNECT\nfoo\nbar\nbaz", "localhost", 39333)
assert process.kind_of?(ReflectorClient)
process = createProcess("foo\nbar\neCONNECT\nbaz", "localhost", 39333)
assert process.kind_of?(ReflectorServer)
process = createProcess("eCONNECT\nfoo\nbar\nbaz", "localhost", 39333)
assert process.kind_of?(ReflectorServer)
process = createProcess("foo\nbar\nbaz", "localhost", 39333)
assert process.nil?
process = createProcess("foo\nbar\nbazeCONNECT", "localhost", 39333)
assert process.nil?
process = createProcess("", "localhost", 39333)
assert process.nil?
end
end

View File

@ -0,0 +1,57 @@
#****************************************************************************
# Copyright (c) quickfixengine.org All rights reserved.
#
# This file is part of the QuickFIX FIX Engine
#
# This file may be distributed under the terms of the quickfixengine.org
# license as defined by quickfixengine.org and appearing in the file
# LICENSE included in the packaging of this file.
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# See http://www.quickfixengine.org/LICENSE for licensing information.
#
# Contact ask@quickfixengine.org if any conditions of this licensing are
# not clear to you.
#****************************************************************************
require "socket"
class SocketServer
def listen(port)
@gs = TCPServer.open(port)
addr = @gs.addr
addr.shift
while !@gs.closed?
ns = @gs.accept
Thread.start do
s = ns
connectAction(s)
receiveAction(s)
s.close
disconnectAction(s)
end
end
end
def stop
if not @gs.nil?
@gs.close
end
end
def closed?
return @gs.closed?
end
def wait
while( closed? )
end
end
end

View File

@ -0,0 +1,68 @@
require 'SocketServer'
require 'runit/testcase'
require "thread"
class SocketServerTestCase < RUNIT::TestCase
def test_listen
socketServer = SocketServer.new
connectQueue = Queue.new
receiveQueue = Queue.new
disconnectQueue = Queue.new
def socketServer.connectQueue=(q)
@connectQueue = q
end
def socketServer.connectQueue
return @connectQueue
end
def socketServer.receiveQueue=(q)
@receiveQueue = q
end
def socketServer.receiveQueue
return @receiveQueue
end
def socketServer.disconnectQueue=(q)
@disconnectQueue = q
end
def socketServer.disconnectQueue
return @disconnectQueue
end
def socketServer.connectAction(s)
@connectQueue.push(true)
end
def socketServer.disconnectAction(s)
@disconnectQueue.push(true)
end
def socketServer.receiveAction(s)
while( str = s.gets )
@receiveQueue.push(str)
end
end
socketServer.connectQueue = connectQueue
socketServer.receiveQueue = receiveQueue
socketServer.disconnectQueue = disconnectQueue
Thread.start do
socketServer.listen(RUNIT::TestCase.port)
end
socketServer.wait
s = TCPSocket.open("localhost", RUNIT::TestCase.port)
assert(connectQueue.pop)
s.write("test\r\n")
s.write("test2\r\n")
assert_equals("test\r\n", receiveQueue.pop)
assert_equals("test2\r\n", receiveQueue.pop)
s.close
assert(disconnectQueue.pop)
socketServer.stop()
end
end

View File

@ -0,0 +1,10 @@
$kidpid = fork;
if($kidpid == 0) {
chdir("..");
exec("debug/quickfix -t server -f cfg/at_server_test.cfg");
}
sleep(6000);
`bash -c "./runall"`;
kill($kidpid);

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5003
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.2
DataDictionary=../spec/FIX42.xml
EnableLastMsgSeqNumProcessed=Y

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5004
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.3
DataDictionary=../spec/FIX43.xml
EnableLastMsgSeqNumProcessed=Y

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5005
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.4
DataDictionary=../spec/FIX44.xml
EnableLastMsgSeqNumProcessed=Y

View File

@ -0,0 +1,14 @@
[DEFAULT]
SocketAcceptPort=5006
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50
DefaultApplVerID=FIX.5.0
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50.xml
EnableLastMsgSeqNumProcessed=Y

View File

@ -0,0 +1,14 @@
[DEFAULT]
SocketAcceptPort=5007
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50SP1
DefaultApplVerID=FIX.5.0SP1
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP1.xml
EnableLastMsgSeqNumProcessed=Y

View File

@ -0,0 +1,14 @@
[DEFAULT]
SocketAcceptPort=5008
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50SP2
DefaultApplVerID=FIX.5.0SP2
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP2.xml
EnableLastMsgSeqNumProcessed=Y

View File

@ -0,0 +1,10 @@
[DEFAULT]
SocketAcceptPort=5005
SenderCompID=ISLD
TargetCompID=TW
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.4
DataDictionary=../spec/FIX44.xml
EnableNextExpectedMsgSeqNum=Y

View File

@ -0,0 +1,12 @@
[DEFAULT]
SocketAcceptPort=5006
SenderCompID=ISLD
TargetCompID=TW
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
DefaultApplVerID=FIX.5.0
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50.xml
EnableNextExpectedMsgSeqNum=Y

View File

@ -0,0 +1,12 @@
[DEFAULT]
SocketAcceptPort=5007
SenderCompID=ISLD
TargetCompID=TW
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
DefaultApplVerID=FIX.5.0SP1
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP1.xml
EnableNextExpectedMsgSeqNum=Y

View File

@ -0,0 +1,12 @@
[DEFAULT]
SocketAcceptPort=5008
SenderCompID=ISLD
TargetCompID=TW
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
DefaultApplVerID=FIX.5.0SP2
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP2.xml
EnableNextExpectedMsgSeqNum=Y

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5001
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.0
DataDictionary=../spec/FIX40.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5002
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.1
DataDictionary=../spec/FIX41.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5003
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.2
DataDictionary=../spec/FIX42.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5004
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.3
DataDictionary=../spec/FIX43.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,11 @@
[DEFAULT]
SocketAcceptPort=5005
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.4
DataDictionary=../spec/FIX44.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,14 @@
[DEFAULT]
SocketAcceptPort=5006
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50
DefaultApplVerID=FIX.5.0
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,14 @@
[DEFAULT]
SocketAcceptPort=5007
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50SP1
DefaultApplVerID=FIX.5.0SP1
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP1.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,14 @@
[DEFAULT]
SocketAcceptPort=5008
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50SP2
DefaultApplVerID=FIX.5.0SP2
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP2.xml
ResendRequestChunkSize=5

View File

@ -0,0 +1,10 @@
[DEFAULT]
SocketAcceptPort=5001
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.0
DataDictionary=../spec/FIX40.xml

View File

@ -0,0 +1,10 @@
[DEFAULT]
SocketAcceptPort=5002
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.1
DataDictionary=../spec/FIX41.xml

View File

@ -0,0 +1,10 @@
[DEFAULT]
SocketAcceptPort=5003
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.2
DataDictionary=../spec/FIX42.xml

View File

@ -0,0 +1,10 @@
[DEFAULT]
SocketAcceptPort=5004
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.3
DataDictionary=../spec/FIX43.xml

View File

@ -0,0 +1,10 @@
[DEFAULT]
SocketAcceptPort=5005
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIX.4.4
DataDictionary=../spec/FIX44.xml

View File

@ -0,0 +1,13 @@
[DEFAULT]
SocketAcceptPort=5006
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50
DefaultApplVerID=FIX.5.0
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50.xml

View File

@ -0,0 +1,13 @@
[DEFAULT]
SocketAcceptPort=5007
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50SP1
DefaultApplVerID=FIX.5.0SP1
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP1.xml

View File

@ -0,0 +1,13 @@
[DEFAULT]
SocketAcceptPort=5008
SenderCompID=ISLD
TargetCompID=TW
ResetOnLogon=Y
FileLogPath=tmp
[SESSION]
BeginString=FIXT.1.1
SessionQualifier=FIX50SP2
DefaultApplVerID=FIX.5.0SP2
TransportDataDictionary=../spec/FIXT11.xml
AppDataDictionary=../spec/FIX50SP2.xml

View File

@ -0,0 +1,5 @@
10=\d{3}
42=\d{8}-\d{2}:\d{2}:\d{2}
52=\d{8}-\d{2}:\d{2}:\d{2}|\d{8}-\d{2}:\d{2}:\d{2}[.]\d{3}
60=\d{8}-\d{2}:\d{2}:\d{2}
122=\d{8}-\d{2}:\d{2}:\d{2}

View File

@ -0,0 +1,21 @@
iCONNECT
I8=FIX.4.235=A34=149=TW52=<TIME>56=ISLD369=098=0108=2
E8=FIX.4.29=6635=A34=149=ISLD52=00000000-00:00:00.00056=TW369=198=0108=210=0
I8=FIX.4.235=134=249=TW52=<TIME>56=ISLD369=1112=HELLO
E8=FIX.4.29=6535=034=249=ISLD52=00000000-00:00:00.00056=TW369=2112=HELLO10=0
I8=FIX.4.235=034=349=TW52=<TIME>56=ISLD369=2
I8=FIX.4.235=034=449=TW52=<TIME>56=ISLD369=2
E8=FIX.4.29=5535=034=349=ISLD52=00000000-00:00:00.00056=TW369=410=0
# logout message and response
I8=FIX.4.235=534=549=TW52=<TIME>56=ISLD369=3
E8=FIX.4.29=5535=534=449=ISLD52=00000000-00:00:00.00056=TW369=510=0
eDISCONNECT

View File

@ -0,0 +1,21 @@
iCONNECT
I8=FIX.4.335=A34=149=TW52=<TIME>56=ISLD369=098=0108=2
E8=FIX.4.39=6635=A34=149=ISLD52=00000000-00:00:00.00056=TW369=198=0108=210=0
I8=FIX.4.335=134=249=TW52=<TIME>56=ISLD369=1112=HELLO
E8=FIX.4.39=6535=034=249=ISLD52=00000000-00:00:00.00056=TW369=2112=HELLO10=0
I8=FIX.4.335=034=349=TW52=<TIME>56=ISLD369=2
I8=FIX.4.335=034=449=TW52=<TIME>56=ISLD369=2
E8=FIX.4.39=5535=034=349=ISLD52=00000000-00:00:00.00056=TW369=410=0
# logout message and response
I8=FIX.4.335=534=549=TW52=<TIME>56=ISLD369=3
E8=FIX.4.39=5535=534=449=ISLD52=00000000-00:00:00.00056=TW369=510=0
eDISCONNECT

View File

@ -0,0 +1,21 @@
iCONNECT
I8=FIX.4.435=A34=149=TW52=<TIME>56=ISLD369=098=0108=2
E8=FIX.4.49=6635=A34=149=ISLD52=00000000-00:00:00.00056=TW369=198=0108=210=0
I8=FIX.4.435=134=249=TW52=<TIME>56=ISLD369=1112=HELLO
E8=FIX.4.49=6535=034=249=ISLD52=00000000-00:00:00.00056=TW369=2112=HELLO10=0
I8=FIX.4.435=034=349=TW52=<TIME>56=ISLD369=2
I8=FIX.4.435=034=449=TW52=<TIME>56=ISLD369=2
E8=FIX.4.49=5535=034=349=ISLD52=00000000-00:00:00.00056=TW369=410=0
# logout message and response
I8=FIX.4.435=534=549=TW52=<TIME>56=ISLD369=3
E8=FIX.4.49=5535=534=449=ISLD52=00000000-00:00:00.00056=TW369=510=0
eDISCONNECT

View File

@ -0,0 +1,21 @@
iCONNECT
I8=FIXT.1.135=A34=149=TW52=<TIME>56=ISLD369=098=0108=21137=7
E8=FIXT.1.19=7335=A34=149=ISLD52=00000000-00:00:00.00056=TW369=198=0108=21137=710=0
I8=FIXT.1.135=134=249=TW52=<TIME>56=ISLD369=1112=HELLO
E8=FIXT.1.19=6535=034=249=ISLD52=00000000-00:00:00.00056=TW369=2112=HELLO10=0
I8=FIXT.1.135=034=349=TW52=<TIME>56=ISLD369=2
I8=FIXT.1.135=034=449=TW52=<TIME>56=ISLD369=2
E8=FIXT.1.19=5535=034=349=ISLD52=00000000-00:00:00.00056=TW369=410=0
# logout message and response
I8=FIXT.1.135=534=549=TW52=<TIME>56=ISLD369=3
E8=FIXT.1.19=5535=534=449=ISLD52=00000000-00:00:00.00056=TW369=510=0
eDISCONNECT

View File

@ -0,0 +1,21 @@
iCONNECT
I8=FIXT.1.135=A34=149=TW52=<TIME>56=ISLD369=098=0108=21137=8
E8=FIXT.1.19=7335=A34=149=ISLD52=00000000-00:00:00.00056=TW369=198=0108=21137=810=0
I8=FIXT.1.135=134=249=TW52=<TIME>56=ISLD369=1112=HELLO
E8=FIXT.1.19=6535=034=249=ISLD52=00000000-00:00:00.00056=TW369=2112=HELLO10=0
I8=FIXT.1.135=034=349=TW52=<TIME>56=ISLD369=2
I8=FIXT.1.135=034=449=TW52=<TIME>56=ISLD369=2
E8=FIXT.1.19=5535=034=349=ISLD52=00000000-00:00:00.00056=TW369=410=0
# logout message and response
I8=FIXT.1.135=534=549=TW52=<TIME>56=ISLD369=3
E8=FIXT.1.19=5535=534=449=ISLD52=00000000-00:00:00.00056=TW369=510=0
eDISCONNECT

View File

@ -0,0 +1,21 @@
iCONNECT
I8=FIXT.1.135=A34=149=TW52=<TIME>56=ISLD369=098=0108=21137=9
E8=FIXT.1.19=7335=A34=149=ISLD52=00000000-00:00:00.00056=TW369=198=0108=21137=910=0
I8=FIXT.1.135=134=249=TW52=<TIME>56=ISLD369=1112=HELLO
E8=FIXT.1.19=6535=034=249=ISLD52=00000000-00:00:00.00056=TW369=2112=HELLO10=0
I8=FIXT.1.135=034=349=TW52=<TIME>56=ISLD369=2
I8=FIXT.1.135=034=449=TW52=<TIME>56=ISLD369=2
E8=FIXT.1.19=5535=034=349=ISLD52=00000000-00:00:00.00056=TW369=410=0
# logout message and response
I8=FIXT.1.135=534=549=TW52=<TIME>56=ISLD369=3
E8=FIXT.1.19=5535=534=449=ISLD52=00000000-00:00:00.00056=TW369=510=0
eDISCONNECT

View File

@ -0,0 +1,16 @@
iCONNECT
#------------------------
# logon message and response
# send logon with sequence number equals to 1
# expect for a logon response with NextExpectedMsgSeqNum field equals to 2
#------------------------
I8=FIX.4.435=A34=149=TW52=<TIME>56=ISLD98=0789=1108=2
E8=FIX.4.49=6635=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=2789=210=0
# logout message and response
I8=FIX.4.435=534=249=TW52=<TIME>56=ISLD
E8=FIX.4.49=4935=534=249=ISLD52=00000000-00:00:00.00056=TW10=0
eDISCONNECT

View File

@ -0,0 +1,10 @@
# If message NextExpectedMsgSeqNum field is too high, we must disconnect
iCONNECT
iSET_SESSION FIX.4.4:ISLD->TW NEXTSENDERSEQNUM=1
iSET_SESSION FIX.4.4:ISLD->TW NEXTTARGETSEQNUM=1
I8=FIX.4.435=A34=100049=TW52=<TIME>56=ISLD98=0789=1200108=30
E8=FIX.4.49=13535=534=149=ISLD52=00000000-00:00:00.00056=TW58=Tag 789 (NextExpectedMsgSeqNum) is higher than expected. Expected 1, Received 120010=0
eDISCONNECT

View File

@ -0,0 +1,14 @@
iCONNECT
iSET_SESSION FIX.4.4:ISLD->TW NEXTSENDERSEQNUM=2000
iSET_SESSION FIX.4.4:ISLD->TW NEXTTARGETSEQNUM=1
I8=FIX.4.435=A34=149=TW52=<TIME>56=ISLD98=0789=1108=30
E8=FIX.4.49=7035=A34=200049=ISLD52=00000000-00:00:00.00056=TW98=0108=30789=210=0
E8=FIX.4.49=9435=434=143=Y49=ISLD52=<TIME>56=TW122=<TIME>36=2001123=Y
# logout message and response
I8=FIX.4.435=534=249=TW52=<TIME>56=ISLD
E8=FIX.4.49=5235=534=200149=ISLD52=00000000-00:00:00.00056=TW10=0
eDISCONNECT

View File

@ -0,0 +1,14 @@
# FIX Logon using tag 141/ResetSeqNumFlag and tag 789/NextExpectedSeqMsgNum
iCONNECT
iSET_SESSION FIX.4.4:ISLD->TW NEXTSENDERSEQNUM=2000
iSET_SESSION FIX.4.4:ISLD->TW NEXTTARGETSEQNUM=2000
I8=FIX.4.435=A34=149=TW52=<TIME>56=ISLD98=0789=1141=Y108=30
E8=FIX.4.49=7335=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=30141=Y789=210=0
# logout message and response
I8=FIX.4.435=534=249=TW52=<TIME>56=ISLD
E8=FIX.4.49=4935=534=249=ISLD52=00000000-00:00:0056=TW10=0
eDISCONNECT

View File

@ -0,0 +1,16 @@
iCONNECT
#------------------------
# logon message and response
# send logon with sequence number equals to 1
# expect for a logon response with NextExpectedMsgSeqNum field equals to 2
#------------------------
I8=FIXT.1.135=A34=149=TW52=<TIME>56=ISLD98=0789=11137=7108=2
E8=FIXT.1.19=7335=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=2789=21137=710=0
# logout message and response
I8=FIXT.1.135=534=249=TW52=<TIME>56=ISLD
E8=FIXT.1.19=4935=534=249=ISLD52=00000000-00:00:00.00056=TW10=0
eDISCONNECT

View File

@ -0,0 +1,9 @@
# If message NextExpectedMsgSeqNum field is too high, we must disconnect
iCONNECT
iSET_SESSION FIXT.1.1:ISLD->TW NEXTSENDERSEQNUM=1
iSET_SESSION FIXT.1.1:ISLD->TW NEXTTARGETSEQNUM=1
I8=FIXT.1.135=A34=100049=TW52=<TIME>56=ISLD98=0789=12001137=7108=30
E8=FIXT.1.19=13535=534=149=ISLD52=00000000-00:00:00.00056=TW58=Tag 789 (NextExpectedMsgSeqNum) is higher than expected. Expected 1, Received 120010=0
eDISCONNECT

View File

@ -0,0 +1,14 @@
iCONNECT
iSET_SESSION FIXT.1.1:ISLD->TW NEXTSENDERSEQNUM=2000
iSET_SESSION FIXT.1.1:ISLD->TW NEXTTARGETSEQNUM=1
I8=FIXT.1.135=A34=149=TW52=<TIME>56=ISLD98=0789=11137=7108=30
E8=FIXT.1.19=7735=A34=200049=ISLD52=00000000-00:00:00.00056=TW98=0108=30789=21137=710=0
E8=FIXT.1.19=9435=434=143=Y49=ISLD52=<TIME>56=TW122=<TIME>36=2001123=Y
# logout message and response
I8=FIXT.1.135=534=249=TW52=<TIME>56=ISLD
E8=FIXT.1.19=5235=534=200149=ISLD52=00000000-00:00:00.00056=TW10=0
eDISCONNECT

View File

@ -0,0 +1,14 @@
# FIX Logon using tag 141/ResetSeqNumFlag and tag 789/NextExpectedSeqMsgNum
iCONNECT
iSET_SESSION FIXT.1.1:ISLD->TW NEXTSENDERSEQNUM=2000
iSET_SESSION FIXT.1.1:ISLD->TW NEXTTARGETSEQNUM=2000
I8=FIXT.1.135=A34=149=TW52=<TIME>56=ISLD98=0789=1141=Y1137=7108=30
E8=FIXT.1.19=8035=A34=149=ISLD52=00000000-00:00:00.00056=TW98=0108=30141=Y789=21137=710=0
# logout message and response
I8=FIXT.1.135=534=249=TW52=<TIME>56=ISLD
E8=FIXT.1.19=4935=534=249=ISLD52=00000000-00:00:00.00056=TW10=0
eDISCONNECT

Some files were not shown because too many files have changed in this diff Show More