From b8b28e7c4a1394f5565f0d43ec3190003add14cd Mon Sep 17 00:00:00 2001 From: "Mark S. Lewis" Date: Fri, 21 Jun 2024 00:10:55 +0100 Subject: [PATCH] v2 implementation based on fabric-chaincode-go/v2 (#137) The v2 implementation retains wire-level compatibility with Fabric but does include the following breaking changes: - Chaincode implementations that make direct use of fabric-protos-go will experience protocol buffer namespace conflicts (as described in https://protobuf.dev/reference/go/faq/). Any use of fabric-protos-go must be replaced by fabric-protos-go-apiv2. Signed-off-by: Mark S. Lewis --- .github/workflows/build.yml | 94 +- .github/workflows/check.yml | 19 + .github/workflows/test-v1.yml | 73 + .github/workflows/test-v2.yml | 74 + .golangci.yml | 5 + Makefile | 30 +- Makefile.v1 | 42 + ci/license-config.json | 18 - ci/license.txt | 2 - integrationtest/Dockerfile | 2 +- v2/.dockerignore | 1 + v2/.mockery.yaml | 11 + v2/ci/scripts/setup-integration-chaincode.sh | 26 + v2/contractapi/api_test.go | 25 + v2/contractapi/contract.go | 128 + v2/contractapi/contract_chaincode.go | 509 ++ v2/contractapi/contract_chaincode_test.go | 666 +++ v2/contractapi/contract_test.go | 83 + .../mock_chaincodeStubInterface_test.go | 2132 +++++++ v2/contractapi/shared_test.go | 19 + v2/contractapi/system_contract.go | 30 + v2/contractapi/system_contract_test.go | 34 + v2/contractapi/transaction_context.go | 64 + v2/contractapi/transaction_context_test.go | 79 + v2/contractapi/utils/undefined_interface.go | 9 + v2/go.mod | 45 + v2/go.sum | 163 + v2/integrationtest/.gitignore | 2 + v2/integrationtest/Dockerfile | 9 + .../chaincode/advancedtypes/go.mod | 34 + .../chaincode/advancedtypes/go.sum | 130 + .../chaincode/advancedtypes/main.go | 107 + v2/integrationtest/chaincode/private/go.mod | 34 + v2/integrationtest/chaincode/private/go.sum | 130 + v2/integrationtest/chaincode/private/main.go | 54 + .../chaincode/simple/Dockerfile | 13 + v2/integrationtest/chaincode/simple/go.mod | 34 + v2/integrationtest/chaincode/simple/go.sum | 130 + v2/integrationtest/chaincode/simple/main.go | 70 + .../chaincode/transactionhooks/go.mod | 34 + .../chaincode/transactionhooks/go.sum | 130 + .../chaincode/transactionhooks/main.go | 86 + v2/integrationtest/cucumber.js | 61 + v2/integrationtest/package-lock.json | 5192 +++++++++++++++++ v2/integrationtest/package.json | 9 + v2/internal/contract_function.go | 377 ++ v2/internal/contract_function_test.go | 573 ++ .../contracts/complexcontract/basicobject.go | 29 + .../complexcontract/complexcontract.go | 147 + .../contract-metadata/metadata.json | 156 + .../extendedsimplecontract.go | 62 + .../simplecontract/simplecontract.go | 73 + .../functionaltests/contracts/utils/utils.go | 65 + .../features/badpaths/errors.feature | 70 + .../goldenpaths/complexchaincode.feature | 35 + .../extendedsimplechaincode.feature | 34 + .../multicontractchaincode.feature | 26 + .../goldenpaths/simplechaincode.feature | 33 + v2/internal/functionaltests/mockstub_test.go | 354 ++ .../functionaltests/step_definitions_test.go | 268 + .../functionaltests/utils/bad_metadata.json | 5 + v2/internal/internal_test.go | 25 + v2/internal/transaction_handler.go | 90 + v2/internal/transaction_handler_test.go | 176 + v2/internal/types/types.go | 362 ++ v2/internal/types/types_test.go | 473 ++ v2/internal/types_handler.go | 156 + v2/internal/types_handler_test.go | 369 ++ v2/internal/utils/utils.go | 42 + v2/internal/utils/utils_test.go | 76 + v2/metadata/a_metadata-packr.go | 13 + v2/metadata/ioutils.go | 33 + v2/metadata/ioutils_test.go | 25 + v2/metadata/metadata.go | 301 + v2/metadata/metadata_main_test.go | 25 + v2/metadata/metadata_test.go | 413 ++ v2/metadata/schema.go | 209 + v2/metadata/schema/schema.json | 352 ++ v2/metadata/schema_test.go | 519 ++ v2/serializer/json_transaction_serializer.go | 151 + .../json_transaction_serializer_test.go | 319 + v2/serializer/serializer_test.go | 25 + v2/serializer/transaction_serializer.go | 29 + 83 files changed, 17018 insertions(+), 114 deletions(-) create mode 100644 .github/workflows/check.yml create mode 100644 .github/workflows/test-v1.yml create mode 100644 .github/workflows/test-v2.yml create mode 100644 Makefile.v1 delete mode 100644 ci/license-config.json delete mode 100644 ci/license.txt create mode 100644 v2/.dockerignore create mode 100644 v2/.mockery.yaml create mode 100755 v2/ci/scripts/setup-integration-chaincode.sh create mode 100644 v2/contractapi/api_test.go create mode 100644 v2/contractapi/contract.go create mode 100644 v2/contractapi/contract_chaincode.go create mode 100644 v2/contractapi/contract_chaincode_test.go create mode 100644 v2/contractapi/contract_test.go create mode 100644 v2/contractapi/mock_chaincodeStubInterface_test.go create mode 100644 v2/contractapi/shared_test.go create mode 100644 v2/contractapi/system_contract.go create mode 100644 v2/contractapi/system_contract_test.go create mode 100644 v2/contractapi/transaction_context.go create mode 100644 v2/contractapi/transaction_context_test.go create mode 100644 v2/contractapi/utils/undefined_interface.go create mode 100644 v2/go.mod create mode 100644 v2/go.sum create mode 100644 v2/integrationtest/.gitignore create mode 100644 v2/integrationtest/Dockerfile create mode 100644 v2/integrationtest/chaincode/advancedtypes/go.mod create mode 100644 v2/integrationtest/chaincode/advancedtypes/go.sum create mode 100644 v2/integrationtest/chaincode/advancedtypes/main.go create mode 100644 v2/integrationtest/chaincode/private/go.mod create mode 100644 v2/integrationtest/chaincode/private/go.sum create mode 100644 v2/integrationtest/chaincode/private/main.go create mode 100644 v2/integrationtest/chaincode/simple/Dockerfile create mode 100644 v2/integrationtest/chaincode/simple/go.mod create mode 100644 v2/integrationtest/chaincode/simple/go.sum create mode 100644 v2/integrationtest/chaincode/simple/main.go create mode 100644 v2/integrationtest/chaincode/transactionhooks/go.mod create mode 100644 v2/integrationtest/chaincode/transactionhooks/go.sum create mode 100644 v2/integrationtest/chaincode/transactionhooks/main.go create mode 100644 v2/integrationtest/cucumber.js create mode 100644 v2/integrationtest/package-lock.json create mode 100644 v2/integrationtest/package.json create mode 100644 v2/internal/contract_function.go create mode 100644 v2/internal/contract_function_test.go create mode 100644 v2/internal/functionaltests/contracts/complexcontract/basicobject.go create mode 100644 v2/internal/functionaltests/contracts/complexcontract/complexcontract.go create mode 100644 v2/internal/functionaltests/contracts/complexcontract/contract-metadata/metadata.json create mode 100644 v2/internal/functionaltests/contracts/extendedsimplecontract/extendedsimplecontract.go create mode 100644 v2/internal/functionaltests/contracts/simplecontract/simplecontract.go create mode 100644 v2/internal/functionaltests/contracts/utils/utils.go create mode 100644 v2/internal/functionaltests/features/badpaths/errors.feature create mode 100644 v2/internal/functionaltests/features/goldenpaths/complexchaincode.feature create mode 100644 v2/internal/functionaltests/features/goldenpaths/extendedsimplechaincode.feature create mode 100644 v2/internal/functionaltests/features/goldenpaths/multicontractchaincode.feature create mode 100644 v2/internal/functionaltests/features/goldenpaths/simplechaincode.feature create mode 100644 v2/internal/functionaltests/mockstub_test.go create mode 100644 v2/internal/functionaltests/step_definitions_test.go create mode 100644 v2/internal/functionaltests/utils/bad_metadata.json create mode 100644 v2/internal/internal_test.go create mode 100644 v2/internal/transaction_handler.go create mode 100644 v2/internal/transaction_handler_test.go create mode 100644 v2/internal/types/types.go create mode 100644 v2/internal/types/types_test.go create mode 100644 v2/internal/types_handler.go create mode 100644 v2/internal/types_handler_test.go create mode 100644 v2/internal/utils/utils.go create mode 100644 v2/internal/utils/utils_test.go create mode 100644 v2/metadata/a_metadata-packr.go create mode 100644 v2/metadata/ioutils.go create mode 100644 v2/metadata/ioutils_test.go create mode 100644 v2/metadata/metadata.go create mode 100644 v2/metadata/metadata_main_test.go create mode 100755 v2/metadata/metadata_test.go create mode 100644 v2/metadata/schema.go create mode 100644 v2/metadata/schema/schema.json create mode 100644 v2/metadata/schema_test.go create mode 100644 v2/serializer/json_transaction_serializer.go create mode 100644 v2/serializer/json_transaction_serializer_test.go create mode 100644 v2/serializer/serializer_test.go create mode 100644 v2/serializer/transaction_serializer.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f8ab9f0..3e2c6db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,93 +8,11 @@ on: workflow_call: jobs: - license_check: - name: License check - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 16 - registry-url: "https://npm.pkg.github.com" - - name: install Tools - run: | - npm install -g license-check-and-add@4.0.5 - - name: Check Licenses - run: license-check-and-add check -f ci/license-config.json + checks: + uses: ./.github/workflows/check.yml - tutorial: - name: Check tutorial - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - name: Check tutorial contents - run: ci/scripts/tutorial-checks.sh + test-v1: + uses: ./.github/workflows/test-v1.yml - lint: - name: Lint - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - name: Staticcheck - run: make staticcheck - - name: golangci-lint - uses: golangci/golangci-lint-action@v6 - with: - version: latest - - unit_test: - name: Unit test - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - name: Run tests (excluding fv) - run: make unit-test - - functional_test: - name: Functional test - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - name: Run functional tests - run: make functional-test - - integration_test: - name: Integration test - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - uses: actions/setup-node@v4 - with: - node-version: 16 - registry-url: "https://npm.pkg.github.com" - - name: Run the integration tests - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - docker build . --file integrationtest/Dockerfile --tag hyperledger/fabric-contract-api-go-integrationtest - - ci/scripts/setup-integration-chaincode.sh - - curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh | bash -s -- samples binary docker - export TEST_NETWORK_DIR=$(pwd)/fabric-samples/test-network - - cd ./integrationtest - npm ci - - npx fabric-chaincode-integration run + test-v2: + uses: ./.github/workflows/test-v2.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..aab2ba9 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,19 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Checks + +on: + workflow_call: + +jobs: + tutorial: + name: Check tutorial + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Check tutorial contents + run: ci/scripts/tutorial-checks.sh diff --git a/.github/workflows/test-v1.yml b/.github/workflows/test-v1.yml new file mode 100644 index 0000000..8d02832 --- /dev/null +++ b/.github/workflows/test-v1.yml @@ -0,0 +1,73 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Test (v1) + +on: + workflow_call: + +jobs: + lint: + name: Lint + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Staticcheck + run: make -f Makefile.v1 staticcheck + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + + unit_test: + name: Unit test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Run tests (excluding fv) + run: make -f Makefile.v1 unit-test + + functional_test: + name: Functional test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Run functional tests + run: make -f Makefile.v1 functional-test + + integration_test: + name: Integration test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/setup-node@v4 + with: + node-version: 16 + registry-url: "https://npm.pkg.github.com" + - name: Run the integration tests + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + docker build . --file integrationtest/Dockerfile --tag hyperledger/fabric-contract-api-go-integrationtest + + ci/scripts/setup-integration-chaincode.sh + + curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh | bash -s -- samples binary docker + export TEST_NETWORK_DIR=$(pwd)/fabric-samples/test-network + + cd ./integrationtest + npm ci + + npx fabric-chaincode-integration run diff --git a/.github/workflows/test-v2.yml b/.github/workflows/test-v2.yml new file mode 100644 index 0000000..d44579d --- /dev/null +++ b/.github/workflows/test-v2.yml @@ -0,0 +1,74 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Test (v2) + +on: + workflow_call: + +jobs: + lint: + name: Lint + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Staticcheck + run: make staticcheck + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + working-directory: v2 + + unit_test: + name: Unit test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Run tests (excluding fv) + run: make unit-test + + functional_test: + name: Functional test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Run functional tests + run: make functional-test + + integration_test: + name: Integration test + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - uses: actions/setup-node@v4 + with: + node-version: 16 + registry-url: "https://npm.pkg.github.com" + - name: Run the integration tests + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + docker build . --file integrationtest/Dockerfile --tag hyperledger/fabric-contract-api-go-integrationtest + + ci/scripts/setup-integration-chaincode.sh + + curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh | bash -s -- samples binary docker + export TEST_NETWORK_DIR=$(pwd)/fabric-samples/test-network + + cd ./integrationtest + npm ci + + npx fabric-chaincode-integration run diff --git a/.golangci.yml b/.golangci.yml index 654059f..ca43d95 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,6 +9,7 @@ linters: - errcheck - gocyclo - gofmt + - goheader - goimports - gosec - gosimple @@ -21,3 +22,7 @@ linters: linters-settings: gocyclo: min-complexity: 18 + goheader: + template: |- + Copyright the Hyperledger Fabric contributors. All rights reserved. + SPDX-License-Identifier: Apache-2.0 diff --git a/Makefile b/Makefile index 4b08b2d..1f27bcc 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,24 @@ # Copyright the Hyperledger Fabric contributors. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -base_dir := $(patsubst %/,%,$(dir $(realpath $(lastword $(MAKEFILE_LIST))))) +base_dir := $(patsubst %/,%,$(dir $(realpath $(lastword $(MAKEFILE_LIST)))))/v2 functional_dir := $(base_dir)/internal/functionaltests go_bin_dir := $(shell go env GOPATH)/bin +mockery_version := 2.43.2 +kernel_name := $(shell uname -s) +machine_hardware := $(shell uname -m) + +.PHONY: test +test: generate lint unit-test functional-test + .PHONY: lint lint: staticcheck golangci-lint .PHONY: staticcheck staticcheck: go install honnef.co/go/tools/cmd/staticcheck@latest - staticcheck -f stylish './...' + cd '$(base_dir)' && staticcheck -f stylish ./... .PHONY: install-golangci-lint install-golangci-lint: @@ -22,11 +29,24 @@ $(go_bin_dir)/golangci-lint: .PHONY: golangci-lint golangci-lint: $(go_bin_dir)/golangci-lint - golangci-lint run + cd '$(base_dir)' && golangci-lint run + +.PHONY: install-mockery +install-mockery: + curl --fail --location \ + 'https://github.com/vektra/mockery/releases/download/v$(mockery_version)/mockery_$(mockery_version)_$(kernel_name)_$(machine_hardware).tar.gz' \ + | tar -C '$(go_bin_dir)' -xzf - mockery + +$(go_bin_dir)/mockery: + $(MAKE) install-mockery + +.PHONY: generate +generate: $(go_bin_dir)/mockery + cd '$(base_dir)' && mockery .PHONY: unit-test unit-test: - go test -race $$(go list '$(base_dir)/...' | grep -v functionaltests) + cd '$(base_dir)' && go test -race $$(go list ./... | grep -v functionaltests) .PHONY: functional-test functional-test: @@ -36,4 +56,4 @@ functional-test: .PHONY: scan scan: go install golang.org/x/vuln/cmd/govulncheck@latest - govulncheck '$(base_dir)/...' + cd '$(base_dir)' && govulncheck ./... diff --git a/Makefile.v1 b/Makefile.v1 new file mode 100644 index 0000000..b3ebb03 --- /dev/null +++ b/Makefile.v1 @@ -0,0 +1,42 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +base_dir := $(patsubst %/,%,$(dir $(realpath $(lastword $(MAKEFILE_LIST))))) +functional_dir := $(base_dir)/internal/functionaltests +go_bin_dir := $(shell go env GOPATH)/bin + +.PHONY: test +test: lint unit-test functional-test + +.PHONY: lint +lint: staticcheck golangci-lint + +.PHONY: staticcheck +staticcheck: + go install honnef.co/go/tools/cmd/staticcheck@latest + cd '$(base_dir)' && staticcheck -f stylish ./... + +.PHONY: install-golangci-lint +install-golangci-lint: + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b '$(go_bin_dir)' + +$(go_bin_dir)/golangci-lint: + $(MAKE) install-golangci-lint + +.PHONY: golangci-lint +golangci-lint: $(go_bin_dir)/golangci-lint + cd '$(base_dir)' && golangci-lint run + +.PHONY: unit-test +unit-test: + cd '$(base_dir)' && go test -race $$(go list ./... | grep -v functionaltests) + +.PHONY: functional-test +functional-test: + go install github.com/cucumber/godog/cmd/godog@v0.12 + cd '$(functional_dir)' && godog run features/* + +.PHONY: scan +scan: + go install golang.org/x/vuln/cmd/govulncheck@latest + cd '$(base_dir)' && govulncheck ./... diff --git a/ci/license-config.json b/ci/license-config.json deleted file mode 100644 index 7ead262..0000000 --- a/ci/license-config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/awjh/license-check-and-add/master/config-schema.json", - "ignore": [".git", "**/*.mod", "**/*.sum", "**/*.md", "**/.gitignore", "**/.dockerignore", "**/*.yml", "**/*.out", ".azure-pipelines/Dockerfile", "CODEOWNERS"], - "license": "ci/license.txt", - "trailingWhitespace": "TRIM", - "licenseFormats": { - "js|go": { - "eachLine": { - "prepend": "// " - } - }, - "^Dockerfile|feature|^Makefile": { - "eachLine": { - "prepend": "# " - } - } - } -} \ No newline at end of file diff --git a/ci/license.txt b/ci/license.txt deleted file mode 100644 index 82e71e9..0000000 --- a/ci/license.txt +++ /dev/null @@ -1,2 +0,0 @@ -Copyright the Hyperledger Fabric contributors. All rights reserved. -SPDX-License-Identifier: Apache-2.0 \ No newline at end of file diff --git a/integrationtest/Dockerfile b/integrationtest/Dockerfile index 8cbf84d..099f67a 100644 --- a/integrationtest/Dockerfile +++ b/integrationtest/Dockerfile @@ -1,7 +1,7 @@ # Copyright the Hyperledger Fabric contributors. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -ARG GO_VER=1.20 +ARG GO_VER=1.21 FROM golang:${GO_VER} diff --git a/v2/.dockerignore b/v2/.dockerignore new file mode 100644 index 0000000..1ee94f2 --- /dev/null +++ b/v2/.dockerignore @@ -0,0 +1 @@ +integrationtest diff --git a/v2/.mockery.yaml b/v2/.mockery.yaml new file mode 100644 index 0000000..d94a327 --- /dev/null +++ b/v2/.mockery.yaml @@ -0,0 +1,11 @@ +with-expecter: true +inpackage: true +exported: true +filename: "mock_{{.InterfaceName}}_test.go" +mockname: "Mock{{.InterfaceName | firstUpper}}" +packages: + github.com/hyperledger/fabric-contract-api-go/v2/contractapi: + config: + dir: "contractapi" + interfaces: + chaincodeStubInterface: diff --git a/v2/ci/scripts/setup-integration-chaincode.sh b/v2/ci/scripts/setup-integration-chaincode.sh new file mode 100755 index 0000000..9a9c9c4 --- /dev/null +++ b/v2/ci/scripts/setup-integration-chaincode.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Adds to go.mod file of each of the integration chaincodes a pointer to use the local files instead of github +# for the contract api go packages. Runs go mod vendor to put these files into a vendor folder so they are used +# by the integration tool which doesn't have the local files. Removes the replace to stop the integration tests +# breaking as the dot path doesn't exist there. + +# Note the actually committed go.mod files of the chaincode already have the +# 'replace github.com/hyperledger/fabric-contract-api-go => ../../..' I + +set -e -u -o pipefail +ROOTDIR=$(cd "$(dirname "$0")" && pwd) + +CHAINCODE_DIR=$ROOTDIR/../../integrationtest/chaincode +ls -lart $CHAINCODE_DIR +pushd $CHAINCODE_DIR +for testCC in */; do + pushd $testCC + go mod vendor + popd +done +popd + diff --git a/v2/contractapi/api_test.go b/v2/contractapi/api_test.go new file mode 100644 index 0000000..53f21db --- /dev/null +++ b/v2/contractapi/api_test.go @@ -0,0 +1,25 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import ( + "fmt" + "os" + "testing" +) + +func TestMain(m *testing.M) { + rc := m.Run() + + if rc == 0 && testing.CoverMode() != "" { + c := testing.Coverage() + + if c < 0.96 { + fmt.Println("Tests passed but coverage failed at", c) + rc = -1 + } + } + + os.Exit(rc) +} diff --git a/v2/contractapi/contract.go b/v2/contractapi/contract.go new file mode 100644 index 0000000..fe2dae7 --- /dev/null +++ b/v2/contractapi/contract.go @@ -0,0 +1,128 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + +// IgnoreContractInterface extends ContractInterface and provides additional functionality +// that can be used to mark which functions should not be accessible by invoking/querying +// chaincode +type IgnoreContractInterface interface { + // GetIgnoredFunctions returns a list of function names for functions that should not + // be included in the produced metadata or accessible by invoking/querying the chaincode. + // Note these functions are still callable by the code just not directly by outside users. + // Those that match functions in the ChaincodeInterface are ignored by default and do not + // need to be included + GetIgnoredFunctions() []string +} + +// EvaluationContractInterface extends ContractInterface and provides additional functionality +// that can be used to improve metadata +type EvaluationContractInterface interface { + // GetEvaluateTransactions returns a list of function names that should be tagged in the + // metadata as "evaluate" to indicate to a user of the chaincode that they should query + // rather than invoke these functions + GetEvaluateTransactions() []string +} + +// ContractInterface defines functions a valid contract should have. Contracts to +// be used in chaincode must implement this interface. +type ContractInterface interface { + // GetInfo returns the information stored for the contract. This information will be + // used to build up the metadata. If version is left blank in this info then "latest" + // will be used in the metadata. If title is blank then the contract's GetName will be + // used, if that is blank then the contract struct name + GetInfo() metadata.InfoMetadata + + // GetUnknownTransaction returns the unknown function to be used for a contract. + // When the contract is used in creating a new chaincode this function is called + // and the unknown transaction returned is stored. The unknown function is then + // called in cases where an unknown function name is passed for a call to the + // contract via Init/Invoke of the chaincode. If nil is returned the + // chaincode uses its default handling for unknown function names + GetUnknownTransaction() interface{} + + // GetBeforeTransaction returns the before function to be used for a contract. + // When the contract is used in creating a new chaincode this function is called + // and the before transaction returned is stored. The before function is then + // called before the named function on each Init/Invoke of that contract via the + // chaincode. When called the before function is passed no extra args, only the + // the transaction context (if specified to take it). If nil is returned + // then no before function is called on Init/Invoke. + GetBeforeTransaction() interface{} + + // GetAfterTransaction returns the after function to be used for a contract. + // When the contract is used in creating a new chaincode this function is called + // and the after transaction returned is stored. The after function is then + // called after the named function on each Init/Invoke of that contract via the + // chaincode. When called the after function is passed the returned value of the + // named function and the transaction context (if the function takes the transaction + // context). If nil is returned then no after function is called on Init/ + // Invoke. + GetAfterTransaction() interface{} + + // GetName returns the name of the contract. When the contract is used + // in creating a new chaincode this function is called and the name returned + // is then used to identify the contract within the chaincode on Init/Invoke calls. + // This function can return a blank string but this is undefined behaviour. + GetName() string + + // GetTransactionContextHandler returns the SettableTransactionContextInterface that is + // used by the functions of the contract. When the contract is used in creating + // a new chaincode this function is called and the transaction context returned + // is stored. When the chaincode is called via Init/Invoke a transaction context + // of the stored type is created and sent as a parameter to the named contract + // function (and before/after and unknown functions) if the function requires the + // context in its list of parameters. If functions taking the transaction context + // take an interface as the context, the transaction context returned by this function + // must meet that interface + GetTransactionContextHandler() SettableTransactionContextInterface +} + +// Contract defines functions for setting and getting before, after and unknown transactions +// and name. Can be embedded in structs to quickly ensure their definition meets the +// ContractInterface. +type Contract struct { + Name string + Info metadata.InfoMetadata + UnknownTransaction interface{} + BeforeTransaction interface{} + AfterTransaction interface{} + TransactionContextHandler SettableTransactionContextInterface +} + +// GetInfo returns the info about the contract for use in metadata +func (c *Contract) GetInfo() metadata.InfoMetadata { + return c.Info +} + +// GetUnknownTransaction returns the current set unknownTransaction, may be nil +func (c *Contract) GetUnknownTransaction() interface{} { + return c.UnknownTransaction +} + +// GetBeforeTransaction returns the current set beforeTransaction, may be nil +func (c *Contract) GetBeforeTransaction() interface{} { + return c.BeforeTransaction +} + +// GetAfterTransaction returns the current set afterTransaction, may be nil +func (c *Contract) GetAfterTransaction() interface{} { + return c.AfterTransaction +} + +// GetName returns the name of the contract +func (c *Contract) GetName() string { + return c.Name +} + +// GetTransactionContextHandler returns the current transaction context set for +// the contract. If none has been set then TransactionContext will be returned +func (c *Contract) GetTransactionContextHandler() SettableTransactionContextInterface { + if c.TransactionContextHandler == nil { + return new(TransactionContext) + } + + return c.TransactionContextHandler +} diff --git a/v2/contractapi/contract_chaincode.go b/v2/contractapi/contract_chaincode.go new file mode 100644 index 0000000..e392d74 --- /dev/null +++ b/v2/contractapi/contract_chaincode.go @@ -0,0 +1,509 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import ( + "encoding/json" + "fmt" + "log" + "os" + "reflect" + "sort" + "strconv" + "strings" + "unicode" + + "github.com/hyperledger/fabric-chaincode-go/v2/pkg/cid" + "github.com/hyperledger/fabric-chaincode-go/v2/shim" + "github.com/hyperledger/fabric-contract-api-go/v2/internal" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/utils" + "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + "github.com/hyperledger/fabric-contract-api-go/v2/serializer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" +) + +//lint:ignore U1000 used for testing +type chaincodeStubInterface interface { + shim.ChaincodeStubInterface +} + +type contractChaincodeContract struct { + info metadata.InfoMetadata + functions map[string]*internal.ContractFunction + unknownTransaction *internal.TransactionHandler + beforeTransaction *internal.TransactionHandler + afterTransaction *internal.TransactionHandler + transactionContextHandler reflect.Type +} + +// ContractChaincode a struct to meet the chaincode interface and provide routing of calls to contracts +type ContractChaincode struct { + DefaultContract string + contracts map[string]contractChaincodeContract + metadata metadata.ContractChaincodeMetadata + Info metadata.InfoMetadata + TransactionSerializer serializer.TransactionSerializer +} + +const ( + // SystemContractName the name of the system smart contract + SystemContractName = "org.hyperledger.fabric" + serverAddressVariable = "CHAINCODE_SERVER_ADDRESS" + chaincodeIdVariable = "CORE_CHAINCODE_ID_NAME" + tlsEnabledVariable = "CORE_PEER_TLS_ENABLED" + rootCertVariable = "CORE_PEER_TLS_ROOTCERT_FILE" + clientKeyVariable = "CORE_TLS_CLIENT_KEY_FILE" + clientCertVariable = "CORE_TLS_CLIENT_CERT_FILE" +) + +// NewChaincode creates a new chaincode using contracts passed. The function parses each +// of the passed functions and stores details about their make-up to be used by the chaincode. +// Public functions of the contracts are stored and are made callable in the chaincode. The function +// will error if contracts are invalid e.g. public functions take in illegal types. A system contract is added +// to the chaincode which provides functionality for getting the metadata of the chaincode. The generated +// metadata is a JSON formatted MetadataContractChaincode containing each contract as a name and details +// of the public functions and types they take in/return. It also outlines version details for contracts and the +// chaincode. If these are blank strings this is set to latest. The names for parameters do not match those used +// in the functions, instead they are recorded as param0, param1, ..., paramN. If there exists a file +// contract-metadata/metadata.json then this will overwrite the generated metadata. The contents of this file must +// validate against the schema. The transaction serializer for the contract is set to be the JSONSerializer by +// default. This can be updated using by changing the TransactionSerializer property +func NewChaincode(contracts ...ContractInterface) (*ContractChaincode, error) { + ciMethods := getCiMethods() + + cc := new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + + for _, contract := range contracts { + additionalExcludes := []string{} + if castContract, ok := contract.(IgnoreContractInterface); ok { + additionalExcludes = castContract.GetIgnoredFunctions() + } + + err := cc.addContract(contract, append(ciMethods, additionalExcludes...)) + + if err != nil { + return nil, err + } + } + + sysC := new(SystemContract) + sysC.Name = SystemContractName + + if err := cc.addContract(sysC, ciMethods); err != nil { + return nil, err + } + + if err := cc.augmentMetadata(); err != nil { + return nil, err + } + + metadataJSON, _ := json.Marshal(cc.metadata) + + sysC.setMetadata(string(metadataJSON)) + + cc.TransactionSerializer = new(serializer.JSONSerializer) + + return cc, nil +} + +// Start starts the chaincode in the fabric shim +func (cc *ContractChaincode) Start() error { + server, err := loadChaincodeServerConfig() + if err != nil { + return err + } + + if server != nil { + server.CC = cc + return server.Start() + } + + return shim.Start(cc) +} + +// Init is called during Instantiate transaction after the chaincode container +// has been established for the first time, passes off details of the request to Invoke +// for handling the request if a function name is passed, otherwise returns shim.Success +func (cc *ContractChaincode) Init(stub shim.ChaincodeStubInterface) *peer.Response { + nsFcn, _ := stub.GetFunctionAndParameters() + if nsFcn == "" { + return shim.Success([]byte("Default initiator successful.")) + } + + return cc.Invoke(stub) +} + +// Invoke is called to update or query the ledger in a proposal transaction. Takes the +// args passed in the transaction and uses the first argument to identify the contract +// and function of that contract to be called. The remaining args are then used as +// parameters to that function. Args are converted from strings to the expected parameter +// types of the function before being passed using the set transaction serializer for the ContractChaincode. +// A transaction context is generated and is passed, if required, as the first parameter to the named function. +// Before and after functions are called before and after the named function passed if the contract defines such +// functions to exist. If the before function returns an error the named function is not called and its error +// is returned in shim.Error. If the after function returns an error then its value is returned +// to shim.Error otherwise the value returned from the named function is returned as shim.Success (formatted by +// the transaction serializer). If an unknown name is passed as part of the first arg a shim.Error is returned. +// If a valid name is passed but the function name is unknown then the contract with that name's +// unknown function is called and its value returned as success or error depending on its return. If no +// unknown function is defined for the contract then shim.Error is returned by Invoke. In the case of +// unknown function names being passed (and the unknown handler returns an error) or the named function +// returning an error then the after function if defined is not called. If the named function or unknown +// function handler returns a non-error type then then the after transaction is sent this value. The same +// transaction context is passed as a pointer to before, after, named and unknown functions on each Invoke. +// If no contract name is passed then the default contract is used. +func (cc *ContractChaincode) Invoke(stub shim.ChaincodeStubInterface) *peer.Response { + + nsFcn, params := stub.GetFunctionAndParameters() + + li := strings.LastIndex(nsFcn, ":") + + var ns string + var fn string + + if li == -1 { + ns = cc.DefaultContract + fn = nsFcn + } else { + ns = nsFcn[:li] + fn = nsFcn[li+1:] + } + + if _, ok := cc.contracts[ns]; !ok { + return shim.Error(fmt.Sprintf("Contract not found with name %s", ns)) + } + + if fn == "" { + return shim.Error("Blank function name passed") + } + + originalFn := fn + + fnRune := []rune(fn) + + if unicode.IsLower(fnRune[0]) { + fnRune[0] = unicode.ToUpper(fnRune[0]) + fn = string(fnRune) + } + + nsContract := cc.contracts[ns] + + ctx := reflect.New(nsContract.transactionContextHandler) + ctxIface := ctx.Interface().(SettableTransactionContextInterface) + ctxIface.SetStub(stub) + + ci, _ := cid.New(stub) + ctxIface.SetClientIdentity(ci) + + beforeTransaction := nsContract.beforeTransaction + + if beforeTransaction != nil { + _, _, errRes := beforeTransaction.Call(ctx, nil, nil) + + if errRes != nil { + return shim.Error(errRes.Error()) + } + } + + var successReturn string + var successIFace interface{} + var errorReturn error + + serializer := cc.TransactionSerializer + + if _, ok := nsContract.functions[fn]; !ok { + unknownTransaction := nsContract.unknownTransaction + if unknownTransaction == nil { + return shim.Error(fmt.Sprintf("Function %s not found in contract %s", originalFn, ns)) + } + + successReturn, successIFace, errorReturn = unknownTransaction.Call(ctx, nil, serializer) + } else { + var transactionSchema *metadata.TransactionMetadata + + for i, v := range cc.metadata.Contracts[ns].Transactions { + if v.Name == fn { + transactionSchema = &cc.metadata.Contracts[ns].Transactions[i] + break + } + } + + successReturn, successIFace, errorReturn = nsContract.functions[fn].Call(ctx, transactionSchema, &cc.metadata.Components, serializer, params...) + } + + if errorReturn != nil { + return shim.Error(errorReturn.Error()) + } + + afterTransaction := nsContract.afterTransaction + + if afterTransaction != nil { + _, _, errRes := afterTransaction.Call(ctx, successIFace, nil) + + if errRes != nil { + return shim.Error(errRes.Error()) + } + } + + return shim.Success([]byte(successReturn)) +} + +func (cc *ContractChaincode) addContract(contract ContractInterface, excludeFuncs []string) error { + ns := contract.GetName() + + if ns == "" { + ns = reflect.TypeOf(contract).Elem().Name() + } + + if _, ok := cc.contracts[ns]; ok { + return fmt.Errorf("multiple contracts being merged into chaincode with name %s", ns) + } + + ccn := contractChaincodeContract{} + ccn.transactionContextHandler = reflect.ValueOf(contract.GetTransactionContextHandler()).Elem().Type() + transactionContextPtrHandler := reflect.ValueOf(contract.GetTransactionContextHandler()).Type() + ccn.functions = make(map[string]*internal.ContractFunction) + ccn.info = contract.GetInfo() + + if ccn.info.Version == "" { + ccn.info.Version = "latest" + } + + if ccn.info.Title == "" { + ccn.info.Title = ns + } + + contractType := reflect.PtrTo(reflect.TypeOf(contract).Elem()) + contractValue := reflect.ValueOf(contract).Elem().Addr() + + ut := contract.GetUnknownTransaction() + + if ut != nil { + var err error + ccn.unknownTransaction, err = internal.NewTransactionHandler(ut, transactionContextPtrHandler, internal.TransactionHandlerTypeUnknown) + + if err != nil { + return err + } + } + + bt := contract.GetBeforeTransaction() + + if bt != nil { + var err error + ccn.beforeTransaction, err = internal.NewTransactionHandler(bt, transactionContextPtrHandler, internal.TransactionHandlerTypeBefore) + + if err != nil { + return err + } + } + + at := contract.GetAfterTransaction() + + if at != nil { + var err error + ccn.afterTransaction, err = internal.NewTransactionHandler(at, transactionContextPtrHandler, internal.TransactionHandlerTypeAfter) + + if err != nil { + return err + } + } + + evaluateMethods := []string{} + + if eci, ok := contract.(EvaluationContractInterface); ok { + evaluateMethods = eci.GetEvaluateTransactions() + } + + for i := 0; i < contractType.NumMethod(); i++ { + typeMethod := contractType.Method(i) + valueMethod := contractValue.Method(i) + + if !utils.StringInSlice(typeMethod.Name, excludeFuncs) { + var err error + + var callType internal.CallType = internal.CallTypeSubmit + + if utils.StringInSlice(typeMethod.Name, evaluateMethods) { + callType = internal.CallTypeEvaluate + } + + ccn.functions[typeMethod.Name], err = internal.NewContractFunctionFromReflect(typeMethod, valueMethod, callType, transactionContextPtrHandler) + + if err != nil { + return err + } + } + } + + if len(ccn.functions) == 0 { + return fmt.Errorf("contracts are required to have at least 1 (non-ignored) public method. Contract %s has none. Method names that have been ignored: %s", ns, utils.SliceAsCommaSentence(excludeFuncs)) + } + + cc.contracts[ns] = ccn + + if cc.DefaultContract == "" { + cc.DefaultContract = ns + } + + return nil +} + +func (cc *ContractChaincode) reflectMetadata() metadata.ContractChaincodeMetadata { + reflectedMetadata := metadata.ContractChaincodeMetadata{} + reflectedMetadata.Contracts = make(map[string]metadata.ContractMetadata) + reflectedMetadata.Components.Schemas = make(map[string]metadata.ObjectMetadata) + reflectedMetadata.Info = &cc.Info + + if cc.Info.Version == "" { + reflectedMetadata.Info.Version = "latest" + } + + if cc.Info.Title == "" { + reflectedMetadata.Info.Title = "undefined" + } + + for key, contract := range cc.contracts { + contractMetadata := metadata.ContractMetadata{} + contractMetadata.Name = key + infoCopy := contract.info + contractMetadata.Info = &infoCopy + + if cc.DefaultContract == key { + contractMetadata.Default = true + } + + for key, fn := range contract.functions { + fnMetadata := fn.ReflectMetadata(key, &reflectedMetadata.Components) + + contractMetadata.Transactions = append(contractMetadata.Transactions, fnMetadata) + } + + sort.Slice(contractMetadata.Transactions, func(i, j int) bool { + return contractMetadata.Transactions[i].Name < contractMetadata.Transactions[j].Name + }) + + reflectedMetadata.Contracts[key] = contractMetadata + } + + return reflectedMetadata +} + +func (cc *ContractChaincode) augmentMetadata() error { + fileMetadata, err := metadata.ReadMetadataFile() + + if err != nil && !strings.Contains(err.Error(), "failed to read metadata from file") { + return err + } + + reflectedMetadata := cc.reflectMetadata() + + fileMetadata.Append(reflectedMetadata) + err = fileMetadata.CompileSchemas() + if err != nil { + return err + } + + err = metadata.ValidateAgainstSchema(fileMetadata) + if err != nil { + return err + } + + cc.metadata = fileMetadata + return nil +} + +func getCiMethods() []string { + contractInterfaceType := reflect.TypeOf((*ContractInterface)(nil)).Elem() + ignoreContractInterfaceType := reflect.TypeOf((*IgnoreContractInterface)(nil)).Elem() + evaluateContractInterfaceType := reflect.TypeOf((*EvaluationContractInterface)(nil)).Elem() + + interfaceTypes := []reflect.Type{contractInterfaceType, ignoreContractInterfaceType, evaluateContractInterfaceType} + + var ciMethods []string + for _, interfaceType := range interfaceTypes { + for i := 0; i < interfaceType.NumMethod(); i++ { + ciMethods = append(ciMethods, interfaceType.Method(i).Name) + } + } + + return ciMethods +} + +func loadChaincodeServerConfig() (*shim.ChaincodeServer, error) { + address := getStringEnv(serverAddressVariable, "") + ccid := getStringEnv(chaincodeIdVariable, "") + + if address == "" || ccid == "" { + return nil, nil + } + + tlsProps, err := loadTLSProperties() + if err != nil { + log.Panicf("error creating getting TLS properties: %v", err) + } + + server := &shim.ChaincodeServer{ + CCID: ccid, + Address: address, + TLSProps: *tlsProps, + } + + return server, nil +} + +func loadTLSProperties() (*shim.TLSProperties, error) { + tlsEnabled := getBoolEnv(tlsEnabledVariable, false) + if !tlsEnabled { + return &shim.TLSProperties{Disabled: true}, nil + } + + key := getStringEnv(clientKeyVariable, "") + cert := getStringEnv(clientCertVariable, "") + root := getStringEnv(rootCertVariable, "") + + var keyBytes, certBytes, rootBytes []byte + var err error + + keyBytes, err = os.ReadFile(key) + if err != nil { + return nil, fmt.Errorf("error while reading the crypto file: %s", err) + } + + certBytes, err = os.ReadFile(cert) + if err != nil { + return nil, fmt.Errorf("error while reading the crypto file: %s", err) + } + + if root != "" { + rootBytes, err = os.ReadFile(root) + if err != nil { + return nil, fmt.Errorf("error while reading the crypto file: %s", err) + } + } + + return &shim.TLSProperties{ + Disabled: false, + Key: keyBytes, + Cert: certBytes, + ClientCACerts: rootBytes, + }, nil +} + +func getStringEnv(key, defaultVal string) string { + value, ok := os.LookupEnv(key) + if !ok { + value = defaultVal + } + return value +} + +func getBoolEnv(key string, defaultVal bool) bool { + value, err := strconv.ParseBool(os.Getenv(key)) + if err != nil { + return defaultVal + } + return value +} diff --git a/v2/contractapi/contract_chaincode_test.go b/v2/contractapi/contract_chaincode_test.go new file mode 100644 index 0000000..c876f79 --- /dev/null +++ b/v2/contractapi/contract_chaincode_test.go @@ -0,0 +1,666 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/hyperledger/fabric-chaincode-go/v2/shim" + "github.com/hyperledger/fabric-contract-api-go/v2/internal" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/utils" + "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + "github.com/hyperledger/fabric-contract-api-go/v2/serializer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +// ================================ +// HELPERS +// ================================ + +const standardValue = "100" +const standardTxID = "1234567890" + +type CallType int + +const ( + initType CallType = iota + invokeType +) + +// AssertProtoEqual ensures an expected protobuf message matches an actual message +func AssertProtoEqual(t *testing.T, expected proto.Message, actual proto.Message) { + t.Helper() + require.True(t, proto.Equal(expected, actual), "Expected %v, got %v", expected, actual) +} + +type simpleStruct struct { + Prop1 string `json:"prop1"` + //lint:ignore U1000 unused + prop2 string +} + +func (ss *simpleStruct) GoodMethod(param1 string, param2 string) string { + return param1 + param2 +} + +func (ss *simpleStruct) AnotherGoodMethod() int { + return 1 +} + +type emptyContract struct { + Contract +} + +type privateContract struct { + Contract +} + +//lint:ignore U1000 unused +func (pc *privateContract) privateMethod() int64 { + return 1 +} + +type badContract struct { + Contract +} + +func (bc *badContract) BadMethod() complex64 { + return 1 +} + +type goodContract struct { + myContract + called []string +} + +func (gc *goodContract) logBefore() { + gc.called = append(gc.called, "Before function called") +} + +func (gc *goodContract) LogNamed() string { + gc.called = append(gc.called, "Named function called") + return "named response" +} + +func (gc *goodContract) logAfter(data interface{}) { + gc.called = append(gc.called, fmt.Sprintf("After function called with %v", data)) +} + +func (gc *goodContract) logUnknown() { + gc.called = append(gc.called, "Unknown function called") +} + +func (gc *goodContract) ReturnsError() error { + return errors.New("Some error") +} + +func (gc *goodContract) ReturnsNothing() {} + +func (gc *goodContract) CheckContextStub(ctx *TransactionContext) (string, error) { + if ctx.GetStub().GetTxID() != standardTxID { + return "", fmt.Errorf("You used a non standard txID [%s]", ctx.GetStub().GetTxID()) + } + + return "Stub as expected", nil +} + +type goodContractCustomContext struct { + Contract +} + +func (sc *goodContractCustomContext) SetValInCustomContext(ctx *customContext) { + _, params := ctx.GetStub().GetFunctionAndParameters() + ctx.prop1 = params[0] +} + +func (sc *goodContractCustomContext) GetValInCustomContext(ctx *customContext) (string, error) { + if ctx.prop1 != standardValue { + return "", errors.New("I wanted a standard value") + } + + return ctx.prop1, nil +} + +func (sc *goodContractCustomContext) CheckCustomContext(ctx *customContext) string { + return ctx.ReturnString() +} + +func (cc *customContext) ReturnString() string { + return "I am custom context" +} + +type ignorableFuncContract struct { + goodContract +} + +func (gifc *ignorableFuncContract) IgnoreMe() {} + +func (gifc *ignorableFuncContract) GetIgnoredFunctions() []string { + return []string{"IgnoreMe"} +} + +type evaluateContract struct { + myContract +} + +func (ec *evaluateContract) GetEvaluateTransactions() []string { + return []string{"ReturnsString"} +} + +type txHandler struct{} + +func (tx *txHandler) Handler() { + // do nothing +} + +func testContractChaincodeContractMatchesContract(t *testing.T, actual contractChaincodeContract, expected contractChaincodeContract) { + t.Helper() + + require.Equal(t, expected.info, actual.info, "should have matching info") + + if actual.beforeTransaction != nil { + require.Equal(t, expected.beforeTransaction.ReflectMetadata("", nil), actual.beforeTransaction.ReflectMetadata("", nil), "should have matching before transactions") + } + + if actual.unknownTransaction != nil { + require.Equal(t, expected.unknownTransaction.ReflectMetadata("", nil), actual.unknownTransaction.ReflectMetadata("", nil), "should have matching before transactions") + } + + if actual.afterTransaction != nil { + require.Equal(t, expected.afterTransaction.ReflectMetadata("", nil), actual.afterTransaction.ReflectMetadata("", nil), "should have matching before transactions") + } + + require.Equal(t, expected.transactionContextHandler, actual.transactionContextHandler, "should have matching transaction contexts") + + for idx, cf := range actual.functions { + require.Equal(t, cf.ReflectMetadata("", nil), expected.functions[idx].ReflectMetadata("", nil), "should have matching functions") + } +} + +func callContractFunctionAndCheckError(t *testing.T, cc *ContractChaincode, arguments []string, callType CallType, expectedMessage string) { + t.Helper() + + callContractFunctionAndCheckResponse(t, cc, arguments, callType, expectedMessage, "error") +} + +func callContractFunctionAndCheckSuccess(t *testing.T, cc *ContractChaincode, arguments []string, callType CallType, expectedMessage string) { + t.Helper() + + callContractFunctionAndCheckResponse(t, cc, arguments, callType, expectedMessage, "success") +} + +func callContractFunctionAndCheckResponse(t *testing.T, cc *ContractChaincode, arguments []string, callType CallType, expectedMessage string, expectedType string) { + t.Helper() + + mockStub := NewMockChaincodeStubInterface(t) + mockStub.EXPECT().GetTxID().Maybe().Return(standardTxID) + mockStub.EXPECT().GetFunctionAndParameters().Maybe().Return(arguments[0], arguments[1:]) + mockStub.EXPECT().GetCreator().Maybe().Return([]byte{}, nil) + + var response *peer.Response + + if callType == initType { + response = cc.Init(mockStub) + } else if callType == invokeType { + response = cc.Invoke(mockStub) + } + + expectedResponse := shim.Success([]byte(expectedMessage)) + + if expectedType == "error" { + expectedResponse = shim.Error(expectedMessage) + } + + AssertProtoEqual(t, expectedResponse, response) +} + +func testCallingContractFunctions(t *testing.T, callType CallType) { + var cc *ContractChaincode + + gc := goodContract{} + cc, _ = NewChaincode(&gc) + + // Should error when name not known + callContractFunctionAndCheckError(t, cc, []string{"somebadname:somebadfunctionname"}, callType, "Contract not found with name somebadname") + + // should return error when function blank + callContractFunctionAndCheckError(t, cc, []string{"goodContract:"}, callType, "Blank function name passed") + + // should return error when function not known and no unknown transaction specified + gc.Name = "customname" + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckError(t, cc, []string{"customname:somebadfunctionname"}, callType, "Function somebadfunctionname not found in contract customname") + + // Should call default chaincode when name not passed + callContractFunctionAndCheckError(t, cc, []string{"somebadfunctionname"}, callType, "Function somebadfunctionname not found in contract customname") + + gc = goodContract{} + cc, _ = NewChaincode(&gc) + + // Should return success when function returns nothing + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:ReturnsNothing"}, callType, "") + + // Should return success when function starts with lower case + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:returnsNothing"}, callType, "") + + // should return success when function returns no error + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:ReturnsString"}, callType, gc.ReturnsString()) + + // Should return error when function returns error + callContractFunctionAndCheckError(t, cc, []string{"goodContract:ReturnsError"}, callType, gc.ReturnsError().Error()) + + // Should return error when function unknown and set unknown function returns error + gc.UnknownTransaction = gc.ReturnsError + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckError(t, cc, []string{"goodContract:somebadfunctionname"}, callType, gc.ReturnsError().Error()) + gc = goodContract{} + + // Should return success when function unknown and set unknown function returns no error + gc.UnknownTransaction = gc.ReturnsString + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:somebadfunctionname"}, callType, gc.ReturnsString()) + gc = goodContract{} + + // Should return error when before function returns error and not call main function + gc.BeforeTransaction = gc.ReturnsError + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckError(t, cc, []string{"goodContract:ReturnsString"}, callType, gc.ReturnsError().Error()) + gc = goodContract{} + + // Should return success from passed function when before function returns no error + gc.BeforeTransaction = gc.ReturnsString + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:ReturnsString"}, callType, gc.ReturnsString()) + gc = goodContract{} + + // Should return error when after function returns error + gc.AfterTransaction = gc.ReturnsError + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckError(t, cc, []string{"goodContract:ReturnsString"}, callType, gc.ReturnsError().Error()) + gc = goodContract{} + + // Should return success from passed function when before function returns error + gc.AfterTransaction = gc.ReturnsString + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:ReturnsString"}, callType, gc.ReturnsString()) + gc = goodContract{} + + // Should call before, named then after functions in order and pass name response + gc.BeforeTransaction = gc.logBefore + gc.AfterTransaction = gc.logAfter + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:LogNamed"}, callType, "named response") + require.Equal(t, []string{"Before function called", "Named function called", "After function called with named response"}, gc.called, "Expected called field of goodContract to have logged in order before, named then after") + gc = goodContract{} + + // Should call before, unknown then after functions in order and pass unknown response + gc.BeforeTransaction = gc.logBefore + gc.AfterTransaction = gc.logAfter + gc.UnknownTransaction = gc.logUnknown + cc, _ = NewChaincode(&gc) + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:somebadfunctionname"}, callType, "") + require.Equal(t, []string{"Before function called", "Unknown function called", "After function called with "}, gc.called, "Expected called field of goodContract to have logged in order before, named then after") + gc = goodContract{} + + // Should pass + + // should pass the stub into transaction context as expected + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:CheckContextStub"}, callType, "Stub as expected") + + sc := goodContractCustomContext{} + sc.TransactionContextHandler = new(customContext) + cc, _ = NewChaincode(&sc) + + //should use a custom transaction context when one is set + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContractCustomContext:CheckCustomContext"}, callType, "I am custom context") + + //should use same ctx for all calls + sc.BeforeTransaction = sc.SetValInCustomContext + cc, _ = NewChaincode(&sc) + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContractCustomContext:GetValInCustomContext", standardValue}, callType, standardValue) + + sc.AfterTransaction = sc.GetValInCustomContext + cc, _ = NewChaincode(&sc) + callContractFunctionAndCheckError(t, cc, []string{"goodContractCustomContext:SetValInCustomContext", "some other value"}, callType, "I wanted a standard value") + + // should use transaction serializer + cc, _ = NewChaincode(&gc) + cc.TransactionSerializer = new(mockSerializer) + callContractFunctionAndCheckSuccess(t, cc, []string{"goodContract:ReturnsString"}, callType, "GOODBYE WORLD") +} + +type mockSerializer struct{} + +func (ms *mockSerializer) FromString(string, reflect.Type, *metadata.ParameterMetadata, *metadata.ComponentMetadata) (reflect.Value, error) { + return reflect.ValueOf("HELLO WORLD"), nil +} + +func (ms *mockSerializer) ToString(reflect.Value, reflect.Type, *metadata.ReturnMetadata, *metadata.ComponentMetadata) (string, error) { + return "GOODBYE WORLD", nil +} + +func jsonCompare(t *testing.T, s1, s2 string) { + t.Helper() + + var o1 interface{} + var o2 interface{} + + var err error + err = json.Unmarshal([]byte(s1), &o1) + require.Nil(t, err, "invalid json supplied for string 1") + + err = json.Unmarshal([]byte(s2), &o2) + require.Nil(t, err, "invalid json supplied for string 2") + + require.True(t, reflect.DeepEqual(o1, o2), "JSON should be equal") +} + +// ================================ +// TESTS +// ================================ + +func TestReflectMetadata(t *testing.T) { + var reflectedMetadata metadata.ContractChaincodeMetadata + + goodMethod := new(simpleStruct).GoodMethod + anotherGoodMethod := new(simpleStruct).AnotherGoodMethod + ctx := reflect.TypeOf(TransactionContext{}) + + info := metadata.InfoMetadata{ + Title: "some chaincode", + Version: "1.0.0", + } + + cc := ContractChaincode{ + Info: info, + } + + cf, _ := internal.NewContractFunctionFromFunc(goodMethod, internal.CallTypeEvaluate, ctx) + cf2, _ := internal.NewContractFunctionFromFunc(anotherGoodMethod, internal.CallTypeEvaluate, ctx) + + cc.contracts = make(map[string]contractChaincodeContract) + cc.contracts["MyContract"] = contractChaincodeContract{ + info: metadata.InfoMetadata{ + Version: "1.1.0", + Title: "MyContract", + }, + functions: map[string]*internal.ContractFunction{ + "GoodMethod": cf, + "AnotherGoodMethod": cf2, + }, + } + + contractMetadata := metadata.ContractMetadata{} + contractMetadata.Name = "MyContract" + contractMetadata.Info = new(metadata.InfoMetadata) + contractMetadata.Info.Version = "1.1.0" + contractMetadata.Info.Title = "MyContract" + contractMetadata.Transactions = []metadata.TransactionMetadata{ + cf2.ReflectMetadata("AnotherGoodMethod", nil), + cf.ReflectMetadata("GoodMethod", nil), + } // alphabetical order + contractMetadata.Default = false + + expectedMetadata := metadata.ContractChaincodeMetadata{} + expectedMetadata.Info = new(metadata.InfoMetadata) + expectedMetadata.Info.Version = "1.0.0" + expectedMetadata.Info.Title = "some chaincode" + expectedMetadata.Components.Schemas = make(map[string]metadata.ObjectMetadata) + expectedMetadata.Contracts = make(map[string]metadata.ContractMetadata) + expectedMetadata.Contracts["MyContract"] = contractMetadata + + // TESTS + + reflectedMetadata = cc.reflectMetadata() + require.Equal(t, expectedMetadata, reflectedMetadata, "should return contract chaincode metadata") + + expectedMetadata.Info.Version = "latest" + cc.Info.Version = "" + expectedMetadata.Info.Title = "undefined" + cc.Info.Title = "" + reflectedMetadata = cc.reflectMetadata() + require.Equal(t, expectedMetadata, reflectedMetadata, "should sub in value for title and version when not set") + + cc.DefaultContract = "MyContract" + reflectedMetadata = cc.reflectMetadata() + contractMetadata.Default = true + expectedMetadata.Contracts["MyContract"] = contractMetadata + require.Equal(t, expectedMetadata, reflectedMetadata, "should return contract chaincode metadata when default") +} + +func TestAugmentMetadata(t *testing.T) { + info := metadata.InfoMetadata{ + Title: "some chaincode", + Version: "1.0.0", + } + + cc := ContractChaincode{ + Info: info, + } + + err := cc.augmentMetadata() + require.NoError(t, err) + + require.Equal(t, cc.reflectMetadata(), cc.metadata, "should return reflected metadata when none supplied as file") +} + +func TestAddContract(t *testing.T) { + var cc *ContractChaincode + var mc *myContract + var err error + + mc = new(myContract) + tx := new(txHandler) + + defaultExcludes := getCiMethods() + + transactionContextPtrHandler := reflect.ValueOf(mc.GetTransactionContextHandler()).Type() + + expectedCCC := contractChaincodeContract{} + expectedCCC.info.Version = "latest" + expectedCCC.info.Title = "myContract" + expectedCCC.functions = make(map[string]*internal.ContractFunction) + expectedCCC.functions["ReturnsString"], _ = internal.NewContractFunctionFromFunc(mc.ReturnsString, internal.CallTypeSubmit, transactionContextPtrHandler) + expectedCCC.transactionContextHandler = reflect.ValueOf(mc.GetTransactionContextHandler()).Elem().Type() + + // TESTS + + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + cc.contracts["customname"] = contractChaincodeContract{} + mc = new(myContract) + mc.Name = "customname" + err = cc.addContract(mc, []string{}) + require.EqualError(t, err, "multiple contracts being merged into chaincode with name customname", "should error when contract already exists with name") + + // should error when no public functions + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + ic := new(emptyContract) + err = cc.addContract(ic, defaultExcludes) + require.EqualError(t, err, fmt.Sprintf("contracts are required to have at least 1 (non-ignored) public method. Contract emptyContract has none. Method names that have been ignored: %s", utils.SliceAsCommaSentence(defaultExcludes)), "should error when contract has no public functions") + + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + pc := new(privateContract) + err = cc.addContract(pc, defaultExcludes) + require.EqualError(t, err, fmt.Sprintf("contracts are required to have at least 1 (non-ignored) public method. Contract privateContract has none. Method names that have been ignored: %s", utils.SliceAsCommaSentence(defaultExcludes)), "should error when contract has no public functions but private ones") + + // should add by default name + existingCCC := contractChaincodeContract{ + info: metadata.InfoMetadata{ + Version: "some version", + }, + } + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + cc.contracts["anotherContract"] = existingCCC + mc = new(myContract) + err = cc.addContract(mc, defaultExcludes) + require.Nil(t, err, "should not error when adding contract using default name") + require.Equal(t, existingCCC, cc.contracts["anotherContract"], "should not affect existing contract in map") + testContractChaincodeContractMatchesContract(t, cc.contracts["myContract"], expectedCCC) + + // should add by custom name + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.Name = "customname" + expectedCCC.info.Title = "customname" + err = cc.addContract(mc, defaultExcludes) + require.Nil(t, err, "should not error when adding contract using custom name") + testContractChaincodeContractMatchesContract(t, cc.contracts["customname"], expectedCCC) + expectedCCC.info.Title = "myContract" + + // should use contracts title and version + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.Info.Version = "1.1.0" + mc.Info.Title = "some title" + expectedCCC.info = metadata.InfoMetadata{ + Version: "1.1.0", + Title: "some title", + } + err = cc.addContract(mc, defaultExcludes) + require.Nil(t, err, "should not error when adding contract using version") + testContractChaincodeContractMatchesContract(t, cc.contracts["myContract"], expectedCCC) + expectedCCC.info = metadata.InfoMetadata{ + Version: "latest", + Title: "myContract", + } + + // should handle evaluate functions + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + oldFunc := expectedCCC.functions["ReturnsString"] + expectedCCC.functions["ReturnsString"], _ = internal.NewContractFunctionFromFunc(mc.ReturnsString, internal.CallTypeEvaluate, transactionContextPtrHandler) + expectedCCC.info.Title = "evaluateContract" + ec := new(evaluateContract) + err = cc.addContract(ec, defaultExcludes) + require.Nil(t, err, "should not error when adding contract using version") + testContractChaincodeContractMatchesContract(t, cc.contracts["evaluateContract"], expectedCCC) + expectedCCC.functions["ReturnsString"] = oldFunc + expectedCCC.info.Title = "myContract" + + // should use before transaction + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.BeforeTransaction = tx.Handler + expectedCCC.beforeTransaction, _ = internal.NewTransactionHandler(tx.Handler, transactionContextPtrHandler, internal.TransactionHandlerTypeBefore) + err = cc.addContract(mc, defaultExcludes) + require.Nil(t, err, "should not error when adding contract using before tx") + testContractChaincodeContractMatchesContract(t, cc.contracts["myContract"], expectedCCC) + + // should use after transaction + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.AfterTransaction = tx.Handler + expectedCCC.afterTransaction, _ = internal.NewTransactionHandler(tx.Handler, transactionContextPtrHandler, internal.TransactionHandlerTypeBefore) + err = cc.addContract(mc, defaultExcludes) + require.Nil(t, err, "should not error when adding contract using after tx") + testContractChaincodeContractMatchesContract(t, cc.contracts["myContract"], expectedCCC) + + // should use unknown transaction + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.UnknownTransaction = tx.Handler + expectedCCC.unknownTransaction, _ = internal.NewTransactionHandler(tx.Handler, transactionContextPtrHandler, internal.TransactionHandlerTypeBefore) + err = cc.addContract(mc, defaultExcludes) + require.Nil(t, err, "should not error when adding contract using unknown tx") + testContractChaincodeContractMatchesContract(t, cc.contracts["myContract"], expectedCCC) + + // should error on bad function + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + bc := new(badContract) + err = cc.addContract(bc, defaultExcludes) + _, expectedErr := internal.NewContractFunctionFromFunc(bc.BadMethod, internal.CallTypeSubmit, transactionContextPtrHandler) + expectedErrStr := strings.Replace(expectedErr.Error(), "Function", "BadMethod", -1) + require.EqualError(t, err, expectedErrStr, "should error when contract has bad method") + + // should error on bad before transaction + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.BeforeTransaction = bc.BadMethod + _, expectedErr = internal.NewTransactionHandler(bc.BadMethod, transactionContextPtrHandler, internal.TransactionHandlerTypeBefore) + err = cc.addContract(mc, defaultExcludes) + require.EqualError(t, err, expectedErr.Error(), "should error when before transaction is bad method") + + // should error on bad after transaction + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.AfterTransaction = bc.BadMethod + _, expectedErr = internal.NewTransactionHandler(bc.BadMethod, transactionContextPtrHandler, internal.TransactionHandlerTypeAfter) + err = cc.addContract(mc, defaultExcludes) + require.EqualError(t, err, expectedErr.Error(), "should error when after transaction is bad method") + + // should error on bad unknown transaction + cc = new(ContractChaincode) + cc.contracts = make(map[string]contractChaincodeContract) + mc = new(myContract) + mc.UnknownTransaction = bc.BadMethod + _, expectedErr = internal.NewTransactionHandler(bc.BadMethod, transactionContextPtrHandler, internal.TransactionHandlerTypeUnknown) + err = cc.addContract(mc, defaultExcludes) + require.EqualError(t, err, expectedErr.Error(), "should error when unknown transaction is bad method") +} + +func TestNewChaincode(t *testing.T) { + var contractChaincode *ContractChaincode + var err error + var expectedErr error + + cc := ContractChaincode{} + cc.contracts = make(map[string]contractChaincodeContract) + + contractChaincode, err = NewChaincode(new(badContract)) + expectedErr = cc.addContract(new(badContract), []string{}) + require.EqualError(t, err, expectedErr.Error(), "should error when bad contract to be added") + require.Nil(t, contractChaincode, "should return blank contract chaincode on error") + + contractChaincode, err = NewChaincode(new(myContract), new(evaluateContract)) + require.Nil(t, err, "should not error when passed valid contracts") + require.Equal(t, 3, len(contractChaincode.contracts), "should add both passed contracts and system contract") + require.Equal(t, reflect.TypeOf(new(serializer.JSONSerializer)), reflect.TypeOf(contractChaincode.TransactionSerializer), "should have set the transaction serializer") + setMetadata, _, _ := contractChaincode.contracts[SystemContractName].functions["GetMetadata"].Call(reflect.ValueOf(nil), nil, nil, new(serializer.JSONSerializer)) + jsonCompare(t, "{\"info\":{\"title\":\"undefined\",\"version\":\"latest\"},\"contracts\":{\"evaluateContract\":{\"info\":{\"title\":\"evaluateContract\",\"version\":\"latest\"},\"name\":\"evaluateContract\",\"transactions\":[{\"returns\":{\"type\":\"string\"},\"tag\":[\"evaluate\", \"EVALUATE\"],\"name\":\"ReturnsString\"}],\"default\": false},\"myContract\":{\"info\":{\"title\":\"myContract\",\"version\":\"latest\"},\"name\":\"myContract\",\"transactions\":[{\"returns\":{\"type\":\"string\"},\"tag\":[\"submit\", \"SUBMIT\"],\"name\":\"ReturnsString\"}], \"default\": true},\"org.hyperledger.fabric\":{\"info\":{\"title\":\"org.hyperledger.fabric\",\"version\":\"latest\"},\"name\":\"org.hyperledger.fabric\",\"transactions\":[{\"returns\":{\"type\":\"string\"},\"tag\":[\"evaluate\", \"EVALUATE\"],\"name\":\"GetMetadata\"}], \"default\": false}},\"components\":{}}", setMetadata) + + contractChaincode, err = NewChaincode(new(ignorableFuncContract)) + _, ok := contractChaincode.contracts["ignorableFuncContract"].functions["IgnoreMe"] + require.Nil(t, err, "should not return error for valid contract with ignores") + require.False(t, ok, "should not include ignored function") +} + +func TestStart(t *testing.T) { + mc := new(myContract) + + cc, _ := NewChaincode(mc) + + require.EqualError(t, cc.Start(), shim.Start(cc).Error(), "should call shim.Start()") +} + +func TestInit(t *testing.T) { + cc, _ := NewChaincode(new(myContract)) + mockStub := NewMockChaincodeStubInterface(t) + mockStub.EXPECT().GetFunctionAndParameters().Maybe().Return("", nil) + AssertProtoEqual(t, shim.Success([]byte("Default initiator successful.")), cc.Init(mockStub)) + + testCallingContractFunctions(t, initType) +} + +func TestInvoke(t *testing.T) { + testCallingContractFunctions(t, invokeType) +} diff --git a/v2/contractapi/contract_test.go b/v2/contractapi/contract_test.go new file mode 100644 index 0000000..6e37db1 --- /dev/null +++ b/v2/contractapi/contract_test.go @@ -0,0 +1,83 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import ( + "testing" + + "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + "github.com/stretchr/testify/assert" +) + +// ================================ +// Tests +// ================================ + +func TestGetUnknownTransaction(t *testing.T) { + var mc myContract + var unknownFn interface{} + + mc = myContract{} + unknownFn = mc.GetUnknownTransaction() + assert.Nil(t, unknownFn, "should not return contractFunction when unknown transaction not set") + + mc = myContract{} + mc.UnknownTransaction = mc.ReturnsString + unknownFn = mc.GetUnknownTransaction() + assert.Equal(t, mc.ReturnsString(), unknownFn.(func() string)(), "function returned should be same value as set for unknown transaction") +} + +func TestGetBeforeTransaction(t *testing.T) { + var mc myContract + var beforeFn interface{} + + mc = myContract{} + beforeFn = mc.GetBeforeTransaction() + assert.Nil(t, beforeFn, "should not return contractFunction when before transaction not set") + + mc = myContract{} + mc.BeforeTransaction = mc.ReturnsString + beforeFn = mc.GetBeforeTransaction() + assert.Equal(t, mc.ReturnsString(), beforeFn.(func() string)(), "function returned should be same value as set for before transaction") +} + +func TestGetAfterTransaction(t *testing.T) { + var mc myContract + var afterFn interface{} + + mc = myContract{} + afterFn = mc.GetAfterTransaction() + assert.Nil(t, afterFn, "should not return contractFunction when after transaction not set") + + mc = myContract{} + mc.AfterTransaction = mc.ReturnsString + afterFn = mc.GetAfterTransaction() + assert.Equal(t, mc.ReturnsString(), afterFn.(func() string)(), "function returned should be same value as set for after transaction") +} + +func TestGetInfo(t *testing.T) { + c := Contract{} + c.Info = metadata.InfoMetadata{} + c.Info.Version = "some version" + + assert.Equal(t, c.Info, c.GetInfo(), "should set the version") +} + +func TestGetName(t *testing.T) { + mc := myContract{} + + assert.Equal(t, "", mc.GetName(), "should have returned blank ns when not set") + + mc.Name = "myname" + assert.Equal(t, "myname", mc.GetName(), "should have returned custom ns when set") +} + +func TestGetTransactionContextHandler(t *testing.T) { + mc := myContract{} + + assert.Equal(t, new(TransactionContext), mc.GetTransactionContextHandler(), "should return default transaction context type when unset") + + mc.TransactionContextHandler = new(customContext) + assert.Equal(t, new(customContext), mc.GetTransactionContextHandler(), "should return custom context when set") +} diff --git a/v2/contractapi/mock_chaincodeStubInterface_test.go b/v2/contractapi/mock_chaincodeStubInterface_test.go new file mode 100644 index 0000000..0f511b8 --- /dev/null +++ b/v2/contractapi/mock_chaincodeStubInterface_test.go @@ -0,0 +1,2132 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package contractapi + +import ( + shim "github.com/hyperledger/fabric-chaincode-go/v2/shim" + peer "github.com/hyperledger/fabric-protos-go-apiv2/peer" + mock "github.com/stretchr/testify/mock" + + timestamppb "google.golang.org/protobuf/types/known/timestamppb" +) + +// MockChaincodeStubInterface is an autogenerated mock type for the chaincodeStubInterface type +type MockChaincodeStubInterface struct { + mock.Mock +} + +type MockChaincodeStubInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *MockChaincodeStubInterface) EXPECT() *MockChaincodeStubInterface_Expecter { + return &MockChaincodeStubInterface_Expecter{mock: &_m.Mock} +} + +// CreateCompositeKey provides a mock function with given fields: objectType, attributes +func (_m *MockChaincodeStubInterface) CreateCompositeKey(objectType string, attributes []string) (string, error) { + ret := _m.Called(objectType, attributes) + + if len(ret) == 0 { + panic("no return value specified for CreateCompositeKey") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string, []string) (string, error)); ok { + return rf(objectType, attributes) + } + if rf, ok := ret.Get(0).(func(string, []string) string); ok { + r0 = rf(objectType, attributes) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(string, []string) error); ok { + r1 = rf(objectType, attributes) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_CreateCompositeKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateCompositeKey' +type MockChaincodeStubInterface_CreateCompositeKey_Call struct { + *mock.Call +} + +// CreateCompositeKey is a helper method to define mock.On call +// - objectType string +// - attributes []string +func (_e *MockChaincodeStubInterface_Expecter) CreateCompositeKey(objectType interface{}, attributes interface{}) *MockChaincodeStubInterface_CreateCompositeKey_Call { + return &MockChaincodeStubInterface_CreateCompositeKey_Call{Call: _e.mock.On("CreateCompositeKey", objectType, attributes)} +} + +func (_c *MockChaincodeStubInterface_CreateCompositeKey_Call) Run(run func(objectType string, attributes []string)) *MockChaincodeStubInterface_CreateCompositeKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].([]string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_CreateCompositeKey_Call) Return(_a0 string, _a1 error) *MockChaincodeStubInterface_CreateCompositeKey_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_CreateCompositeKey_Call) RunAndReturn(run func(string, []string) (string, error)) *MockChaincodeStubInterface_CreateCompositeKey_Call { + _c.Call.Return(run) + return _c +} + +// DelPrivateData provides a mock function with given fields: collection, key +func (_m *MockChaincodeStubInterface) DelPrivateData(collection string, key string) error { + ret := _m.Called(collection, key) + + if len(ret) == 0 { + panic("no return value specified for DelPrivateData") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(collection, key) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_DelPrivateData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DelPrivateData' +type MockChaincodeStubInterface_DelPrivateData_Call struct { + *mock.Call +} + +// DelPrivateData is a helper method to define mock.On call +// - collection string +// - key string +func (_e *MockChaincodeStubInterface_Expecter) DelPrivateData(collection interface{}, key interface{}) *MockChaincodeStubInterface_DelPrivateData_Call { + return &MockChaincodeStubInterface_DelPrivateData_Call{Call: _e.mock.On("DelPrivateData", collection, key)} +} + +func (_c *MockChaincodeStubInterface_DelPrivateData_Call) Run(run func(collection string, key string)) *MockChaincodeStubInterface_DelPrivateData_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_DelPrivateData_Call) Return(_a0 error) *MockChaincodeStubInterface_DelPrivateData_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_DelPrivateData_Call) RunAndReturn(run func(string, string) error) *MockChaincodeStubInterface_DelPrivateData_Call { + _c.Call.Return(run) + return _c +} + +// DelState provides a mock function with given fields: key +func (_m *MockChaincodeStubInterface) DelState(key string) error { + ret := _m.Called(key) + + if len(ret) == 0 { + panic("no return value specified for DelState") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(key) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_DelState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DelState' +type MockChaincodeStubInterface_DelState_Call struct { + *mock.Call +} + +// DelState is a helper method to define mock.On call +// - key string +func (_e *MockChaincodeStubInterface_Expecter) DelState(key interface{}) *MockChaincodeStubInterface_DelState_Call { + return &MockChaincodeStubInterface_DelState_Call{Call: _e.mock.On("DelState", key)} +} + +func (_c *MockChaincodeStubInterface_DelState_Call) Run(run func(key string)) *MockChaincodeStubInterface_DelState_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_DelState_Call) Return(_a0 error) *MockChaincodeStubInterface_DelState_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_DelState_Call) RunAndReturn(run func(string) error) *MockChaincodeStubInterface_DelState_Call { + _c.Call.Return(run) + return _c +} + +// GetArgs provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetArgs() [][]byte { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetArgs") + } + + var r0 [][]byte + if rf, ok := ret.Get(0).(func() [][]byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([][]byte) + } + } + + return r0 +} + +// MockChaincodeStubInterface_GetArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetArgs' +type MockChaincodeStubInterface_GetArgs_Call struct { + *mock.Call +} + +// GetArgs is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetArgs() *MockChaincodeStubInterface_GetArgs_Call { + return &MockChaincodeStubInterface_GetArgs_Call{Call: _e.mock.On("GetArgs")} +} + +func (_c *MockChaincodeStubInterface_GetArgs_Call) Run(run func()) *MockChaincodeStubInterface_GetArgs_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetArgs_Call) Return(_a0 [][]byte) *MockChaincodeStubInterface_GetArgs_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_GetArgs_Call) RunAndReturn(run func() [][]byte) *MockChaincodeStubInterface_GetArgs_Call { + _c.Call.Return(run) + return _c +} + +// GetArgsSlice provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetArgsSlice() ([]byte, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetArgsSlice") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetArgsSlice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetArgsSlice' +type MockChaincodeStubInterface_GetArgsSlice_Call struct { + *mock.Call +} + +// GetArgsSlice is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetArgsSlice() *MockChaincodeStubInterface_GetArgsSlice_Call { + return &MockChaincodeStubInterface_GetArgsSlice_Call{Call: _e.mock.On("GetArgsSlice")} +} + +func (_c *MockChaincodeStubInterface_GetArgsSlice_Call) Run(run func()) *MockChaincodeStubInterface_GetArgsSlice_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetArgsSlice_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetArgsSlice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetArgsSlice_Call) RunAndReturn(run func() ([]byte, error)) *MockChaincodeStubInterface_GetArgsSlice_Call { + _c.Call.Return(run) + return _c +} + +// GetBinding provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetBinding() ([]byte, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetBinding") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetBinding_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBinding' +type MockChaincodeStubInterface_GetBinding_Call struct { + *mock.Call +} + +// GetBinding is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetBinding() *MockChaincodeStubInterface_GetBinding_Call { + return &MockChaincodeStubInterface_GetBinding_Call{Call: _e.mock.On("GetBinding")} +} + +func (_c *MockChaincodeStubInterface_GetBinding_Call) Run(run func()) *MockChaincodeStubInterface_GetBinding_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetBinding_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetBinding_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetBinding_Call) RunAndReturn(run func() ([]byte, error)) *MockChaincodeStubInterface_GetBinding_Call { + _c.Call.Return(run) + return _c +} + +// GetChannelID provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetChannelID() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetChannelID") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockChaincodeStubInterface_GetChannelID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetChannelID' +type MockChaincodeStubInterface_GetChannelID_Call struct { + *mock.Call +} + +// GetChannelID is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetChannelID() *MockChaincodeStubInterface_GetChannelID_Call { + return &MockChaincodeStubInterface_GetChannelID_Call{Call: _e.mock.On("GetChannelID")} +} + +func (_c *MockChaincodeStubInterface_GetChannelID_Call) Run(run func()) *MockChaincodeStubInterface_GetChannelID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetChannelID_Call) Return(_a0 string) *MockChaincodeStubInterface_GetChannelID_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_GetChannelID_Call) RunAndReturn(run func() string) *MockChaincodeStubInterface_GetChannelID_Call { + _c.Call.Return(run) + return _c +} + +// GetCreator provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetCreator() ([]byte, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetCreator") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetCreator_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCreator' +type MockChaincodeStubInterface_GetCreator_Call struct { + *mock.Call +} + +// GetCreator is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetCreator() *MockChaincodeStubInterface_GetCreator_Call { + return &MockChaincodeStubInterface_GetCreator_Call{Call: _e.mock.On("GetCreator")} +} + +func (_c *MockChaincodeStubInterface_GetCreator_Call) Run(run func()) *MockChaincodeStubInterface_GetCreator_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetCreator_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetCreator_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetCreator_Call) RunAndReturn(run func() ([]byte, error)) *MockChaincodeStubInterface_GetCreator_Call { + _c.Call.Return(run) + return _c +} + +// GetDecorations provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetDecorations() map[string][]byte { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetDecorations") + } + + var r0 map[string][]byte + if rf, ok := ret.Get(0).(func() map[string][]byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string][]byte) + } + } + + return r0 +} + +// MockChaincodeStubInterface_GetDecorations_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDecorations' +type MockChaincodeStubInterface_GetDecorations_Call struct { + *mock.Call +} + +// GetDecorations is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetDecorations() *MockChaincodeStubInterface_GetDecorations_Call { + return &MockChaincodeStubInterface_GetDecorations_Call{Call: _e.mock.On("GetDecorations")} +} + +func (_c *MockChaincodeStubInterface_GetDecorations_Call) Run(run func()) *MockChaincodeStubInterface_GetDecorations_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetDecorations_Call) Return(_a0 map[string][]byte) *MockChaincodeStubInterface_GetDecorations_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_GetDecorations_Call) RunAndReturn(run func() map[string][]byte) *MockChaincodeStubInterface_GetDecorations_Call { + _c.Call.Return(run) + return _c +} + +// GetFunctionAndParameters provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetFunctionAndParameters() (string, []string) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetFunctionAndParameters") + } + + var r0 string + var r1 []string + if rf, ok := ret.Get(0).(func() (string, []string)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func() []string); ok { + r1 = rf() + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]string) + } + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetFunctionAndParameters_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFunctionAndParameters' +type MockChaincodeStubInterface_GetFunctionAndParameters_Call struct { + *mock.Call +} + +// GetFunctionAndParameters is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetFunctionAndParameters() *MockChaincodeStubInterface_GetFunctionAndParameters_Call { + return &MockChaincodeStubInterface_GetFunctionAndParameters_Call{Call: _e.mock.On("GetFunctionAndParameters")} +} + +func (_c *MockChaincodeStubInterface_GetFunctionAndParameters_Call) Run(run func()) *MockChaincodeStubInterface_GetFunctionAndParameters_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetFunctionAndParameters_Call) Return(_a0 string, _a1 []string) *MockChaincodeStubInterface_GetFunctionAndParameters_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetFunctionAndParameters_Call) RunAndReturn(run func() (string, []string)) *MockChaincodeStubInterface_GetFunctionAndParameters_Call { + _c.Call.Return(run) + return _c +} + +// GetHistoryForKey provides a mock function with given fields: key +func (_m *MockChaincodeStubInterface) GetHistoryForKey(key string) (shim.HistoryQueryIteratorInterface, error) { + ret := _m.Called(key) + + if len(ret) == 0 { + panic("no return value specified for GetHistoryForKey") + } + + var r0 shim.HistoryQueryIteratorInterface + var r1 error + if rf, ok := ret.Get(0).(func(string) (shim.HistoryQueryIteratorInterface, error)); ok { + return rf(key) + } + if rf, ok := ret.Get(0).(func(string) shim.HistoryQueryIteratorInterface); ok { + r0 = rf(key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.HistoryQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetHistoryForKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetHistoryForKey' +type MockChaincodeStubInterface_GetHistoryForKey_Call struct { + *mock.Call +} + +// GetHistoryForKey is a helper method to define mock.On call +// - key string +func (_e *MockChaincodeStubInterface_Expecter) GetHistoryForKey(key interface{}) *MockChaincodeStubInterface_GetHistoryForKey_Call { + return &MockChaincodeStubInterface_GetHistoryForKey_Call{Call: _e.mock.On("GetHistoryForKey", key)} +} + +func (_c *MockChaincodeStubInterface_GetHistoryForKey_Call) Run(run func(key string)) *MockChaincodeStubInterface_GetHistoryForKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetHistoryForKey_Call) Return(_a0 shim.HistoryQueryIteratorInterface, _a1 error) *MockChaincodeStubInterface_GetHistoryForKey_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetHistoryForKey_Call) RunAndReturn(run func(string) (shim.HistoryQueryIteratorInterface, error)) *MockChaincodeStubInterface_GetHistoryForKey_Call { + _c.Call.Return(run) + return _c +} + +// GetPrivateData provides a mock function with given fields: collection, key +func (_m *MockChaincodeStubInterface) GetPrivateData(collection string, key string) ([]byte, error) { + ret := _m.Called(collection, key) + + if len(ret) == 0 { + panic("no return value specified for GetPrivateData") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { + return rf(collection, key) + } + if rf, ok := ret.Get(0).(func(string, string) []byte); ok { + r0 = rf(collection, key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(collection, key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetPrivateData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivateData' +type MockChaincodeStubInterface_GetPrivateData_Call struct { + *mock.Call +} + +// GetPrivateData is a helper method to define mock.On call +// - collection string +// - key string +func (_e *MockChaincodeStubInterface_Expecter) GetPrivateData(collection interface{}, key interface{}) *MockChaincodeStubInterface_GetPrivateData_Call { + return &MockChaincodeStubInterface_GetPrivateData_Call{Call: _e.mock.On("GetPrivateData", collection, key)} +} + +func (_c *MockChaincodeStubInterface_GetPrivateData_Call) Run(run func(collection string, key string)) *MockChaincodeStubInterface_GetPrivateData_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateData_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetPrivateData_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateData_Call) RunAndReturn(run func(string, string) ([]byte, error)) *MockChaincodeStubInterface_GetPrivateData_Call { + _c.Call.Return(run) + return _c +} + +// GetPrivateDataByPartialCompositeKey provides a mock function with given fields: collection, objectType, keys +func (_m *MockChaincodeStubInterface) GetPrivateDataByPartialCompositeKey(collection string, objectType string, keys []string) (shim.StateQueryIteratorInterface, error) { + ret := _m.Called(collection, objectType, keys) + + if len(ret) == 0 { + panic("no return value specified for GetPrivateDataByPartialCompositeKey") + } + + var r0 shim.StateQueryIteratorInterface + var r1 error + if rf, ok := ret.Get(0).(func(string, string, []string) (shim.StateQueryIteratorInterface, error)); ok { + return rf(collection, objectType, keys) + } + if rf, ok := ret.Get(0).(func(string, string, []string) shim.StateQueryIteratorInterface); ok { + r0 = rf(collection, objectType, keys) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, string, []string) error); ok { + r1 = rf(collection, objectType, keys) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivateDataByPartialCompositeKey' +type MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call struct { + *mock.Call +} + +// GetPrivateDataByPartialCompositeKey is a helper method to define mock.On call +// - collection string +// - objectType string +// - keys []string +func (_e *MockChaincodeStubInterface_Expecter) GetPrivateDataByPartialCompositeKey(collection interface{}, objectType interface{}, keys interface{}) *MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call { + return &MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call{Call: _e.mock.On("GetPrivateDataByPartialCompositeKey", collection, objectType, keys)} +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call) Run(run func(collection string, objectType string, keys []string)) *MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string), args[2].([]string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 error) *MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call) RunAndReturn(run func(string, string, []string) (shim.StateQueryIteratorInterface, error)) *MockChaincodeStubInterface_GetPrivateDataByPartialCompositeKey_Call { + _c.Call.Return(run) + return _c +} + +// GetPrivateDataByRange provides a mock function with given fields: collection, startKey, endKey +func (_m *MockChaincodeStubInterface) GetPrivateDataByRange(collection string, startKey string, endKey string) (shim.StateQueryIteratorInterface, error) { + ret := _m.Called(collection, startKey, endKey) + + if len(ret) == 0 { + panic("no return value specified for GetPrivateDataByRange") + } + + var r0 shim.StateQueryIteratorInterface + var r1 error + if rf, ok := ret.Get(0).(func(string, string, string) (shim.StateQueryIteratorInterface, error)); ok { + return rf(collection, startKey, endKey) + } + if rf, ok := ret.Get(0).(func(string, string, string) shim.StateQueryIteratorInterface); ok { + r0 = rf(collection, startKey, endKey) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, string, string) error); ok { + r1 = rf(collection, startKey, endKey) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetPrivateDataByRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivateDataByRange' +type MockChaincodeStubInterface_GetPrivateDataByRange_Call struct { + *mock.Call +} + +// GetPrivateDataByRange is a helper method to define mock.On call +// - collection string +// - startKey string +// - endKey string +func (_e *MockChaincodeStubInterface_Expecter) GetPrivateDataByRange(collection interface{}, startKey interface{}, endKey interface{}) *MockChaincodeStubInterface_GetPrivateDataByRange_Call { + return &MockChaincodeStubInterface_GetPrivateDataByRange_Call{Call: _e.mock.On("GetPrivateDataByRange", collection, startKey, endKey)} +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataByRange_Call) Run(run func(collection string, startKey string, endKey string)) *MockChaincodeStubInterface_GetPrivateDataByRange_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataByRange_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 error) *MockChaincodeStubInterface_GetPrivateDataByRange_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataByRange_Call) RunAndReturn(run func(string, string, string) (shim.StateQueryIteratorInterface, error)) *MockChaincodeStubInterface_GetPrivateDataByRange_Call { + _c.Call.Return(run) + return _c +} + +// GetPrivateDataHash provides a mock function with given fields: collection, key +func (_m *MockChaincodeStubInterface) GetPrivateDataHash(collection string, key string) ([]byte, error) { + ret := _m.Called(collection, key) + + if len(ret) == 0 { + panic("no return value specified for GetPrivateDataHash") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { + return rf(collection, key) + } + if rf, ok := ret.Get(0).(func(string, string) []byte); ok { + r0 = rf(collection, key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(collection, key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetPrivateDataHash_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivateDataHash' +type MockChaincodeStubInterface_GetPrivateDataHash_Call struct { + *mock.Call +} + +// GetPrivateDataHash is a helper method to define mock.On call +// - collection string +// - key string +func (_e *MockChaincodeStubInterface_Expecter) GetPrivateDataHash(collection interface{}, key interface{}) *MockChaincodeStubInterface_GetPrivateDataHash_Call { + return &MockChaincodeStubInterface_GetPrivateDataHash_Call{Call: _e.mock.On("GetPrivateDataHash", collection, key)} +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataHash_Call) Run(run func(collection string, key string)) *MockChaincodeStubInterface_GetPrivateDataHash_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataHash_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetPrivateDataHash_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataHash_Call) RunAndReturn(run func(string, string) ([]byte, error)) *MockChaincodeStubInterface_GetPrivateDataHash_Call { + _c.Call.Return(run) + return _c +} + +// GetPrivateDataQueryResult provides a mock function with given fields: collection, query +func (_m *MockChaincodeStubInterface) GetPrivateDataQueryResult(collection string, query string) (shim.StateQueryIteratorInterface, error) { + ret := _m.Called(collection, query) + + if len(ret) == 0 { + panic("no return value specified for GetPrivateDataQueryResult") + } + + var r0 shim.StateQueryIteratorInterface + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (shim.StateQueryIteratorInterface, error)); ok { + return rf(collection, query) + } + if rf, ok := ret.Get(0).(func(string, string) shim.StateQueryIteratorInterface); ok { + r0 = rf(collection, query) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(collection, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetPrivateDataQueryResult_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivateDataQueryResult' +type MockChaincodeStubInterface_GetPrivateDataQueryResult_Call struct { + *mock.Call +} + +// GetPrivateDataQueryResult is a helper method to define mock.On call +// - collection string +// - query string +func (_e *MockChaincodeStubInterface_Expecter) GetPrivateDataQueryResult(collection interface{}, query interface{}) *MockChaincodeStubInterface_GetPrivateDataQueryResult_Call { + return &MockChaincodeStubInterface_GetPrivateDataQueryResult_Call{Call: _e.mock.On("GetPrivateDataQueryResult", collection, query)} +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataQueryResult_Call) Run(run func(collection string, query string)) *MockChaincodeStubInterface_GetPrivateDataQueryResult_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataQueryResult_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 error) *MockChaincodeStubInterface_GetPrivateDataQueryResult_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataQueryResult_Call) RunAndReturn(run func(string, string) (shim.StateQueryIteratorInterface, error)) *MockChaincodeStubInterface_GetPrivateDataQueryResult_Call { + _c.Call.Return(run) + return _c +} + +// GetPrivateDataValidationParameter provides a mock function with given fields: collection, key +func (_m *MockChaincodeStubInterface) GetPrivateDataValidationParameter(collection string, key string) ([]byte, error) { + ret := _m.Called(collection, key) + + if len(ret) == 0 { + panic("no return value specified for GetPrivateDataValidationParameter") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(string, string) ([]byte, error)); ok { + return rf(collection, key) + } + if rf, ok := ret.Get(0).(func(string, string) []byte); ok { + r0 = rf(collection, key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(collection, key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivateDataValidationParameter' +type MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call struct { + *mock.Call +} + +// GetPrivateDataValidationParameter is a helper method to define mock.On call +// - collection string +// - key string +func (_e *MockChaincodeStubInterface_Expecter) GetPrivateDataValidationParameter(collection interface{}, key interface{}) *MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call { + return &MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call{Call: _e.mock.On("GetPrivateDataValidationParameter", collection, key)} +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call) Run(run func(collection string, key string)) *MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call) RunAndReturn(run func(string, string) ([]byte, error)) *MockChaincodeStubInterface_GetPrivateDataValidationParameter_Call { + _c.Call.Return(run) + return _c +} + +// GetQueryResult provides a mock function with given fields: query +func (_m *MockChaincodeStubInterface) GetQueryResult(query string) (shim.StateQueryIteratorInterface, error) { + ret := _m.Called(query) + + if len(ret) == 0 { + panic("no return value specified for GetQueryResult") + } + + var r0 shim.StateQueryIteratorInterface + var r1 error + if rf, ok := ret.Get(0).(func(string) (shim.StateQueryIteratorInterface, error)); ok { + return rf(query) + } + if rf, ok := ret.Get(0).(func(string) shim.StateQueryIteratorInterface); ok { + r0 = rf(query) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetQueryResult_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetQueryResult' +type MockChaincodeStubInterface_GetQueryResult_Call struct { + *mock.Call +} + +// GetQueryResult is a helper method to define mock.On call +// - query string +func (_e *MockChaincodeStubInterface_Expecter) GetQueryResult(query interface{}) *MockChaincodeStubInterface_GetQueryResult_Call { + return &MockChaincodeStubInterface_GetQueryResult_Call{Call: _e.mock.On("GetQueryResult", query)} +} + +func (_c *MockChaincodeStubInterface_GetQueryResult_Call) Run(run func(query string)) *MockChaincodeStubInterface_GetQueryResult_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetQueryResult_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 error) *MockChaincodeStubInterface_GetQueryResult_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetQueryResult_Call) RunAndReturn(run func(string) (shim.StateQueryIteratorInterface, error)) *MockChaincodeStubInterface_GetQueryResult_Call { + _c.Call.Return(run) + return _c +} + +// GetQueryResultWithPagination provides a mock function with given fields: query, pageSize, bookmark +func (_m *MockChaincodeStubInterface) GetQueryResultWithPagination(query string, pageSize int32, bookmark string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { + ret := _m.Called(query, pageSize, bookmark) + + if len(ret) == 0 { + panic("no return value specified for GetQueryResultWithPagination") + } + + var r0 shim.StateQueryIteratorInterface + var r1 *peer.QueryResponseMetadata + var r2 error + if rf, ok := ret.Get(0).(func(string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)); ok { + return rf(query, pageSize, bookmark) + } + if rf, ok := ret.Get(0).(func(string, int32, string) shim.StateQueryIteratorInterface); ok { + r0 = rf(query, pageSize, bookmark) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, int32, string) *peer.QueryResponseMetadata); ok { + r1 = rf(query, pageSize, bookmark) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*peer.QueryResponseMetadata) + } + } + + if rf, ok := ret.Get(2).(func(string, int32, string) error); ok { + r2 = rf(query, pageSize, bookmark) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MockChaincodeStubInterface_GetQueryResultWithPagination_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetQueryResultWithPagination' +type MockChaincodeStubInterface_GetQueryResultWithPagination_Call struct { + *mock.Call +} + +// GetQueryResultWithPagination is a helper method to define mock.On call +// - query string +// - pageSize int32 +// - bookmark string +func (_e *MockChaincodeStubInterface_Expecter) GetQueryResultWithPagination(query interface{}, pageSize interface{}, bookmark interface{}) *MockChaincodeStubInterface_GetQueryResultWithPagination_Call { + return &MockChaincodeStubInterface_GetQueryResultWithPagination_Call{Call: _e.mock.On("GetQueryResultWithPagination", query, pageSize, bookmark)} +} + +func (_c *MockChaincodeStubInterface_GetQueryResultWithPagination_Call) Run(run func(query string, pageSize int32, bookmark string)) *MockChaincodeStubInterface_GetQueryResultWithPagination_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(int32), args[2].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetQueryResultWithPagination_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 *peer.QueryResponseMetadata, _a2 error) *MockChaincodeStubInterface_GetQueryResultWithPagination_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *MockChaincodeStubInterface_GetQueryResultWithPagination_Call) RunAndReturn(run func(string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) *MockChaincodeStubInterface_GetQueryResultWithPagination_Call { + _c.Call.Return(run) + return _c +} + +// GetSignedProposal provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetSignedProposal() (*peer.SignedProposal, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetSignedProposal") + } + + var r0 *peer.SignedProposal + var r1 error + if rf, ok := ret.Get(0).(func() (*peer.SignedProposal, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *peer.SignedProposal); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*peer.SignedProposal) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetSignedProposal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSignedProposal' +type MockChaincodeStubInterface_GetSignedProposal_Call struct { + *mock.Call +} + +// GetSignedProposal is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetSignedProposal() *MockChaincodeStubInterface_GetSignedProposal_Call { + return &MockChaincodeStubInterface_GetSignedProposal_Call{Call: _e.mock.On("GetSignedProposal")} +} + +func (_c *MockChaincodeStubInterface_GetSignedProposal_Call) Run(run func()) *MockChaincodeStubInterface_GetSignedProposal_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetSignedProposal_Call) Return(_a0 *peer.SignedProposal, _a1 error) *MockChaincodeStubInterface_GetSignedProposal_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetSignedProposal_Call) RunAndReturn(run func() (*peer.SignedProposal, error)) *MockChaincodeStubInterface_GetSignedProposal_Call { + _c.Call.Return(run) + return _c +} + +// GetState provides a mock function with given fields: key +func (_m *MockChaincodeStubInterface) GetState(key string) ([]byte, error) { + ret := _m.Called(key) + + if len(ret) == 0 { + panic("no return value specified for GetState") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]byte, error)); ok { + return rf(key) + } + if rf, ok := ret.Get(0).(func(string) []byte); ok { + r0 = rf(key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetState' +type MockChaincodeStubInterface_GetState_Call struct { + *mock.Call +} + +// GetState is a helper method to define mock.On call +// - key string +func (_e *MockChaincodeStubInterface_Expecter) GetState(key interface{}) *MockChaincodeStubInterface_GetState_Call { + return &MockChaincodeStubInterface_GetState_Call{Call: _e.mock.On("GetState", key)} +} + +func (_c *MockChaincodeStubInterface_GetState_Call) Run(run func(key string)) *MockChaincodeStubInterface_GetState_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetState_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetState_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetState_Call) RunAndReturn(run func(string) ([]byte, error)) *MockChaincodeStubInterface_GetState_Call { + _c.Call.Return(run) + return _c +} + +// GetStateByPartialCompositeKey provides a mock function with given fields: objectType, keys +func (_m *MockChaincodeStubInterface) GetStateByPartialCompositeKey(objectType string, keys []string) (shim.StateQueryIteratorInterface, error) { + ret := _m.Called(objectType, keys) + + if len(ret) == 0 { + panic("no return value specified for GetStateByPartialCompositeKey") + } + + var r0 shim.StateQueryIteratorInterface + var r1 error + if rf, ok := ret.Get(0).(func(string, []string) (shim.StateQueryIteratorInterface, error)); ok { + return rf(objectType, keys) + } + if rf, ok := ret.Get(0).(func(string, []string) shim.StateQueryIteratorInterface); ok { + r0 = rf(objectType, keys) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, []string) error); ok { + r1 = rf(objectType, keys) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStateByPartialCompositeKey' +type MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call struct { + *mock.Call +} + +// GetStateByPartialCompositeKey is a helper method to define mock.On call +// - objectType string +// - keys []string +func (_e *MockChaincodeStubInterface_Expecter) GetStateByPartialCompositeKey(objectType interface{}, keys interface{}) *MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call { + return &MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call{Call: _e.mock.On("GetStateByPartialCompositeKey", objectType, keys)} +} + +func (_c *MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call) Run(run func(objectType string, keys []string)) *MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].([]string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 error) *MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call) RunAndReturn(run func(string, []string) (shim.StateQueryIteratorInterface, error)) *MockChaincodeStubInterface_GetStateByPartialCompositeKey_Call { + _c.Call.Return(run) + return _c +} + +// GetStateByPartialCompositeKeyWithPagination provides a mock function with given fields: objectType, keys, pageSize, bookmark +func (_m *MockChaincodeStubInterface) GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, pageSize int32, bookmark string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { + ret := _m.Called(objectType, keys, pageSize, bookmark) + + if len(ret) == 0 { + panic("no return value specified for GetStateByPartialCompositeKeyWithPagination") + } + + var r0 shim.StateQueryIteratorInterface + var r1 *peer.QueryResponseMetadata + var r2 error + if rf, ok := ret.Get(0).(func(string, []string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)); ok { + return rf(objectType, keys, pageSize, bookmark) + } + if rf, ok := ret.Get(0).(func(string, []string, int32, string) shim.StateQueryIteratorInterface); ok { + r0 = rf(objectType, keys, pageSize, bookmark) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, []string, int32, string) *peer.QueryResponseMetadata); ok { + r1 = rf(objectType, keys, pageSize, bookmark) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*peer.QueryResponseMetadata) + } + } + + if rf, ok := ret.Get(2).(func(string, []string, int32, string) error); ok { + r2 = rf(objectType, keys, pageSize, bookmark) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStateByPartialCompositeKeyWithPagination' +type MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call struct { + *mock.Call +} + +// GetStateByPartialCompositeKeyWithPagination is a helper method to define mock.On call +// - objectType string +// - keys []string +// - pageSize int32 +// - bookmark string +func (_e *MockChaincodeStubInterface_Expecter) GetStateByPartialCompositeKeyWithPagination(objectType interface{}, keys interface{}, pageSize interface{}, bookmark interface{}) *MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call { + return &MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call{Call: _e.mock.On("GetStateByPartialCompositeKeyWithPagination", objectType, keys, pageSize, bookmark)} +} + +func (_c *MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call) Run(run func(objectType string, keys []string, pageSize int32, bookmark string)) *MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].([]string), args[2].(int32), args[3].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 *peer.QueryResponseMetadata, _a2 error) *MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call) RunAndReturn(run func(string, []string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) *MockChaincodeStubInterface_GetStateByPartialCompositeKeyWithPagination_Call { + _c.Call.Return(run) + return _c +} + +// GetStateByRange provides a mock function with given fields: startKey, endKey +func (_m *MockChaincodeStubInterface) GetStateByRange(startKey string, endKey string) (shim.StateQueryIteratorInterface, error) { + ret := _m.Called(startKey, endKey) + + if len(ret) == 0 { + panic("no return value specified for GetStateByRange") + } + + var r0 shim.StateQueryIteratorInterface + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (shim.StateQueryIteratorInterface, error)); ok { + return rf(startKey, endKey) + } + if rf, ok := ret.Get(0).(func(string, string) shim.StateQueryIteratorInterface); ok { + r0 = rf(startKey, endKey) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(startKey, endKey) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetStateByRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStateByRange' +type MockChaincodeStubInterface_GetStateByRange_Call struct { + *mock.Call +} + +// GetStateByRange is a helper method to define mock.On call +// - startKey string +// - endKey string +func (_e *MockChaincodeStubInterface_Expecter) GetStateByRange(startKey interface{}, endKey interface{}) *MockChaincodeStubInterface_GetStateByRange_Call { + return &MockChaincodeStubInterface_GetStateByRange_Call{Call: _e.mock.On("GetStateByRange", startKey, endKey)} +} + +func (_c *MockChaincodeStubInterface_GetStateByRange_Call) Run(run func(startKey string, endKey string)) *MockChaincodeStubInterface_GetStateByRange_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByRange_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 error) *MockChaincodeStubInterface_GetStateByRange_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByRange_Call) RunAndReturn(run func(string, string) (shim.StateQueryIteratorInterface, error)) *MockChaincodeStubInterface_GetStateByRange_Call { + _c.Call.Return(run) + return _c +} + +// GetStateByRangeWithPagination provides a mock function with given fields: startKey, endKey, pageSize, bookmark +func (_m *MockChaincodeStubInterface) GetStateByRangeWithPagination(startKey string, endKey string, pageSize int32, bookmark string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { + ret := _m.Called(startKey, endKey, pageSize, bookmark) + + if len(ret) == 0 { + panic("no return value specified for GetStateByRangeWithPagination") + } + + var r0 shim.StateQueryIteratorInterface + var r1 *peer.QueryResponseMetadata + var r2 error + if rf, ok := ret.Get(0).(func(string, string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)); ok { + return rf(startKey, endKey, pageSize, bookmark) + } + if rf, ok := ret.Get(0).(func(string, string, int32, string) shim.StateQueryIteratorInterface); ok { + r0 = rf(startKey, endKey, pageSize, bookmark) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(shim.StateQueryIteratorInterface) + } + } + + if rf, ok := ret.Get(1).(func(string, string, int32, string) *peer.QueryResponseMetadata); ok { + r1 = rf(startKey, endKey, pageSize, bookmark) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*peer.QueryResponseMetadata) + } + } + + if rf, ok := ret.Get(2).(func(string, string, int32, string) error); ok { + r2 = rf(startKey, endKey, pageSize, bookmark) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MockChaincodeStubInterface_GetStateByRangeWithPagination_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStateByRangeWithPagination' +type MockChaincodeStubInterface_GetStateByRangeWithPagination_Call struct { + *mock.Call +} + +// GetStateByRangeWithPagination is a helper method to define mock.On call +// - startKey string +// - endKey string +// - pageSize int32 +// - bookmark string +func (_e *MockChaincodeStubInterface_Expecter) GetStateByRangeWithPagination(startKey interface{}, endKey interface{}, pageSize interface{}, bookmark interface{}) *MockChaincodeStubInterface_GetStateByRangeWithPagination_Call { + return &MockChaincodeStubInterface_GetStateByRangeWithPagination_Call{Call: _e.mock.On("GetStateByRangeWithPagination", startKey, endKey, pageSize, bookmark)} +} + +func (_c *MockChaincodeStubInterface_GetStateByRangeWithPagination_Call) Run(run func(startKey string, endKey string, pageSize int32, bookmark string)) *MockChaincodeStubInterface_GetStateByRangeWithPagination_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string), args[2].(int32), args[3].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByRangeWithPagination_Call) Return(_a0 shim.StateQueryIteratorInterface, _a1 *peer.QueryResponseMetadata, _a2 error) *MockChaincodeStubInterface_GetStateByRangeWithPagination_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateByRangeWithPagination_Call) RunAndReturn(run func(string, string, int32, string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error)) *MockChaincodeStubInterface_GetStateByRangeWithPagination_Call { + _c.Call.Return(run) + return _c +} + +// GetStateValidationParameter provides a mock function with given fields: key +func (_m *MockChaincodeStubInterface) GetStateValidationParameter(key string) ([]byte, error) { + ret := _m.Called(key) + + if len(ret) == 0 { + panic("no return value specified for GetStateValidationParameter") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]byte, error)); ok { + return rf(key) + } + if rf, ok := ret.Get(0).(func(string) []byte); ok { + r0 = rf(key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetStateValidationParameter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStateValidationParameter' +type MockChaincodeStubInterface_GetStateValidationParameter_Call struct { + *mock.Call +} + +// GetStateValidationParameter is a helper method to define mock.On call +// - key string +func (_e *MockChaincodeStubInterface_Expecter) GetStateValidationParameter(key interface{}) *MockChaincodeStubInterface_GetStateValidationParameter_Call { + return &MockChaincodeStubInterface_GetStateValidationParameter_Call{Call: _e.mock.On("GetStateValidationParameter", key)} +} + +func (_c *MockChaincodeStubInterface_GetStateValidationParameter_Call) Run(run func(key string)) *MockChaincodeStubInterface_GetStateValidationParameter_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateValidationParameter_Call) Return(_a0 []byte, _a1 error) *MockChaincodeStubInterface_GetStateValidationParameter_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStateValidationParameter_Call) RunAndReturn(run func(string) ([]byte, error)) *MockChaincodeStubInterface_GetStateValidationParameter_Call { + _c.Call.Return(run) + return _c +} + +// GetStringArgs provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetStringArgs() []string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetStringArgs") + } + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + return r0 +} + +// MockChaincodeStubInterface_GetStringArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStringArgs' +type MockChaincodeStubInterface_GetStringArgs_Call struct { + *mock.Call +} + +// GetStringArgs is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetStringArgs() *MockChaincodeStubInterface_GetStringArgs_Call { + return &MockChaincodeStubInterface_GetStringArgs_Call{Call: _e.mock.On("GetStringArgs")} +} + +func (_c *MockChaincodeStubInterface_GetStringArgs_Call) Run(run func()) *MockChaincodeStubInterface_GetStringArgs_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStringArgs_Call) Return(_a0 []string) *MockChaincodeStubInterface_GetStringArgs_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_GetStringArgs_Call) RunAndReturn(run func() []string) *MockChaincodeStubInterface_GetStringArgs_Call { + _c.Call.Return(run) + return _c +} + +// GetTransient provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetTransient() (map[string][]byte, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetTransient") + } + + var r0 map[string][]byte + var r1 error + if rf, ok := ret.Get(0).(func() (map[string][]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() map[string][]byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string][]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetTransient_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTransient' +type MockChaincodeStubInterface_GetTransient_Call struct { + *mock.Call +} + +// GetTransient is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetTransient() *MockChaincodeStubInterface_GetTransient_Call { + return &MockChaincodeStubInterface_GetTransient_Call{Call: _e.mock.On("GetTransient")} +} + +func (_c *MockChaincodeStubInterface_GetTransient_Call) Run(run func()) *MockChaincodeStubInterface_GetTransient_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetTransient_Call) Return(_a0 map[string][]byte, _a1 error) *MockChaincodeStubInterface_GetTransient_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetTransient_Call) RunAndReturn(run func() (map[string][]byte, error)) *MockChaincodeStubInterface_GetTransient_Call { + _c.Call.Return(run) + return _c +} + +// GetTxID provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetTxID() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetTxID") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockChaincodeStubInterface_GetTxID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTxID' +type MockChaincodeStubInterface_GetTxID_Call struct { + *mock.Call +} + +// GetTxID is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetTxID() *MockChaincodeStubInterface_GetTxID_Call { + return &MockChaincodeStubInterface_GetTxID_Call{Call: _e.mock.On("GetTxID")} +} + +func (_c *MockChaincodeStubInterface_GetTxID_Call) Run(run func()) *MockChaincodeStubInterface_GetTxID_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetTxID_Call) Return(_a0 string) *MockChaincodeStubInterface_GetTxID_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_GetTxID_Call) RunAndReturn(run func() string) *MockChaincodeStubInterface_GetTxID_Call { + _c.Call.Return(run) + return _c +} + +// GetTxTimestamp provides a mock function with given fields: +func (_m *MockChaincodeStubInterface) GetTxTimestamp() (*timestamppb.Timestamp, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetTxTimestamp") + } + + var r0 *timestamppb.Timestamp + var r1 error + if rf, ok := ret.Get(0).(func() (*timestamppb.Timestamp, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *timestamppb.Timestamp); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*timestamppb.Timestamp) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockChaincodeStubInterface_GetTxTimestamp_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTxTimestamp' +type MockChaincodeStubInterface_GetTxTimestamp_Call struct { + *mock.Call +} + +// GetTxTimestamp is a helper method to define mock.On call +func (_e *MockChaincodeStubInterface_Expecter) GetTxTimestamp() *MockChaincodeStubInterface_GetTxTimestamp_Call { + return &MockChaincodeStubInterface_GetTxTimestamp_Call{Call: _e.mock.On("GetTxTimestamp")} +} + +func (_c *MockChaincodeStubInterface_GetTxTimestamp_Call) Run(run func()) *MockChaincodeStubInterface_GetTxTimestamp_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockChaincodeStubInterface_GetTxTimestamp_Call) Return(_a0 *timestamppb.Timestamp, _a1 error) *MockChaincodeStubInterface_GetTxTimestamp_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockChaincodeStubInterface_GetTxTimestamp_Call) RunAndReturn(run func() (*timestamppb.Timestamp, error)) *MockChaincodeStubInterface_GetTxTimestamp_Call { + _c.Call.Return(run) + return _c +} + +// InvokeChaincode provides a mock function with given fields: chaincodeName, args, channel +func (_m *MockChaincodeStubInterface) InvokeChaincode(chaincodeName string, args [][]byte, channel string) *peer.Response { + ret := _m.Called(chaincodeName, args, channel) + + if len(ret) == 0 { + panic("no return value specified for InvokeChaincode") + } + + var r0 *peer.Response + if rf, ok := ret.Get(0).(func(string, [][]byte, string) *peer.Response); ok { + r0 = rf(chaincodeName, args, channel) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*peer.Response) + } + } + + return r0 +} + +// MockChaincodeStubInterface_InvokeChaincode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InvokeChaincode' +type MockChaincodeStubInterface_InvokeChaincode_Call struct { + *mock.Call +} + +// InvokeChaincode is a helper method to define mock.On call +// - chaincodeName string +// - args [][]byte +// - channel string +func (_e *MockChaincodeStubInterface_Expecter) InvokeChaincode(chaincodeName interface{}, args interface{}, channel interface{}) *MockChaincodeStubInterface_InvokeChaincode_Call { + return &MockChaincodeStubInterface_InvokeChaincode_Call{Call: _e.mock.On("InvokeChaincode", chaincodeName, args, channel)} +} + +func (_c *MockChaincodeStubInterface_InvokeChaincode_Call) Run(run func(chaincodeName string, args [][]byte, channel string)) *MockChaincodeStubInterface_InvokeChaincode_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].([][]byte), args[2].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_InvokeChaincode_Call) Return(_a0 *peer.Response) *MockChaincodeStubInterface_InvokeChaincode_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_InvokeChaincode_Call) RunAndReturn(run func(string, [][]byte, string) *peer.Response) *MockChaincodeStubInterface_InvokeChaincode_Call { + _c.Call.Return(run) + return _c +} + +// PurgePrivateData provides a mock function with given fields: collection, key +func (_m *MockChaincodeStubInterface) PurgePrivateData(collection string, key string) error { + ret := _m.Called(collection, key) + + if len(ret) == 0 { + panic("no return value specified for PurgePrivateData") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(collection, key) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_PurgePrivateData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PurgePrivateData' +type MockChaincodeStubInterface_PurgePrivateData_Call struct { + *mock.Call +} + +// PurgePrivateData is a helper method to define mock.On call +// - collection string +// - key string +func (_e *MockChaincodeStubInterface_Expecter) PurgePrivateData(collection interface{}, key interface{}) *MockChaincodeStubInterface_PurgePrivateData_Call { + return &MockChaincodeStubInterface_PurgePrivateData_Call{Call: _e.mock.On("PurgePrivateData", collection, key)} +} + +func (_c *MockChaincodeStubInterface_PurgePrivateData_Call) Run(run func(collection string, key string)) *MockChaincodeStubInterface_PurgePrivateData_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_PurgePrivateData_Call) Return(_a0 error) *MockChaincodeStubInterface_PurgePrivateData_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_PurgePrivateData_Call) RunAndReturn(run func(string, string) error) *MockChaincodeStubInterface_PurgePrivateData_Call { + _c.Call.Return(run) + return _c +} + +// PutPrivateData provides a mock function with given fields: collection, key, value +func (_m *MockChaincodeStubInterface) PutPrivateData(collection string, key string, value []byte) error { + ret := _m.Called(collection, key, value) + + if len(ret) == 0 { + panic("no return value specified for PutPrivateData") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string, []byte) error); ok { + r0 = rf(collection, key, value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_PutPrivateData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutPrivateData' +type MockChaincodeStubInterface_PutPrivateData_Call struct { + *mock.Call +} + +// PutPrivateData is a helper method to define mock.On call +// - collection string +// - key string +// - value []byte +func (_e *MockChaincodeStubInterface_Expecter) PutPrivateData(collection interface{}, key interface{}, value interface{}) *MockChaincodeStubInterface_PutPrivateData_Call { + return &MockChaincodeStubInterface_PutPrivateData_Call{Call: _e.mock.On("PutPrivateData", collection, key, value)} +} + +func (_c *MockChaincodeStubInterface_PutPrivateData_Call) Run(run func(collection string, key string, value []byte)) *MockChaincodeStubInterface_PutPrivateData_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string), args[2].([]byte)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_PutPrivateData_Call) Return(_a0 error) *MockChaincodeStubInterface_PutPrivateData_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_PutPrivateData_Call) RunAndReturn(run func(string, string, []byte) error) *MockChaincodeStubInterface_PutPrivateData_Call { + _c.Call.Return(run) + return _c +} + +// PutState provides a mock function with given fields: key, value +func (_m *MockChaincodeStubInterface) PutState(key string, value []byte) error { + ret := _m.Called(key, value) + + if len(ret) == 0 { + panic("no return value specified for PutState") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, []byte) error); ok { + r0 = rf(key, value) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_PutState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PutState' +type MockChaincodeStubInterface_PutState_Call struct { + *mock.Call +} + +// PutState is a helper method to define mock.On call +// - key string +// - value []byte +func (_e *MockChaincodeStubInterface_Expecter) PutState(key interface{}, value interface{}) *MockChaincodeStubInterface_PutState_Call { + return &MockChaincodeStubInterface_PutState_Call{Call: _e.mock.On("PutState", key, value)} +} + +func (_c *MockChaincodeStubInterface_PutState_Call) Run(run func(key string, value []byte)) *MockChaincodeStubInterface_PutState_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].([]byte)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_PutState_Call) Return(_a0 error) *MockChaincodeStubInterface_PutState_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_PutState_Call) RunAndReturn(run func(string, []byte) error) *MockChaincodeStubInterface_PutState_Call { + _c.Call.Return(run) + return _c +} + +// SetEvent provides a mock function with given fields: name, payload +func (_m *MockChaincodeStubInterface) SetEvent(name string, payload []byte) error { + ret := _m.Called(name, payload) + + if len(ret) == 0 { + panic("no return value specified for SetEvent") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, []byte) error); ok { + r0 = rf(name, payload) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_SetEvent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetEvent' +type MockChaincodeStubInterface_SetEvent_Call struct { + *mock.Call +} + +// SetEvent is a helper method to define mock.On call +// - name string +// - payload []byte +func (_e *MockChaincodeStubInterface_Expecter) SetEvent(name interface{}, payload interface{}) *MockChaincodeStubInterface_SetEvent_Call { + return &MockChaincodeStubInterface_SetEvent_Call{Call: _e.mock.On("SetEvent", name, payload)} +} + +func (_c *MockChaincodeStubInterface_SetEvent_Call) Run(run func(name string, payload []byte)) *MockChaincodeStubInterface_SetEvent_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].([]byte)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_SetEvent_Call) Return(_a0 error) *MockChaincodeStubInterface_SetEvent_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_SetEvent_Call) RunAndReturn(run func(string, []byte) error) *MockChaincodeStubInterface_SetEvent_Call { + _c.Call.Return(run) + return _c +} + +// SetPrivateDataValidationParameter provides a mock function with given fields: collection, key, ep +func (_m *MockChaincodeStubInterface) SetPrivateDataValidationParameter(collection string, key string, ep []byte) error { + ret := _m.Called(collection, key, ep) + + if len(ret) == 0 { + panic("no return value specified for SetPrivateDataValidationParameter") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string, []byte) error); ok { + r0 = rf(collection, key, ep) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPrivateDataValidationParameter' +type MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call struct { + *mock.Call +} + +// SetPrivateDataValidationParameter is a helper method to define mock.On call +// - collection string +// - key string +// - ep []byte +func (_e *MockChaincodeStubInterface_Expecter) SetPrivateDataValidationParameter(collection interface{}, key interface{}, ep interface{}) *MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call { + return &MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call{Call: _e.mock.On("SetPrivateDataValidationParameter", collection, key, ep)} +} + +func (_c *MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call) Run(run func(collection string, key string, ep []byte)) *MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(string), args[2].([]byte)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call) Return(_a0 error) *MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call) RunAndReturn(run func(string, string, []byte) error) *MockChaincodeStubInterface_SetPrivateDataValidationParameter_Call { + _c.Call.Return(run) + return _c +} + +// SetStateValidationParameter provides a mock function with given fields: key, ep +func (_m *MockChaincodeStubInterface) SetStateValidationParameter(key string, ep []byte) error { + ret := _m.Called(key, ep) + + if len(ret) == 0 { + panic("no return value specified for SetStateValidationParameter") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, []byte) error); ok { + r0 = rf(key, ep) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockChaincodeStubInterface_SetStateValidationParameter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetStateValidationParameter' +type MockChaincodeStubInterface_SetStateValidationParameter_Call struct { + *mock.Call +} + +// SetStateValidationParameter is a helper method to define mock.On call +// - key string +// - ep []byte +func (_e *MockChaincodeStubInterface_Expecter) SetStateValidationParameter(key interface{}, ep interface{}) *MockChaincodeStubInterface_SetStateValidationParameter_Call { + return &MockChaincodeStubInterface_SetStateValidationParameter_Call{Call: _e.mock.On("SetStateValidationParameter", key, ep)} +} + +func (_c *MockChaincodeStubInterface_SetStateValidationParameter_Call) Run(run func(key string, ep []byte)) *MockChaincodeStubInterface_SetStateValidationParameter_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].([]byte)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_SetStateValidationParameter_Call) Return(_a0 error) *MockChaincodeStubInterface_SetStateValidationParameter_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockChaincodeStubInterface_SetStateValidationParameter_Call) RunAndReturn(run func(string, []byte) error) *MockChaincodeStubInterface_SetStateValidationParameter_Call { + _c.Call.Return(run) + return _c +} + +// SplitCompositeKey provides a mock function with given fields: compositeKey +func (_m *MockChaincodeStubInterface) SplitCompositeKey(compositeKey string) (string, []string, error) { + ret := _m.Called(compositeKey) + + if len(ret) == 0 { + panic("no return value specified for SplitCompositeKey") + } + + var r0 string + var r1 []string + var r2 error + if rf, ok := ret.Get(0).(func(string) (string, []string, error)); ok { + return rf(compositeKey) + } + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(compositeKey) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(string) []string); ok { + r1 = rf(compositeKey) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]string) + } + } + + if rf, ok := ret.Get(2).(func(string) error); ok { + r2 = rf(compositeKey) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// MockChaincodeStubInterface_SplitCompositeKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SplitCompositeKey' +type MockChaincodeStubInterface_SplitCompositeKey_Call struct { + *mock.Call +} + +// SplitCompositeKey is a helper method to define mock.On call +// - compositeKey string +func (_e *MockChaincodeStubInterface_Expecter) SplitCompositeKey(compositeKey interface{}) *MockChaincodeStubInterface_SplitCompositeKey_Call { + return &MockChaincodeStubInterface_SplitCompositeKey_Call{Call: _e.mock.On("SplitCompositeKey", compositeKey)} +} + +func (_c *MockChaincodeStubInterface_SplitCompositeKey_Call) Run(run func(compositeKey string)) *MockChaincodeStubInterface_SplitCompositeKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockChaincodeStubInterface_SplitCompositeKey_Call) Return(_a0 string, _a1 []string, _a2 error) *MockChaincodeStubInterface_SplitCompositeKey_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *MockChaincodeStubInterface_SplitCompositeKey_Call) RunAndReturn(run func(string) (string, []string, error)) *MockChaincodeStubInterface_SplitCompositeKey_Call { + _c.Call.Return(run) + return _c +} + +// NewMockChaincodeStubInterface creates a new instance of MockChaincodeStubInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockChaincodeStubInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *MockChaincodeStubInterface { + mock := &MockChaincodeStubInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/v2/contractapi/shared_test.go b/v2/contractapi/shared_test.go new file mode 100644 index 0000000..89e3c87 --- /dev/null +++ b/v2/contractapi/shared_test.go @@ -0,0 +1,19 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +type myContract struct { + Contract + //lint:ignore U1000 unused + called []string +} + +func (mc *myContract) ReturnsString() string { + return "Some string" +} + +type customContext struct { + TransactionContext + prop1 string +} diff --git a/v2/contractapi/system_contract.go b/v2/contractapi/system_contract.go new file mode 100644 index 0000000..74187d7 --- /dev/null +++ b/v2/contractapi/system_contract.go @@ -0,0 +1,30 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +// SystemContract contract added to all chaincode to provide access to metdata +type SystemContract struct { + Contract + metadata string +} + +func (sc *SystemContract) setMetadata(metadata string) { + sc.metadata = metadata +} + +// GetMetadata returns JSON formatted metadata of chaincode +// the system contract is part of. This metadata is composed +// of reflected metadata combined with the metadata file +// if used +func (sc *SystemContract) GetMetadata() string { + return sc.metadata +} + +// GetEvaluateTransactions returns the transactions that +// exist in system contract which should be marked as +// evaluate transaction in the metadata. I.e. should be called +// by query transaction +func (sc *SystemContract) GetEvaluateTransactions() []string { + return []string{"GetMetadata"} +} diff --git a/v2/contractapi/system_contract_test.go b/v2/contractapi/system_contract_test.go new file mode 100644 index 0000000..66b5bfb --- /dev/null +++ b/v2/contractapi/system_contract_test.go @@ -0,0 +1,34 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// ================================ +// Tests +// ================================ + +func TestSetMetadata(t *testing.T) { + sc := SystemContract{} + sc.setMetadata("my metadata") + + assert.Equal(t, "my metadata", sc.metadata, "should have set metadata field") +} + +func TestGetMetadata(t *testing.T) { + sc := SystemContract{} + sc.metadata = "my metadata" + + assert.Equal(t, "my metadata", sc.GetMetadata(), "should have returned metadata field") +} + +func TestGetEvaluateTransactions(t *testing.T) { + sc := SystemContract{} + + assert.Equal(t, []string{"GetMetadata"}, sc.GetEvaluateTransactions(), "should have returned functions names that should be evaluate") +} diff --git a/v2/contractapi/transaction_context.go b/v2/contractapi/transaction_context.go new file mode 100644 index 0000000..aeacb04 --- /dev/null +++ b/v2/contractapi/transaction_context.go @@ -0,0 +1,64 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import ( + "github.com/hyperledger/fabric-chaincode-go/v2/pkg/cid" + "github.com/hyperledger/fabric-chaincode-go/v2/shim" +) + +// TransactionContextInterface defines the interface which TransactionContext +// meets. This can be taken by transacton functions on a contract which has not set +// a custom transaction context to allow transaction functions to take an interface +// to simplify unit testing. +type TransactionContextInterface interface { + // GetStub should provide a way to access the stub set by Init/Invoke + GetStub() shim.ChaincodeStubInterface + // GetClientIdentity should provide a way to access the client identity set by Init/Invoke + GetClientIdentity() cid.ClientIdentity +} + +// SettableTransactionContextInterface defines functions a valid transaction context +// should have. Transaction context's set for contracts to be used in chaincode +// must implement this interface. +type SettableTransactionContextInterface interface { + // SetStub should provide a way to pass the stub from a chaincode transaction + // call to the transaction context so that it can be used by contract functions. + // This is called by Init/Invoke with the stub passed. + SetStub(shim.ChaincodeStubInterface) + // SetClientIdentity should provide a way to pass the client identity from a chaincode + // transaction call to the transaction context so that it can be used by contract functions. + // This is called by Init/Invoke with the stub passed. + SetClientIdentity(ci cid.ClientIdentity) +} + +// TransactionContext is a basic transaction context to be used in contracts, +// containing minimal required functionality use in contracts as part of +// chaincode. Provides access to the stub and clientIdentity of a transaction. +// If a contract implements the ContractInterface using the Contract struct then +// this is the default transaction context that will be used. +type TransactionContext struct { + stub shim.ChaincodeStubInterface + clientIdentity cid.ClientIdentity +} + +// SetStub stores the passed stub in the transaction context +func (ctx *TransactionContext) SetStub(stub shim.ChaincodeStubInterface) { + ctx.stub = stub +} + +// SetClientIdentity stores the passed stub in the transaction context +func (ctx *TransactionContext) SetClientIdentity(ci cid.ClientIdentity) { + ctx.clientIdentity = ci +} + +// GetStub returns the current set stub +func (ctx *TransactionContext) GetStub() shim.ChaincodeStubInterface { + return ctx.stub +} + +// GetClientIdentity returns the current set client identity +func (ctx *TransactionContext) GetClientIdentity() cid.ClientIdentity { + return ctx.clientIdentity +} diff --git a/v2/contractapi/transaction_context_test.go b/v2/contractapi/transaction_context_test.go new file mode 100644 index 0000000..50f1c11 --- /dev/null +++ b/v2/contractapi/transaction_context_test.go @@ -0,0 +1,79 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package contractapi + +import ( + "crypto/x509" + "testing" + + "github.com/stretchr/testify/assert" +) + +// ================================ +// Helpers +// ================================ + +type mockClientIdentity struct{} + +func (mci *mockClientIdentity) GetID() (string, error) { + return "", nil +} + +func (mci *mockClientIdentity) GetMSPID() (string, error) { + return "", nil +} + +func (mci *mockClientIdentity) GetAttributeValue(string) (string, bool, error) { + return "", false, nil +} + +func (mci *mockClientIdentity) AssertAttributeValue(string, string) error { + return nil +} + +func (mci *mockClientIdentity) GetX509Certificate() (*x509.Certificate, error) { + return nil, nil +} + +// ================================ +// Tests +// ================================ + +func TestSetStub(t *testing.T) { + stub := NewMockChaincodeStubInterface(t) + ctx := TransactionContext{} + + ctx.SetStub(stub) + + assert.Equal(t, stub, ctx.stub, "should have set the same stub as passed") +} + +func TestGetStub(t *testing.T) { + stub := NewMockChaincodeStubInterface(t) + ctx := TransactionContext{} + ctx.stub = stub + + actual := ctx.GetStub() + + assert.Equal(t, stub, actual, "should have returned same stub as set") +} + +func TestSetClientIdentity(t *testing.T) { + ci := new(mockClientIdentity) + + ctx := TransactionContext{} + + ctx.SetClientIdentity(ci) + + assert.Equal(t, ci, ctx.clientIdentity, "should have set the same client identity as passed") +} + +func TestGetClientIdentity(t *testing.T) { + ci := new(mockClientIdentity) + + ctx := TransactionContext{} + ctx.clientIdentity = ci + + assert.Equal(t, ci, ctx.GetClientIdentity(), "should have returned same client identity as set") +} diff --git a/v2/contractapi/utils/undefined_interface.go b/v2/contractapi/utils/undefined_interface.go new file mode 100644 index 0000000..14552b1 --- /dev/null +++ b/v2/contractapi/utils/undefined_interface.go @@ -0,0 +1,9 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +// UndefinedInterface the type of nil passed to an after transaction when +// the contract function called as part of the transaction does not specify +// a success return type or its return type is interface{} and value nil +type UndefinedInterface struct{} diff --git a/v2/go.mod b/v2/go.mod new file mode 100644 index 0000000..9ebcb0d --- /dev/null +++ b/v2/go.mod @@ -0,0 +1,45 @@ +module github.com/hyperledger/fabric-contract-api-go/v2 + +go 1.21 + +require ( + github.com/cucumber/godog v0.14.1 + github.com/go-openapi/spec v0.21.0 + github.com/gobuffalo/packr v1.30.1 + github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af + github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 + github.com/stretchr/testify v1.9.0 + github.com/xeipuuv/gojsonschema v1.2.0 + google.golang.org/protobuf v1.34.2 +) + +require ( + github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect + github.com/cucumber/messages/go/v21 v21.0.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/gobuffalo/envy v1.10.2 // indirect + github.com/gobuffalo/packd v1.0.2 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-memdb v1.3.4 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.64.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/v2/go.sum b/v2/go.sum new file mode 100644 index 0000000..ab567c5 --- /dev/null +++ b/v2/go.sum @@ -0,0 +1,163 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= +github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= +github.com/cucumber/godog v0.14.1 h1:HGZhcOyyfaKclHjJ+r/q93iaTJZLKYW6Tv3HkmUE6+M= +github.com/cucumber/godog v0.14.1/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= +github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= +github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +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/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= +github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= +github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +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/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= +github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af h1:WT4NjX7Uk03GSeH++jF3a0wp4FhybTM86zDPCETvmSk= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af/go.mod h1:f/ER25FaBepxJugwpLhbD2hLAoZaZEVqkBjOcHjw72Y= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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.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.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.8.2/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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-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-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/yaml.v2 v2.2.2/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/integrationtest/.gitignore b/v2/integrationtest/.gitignore new file mode 100644 index 0000000..833ca2f --- /dev/null +++ b/v2/integrationtest/.gitignore @@ -0,0 +1,2 @@ +node_modules +fabric \ No newline at end of file diff --git a/v2/integrationtest/Dockerfile b/v2/integrationtest/Dockerfile new file mode 100644 index 0000000..099f67a --- /dev/null +++ b/v2/integrationtest/Dockerfile @@ -0,0 +1,9 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +ARG GO_VER=1.21 + +FROM golang:${GO_VER} + +WORKDIR $GOPATH/src/github.com/hyperledger/fabric-contract-api-go +COPY . . diff --git a/v2/integrationtest/chaincode/advancedtypes/go.mod b/v2/integrationtest/chaincode/advancedtypes/go.mod new file mode 100644 index 0000000..873f5a7 --- /dev/null +++ b/v2/integrationtest/chaincode/advancedtypes/go.mod @@ -0,0 +1,34 @@ +module github.com/hyperledger/fabric-chaincode-integration/advancedtypes + +go 1.21 + +require github.com/hyperledger/fabric-contract-api-go/v2 v2.0.0 + +require ( + 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/gobuffalo/envy v1.10.2 // indirect + github.com/gobuffalo/packd v1.0.2 // indirect + github.com/gobuffalo/packr v1.30.1 // indirect + github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af // indirect + github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/hyperledger/fabric-contract-api-go/v2 => ../../.. diff --git a/v2/integrationtest/chaincode/advancedtypes/go.sum b/v2/integrationtest/chaincode/advancedtypes/go.sum new file mode 100644 index 0000000..d97672b --- /dev/null +++ b/v2/integrationtest/chaincode/advancedtypes/go.sum @@ -0,0 +1,130 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +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/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= +github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= +github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af h1:WT4NjX7Uk03GSeH++jF3a0wp4FhybTM86zDPCETvmSk= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af/go.mod h1:f/ER25FaBepxJugwpLhbD2hLAoZaZEVqkBjOcHjw72Y= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +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/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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-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-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/yaml.v2 v2.2.2/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/integrationtest/chaincode/advancedtypes/main.go b/v2/integrationtest/chaincode/advancedtypes/main.go new file mode 100644 index 0000000..d2551d6 --- /dev/null +++ b/v2/integrationtest/chaincode/advancedtypes/main.go @@ -0,0 +1,107 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" + +// BasicAsset holds and ID and value +type BasicAsset struct { + ID string `json:"id"` + Value int `json:"value"` +} + +// Description holds details about an asset +type Description struct { + Colour string `json:"colour"` + Owners []string `json:"owners"` +} + +// ComplexAsset is a basic asset with more detail +type ComplexAsset struct { + BasicAsset + Description Description `json:"description"` +} + +// AdvancedTypesContract a contract for managing non-string types in the chaincode +type AdvancedTypesContract struct { + contractapi.Contract +} + +// GetInt returns 1 +func (atc *AdvancedTypesContract) GetInt() int { + return 1 +} + +// CallAndResponseInt returns sent int +func (atc *AdvancedTypesContract) CallAndResponseInt(sent int) int { + return sent + 1 +} + +// GetFloat returns 1.1 +func (atc *AdvancedTypesContract) GetFloat() float64 { + return 1.1 +} + +// CallAndResponseFloat returns sent float +func (atc *AdvancedTypesContract) CallAndResponseFloat(sent float64) float64 { + return sent +} + +// GetBool returns true +func (atc *AdvancedTypesContract) GetBool() bool { + return true +} + +// CallAndResponseBool returns sent bool +func (atc *AdvancedTypesContract) CallAndResponseBool(sent bool) bool { + return !sent +} + +// GetArray returns int array 1,2,3 +func (atc *AdvancedTypesContract) GetArray() []int { + return []int{1, 2, 3} +} + +// CallAndResponseArray returns sent bool array +func (atc *AdvancedTypesContract) CallAndResponseArray(sent []string) []string { + for i := 0; i < len(sent)/2; i++ { + j := len(sent) - i - 1 + sent[i], sent[j] = sent[j], sent[i] + } + return sent +} + +// GetBasicAsset returns a basic asset with id "OBJECT_1" and value 100 +func (atc *AdvancedTypesContract) GetBasicAsset() BasicAsset { + return BasicAsset{"OBJECT_1", 100} +} + +// CallAndResponseBasicAsset returns sent basic asset +func (atc *AdvancedTypesContract) CallAndResponseBasicAsset(sent BasicAsset) BasicAsset { + return sent +} + +// GetComplexAsset returns a basic asset with id "OBJECT_1" and value 100 +func (atc *AdvancedTypesContract) GetComplexAsset() ComplexAsset { + return ComplexAsset{BasicAsset{"OBJECT_2", 100}, Description{"Vermillion", []string{"Alice", "Bob"}}} +} + +// CallAndResponseComplexAsset returns sent complex asset +func (atc *AdvancedTypesContract) CallAndResponseComplexAsset(sent ComplexAsset) ComplexAsset { + return sent +} + +func main() { + advancedTypes := new(AdvancedTypesContract) + + cc, err := contractapi.NewChaincode(advancedTypes) + + if err != nil { + panic(err.Error()) + } + + if err := cc.Start(); err != nil { + panic(err.Error()) + } +} diff --git a/v2/integrationtest/chaincode/private/go.mod b/v2/integrationtest/chaincode/private/go.mod new file mode 100644 index 0000000..47fa552 --- /dev/null +++ b/v2/integrationtest/chaincode/private/go.mod @@ -0,0 +1,34 @@ +module github.com/hyperledger/fabric-chaincode-integration/chaincodeprivate + +go 1.21 + +require github.com/hyperledger/fabric-contract-api-go/v2 v2.0.0 + +require ( + 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/gobuffalo/envy v1.10.2 // indirect + github.com/gobuffalo/packd v1.0.2 // indirect + github.com/gobuffalo/packr v1.30.1 // indirect + github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af // indirect + github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/hyperledger/fabric-contract-api-go/v2 => ../../.. diff --git a/v2/integrationtest/chaincode/private/go.sum b/v2/integrationtest/chaincode/private/go.sum new file mode 100644 index 0000000..d97672b --- /dev/null +++ b/v2/integrationtest/chaincode/private/go.sum @@ -0,0 +1,130 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +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/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= +github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= +github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af h1:WT4NjX7Uk03GSeH++jF3a0wp4FhybTM86zDPCETvmSk= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af/go.mod h1:f/ER25FaBepxJugwpLhbD2hLAoZaZEVqkBjOcHjw72Y= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +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/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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-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-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/yaml.v2 v2.2.2/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/integrationtest/chaincode/private/main.go b/v2/integrationtest/chaincode/private/main.go new file mode 100644 index 0000000..e0ee71a --- /dev/null +++ b/v2/integrationtest/chaincode/private/main.go @@ -0,0 +1,54 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "errors" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" +) + +// PrivateContract with biz logic +type PrivateContract struct { + contractapi.Contract +} + +// PutPrivateState - Adds a key value pair to the private collection Org1AndOrg2 +func (sc *PrivateContract) PutPrivateState(ctx contractapi.TransactionContextInterface, key string, value string) error { + return ctx.GetStub().PutPrivateData("Org1AndOrg2", key, []byte(value)) +} + +// GetPrivateState - Gets the value for a key from the private collection Org1AndOrg2 +func (sc *PrivateContract) GetPrivateState(ctx contractapi.TransactionContextInterface, key string) (string, error) { + bytes, err := ctx.GetStub().GetPrivateData("Org1AndOrg2", key) + + if err != nil { + return "", errors.New("Could not read private collection Org1AndOrg2") + } + + if bytes == nil { + return "", errors.New("No value found for " + key) + } + + return string(bytes), nil +} + +// DeletePrivateState - Deletes a key from the private collection Org1AndOrg2 +func (sc *PrivateContract) DeletePrivateState(ctx contractapi.TransactionContextInterface, key string) error { + return ctx.GetStub().DelPrivateData("Org1AndOrg2", key) +} + +func main() { + privateContract := new(PrivateContract) + + cc, err := contractapi.NewChaincode(privateContract) + + if err != nil { + panic(err.Error()) + } + + if err := cc.Start(); err != nil { + panic(err.Error()) + } +} diff --git a/v2/integrationtest/chaincode/simple/Dockerfile b/v2/integrationtest/chaincode/simple/Dockerfile new file mode 100644 index 0000000..63e6581 --- /dev/null +++ b/v2/integrationtest/chaincode/simple/Dockerfile @@ -0,0 +1,13 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +FROM hyperledger/fabric-contract-api-go-integrationtest + +WORKDIR $GOPATH/src/github.com/hyperledger/fabric-contract-api-go/integrationtest/chaincode/simple +COPY . . + +RUN go get -d -v ./... +RUN go install -v ./... + +EXPOSE 9999 +CMD ["/go/bin/simple"] diff --git a/v2/integrationtest/chaincode/simple/go.mod b/v2/integrationtest/chaincode/simple/go.mod new file mode 100644 index 0000000..a82ba00 --- /dev/null +++ b/v2/integrationtest/chaincode/simple/go.mod @@ -0,0 +1,34 @@ +module github.com/hyperledger/fabric-chaincode-integration/simple + +go 1.21 + +require github.com/hyperledger/fabric-contract-api-go/v2 v2.0.0 + +require ( + 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/gobuffalo/envy v1.10.2 // indirect + github.com/gobuffalo/packd v1.0.2 // indirect + github.com/gobuffalo/packr v1.30.1 // indirect + github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af // indirect + github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/hyperledger/fabric-contract-api-go/v2 => ../../.. diff --git a/v2/integrationtest/chaincode/simple/go.sum b/v2/integrationtest/chaincode/simple/go.sum new file mode 100644 index 0000000..d97672b --- /dev/null +++ b/v2/integrationtest/chaincode/simple/go.sum @@ -0,0 +1,130 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +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/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= +github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= +github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af h1:WT4NjX7Uk03GSeH++jF3a0wp4FhybTM86zDPCETvmSk= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af/go.mod h1:f/ER25FaBepxJugwpLhbD2hLAoZaZEVqkBjOcHjw72Y= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +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/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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-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-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/yaml.v2 v2.2.2/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/integrationtest/chaincode/simple/main.go b/v2/integrationtest/chaincode/simple/main.go new file mode 100644 index 0000000..4ed729d --- /dev/null +++ b/v2/integrationtest/chaincode/simple/main.go @@ -0,0 +1,70 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" +) + +// SimpleContract with biz logic +type SimpleContract struct { + contractapi.Contract +} + +// HelloWorld - returns a string +func (sc *SimpleContract) HelloWorld(ctx contractapi.TransactionContextInterface) string { + return "Hello World" +} + +// CallAndResponse - Returns the string you send +func (sc *SimpleContract) CallAndResponse(ctx contractapi.TransactionContextInterface, value string) string { + return value +} + +// PutState - Adds a key value pair to the world state +func (sc *SimpleContract) PutState(ctx contractapi.TransactionContextInterface, key string, value string) error { + return ctx.GetStub().PutState(key, []byte(value)) +} + +// GetState - Gets the value for a key from the world state +func (sc *SimpleContract) GetState(ctx contractapi.TransactionContextInterface, key string) (string, error) { + bytes, err := ctx.GetStub().GetState(key) + + if err != nil { + return "", nil + } + + return string(bytes), nil +} + +// ExistsState returns true when asset with given ID exists in world state +func (sc *SimpleContract) ExistsState(ctx contractapi.TransactionContextInterface, id string) (bool, error) { + bytes, err := ctx.GetStub().GetState(id) + if err != nil { + return false, fmt.Errorf("failed to read from world state: %v", err) + } + + return bytes != nil, nil +} + +// DeleteState - Deletes a key from the world state +func (sc *SimpleContract) DeleteState(ctx contractapi.TransactionContextInterface, key string) error { + return ctx.GetStub().DelState(key) +} + +func main() { + simpleContract := new(SimpleContract) + + cc, err := contractapi.NewChaincode(simpleContract) + + if err != nil { + panic(err.Error()) + } + + if err := cc.Start(); err != nil { + panic(err.Error()) + } +} diff --git a/v2/integrationtest/chaincode/transactionhooks/go.mod b/v2/integrationtest/chaincode/transactionhooks/go.mod new file mode 100644 index 0000000..bf3231a --- /dev/null +++ b/v2/integrationtest/chaincode/transactionhooks/go.mod @@ -0,0 +1,34 @@ +module github.com/hyperledger/fabric-chaincode-integration/transactionhooks + +go 1.21 + +require github.com/hyperledger/fabric-contract-api-go/v2 v2.0.0 + +require ( + 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/gobuffalo/envy v1.10.2 // indirect + github.com/gobuffalo/packd v1.0.2 // indirect + github.com/gobuffalo/packr v1.30.1 // indirect + github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af // indirect + github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/hyperledger/fabric-contract-api-go/v2 => ../../.. diff --git a/v2/integrationtest/chaincode/transactionhooks/go.sum b/v2/integrationtest/chaincode/transactionhooks/go.sum new file mode 100644 index 0000000..d97672b --- /dev/null +++ b/v2/integrationtest/chaincode/transactionhooks/go.sum @@ -0,0 +1,130 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +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/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= +github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= +github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af h1:WT4NjX7Uk03GSeH++jF3a0wp4FhybTM86zDPCETvmSk= +github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af/go.mod h1:f/ER25FaBepxJugwpLhbD2hLAoZaZEVqkBjOcHjw72Y= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +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/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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-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-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/yaml.v2 v2.2.2/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/integrationtest/chaincode/transactionhooks/main.go b/v2/integrationtest/chaincode/transactionhooks/main.go new file mode 100644 index 0000000..0156469 --- /dev/null +++ b/v2/integrationtest/chaincode/transactionhooks/main.go @@ -0,0 +1,86 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "errors" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi/utils" +) + +// TransactionContext a custom transaction context +type TransactionContext struct { + contractapi.TransactionContext + + beforeKey string + beforeValue []byte +} + +func beforeTransaction(ctx *TransactionContext) error { + transient, _ := ctx.GetStub().GetTransient() + + if val, ok := transient["fail"]; ok && string(val) == "BEFORE" { + return errors.New("Before transaction failed") + } + + ctx.beforeKey = string(transient["before_key"]) + ctx.beforeValue = transient["before_value"] + + return nil +} + +func afterTransaction(ctx *TransactionContext, value interface{}) error { + transient, _ := ctx.GetStub().GetTransient() + + if val, ok := transient["fail"]; ok && string(val) == "AFTER" { + return errors.New("After transaction failed") + } + + if _, ok := value.(*utils.UndefinedInterface); ok { + return nil + } + + afterKey := string(transient["after_key"]) + + return ctx.GetStub().PutState(afterKey, []byte(value.(string))) +} + +// TransactionHooksContract with biz logic +type TransactionHooksContract struct { + contractapi.Contract +} + +// WriteBeforeValue writes to the world state the value stored in the context by the before function +func (thc *TransactionHooksContract) WriteBeforeValue(ctx *TransactionContext) error { + transient, _ := ctx.GetStub().GetTransient() + + if val, ok := transient["fail"]; ok && string(val) == "NAMED" { + return errors.New("Named transaction failed") + } + + return ctx.GetStub().PutState(ctx.beforeKey, ctx.beforeValue) +} + +// PassAfterValue returns a passed value so that it can be written to the world state by the after function +func (thc *TransactionHooksContract) PassAfterValue(ctx *TransactionContext, passToAfter string) string { + return passToAfter +} + +func main() { + transactionhooksContract := new(TransactionHooksContract) + transactionhooksContract.TransactionContextHandler = new(TransactionContext) + transactionhooksContract.BeforeTransaction = beforeTransaction + transactionhooksContract.AfterTransaction = afterTransaction + + cc, err := contractapi.NewChaincode(transactionhooksContract) + + if err != nil { + panic(err.Error()) + } + + if err := cc.Start(); err != nil { + panic(err.Error()) + } +} diff --git a/v2/integrationtest/cucumber.js b/v2/integrationtest/cucumber.js new file mode 100644 index 0000000..47122c9 --- /dev/null +++ b/v2/integrationtest/cucumber.js @@ -0,0 +1,61 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + + +// Configuration for running the chaincode tests +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +const TEST_NETWORK_DIR= path.resolve(process.env['TEST_NETWORK_DIR']) + +// it's important to note the langauge of the chaincode implementations. +const config = { + TestNetwork: { + rootDir: TEST_NETWORK_DIR, + chaincodes: { + simple: { path: path.resolve('./chaincode/simple'), lang: "golang" }, + ccaas: { path: path.resolve('./chaincode/simple'), lang: "ccaas" }, + advancedtypes: { path: path.resolve('./chaincode/advancedtypes'), lang: "golang"} + }, + cryptoPath : path.resolve(TEST_NETWORK_DIR, 'organizations', 'peerOrganizations', 'org1.example.com'), + env: "", + peerEndpoint : 'localhost:7051', + useExisting: false + } +} + +// ----------------------------------------- + +// These configurations affect how the cucumber framework is used. In general these do not +// need to be modified + +// this configuration is only used when developing changes to the tool itself +let dev = [ + './features/**/*.feature', // Specify our feature files + '--require-module ts-node/register', // Load TypeScript module + `--require ./src/step-definitions/**/*.ts`, // Load step definitions + '--format progress-bar', // Load custom formatter + '--format @cucumber/pretty-formatter', // Load custom formatter, + `--world-parameters ${JSON.stringify(config)}`, + '--publish-quiet' +].join(' '); + +// This should be in used in all other circumstances +const installDir = path.resolve(process.cwd(),'node_modules','@hyperledger', 'fabric-chaincode-integration'); + +let prod = [ + `${installDir}/features/**/*.feature`, // Specify our feature files + '--require-module ts-node/register', // Load TypeScript module + `--require ${installDir}/dist/step-definitions/**/*.js`, // Load step definitions + '--format progress-bar', // Load custom formatter + '--format @cucumber/pretty-formatter', // Load custom formatter, + `--world-parameters ${JSON.stringify(config)}`, + '--publish-quiet' +].join(' '); + +module.exports = { + default: prod, + prod, + dev +}; + diff --git a/v2/integrationtest/package-lock.json b/v2/integrationtest/package-lock.json new file mode 100644 index 0000000..cf8ef6c --- /dev/null +++ b/v2/integrationtest/package-lock.json @@ -0,0 +1,5192 @@ +{ + "name": "integrationtest", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "@hyperledger/fabric-chaincode-integration": "^0.8.2", + "@hyperledger/fabric-gateway": "^1.5.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cucumber/create-meta": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/create-meta/-/create-meta-5.0.0.tgz", + "integrity": "sha512-Z5kMZkUff00S3/KSnKzB/KOm2UIxMXY1xXmj2dQMlD49lV6v/W8EEvgDMNtQotQNSOQU5bDupmWQpk+o16tXIw==", + "dependencies": { + "@cucumber/messages": "^16.0.0" + } + }, + "node_modules/@cucumber/create-meta/node_modules/@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "dependencies": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/create-meta/node_modules/@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "node_modules/@cucumber/create-meta/node_modules/class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + }, + "node_modules/@cucumber/cucumber": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-7.3.2.tgz", + "integrity": "sha512-qqptM9w+UqXEYBAkrIGpIVPXDWv+zp0LrS89LiwHZwBp0cJg00su/iPMZ4j8TvCJiKfAwJXsAI1yjrd1POtU+w==", + "dependencies": { + "@cucumber/create-meta": "^5.0.0", + "@cucumber/cucumber-expressions": "^12.1.1", + "@cucumber/gherkin": "^19.0.3", + "@cucumber/gherkin-streams": "^2.0.2", + "@cucumber/html-formatter": "^15.0.2", + "@cucumber/messages": "^16.0.1", + "@cucumber/tag-expressions": "^3.0.1", + "assertion-error-formatter": "^3.0.0", + "bluebird": "^3.7.2", + "capital-case": "^1.0.4", + "cli-table3": "0.6.1", + "colors": "1.4.0", + "commander": "^7.0.0", + "create-require": "^1.1.1", + "duration": "^0.2.2", + "durations": "^3.4.2", + "figures": "^3.2.0", + "glob": "^7.1.6", + "indent-string": "^4.0.0", + "is-generator": "^1.0.3", + "is-stream": "^2.0.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash": "^4.17.21", + "mz": "^2.7.0", + "progress": "^2.0.3", + "resolve": "^1.19.0", + "resolve-pkg": "^2.0.0", + "stack-chain": "^2.0.0", + "stacktrace-js": "^2.0.2", + "string-argv": "^0.3.1", + "tmp": "^0.2.1", + "util-arity": "^1.1.0", + "verror": "^1.10.0" + }, + "bin": { + "cucumber-js": "bin/cucumber-js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@cucumber/cucumber-expressions": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-12.1.3.tgz", + "integrity": "sha512-LB8MAzE4F/t2KIgsDEz4gZH0xSI4aG0/LmYUPyISPPjUS1pI/yGWWyeX2WsiUQxpSs765WcNIq5Bggt7gGGO3Q==", + "dependencies": { + "regexp-match-indices": "1.0.2" + } + }, + "node_modules/@cucumber/cucumber/node_modules/@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "dependencies": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/cucumber/node_modules/@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "node_modules/@cucumber/cucumber/node_modules/class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + }, + "node_modules/@cucumber/gherkin": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-19.0.3.tgz", + "integrity": "sha512-gWdMm8mfRk3P+VugJWvNALaQV5QnT+5RkqWy3tO+4NsMSQZPo5p4V4vXwriQZ/sZR1Wni5TDRztuRsKLgZ3XHA==", + "dependencies": { + "@cucumber/message-streams": "^2.0.0", + "@cucumber/messages": "^16.0.1" + } + }, + "node_modules/@cucumber/gherkin-streams": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-streams/-/gherkin-streams-2.0.2.tgz", + "integrity": "sha512-cKmXOBz4OwGlrHMBCc4qCC3KzLaqcEZ11nWWskIbv6jyfvlIRuM2OgEF6VLcNVewczifW1p6DrDj0OO+BeXocA==", + "dependencies": { + "@cucumber/gherkin": "^19.0.1", + "@cucumber/message-streams": "^2.0.0", + "@cucumber/messages": "^16.0.0", + "commander": "7.2.0", + "source-map-support": "0.5.19" + }, + "bin": { + "gherkin-javascript": "bin/gherkin" + } + }, + "node_modules/@cucumber/gherkin-streams/node_modules/@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "dependencies": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/gherkin-streams/node_modules/@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "node_modules/@cucumber/gherkin-streams/node_modules/class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + }, + "node_modules/@cucumber/gherkin/node_modules/@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "dependencies": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/gherkin/node_modules/@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "node_modules/@cucumber/gherkin/node_modules/class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + }, + "node_modules/@cucumber/html-formatter": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-15.0.2.tgz", + "integrity": "sha512-j+YGY4ytj78G/v1gZo53D+vuKXlTg/oxNwSCCGvRQo75+AqYDJSkm/vexXJQ5lY1rXAvlbZ9KI6jhg6LDs0YdQ==", + "dependencies": { + "@cucumber/messages": "^16.0.1", + "commander": "7.2.0", + "source-map-support": "0.5.19" + }, + "bin": { + "cucumber-html-formatter": "bin/cucumber-html-formatter.js" + } + }, + "node_modules/@cucumber/html-formatter/node_modules/@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "dependencies": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/html-formatter/node_modules/@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "node_modules/@cucumber/html-formatter/node_modules/class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + }, + "node_modules/@cucumber/message-streams": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-2.1.0.tgz", + "integrity": "sha512-Yh3mw3qv6QL9NI/ihkZF8V9MX2GbnR6oktv34kC3uAbrQy9d/b2SZ3HNjG3J9JQqpV4B7Om3SPElJYIeo66TrA==", + "dependencies": { + "@cucumber/messages": "^16.0.1" + } + }, + "node_modules/@cucumber/message-streams/node_modules/@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "dependencies": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/message-streams/node_modules/@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "node_modules/@cucumber/message-streams/node_modules/class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + }, + "node_modules/@cucumber/messages": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.2.tgz", + "integrity": "sha512-vhWkNmQco+7tk/DWqpN0/R9KTNvsKsXVfZ7IsJs+dEeWmTuRztklHq8lJalwMSQBl71+2/KqGHzOO4BMTC9wIQ==", + "peer": true, + "dependencies": { + "@types/uuid": "8.3.4", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/pretty-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/pretty-formatter/-/pretty-formatter-1.0.0.tgz", + "integrity": "sha512-wcnIMN94HyaHGsfq72dgCvr1d8q6VGH4Y6Gl5weJ2TNZw1qn2UY85Iki4c9VdaLUONYnyYH3+178YB+9RFe/Hw==", + "dependencies": { + "ansi-styles": "^5.0.0", + "cli-table3": "^0.6.0", + "figures": "^3.2.0", + "ts-dedent": "^2.0.0" + }, + "peerDependencies": { + "@cucumber/cucumber": ">=7.0.0", + "@cucumber/messages": "*" + } + }, + "node_modules/@cucumber/pretty-formatter/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@cucumber/tag-expressions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-3.0.1.tgz", + "integrity": "sha512-OGCXaJ1BQXmQ5b9pw+JYsBGumK2/LPZiLmbj1o1JFVeSNs2PY8WPQFSyXrskhrHz5Nd/6lYg7lvGMtFHOncC4w==" + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.10.9", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.9.tgz", + "integrity": "sha512-5tcgUctCG0qoNyfChZifz2tJqbRbXVO9J7X6duFcOjY3HUNCxg5D0ZCK7EP9vIcZ0zRpLU9bWkyCqVCLZ46IbQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hyperledger/fabric-chaincode-integration": { + "version": "0.8.2", + "resolved": "https://npm.pkg.github.com/download/@hyperledger/fabric-chaincode-integration/0.8.2/b33d26c0a008d38245d21d985dbaaa39b1e6baf2", + "integrity": "sha512-n62DXAajf57RdT+hSsMwhRMHtbtPBUuEWUlp5LqT4h407MgTITGpZyi4QVpea96KyEg4t77LxpSDCg6LSxUQ0g==", + "dependencies": { + "@cucumber/cucumber": "^7.3.2", + "@cucumber/pretty-formatter": "^1.0.0-alpha.1", + "@grpc/grpc-js": "^1.10.9", + "@hyperledger/fabric-gateway": "^1.0.1", + "@tsconfig/node14": "^1.0.1", + "@types/yargs": "^17.0.9", + "ajv": "^6.11.0", + "assert": "^2.0.0", + "chai": "^4.2.0", + "chalk": "^3.0.0", + "cucumber-tsflow": "^4.0.0-rc.1", + "fast-safe-stringify": "^2.0.7", + "fs-extra": "^8.1.0", + "js-yaml": "^3.13.1", + "lodash": "^4.17.15", + "nano": "^10.1.3", + "ts-node": "^8.6.2", + "typescript": "~4.5.2", + "winston": "^3.2.1", + "yargs": "^17.3.1" + }, + "bin": { + "fabric-chaincode-integration": "dist/run.js" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@hyperledger/fabric-gateway": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@hyperledger/fabric-gateway/-/fabric-gateway-1.5.0.tgz", + "integrity": "sha512-36DRkcUrtJA4Ktr9uPYlEfWte79ceFd53oYVvFOLImxkvL0Tm4gqyvR6Pg7J9EgdhXZ9StR7FLYxILtS52RB4Q==", + "dependencies": { + "@grpc/grpc-js": "^1.10.0", + "@hyperledger/fabric-protos": "^0.3.0", + "asn1.js": "^5.4.0", + "bn.js": "^5.2.0", + "elliptic": "^6.5.0", + "google-protobuf": "^3.21.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "optionalDependencies": { + "pkcs11js": "^2.1.0" + } + }, + "node_modules/@hyperledger/fabric-gateway/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@hyperledger/fabric-protos": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@hyperledger/fabric-protos/-/fabric-protos-0.3.3.tgz", + "integrity": "sha512-AQbxHGmNj6/D80GZzG7+Nw1WCd4GEfLITk83Yg2yO8zylzKsw5onE5X1+aIsC4jdILY4tSY/Dyy0s1u4cpB9+A==", + "dependencies": { + "@grpc/grpc-js": "^1.9.0", + "google-protobuf": "^3.21.0" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "node_modules/@types/node": { + "version": "20.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", + "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "peer": true + }, + "node_modules/@types/yargs": { + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "engines": { + "node": "*" + } + }, + "node_modules/assertion-error-formatter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", + "integrity": "sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==", + "dependencies": { + "diff": "^4.0.1", + "pad-right": "^0.2.2", + "repeat-string": "^1.6.1" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "engines": { + "node": "*" + } + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "peer": true + }, + "node_modules/cli-table3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "1.4.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/cucumber-tsflow": { + "version": "4.0.0-rc.1", + "resolved": "https://registry.npmjs.org/cucumber-tsflow/-/cucumber-tsflow-4.0.0-rc.1.tgz", + "integrity": "sha512-RtZHbc1dO4VU73jWUJgZXmBrlZ9fifaBARZYnd7Ok2zgyApKZo8KH/PY6V63YYJBeaGK9p4Cd8jlMJvSGI/lhg==", + "dependencies": { + "callsites": "^3.1.0", + "log4js": "^6.3.0", + "source-map-support": "^0.5.19", + "underscore": "^1.8.3" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/date-format": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.11.tgz", + "integrity": "sha512-VS20KRyorrbMCQmpdl2hg5KaOUsda1RbnsJg461FfrcyCUg+pkd0b40BSW4niQyTheww4DBXQnS7HwSrKkipLw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, + "node_modules/durations": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/durations/-/durations-3.4.2.tgz", + "integrity": "sha512-V/lf7y33dGaypZZetVI1eu7BmvkbC4dItq12OElLRpKuaU5JxQstV2zHwLv8P7cNbQ+KL1WD80zMCTx5dNC4dg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", + "integrity": "sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==" + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/knuth-shuffle-seeded": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", + "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", + "dependencies": { + "seed-random": "~2.2.0" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/log4js": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.6.0.tgz", + "integrity": "sha512-3v8R7fd45UB6THucSht6wN2/7AZEruQbXdjygPZcxt5TA/msO6si9CN5MefUuKXbYnJHTBnYcx4famwcyQd+sA==", + "dependencies": { + "date-format": "^4.0.11", + "debug": "^4.3.4", + "flatted": "^3.2.5", + "rfdc": "^1.3.0", + "streamroller": "^3.1.1" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/logform": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", + "dependencies": { + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nano": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/nano/-/nano-10.1.3.tgz", + "integrity": "sha512-q/hKQJJH3FhkkuJ3ojbgDph2StlSXFBPNkpZBZlsvZDbuYfxKJ4VtunEeilthcZtuIplIk1zVX5o2RgKTUTO+Q==", + "dependencies": { + "axios": "^1.6.2", + "node-abort-controller": "^3.0.1", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/pad-right": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", + "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", + "dependencies": { + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "engines": { + "node": "*" + } + }, + "node_modules/pkcs11js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkcs11js/-/pkcs11js-2.1.0.tgz", + "integrity": "sha512-8i3yzNHoN0DcawytjXqGK4tjvQP/VrcPi1j0Bvhy8PtckBbsETSECyNsBxwY6eAnvGhsJEDyFq42ZPNZR9P/MA==", + "hasInstallScript": true, + "optional": true, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/PeculiarVentures" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/protobufjs": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz", + "integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, + "node_modules/regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "dependencies": { + "regexp-tree": "^0.1.11" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz", + "integrity": "sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-chain": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-2.0.0.tgz", + "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==" + }, + "node_modules/stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "node_modules/stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + } + }, + "node_modules/stacktrace-gps/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "node_modules/streamroller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.1.tgz", + "integrity": "sha512-iPhtd9unZ6zKdWgMeYGfSBuqCngyJy1B/GPi/lTpwGpa3bajuX30GjUVd0/Tn/Xhg0mr4DOSENozz9Y06qyonQ==", + "dependencies": { + "date-format": "^4.0.10", + "debug": "^4.3.4", + "fs-extra": "^10.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "dependencies": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/winston": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.1.tgz", + "integrity": "sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w==", + "dependencies": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" + }, + "@cucumber/create-meta": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/create-meta/-/create-meta-5.0.0.tgz", + "integrity": "sha512-Z5kMZkUff00S3/KSnKzB/KOm2UIxMXY1xXmj2dQMlD49lV6v/W8EEvgDMNtQotQNSOQU5bDupmWQpk+o16tXIw==", + "requires": { + "@cucumber/messages": "^16.0.0" + }, + "dependencies": { + "@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "requires": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + } + } + }, + "@cucumber/cucumber": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-7.3.2.tgz", + "integrity": "sha512-qqptM9w+UqXEYBAkrIGpIVPXDWv+zp0LrS89LiwHZwBp0cJg00su/iPMZ4j8TvCJiKfAwJXsAI1yjrd1POtU+w==", + "requires": { + "@cucumber/create-meta": "^5.0.0", + "@cucumber/cucumber-expressions": "^12.1.1", + "@cucumber/gherkin": "^19.0.3", + "@cucumber/gherkin-streams": "^2.0.2", + "@cucumber/html-formatter": "^15.0.2", + "@cucumber/messages": "^16.0.1", + "@cucumber/tag-expressions": "^3.0.1", + "assertion-error-formatter": "^3.0.0", + "bluebird": "^3.7.2", + "capital-case": "^1.0.4", + "cli-table3": "0.6.1", + "colors": "1.4.0", + "commander": "^7.0.0", + "create-require": "^1.1.1", + "duration": "^0.2.2", + "durations": "^3.4.2", + "figures": "^3.2.0", + "glob": "^7.1.6", + "indent-string": "^4.0.0", + "is-generator": "^1.0.3", + "is-stream": "^2.0.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash": "^4.17.21", + "mz": "^2.7.0", + "progress": "^2.0.3", + "resolve": "^1.19.0", + "resolve-pkg": "^2.0.0", + "stack-chain": "^2.0.0", + "stacktrace-js": "^2.0.2", + "string-argv": "^0.3.1", + "tmp": "^0.2.1", + "util-arity": "^1.1.0", + "verror": "^1.10.0" + }, + "dependencies": { + "@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "requires": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + } + } + }, + "@cucumber/cucumber-expressions": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-12.1.3.tgz", + "integrity": "sha512-LB8MAzE4F/t2KIgsDEz4gZH0xSI4aG0/LmYUPyISPPjUS1pI/yGWWyeX2WsiUQxpSs765WcNIq5Bggt7gGGO3Q==", + "requires": { + "regexp-match-indices": "1.0.2" + } + }, + "@cucumber/gherkin": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-19.0.3.tgz", + "integrity": "sha512-gWdMm8mfRk3P+VugJWvNALaQV5QnT+5RkqWy3tO+4NsMSQZPo5p4V4vXwriQZ/sZR1Wni5TDRztuRsKLgZ3XHA==", + "requires": { + "@cucumber/message-streams": "^2.0.0", + "@cucumber/messages": "^16.0.1" + }, + "dependencies": { + "@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "requires": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + } + } + }, + "@cucumber/gherkin-streams": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-streams/-/gherkin-streams-2.0.2.tgz", + "integrity": "sha512-cKmXOBz4OwGlrHMBCc4qCC3KzLaqcEZ11nWWskIbv6jyfvlIRuM2OgEF6VLcNVewczifW1p6DrDj0OO+BeXocA==", + "requires": { + "@cucumber/gherkin": "^19.0.1", + "@cucumber/message-streams": "^2.0.0", + "@cucumber/messages": "^16.0.0", + "commander": "7.2.0", + "source-map-support": "0.5.19" + }, + "dependencies": { + "@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "requires": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + } + } + }, + "@cucumber/html-formatter": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-15.0.2.tgz", + "integrity": "sha512-j+YGY4ytj78G/v1gZo53D+vuKXlTg/oxNwSCCGvRQo75+AqYDJSkm/vexXJQ5lY1rXAvlbZ9KI6jhg6LDs0YdQ==", + "requires": { + "@cucumber/messages": "^16.0.1", + "commander": "7.2.0", + "source-map-support": "0.5.19" + }, + "dependencies": { + "@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "requires": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + } + } + }, + "@cucumber/message-streams": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-2.1.0.tgz", + "integrity": "sha512-Yh3mw3qv6QL9NI/ihkZF8V9MX2GbnR6oktv34kC3uAbrQy9d/b2SZ3HNjG3J9JQqpV4B7Om3SPElJYIeo66TrA==", + "requires": { + "@cucumber/messages": "^16.0.1" + }, + "dependencies": { + "@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "requires": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, + "class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==" + } + } + }, + "@cucumber/messages": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.2.tgz", + "integrity": "sha512-vhWkNmQco+7tk/DWqpN0/R9KTNvsKsXVfZ7IsJs+dEeWmTuRztklHq8lJalwMSQBl71+2/KqGHzOO4BMTC9wIQ==", + "peer": true, + "requires": { + "@types/uuid": "8.3.4", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "@cucumber/pretty-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/pretty-formatter/-/pretty-formatter-1.0.0.tgz", + "integrity": "sha512-wcnIMN94HyaHGsfq72dgCvr1d8q6VGH4Y6Gl5weJ2TNZw1qn2UY85Iki4c9VdaLUONYnyYH3+178YB+9RFe/Hw==", + "requires": { + "ansi-styles": "^5.0.0", + "cli-table3": "^0.6.0", + "figures": "^3.2.0", + "ts-dedent": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + } + } + }, + "@cucumber/tag-expressions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-3.0.1.tgz", + "integrity": "sha512-OGCXaJ1BQXmQ5b9pw+JYsBGumK2/LPZiLmbj1o1JFVeSNs2PY8WPQFSyXrskhrHz5Nd/6lYg7lvGMtFHOncC4w==" + }, + "@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@grpc/grpc-js": { + "version": "1.10.9", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.9.tgz", + "integrity": "sha512-5tcgUctCG0qoNyfChZifz2tJqbRbXVO9J7X6duFcOjY3HUNCxg5D0ZCK7EP9vIcZ0zRpLU9bWkyCqVCLZ46IbQ==", + "requires": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + } + }, + "@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "requires": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + } + }, + "@hyperledger/fabric-chaincode-integration": { + "version": "0.8.2", + "resolved": "https://npm.pkg.github.com/download/@hyperledger/fabric-chaincode-integration/0.8.2/b33d26c0a008d38245d21d985dbaaa39b1e6baf2", + "integrity": "sha512-n62DXAajf57RdT+hSsMwhRMHtbtPBUuEWUlp5LqT4h407MgTITGpZyi4QVpea96KyEg4t77LxpSDCg6LSxUQ0g==", + "requires": { + "@cucumber/cucumber": "^7.3.2", + "@cucumber/pretty-formatter": "^1.0.0-alpha.1", + "@grpc/grpc-js": "^1.10.9", + "@hyperledger/fabric-gateway": "^1.0.1", + "@tsconfig/node14": "^1.0.1", + "@types/yargs": "^17.0.9", + "ajv": "^6.11.0", + "assert": "^2.0.0", + "chai": "^4.2.0", + "chalk": "^3.0.0", + "cucumber-tsflow": "^4.0.0-rc.1", + "fast-safe-stringify": "^2.0.7", + "fs-extra": "^8.1.0", + "js-yaml": "^3.13.1", + "lodash": "^4.17.15", + "nano": "^10.1.3", + "ts-node": "^8.6.2", + "typescript": "~4.5.2", + "winston": "^3.2.1", + "yargs": "^17.3.1" + } + }, + "@hyperledger/fabric-gateway": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@hyperledger/fabric-gateway/-/fabric-gateway-1.5.0.tgz", + "integrity": "sha512-36DRkcUrtJA4Ktr9uPYlEfWte79ceFd53oYVvFOLImxkvL0Tm4gqyvR6Pg7J9EgdhXZ9StR7FLYxILtS52RB4Q==", + "requires": { + "@grpc/grpc-js": "^1.10.0", + "@hyperledger/fabric-protos": "^0.3.0", + "asn1.js": "^5.4.0", + "bn.js": "^5.2.0", + "elliptic": "^6.5.0", + "google-protobuf": "^3.21.0", + "pkcs11js": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + } + } + }, + "@hyperledger/fabric-protos": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@hyperledger/fabric-protos/-/fabric-protos-0.3.3.tgz", + "integrity": "sha512-AQbxHGmNj6/D80GZzG7+Nw1WCd4GEfLITk83Yg2yO8zylzKsw5onE5X1+aIsC4jdILY4tSY/Dyy0s1u4cpB9+A==", + "requires": { + "@grpc/grpc-js": "^1.9.0", + "google-protobuf": "^3.21.0" + } + }, + "@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==" + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@types/node": { + "version": "20.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", + "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "peer": true + }, + "@types/yargs": { + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz", + "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, + "assertion-error-formatter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz", + "integrity": "sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==", + "requires": { + "diff": "^4.0.1", + "pad-right": "^0.2.2", + "repeat-string": "^1.6.1" + } + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==" + }, + "class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "peer": true + }, + "cli-table3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "requires": { + "colors": "1.4.0", + "string-width": "^4.2.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + }, + "dependencies": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "cucumber-tsflow": { + "version": "4.0.0-rc.1", + "resolved": "https://registry.npmjs.org/cucumber-tsflow/-/cucumber-tsflow-4.0.0-rc.1.tgz", + "integrity": "sha512-RtZHbc1dO4VU73jWUJgZXmBrlZ9fifaBARZYnd7Ok2zgyApKZo8KH/PY6V63YYJBeaGK9p4Cd8jlMJvSGI/lhg==", + "requires": { + "callsites": "^3.1.0", + "log4js": "^6.3.0", + "source-map-support": "^0.5.19", + "underscore": "^1.8.3" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "date-format": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.11.tgz", + "integrity": "sha512-VS20KRyorrbMCQmpdl2hg5KaOUsda1RbnsJg461FfrcyCUg+pkd0b40BSW4niQyTheww4DBXQnS7HwSrKkipLw==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "^4.0.0" + } + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "requires": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, + "durations": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/durations/-/durations-3.4.2.tgz", + "integrity": "sha512-V/lf7y33dGaypZZetVI1eu7BmvkbC4dItq12OElLRpKuaU5JxQstV2zHwLv8P7cNbQ+KL1WD80zMCTx5dNC4dg==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==" + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "requires": { + "type": "^2.5.0" + }, + "dependencies": { + "type": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==" + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==" + }, + "get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", + "integrity": "sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "knuth-shuffle-seeded": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", + "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", + "requires": { + "seed-random": "~2.2.0" + } + }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "log4js": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.6.0.tgz", + "integrity": "sha512-3v8R7fd45UB6THucSht6wN2/7AZEruQbXdjygPZcxt5TA/msO6si9CN5MefUuKXbYnJHTBnYcx4famwcyQd+sA==", + "requires": { + "date-format": "^4.0.11", + "debug": "^4.3.4", + "flatted": "^3.2.5", + "rfdc": "^1.3.0", + "streamroller": "^3.1.1" + } + }, + "logform": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", + "requires": { + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "requires": { + "get-func-name": "^2.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nano": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/nano/-/nano-10.1.3.tgz", + "integrity": "sha512-q/hKQJJH3FhkkuJ3ojbgDph2StlSXFBPNkpZBZlsvZDbuYfxKJ4VtunEeilthcZtuIplIk1zVX5o2RgKTUTO+Q==", + "requires": { + "axios": "^1.6.2", + "node-abort-controller": "^3.0.1", + "qs": "^6.11.0" + } + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, + "pad-right": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", + "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", + "requires": { + "repeat-string": "^1.5.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==" + }, + "pkcs11js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkcs11js/-/pkcs11js-2.1.0.tgz", + "integrity": "sha512-8i3yzNHoN0DcawytjXqGK4tjvQP/VrcPi1j0Bvhy8PtckBbsETSECyNsBxwY6eAnvGhsJEDyFq42ZPNZR9P/MA==", + "optional": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "protobufjs": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz", + "integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "requires": { + "side-channel": "^1.0.6" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, + "regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "requires": { + "regexp-tree": "^0.1.11" + } + }, + "regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==" + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "resolve-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz", + "integrity": "sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==", + "requires": { + "resolve-from": "^5.0.0" + } + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==" + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "stack-chain": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-2.0.0.tgz", + "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==" + }, + "stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "requires": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==" + } + } + }, + "stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "requires": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "streamroller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.1.tgz", + "integrity": "sha512-iPhtd9unZ6zKdWgMeYGfSBuqCngyJy1B/GPi/lTpwGpa3bajuX30GjUVd0/Tn/Xhg0mr4DOSENozz9Y06qyonQ==", + "requires": { + "date-format": "^4.0.10", + "debug": "^4.3.4", + "fs-extra": "^10.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "requires": { + "rimraf": "^3.0.0" + } + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==" + }, + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==" + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "underscore": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + } + }, + "winston": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.1.tgz", + "integrity": "sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w==", + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + } + }, + "winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "requires": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + } + } +} diff --git a/v2/integrationtest/package.json b/v2/integrationtest/package.json new file mode 100644 index 0000000..beb9aae --- /dev/null +++ b/v2/integrationtest/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "@hyperledger/fabric-chaincode-integration": "^0.8.2", + "@hyperledger/fabric-gateway": "^1.5.0" + }, + "scripts": { + "test": "fabric-chaincode-integration" + } +} diff --git a/v2/internal/contract_function.go b/v2/internal/contract_function.go new file mode 100644 index 0000000..cb64880 --- /dev/null +++ b/v2/internal/contract_function.go @@ -0,0 +1,377 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "errors" + "fmt" + "reflect" + + "github.com/hyperledger/fabric-contract-api-go/v2/internal/types" + metadata "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + "github.com/hyperledger/fabric-contract-api-go/v2/serializer" +) + +type contractFunctionParams struct { + context reflect.Type + fields []reflect.Type +} + +type contractFunctionReturns struct { + success reflect.Type + error bool +} + +// CallType enum for type of call that should be used for method submit vs evaluate +type CallType int + +const ( + // CallTypeNA contract function isnt callabale by invoke/query + CallTypeNA = iota + // CallTypeSubmit contract function should be called by invoke + CallTypeSubmit + // CallTypeEvaluate contract function should be called by query + CallTypeEvaluate +) + +// ContractFunction contains a description of a function so that it can be called by a chaincode +type ContractFunction struct { + function reflect.Value + callType CallType + params contractFunctionParams + returns contractFunctionReturns +} + +// Call calls function in a contract using string args and handles formatting the response into useful types +func (cf ContractFunction) Call(ctx reflect.Value, supplementaryMetadata *metadata.TransactionMetadata, components *metadata.ComponentMetadata, serializer serializer.TransactionSerializer, params ...string) (string, interface{}, error) { + var parameterMetadata []metadata.ParameterMetadata + if supplementaryMetadata != nil { + parameterMetadata = supplementaryMetadata.Parameters + } + + values, err := cf.formatArgs(ctx, parameterMetadata, components, params, serializer) + + if err != nil { + return "", nil, err + } + + someResp := cf.function.Call(values) + + var returnsMetadata *metadata.ReturnMetadata + if supplementaryMetadata != nil { + returnsMetadata = &supplementaryMetadata.Returns + } + + return cf.handleResponse(someResp, returnsMetadata, components, serializer) +} + +// ReflectMetadata returns the metadata for contract function +func (cf ContractFunction) ReflectMetadata(name string, existingComponents *metadata.ComponentMetadata) metadata.TransactionMetadata { + transactionMetadata := metadata.TransactionMetadata{} + transactionMetadata.Name = name + transactionMetadata.Tag = []string{} + + txType := "SUBMIT" + txTypeDeprecated := "submit" + + if cf.callType == CallTypeEvaluate { + txType = "EVALUATE" + txTypeDeprecated = "evaluate" + } + + transactionMetadata.Tag = append(transactionMetadata.Tag, txTypeDeprecated) + transactionMetadata.Tag = append(transactionMetadata.Tag, txType) + + for index, field := range cf.params.fields { + schema, _ := metadata.GetSchema(field, existingComponents) + + param := metadata.ParameterMetadata{} + param.Name = fmt.Sprintf("param%d", index) + param.Schema = schema + + transactionMetadata.Parameters = append(transactionMetadata.Parameters, param) + } + + if cf.returns.success != nil { + schema, _ := metadata.GetSchema(cf.returns.success, existingComponents) + + transactionMetadata.Returns = metadata.ReturnMetadata{Schema: schema} + } + + return transactionMetadata +} + +type formatArgResult struct { + paramName string + converted reflect.Value + err error +} + +func (cf *ContractFunction) formatArgs(ctx reflect.Value, supplementaryMetadata []metadata.ParameterMetadata, components *metadata.ComponentMetadata, params []string, serializer serializer.TransactionSerializer) ([]reflect.Value, error) { + numParams := len(cf.params.fields) + + if supplementaryMetadata != nil { + if len(supplementaryMetadata) != numParams { + return nil, fmt.Errorf("incorrect number of params in supplementary metadata. Expected %d, received %d", numParams, len(supplementaryMetadata)) + } + } + + values := []reflect.Value{} + + if cf.params.context != nil { + values = append(values, ctx) + } + + if len(params) < numParams { + return nil, fmt.Errorf("incorrect number of params. Expected %d, received %d", numParams, len(params)) + } + + channels := []chan formatArgResult{} + + for i := 0; i < numParams; i++ { + + fieldType := cf.params.fields[i] + + var paramMetadata *metadata.ParameterMetadata + + if supplementaryMetadata != nil { + paramMetadata = &supplementaryMetadata[i] + } + + c := make(chan formatArgResult) + go cf.formatArg(params[i], fieldType, paramMetadata, components, serializer, c) + channels = append(channels, c) + } + + for _, channel := range channels { + for res := range channel { + + if res.err != nil { + return nil, fmt.Errorf("error managing parameter%s. %s", res.paramName, res.err.Error()) + } + + values = append(values, res.converted) + } + } + + return values, nil +} + +func (cf *ContractFunction) formatArg(param string, fieldType reflect.Type, parameterMetadata *metadata.ParameterMetadata, components *metadata.ComponentMetadata, serializer serializer.TransactionSerializer, c chan formatArgResult) { + defer close(c) + + converted, err := serializer.FromString(param, fieldType, parameterMetadata, components) + + paramName := "" + + if parameterMetadata != nil { + paramName = " " + parameterMetadata.Name + } + + res := new(formatArgResult) + res.paramName = paramName + res.converted = converted + res.err = err + + c <- *res +} + +func (cf *ContractFunction) handleResponse(response []reflect.Value, returnsMetadata *metadata.ReturnMetadata, components *metadata.ComponentMetadata, serializer serializer.TransactionSerializer) (string, interface{}, error) { + expectedLength := 0 + + returnsSuccess := cf.returns.success != nil + + if returnsSuccess && cf.returns.error { + expectedLength = 2 + } else if returnsSuccess || cf.returns.error { + expectedLength = 1 + } + + if len(response) == expectedLength { + + var successResponse reflect.Value + var errorResponse reflect.Value + + if returnsSuccess && cf.returns.error { + successResponse = response[0] + errorResponse = response[1] + } else if returnsSuccess { + successResponse = response[0] + } else if cf.returns.error { + errorResponse = response[0] + } + + var successString string + var errorError error + var iface interface{} + + if successResponse.IsValid() { + if serializer != nil { + var err error + successString, err = serializer.ToString(successResponse, cf.returns.success, returnsMetadata, components) + + if err != nil { + return "", nil, fmt.Errorf("error handling success response. %s", err.Error()) + } + } + + iface = successResponse.Interface() + } + + if errorResponse.IsValid() && !errorResponse.IsNil() { + errorError = errorResponse.Interface().(error) + } + + return successString, iface, errorError + } + + return "", nil, errors.New("response does not match expected return for given function") +} + +func newContractFunction(fnValue reflect.Value, callType CallType, paramDetails contractFunctionParams, returnDetails contractFunctionReturns) *ContractFunction { + cf := ContractFunction{} + cf.callType = callType + cf.function = fnValue + cf.params = paramDetails + cf.returns = returnDetails + + return &cf +} + +// NewContractFunctionFromFunc creates a new contract function from a given function +func NewContractFunctionFromFunc(fn interface{}, callType CallType, contextHandlerType reflect.Type) (*ContractFunction, error) { + fnType := reflect.TypeOf(fn) + fnValue := reflect.ValueOf(fn) + + if fnType.Kind() != reflect.Func { + return nil, fmt.Errorf("cannot create new contract function from %s. Can only use func", fnType.Kind()) + } + + myMethod := reflect.Method{} + myMethod.Func = fnValue + myMethod.Type = fnType + + paramDetails, returnDetails, err := parseMethod(myMethod, contextHandlerType) + + if err != nil { + return nil, err + } + + return newContractFunction(fnValue, callType, paramDetails, returnDetails), nil +} + +// NewContractFunctionFromReflect creates a new contract function from a reflected method +func NewContractFunctionFromReflect(typeMethod reflect.Method, valueMethod reflect.Value, callType CallType, contextHandlerType reflect.Type) (*ContractFunction, error) { + paramDetails, returnDetails, err := parseMethod(typeMethod, contextHandlerType) + + if err != nil { + return nil, err + } + + return newContractFunction(valueMethod, callType, paramDetails, returnDetails), nil +} + +// Setup + +func parseMethod(typeMethod reflect.Method, contextHandlerType reflect.Type) (contractFunctionParams, contractFunctionReturns, error) { + myContractFnParams, err := methodToContractFunctionParams(typeMethod, contextHandlerType) + + if err != nil { + return contractFunctionParams{}, contractFunctionReturns{}, err + } + + myContractFnReturns, err := methodToContractFunctionReturns(typeMethod) + + if err != nil { + return contractFunctionParams{}, contractFunctionReturns{}, err + } + + return myContractFnParams, myContractFnReturns, nil +} + +func methodToContractFunctionParams(typeMethod reflect.Method, contextHandlerType reflect.Type) (contractFunctionParams, error) { + myContractFnParams := contractFunctionParams{} + + usesCtx := (reflect.Type)(nil) + + numIn := typeMethod.Type.NumIn() + + startIndex := 1 + methodName := typeMethod.Name + + if methodName == "" { + startIndex = 0 + methodName = "Function" + } + + for i := startIndex; i < numIn; i++ { + inType := typeMethod.Type.In(i) + + typeError := typeIsValid(inType, nil, false) + + isCtx := inType == contextHandlerType + + if typeError != nil && !isCtx && i == startIndex && inType.Kind() == reflect.Interface { + invalidInterfaceTypeErr := fmt.Sprintf("%s contains invalid transaction context interface type. Set transaction context for contract does not meet interface used in method.", methodName) + + err := typeMatchesInterface(contextHandlerType, inType) + + if err != nil { + return contractFunctionParams{}, fmt.Errorf("%s %s", invalidInterfaceTypeErr, err.Error()) + } + + isCtx = true + } + + if typeError != nil && !isCtx { + return contractFunctionParams{}, fmt.Errorf("%s contains invalid parameter type. %s", methodName, typeError.Error()) + } else if i != startIndex && isCtx { + return contractFunctionParams{}, fmt.Errorf("functions requiring the TransactionContext must require it as the first parameter. %s takes it in as parameter %d", methodName, i-startIndex) + } else if isCtx { + usesCtx = contextHandlerType + } else { + myContractFnParams.fields = append(myContractFnParams.fields, inType) + } + } + + myContractFnParams.context = usesCtx + return myContractFnParams, nil +} + +func methodToContractFunctionReturns(typeMethod reflect.Method) (contractFunctionReturns, error) { + numOut := typeMethod.Type.NumOut() + + methodName := typeMethod.Name + + if methodName == "" { + methodName = "Function" + } + + if numOut > 2 { + return contractFunctionReturns{}, fmt.Errorf("functions may only return a maximum of two values. %s returns %d", methodName, numOut) + } else if numOut == 1 { + outType := typeMethod.Type.Out(0) + + typeError := typeIsValid(outType, nil, true) + + if typeError != nil { + return contractFunctionReturns{}, fmt.Errorf("%s contains invalid single return type. %s", methodName, typeError.Error()) + } else if outType == types.ErrorType { + return contractFunctionReturns{nil, true}, nil + } + return contractFunctionReturns{outType, false}, nil + } else if numOut == 2 { + firstOut := typeMethod.Type.Out(0) + secondOut := typeMethod.Type.Out(1) + + firstTypeError := typeIsValid(firstOut, nil, true) + if firstTypeError != nil && firstOut != types.ErrorType { + return contractFunctionReturns{}, fmt.Errorf("%s contains invalid first return type. %s", methodName, firstTypeError.Error()) + } else if secondOut.String() != "error" { + return contractFunctionReturns{}, fmt.Errorf("%s contains invalid second return type. Type %s is not valid. Expected error", methodName, secondOut.String()) + } + return contractFunctionReturns{firstOut, true}, nil + } + return contractFunctionReturns{nil, false}, nil +} diff --git a/v2/internal/contract_function_test.go b/v2/internal/contract_function_test.go new file mode 100644 index 0000000..31b8543 --- /dev/null +++ b/v2/internal/contract_function_test.go @@ -0,0 +1,573 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "errors" + "fmt" + "reflect" + "testing" + + "github.com/go-openapi/spec" + metadata "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + "github.com/hyperledger/fabric-contract-api-go/v2/serializer" + "github.com/stretchr/testify/assert" + "github.com/xeipuuv/gojsonschema" +) + +// ================================ +// HELPERS +// ================================ + +type TransactionInterface interface { + SomeFunction(string) string +} + +func (tc *TransactionContext) SomeFunction(param0 string) string { + return "" +} + +type BadTransactionInterface interface { + SomeOtherFunction() string +} + +type simpleStruct struct { + Prop1 string `json:"prop1"` + //lint:ignore U1000 unused + prop2 string +} + +func (ss *simpleStruct) GoodMethod(param1 string, param2 string) string { + return param1 + param2 +} + +func (ss *simpleStruct) GoodTransactionMethod(ctx *TransactionContext, param1 string, param2 string) string { + return param1 + param2 +} + +func (ss *simpleStruct) GoodTransactionInterfaceMethod(ctx TransactionInterface, param1 string, param2 string) string { + return param1 + param2 +} + +func (ss *simpleStruct) GoodReturnMethod(param1 string) (string, error) { + return param1, nil +} + +func (ss *simpleStruct) GoodErrorMethod() error { + return nil +} + +func (ss *simpleStruct) GoodMethodNoReturn(param1 string, param2 string) { + // do nothing +} + +func (ss *simpleStruct) BadMethod(param1 complex64) complex64 { + return param1 +} + +func (ss *simpleStruct) BadMethodGoodTransaction(ctx *TransactionContext, param1 complex64) complex64 { + return param1 +} + +func (ss *simpleStruct) BadTransactionMethod(param1 string, ctx *TransactionContext) string { + return param1 +} + +func (ss *simpleStruct) BadTransactionInterfaceMethod(ctx BadTransactionInterface, param1 string) string { + return param1 +} + +func (ss *simpleStruct) BadReturnMethod(param1 string) (string, string, error) { + return param1, "", nil +} + +func (ss *simpleStruct) BadMethodFirstReturn(param1 complex64) (complex64, error) { + return param1, nil +} + +func (ss *simpleStruct) BadMethodSecondReturn(param1 string) (string, string) { + return param1, param1 +} + +type mockSerializer struct{} + +func (ms *mockSerializer) FromString(string, reflect.Type, *metadata.ParameterMetadata, *metadata.ComponentMetadata) (reflect.Value, error) { + return reflect.ValueOf("HELLO WORLD"), nil +} + +func (ms *mockSerializer) ToString(reflect.Value, reflect.Type, *metadata.ReturnMetadata, *metadata.ComponentMetadata) (string, error) { + return "", errors.New("Serializer error") +} + +func getMethodByName(strct interface{}, methodName string) (reflect.Method, reflect.Value) { + strctType := reflect.TypeOf(strct) + strctVal := reflect.ValueOf(strct) + + for i := 0; i < strctType.NumMethod(); i++ { + if strctType.Method(i).Name == methodName { + return strctType.Method(i), strctVal.Method(i) + } + } + + panic(fmt.Sprintf("Function with name %s does not exist for interface passed", methodName)) +} + +func setContractFunctionReturns(cf *ContractFunction, successReturn reflect.Type, returnsError bool) { + cfr := contractFunctionReturns{} + cfr.success = successReturn + cfr.error = returnsError + + cf.returns = cfr +} + +func testHandleResponse(t *testing.T, successReturn reflect.Type, errorReturn bool, response []reflect.Value, expectedString string, expectedValue interface{}, expectedError error, serializer serializer.TransactionSerializer) { + t.Helper() + + cf := ContractFunction{} + + setContractFunctionReturns(&cf, successReturn, errorReturn) + strResp, valueResp, errResp := cf.handleResponse(response, nil, nil, serializer) + + assert.Equal(t, expectedString, strResp, "should have returned string value from response") + assert.Equal(t, expectedValue, valueResp, "should have returned actual value from response") + assert.Equal(t, expectedError, errResp, "should have returned error value from response") +} + +func createGoJSONSchemaSchema(propName string, schema *spec.Schema, components *metadata.ComponentMetadata) *gojsonschema.Schema { + combined := make(map[string]interface{}) + combined["components"] = components + combined["properties"] = make(map[string]interface{}) + combined["properties"].(map[string]interface{})[propName] = schema + + combinedLoader := gojsonschema.NewGoLoader(combined) + + gjs, _ := gojsonschema.NewSchema(combinedLoader) + + return gjs +} + +// ================================ +// Tests +// ================================ + +func TestHandleResponse(t *testing.T) { + var response []reflect.Value + err := errors.New("some error") + + serializer := new(serializer.JSONSerializer) + + // Should return error when wrong return length + testHandleResponse(t, reflect.TypeOf(""), true, response, "", nil, errors.New("response does not match expected return for given function"), serializer) + + // Should return blank string and nil for error when no return specified + testHandleResponse(t, nil, false, response, "", nil, nil, serializer) + + // Should return specified value for single success return + response = []reflect.Value{reflect.ValueOf(1)} + testHandleResponse(t, reflect.TypeOf(1), false, response, "1", 1, nil, serializer) + + // should return nil for error for single error return type when response is nil + response = []reflect.Value{reflect.ValueOf(nil)} + testHandleResponse(t, nil, true, response, "", nil, nil, serializer) + + // should return value for error for single error return type when response is an error + response = []reflect.Value{reflect.ValueOf(err)} + testHandleResponse(t, nil, true, response, "", nil, err, serializer) + + // should return nil for error and value for success for both success and error return type when response has nil error but success + response = []reflect.Value{reflect.ValueOf(uint(1)), reflect.ValueOf(nil)} + testHandleResponse(t, reflect.TypeOf(uint(1)), true, response, "1", uint(1), nil, serializer) + + // should return value for both error and success when function has both success and error return type and response has an error and success value + response = []reflect.Value{reflect.ValueOf(true), reflect.ValueOf(err)} + testHandleResponse(t, reflect.TypeOf(true), true, response, "true", true, err, serializer) + + // should return error when serializer ToString fails + mockSerializerVal := new(mockSerializer) + _, expectedErr := mockSerializerVal.ToString(reflect.ValueOf("NaN"), reflect.TypeOf(1), nil, nil) + response = []reflect.Value{reflect.ValueOf("NaN")} + testHandleResponse(t, reflect.TypeOf(1), false, response, "", nil, fmt.Errorf("error handling success response. %s", expectedErr.Error()), mockSerializerVal) +} + +func TestFormatArgs(t *testing.T) { + var args []reflect.Value + var err error + + fn := ContractFunction{} + fn.params = contractFunctionParams{} + fn.params.fields = []reflect.Type{reflect.TypeOf(1), reflect.TypeOf(2)} + + supplementaryMetadata := metadata.TransactionMetadata{} + serializer := new(serializer.JSONSerializer) + + ctx := reflect.Value{} + + supplementaryMetadata.Parameters = []metadata.ParameterMetadata{} + args, err = fn.formatArgs(ctx, supplementaryMetadata.Parameters, nil, []string{}, serializer) + assert.EqualError(t, err, "incorrect number of params in supplementary metadata. Expected 2, received 0", "should return error when metadata is incorrect") + assert.Nil(t, args, "should not return values when metadata error occurs") + + args, err = fn.formatArgs(ctx, nil, nil, []string{}, serializer) + assert.EqualError(t, err, "incorrect number of params. Expected 2, received 0", "should return error when number of params is incorrect") + assert.Nil(t, args, "should not return values when param error occurs") + + _, fromStringErr := serializer.FromString("NaN", reflect.TypeOf(1), nil, nil) + args, err = fn.formatArgs(ctx, nil, nil, []string{"1", "NaN"}, serializer) + assert.EqualError(t, err, fmt.Sprintf("error managing parameter. %s", fromStringErr.Error()), "should return error when type of params is incorrect") + assert.Nil(t, args, "should not return values when from string error occurs") + + args, err = fn.formatArgs(ctx, nil, nil, []string{"1", "2"}, serializer) + assert.Nil(t, err, "should not error for valid values") + assert.Equal(t, 1, args[0].Interface(), "should return converted values") + assert.Equal(t, 2, args[1].Interface(), "should return converted values") + + supplementaryMetadata.Parameters = []metadata.ParameterMetadata{ + { + Name: "param1", + Schema: spec.Int64Property(), + CompiledSchema: createGoJSONSchemaSchema("param1", spec.Int64Property(), nil), + }, + { + Name: "param2", + Schema: spec.Int64Property(), + CompiledSchema: createGoJSONSchemaSchema("param1", spec.Int64Property(), nil), + }, + } + args, err = fn.formatArgs(ctx, supplementaryMetadata.Parameters, nil, []string{"1", "2"}, serializer) + assert.Nil(t, err, "should not error for valid values which validates against metadata") + assert.Equal(t, 1, args[0].Interface(), "should return converted values validated against metadata") + assert.Equal(t, 2, args[1].Interface(), "should return converted values validated against metadata") + + fn.params.context = reflect.TypeOf(ctx) + args, err = fn.formatArgs(ctx, nil, nil, []string{"1", "2"}, serializer) + assert.Nil(t, err, "should not error for valid values with context") + assert.Equal(t, ctx, args[0], "should return converted values and context") + assert.Equal(t, 1, args[1].Interface(), "should return converted values and context") + assert.Equal(t, 2, args[2].Interface(), "should return converted values and context") +} + +func TestMethodToContractFunctionParams(t *testing.T) { + var params contractFunctionParams + var err error + var validTypeErr error + + ctx := reflect.TypeOf(new(TransactionContext)) + + badMethod, _ := getMethodByName(new(simpleStruct), "BadMethod") + validTypeErr = typeIsValid(reflect.TypeOf(complex64(1)), []reflect.Type{}, false) + params, err = methodToContractFunctionParams(badMethod, ctx) + assert.EqualError(t, err, fmt.Sprintf("BadMethod contains invalid parameter type. %s", validTypeErr.Error()), "should error when type is valid fails on first param") + assert.Equal(t, params, contractFunctionParams{}, "should return blank params for invalid first param type") + + interfaceType := reflect.TypeOf((*BadTransactionInterface)(nil)).Elem() + badInterfaceMethod, _ := getMethodByName(new(simpleStruct), "BadTransactionInterfaceMethod") + matchesInterfaceErr := typeMatchesInterface(ctx, interfaceType) + params, err = methodToContractFunctionParams(badInterfaceMethod, ctx) + assert.EqualError(t, err, fmt.Sprintf("BadTransactionInterfaceMethod contains invalid transaction context interface type. Set transaction context for contract does not meet interface used in method. %s", matchesInterfaceErr.Error()), "should error when match on interface fails on first param") + assert.Equal(t, params, contractFunctionParams{}, "should return blank params for invalid first param type") + + badCtxMethod, _ := getMethodByName(new(simpleStruct), "BadTransactionMethod") + params, err = methodToContractFunctionParams(badCtxMethod, ctx) + assert.EqualError(t, err, "functions requiring the TransactionContext must require it as the first parameter. BadTransactionMethod takes it in as parameter 1", "should error when ctx in wrong position") + assert.Equal(t, params, contractFunctionParams{}, "should return blank params when context in wrong position") + + badMethodGoodTransaction, _ := getMethodByName(new(simpleStruct), "BadMethodGoodTransaction") + validTypeErr = typeIsValid(reflect.TypeOf(complex64(1)), []reflect.Type{}, false) + params, err = methodToContractFunctionParams(badMethodGoodTransaction, ctx) + assert.EqualError(t, err, fmt.Sprintf("BadMethodGoodTransaction contains invalid parameter type. %s", validTypeErr.Error()), "should error when type is valid fails but first param valid") + assert.Equal(t, params, contractFunctionParams{}, "should return blank params for invalid param type when first param is ctx") + + goodMethod, _ := getMethodByName(new(simpleStruct), "GoodMethod") + params, err = methodToContractFunctionParams(goodMethod, ctx) + assert.Nil(t, err, "should not error for valid function") + assert.Equal(t, params, contractFunctionParams{ + context: nil, + fields: []reflect.Type{ + reflect.TypeOf(""), + reflect.TypeOf(""), + }, + }, "should return params without context when none specified") + + goodTransactionMethod, _ := getMethodByName(new(simpleStruct), "GoodTransactionMethod") + params, err = methodToContractFunctionParams(goodTransactionMethod, ctx) + assert.Nil(t, err, "should not error for valid function") + assert.Equal(t, params, contractFunctionParams{ + context: ctx, + fields: []reflect.Type{ + reflect.TypeOf(""), + reflect.TypeOf(""), + }, + }, "should return params with context when one specified") + + goodTransactionInterfaceMethod, _ := getMethodByName(new(simpleStruct), "GoodTransactionInterfaceMethod") + params, err = methodToContractFunctionParams(goodTransactionInterfaceMethod, ctx) + assert.Nil(t, err, "should not error for valid function") + assert.Equal(t, params, contractFunctionParams{ + context: ctx, + fields: []reflect.Type{ + reflect.TypeOf(""), + reflect.TypeOf(""), + }, + }, "should return params with context when one specified") + + method := new(simpleStruct).GoodMethod + funcMethod := reflect.Method{} + funcMethod.Func = reflect.ValueOf(method) + funcMethod.Type = reflect.TypeOf(method) + params, err = methodToContractFunctionParams(funcMethod, ctx) + assert.Nil(t, err, "should not error for valid function") + assert.Equal(t, params, contractFunctionParams{ + context: nil, + fields: []reflect.Type{ + reflect.TypeOf(""), + reflect.TypeOf(""), + }, + }, "should return params without context when none specified for method from function") +} + +func TestMethodToContractFunctionReturns(t *testing.T) { + var returns contractFunctionReturns + var err error + var invalidTypeError error + + badReturnMethod, _ := getMethodByName(new(simpleStruct), "BadReturnMethod") + returns, err = methodToContractFunctionReturns(badReturnMethod) + assert.EqualError(t, err, "functions may only return a maximum of two values. BadReturnMethod returns 3", "should error when more than two return values") + assert.Equal(t, returns, contractFunctionReturns{}, "should return nothing for returns when errors for bad return length") + + badMethod, _ := getMethodByName(new(simpleStruct), "BadMethod") + invalidTypeError = typeIsValid(reflect.TypeOf(complex64(1)), []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}, true) + returns, err = methodToContractFunctionReturns(badMethod) + assert.EqualError(t, err, fmt.Sprintf("BadMethod contains invalid single return type. %s", invalidTypeError.Error()), "should error when bad return type on single return") + assert.Equal(t, returns, contractFunctionReturns{}, "should return nothing for returns when errors for single return type") + + badMethodFirstReturn, _ := getMethodByName(new(simpleStruct), "BadMethodFirstReturn") + invalidTypeError = typeIsValid(reflect.TypeOf(complex64(1)), []reflect.Type{}, true) + returns, err = methodToContractFunctionReturns(badMethodFirstReturn) + assert.EqualError(t, err, fmt.Sprintf("BadMethodFirstReturn contains invalid first return type. %s", invalidTypeError.Error()), "should error when bad return type on first return") + assert.Equal(t, returns, contractFunctionReturns{}, "should return nothing for returns when errors for first return type") + + badMethodSecondReturn, _ := getMethodByName(new(simpleStruct), "BadMethodSecondReturn") + returns, err = methodToContractFunctionReturns(badMethodSecondReturn) + assert.EqualError(t, err, "BadMethodSecondReturn contains invalid second return type. Type string is not valid. Expected error", "should error when bad return type on second return") + assert.Equal(t, returns, contractFunctionReturns{}, "should return nothing for returns when errors for second return type") + + goodMethodNoReturn, _ := getMethodByName(new(simpleStruct), "GoodMethodNoReturn") + returns, err = methodToContractFunctionReturns(goodMethodNoReturn) + assert.Nil(t, err, "should not error when no return specified") + assert.Equal(t, returns, contractFunctionReturns{nil, false}, "should return contractFunctionReturns for no return types") + + goodMethod, _ := getMethodByName(new(simpleStruct), "GoodMethod") + returns, err = methodToContractFunctionReturns(goodMethod) + assert.Nil(t, err, "should not error when single non error return type specified") + assert.Equal(t, returns, contractFunctionReturns{reflect.TypeOf(""), false}, "should return contractFunctionReturns for single error return types") + + goodErrorMethod, _ := getMethodByName(new(simpleStruct), "GoodErrorMethod") + returns, err = methodToContractFunctionReturns(goodErrorMethod) + assert.Nil(t, err, "should not error when single error return type specified") + assert.Equal(t, returns, contractFunctionReturns{nil, true}, "should return contractFunctionReturns for single error return types") + + goodReturnMethod, _ := getMethodByName(new(simpleStruct), "GoodReturnMethod") + returns, err = methodToContractFunctionReturns(goodReturnMethod) + assert.Nil(t, err, "should not error when good double return type specified") + assert.Equal(t, returns, contractFunctionReturns{reflect.TypeOf(""), true}, "should return contractFunctionReturns for double return types") + + method := new(simpleStruct).GoodReturnMethod + funcMethod := reflect.Method{} + funcMethod.Func = reflect.ValueOf(method) + funcMethod.Type = reflect.TypeOf(method) + returns, err = methodToContractFunctionReturns(funcMethod) + assert.Nil(t, err, "should not error when good double return type specified when method got from function") + assert.Equal(t, returns, contractFunctionReturns{reflect.TypeOf(""), true}, "should return contractFunctionReturns for double return types when method got from function") +} + +func TestParseMethod(t *testing.T) { + var params contractFunctionParams + var returns contractFunctionReturns + var err error + + ctx := reflect.TypeOf(new(TransactionContext)) + + badMethod, _ := getMethodByName(new(simpleStruct), "BadMethod") + _, paramErr := methodToContractFunctionParams(badMethod, ctx) + params, returns, err = parseMethod(badMethod, ctx) + assert.EqualError(t, err, paramErr.Error(), "should return an error when get params errors") + assert.Equal(t, contractFunctionParams{}, params, "should return no param detail when get params errors") + assert.Equal(t, contractFunctionReturns{}, returns, "should return no return detail when get params errors") + + badReturnMethod, _ := getMethodByName(new(simpleStruct), "BadReturnMethod") + _, returnErr := methodToContractFunctionReturns(badReturnMethod) + params, returns, err = parseMethod(badReturnMethod, ctx) + assert.EqualError(t, err, returnErr.Error(), "should return an error when get returns errors") + assert.Equal(t, contractFunctionParams{}, params, "should return no param detail when get returns errors") + assert.Equal(t, contractFunctionReturns{}, returns, "should return no return detail when get returns errors") + + goodMethod, _ := getMethodByName(new(simpleStruct), "GoodMethod") + expectedParam, _ := methodToContractFunctionParams(goodMethod, ctx) + expectedReturn, _ := methodToContractFunctionReturns(goodMethod) + params, returns, err = parseMethod(goodMethod, ctx) + assert.Nil(t, err, "should not error for valid function") + assert.Equal(t, expectedParam, params, "should return params for valid function") + assert.Equal(t, expectedReturn, returns, "should return returns for valid function") +} + +func TestNewContractFunction(t *testing.T) { + method := new(simpleStruct).GoodMethod + fnValue := reflect.ValueOf(method) + + params := contractFunctionParams{ + nil, + []reflect.Type{reflect.TypeOf("")}, + } + + returns := contractFunctionReturns{ + reflect.TypeOf(""), + true, + } + + expectedCf := &ContractFunction{fnValue, CallTypeEvaluate, params, returns} + + cf := newContractFunction(fnValue, CallTypeEvaluate, params, returns) + + assert.Equal(t, cf, expectedCf, "should create contract function from passed in components") +} + +func TestNewContractFunctionFromFunc(t *testing.T) { + var cf *ContractFunction + var err error + var method interface{} + var funcMethod reflect.Method + + ctx := reflect.TypeOf(new(TransactionContext)) + + cf, err = NewContractFunctionFromFunc("", CallTypeSubmit, ctx) + assert.EqualError(t, err, "cannot create new contract function from string. Can only use func", "should return error if interface passed not a func") + assert.Nil(t, cf, "should not return contract function if interface passed not a func") + + method = new(simpleStruct).BadMethod + funcMethod = reflect.Method{} + funcMethod.Func = reflect.ValueOf(method) + funcMethod.Type = reflect.TypeOf(method) + _, _, parseErr := parseMethod(funcMethod, ctx) + cf, err = NewContractFunctionFromFunc(method, CallTypeSubmit, ctx) + assert.EqualError(t, err, parseErr.Error(), "should return error from failed parsing") + assert.Nil(t, cf, "should not return contract function if parse fails") + + method = new(simpleStruct).GoodMethod + funcMethod = reflect.Method{} + funcMethod.Func = reflect.ValueOf(method) + funcMethod.Type = reflect.TypeOf(method) + params, returns, _ := parseMethod(funcMethod, ctx) + expectedCf := newContractFunction(reflect.ValueOf(method), CallTypeSubmit, params, returns) + cf, err = NewContractFunctionFromFunc(method, CallTypeSubmit, ctx) + assert.Nil(t, err, "should not error when parse successful from func") + assert.Equal(t, expectedCf, cf, "should return contract function for good method from func") +} + +func TestNewContractFunctionFromReflect(t *testing.T) { + var cf *ContractFunction + var err error + + ctx := reflect.TypeOf(new(TransactionContext)) + + badMethod, badMethodValue := getMethodByName(new(simpleStruct), "BadMethod") + _, _, parseErr := parseMethod(badMethod, ctx) + cf, err = NewContractFunctionFromReflect(badMethod, badMethodValue, CallTypeEvaluate, ctx) + assert.EqualError(t, err, parseErr.Error(), "should return parse error on parsing failure") + assert.Nil(t, cf, "should not return contract function on error") + + goodMethod, goodMethodValue := getMethodByName(new(simpleStruct), "GoodMethod") + params, returns, _ := parseMethod(goodMethod, ctx) + expectedCf := newContractFunction(goodMethodValue, CallTypeEvaluate, params, returns) + cf, err = NewContractFunctionFromReflect(goodMethod, goodMethodValue, CallTypeEvaluate, ctx) + assert.Nil(t, err, "should not error when parse successful from reflect") + assert.Equal(t, expectedCf, cf, "should return contract function for good method from reflect") +} + +func TestReflectMetadata(t *testing.T) { + var txMetadata metadata.TransactionMetadata + + testCf := ContractFunction{ + params: contractFunctionParams{ + nil, + []reflect.Type{reflect.TypeOf(""), reflect.TypeOf(true)}, + }, + returns: contractFunctionReturns{ + success: reflect.TypeOf(1), + }, + } + + txMetadata = testCf.ReflectMetadata("some tx", nil) + expectedMetadata := metadata.TransactionMetadata{ + Parameters: []metadata.ParameterMetadata{ + {Name: "param0", Schema: spec.StringProperty()}, + {Name: "param1", Schema: spec.BoolProperty()}, + }, + Returns: metadata.ReturnMetadata{Schema: spec.Int64Property()}, + Tag: []string{"submit", "SUBMIT"}, + Name: "some tx", + } + assert.Equal(t, expectedMetadata, txMetadata, "should return metadata for submit transaction") + + testCf.callType = CallTypeEvaluate + txMetadata = testCf.ReflectMetadata("some tx", nil) + expectedMetadata.Tag = []string{"evaluate", "EVALUATE"} + assert.Equal(t, expectedMetadata, txMetadata, "should return metadata for evaluate transaction") +} + +func TestCall(t *testing.T) { + var expectedStr string + var expectedIface interface{} + var expectedErr error + var actualStr string + var actualIface interface{} + var actualErr error + + ctx := reflect.ValueOf(TransactionContext{}) + + testCf := ContractFunction{ + function: reflect.ValueOf(new(simpleStruct).GoodMethod), + params: contractFunctionParams{ + nil, + []reflect.Type{reflect.TypeOf(""), reflect.TypeOf("")}, + }, + returns: contractFunctionReturns{ + success: reflect.TypeOf(""), + }, + } + + serializer := new(serializer.JSONSerializer) + + actualStr, actualIface, actualErr = testCf.Call(ctx, nil, nil, serializer, "some data") + _, expectedErr = testCf.formatArgs(ctx, nil, nil, []string{"some data"}, serializer) + assert.EqualError(t, actualErr, expectedErr.Error(), "should error when formatting args fails") + assert.Nil(t, actualIface, "should not return an interface when format args fails") + assert.Equal(t, "", actualStr, "should return empty string when format args fails") + + expectedStr, expectedIface, expectedErr = testCf.handleResponse([]reflect.Value{reflect.ValueOf("helloworld")}, nil, nil, serializer) + actualStr, actualIface, actualErr = testCf.Call(ctx, nil, nil, serializer, "hello", "world") + assert.Equal(t, actualErr, expectedErr, "should return same error as handle response for good function") + assert.Equal(t, expectedStr, actualStr, "should return same string as handle response for good function and params") + assert.Equal(t, expectedIface, actualIface, "should return same interface as handle response for good function and params") + + combined := make(map[string]interface{}) + combined["components"] = nil + combined["properties"] = make(map[string]interface{}) + combined["properties"].(map[string]interface{})["param0"] = spec.StringProperty() + combined["properties"].(map[string]interface{})["param1"] = spec.StringProperty() + combined["properties"].(map[string]interface{})["returns"] = spec.StringProperty() + combinedLoader := gojsonschema.NewGoLoader(combined) + compiled, _ := gojsonschema.NewSchema(combinedLoader) + schema := metadata.TransactionMetadata{} + schema.Parameters = []metadata.ParameterMetadata{ + {Name: "param0", Schema: spec.StringProperty(), CompiledSchema: compiled}, + {Name: "param1", Schema: spec.StringProperty(), CompiledSchema: compiled}, + } + schema.Returns = metadata.ReturnMetadata{Schema: spec.StringProperty(), CompiledSchema: compiled} + expectedStr, expectedIface, expectedErr = testCf.handleResponse([]reflect.Value{reflect.ValueOf("helloworld")}, &schema.Returns, nil, serializer) + actualStr, actualIface, actualErr = testCf.Call(ctx, &schema, nil, serializer, "hello", "world") + assert.Equal(t, actualErr, expectedErr, "should return same error as handle response for good function with schema") + assert.Equal(t, expectedStr, actualStr, "should return same string as handle response for good function and params with schema") + assert.Equal(t, expectedIface, actualIface, "should return same interface as handle response for good function and params with schema") +} diff --git a/v2/internal/functionaltests/contracts/complexcontract/basicobject.go b/v2/internal/functionaltests/contracts/complexcontract/basicobject.go new file mode 100644 index 0000000..917c3f2 --- /dev/null +++ b/v2/internal/functionaltests/contracts/complexcontract/basicobject.go @@ -0,0 +1,29 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package complexcontract + +// BasicOwner details about an owner +type BasicOwner struct { + Name string `json:"name"` + Contact string `json:"contact"` +} + +// BasicObject a basic object +type BasicObject struct { + ID string `json:"id"` + Owner BasicOwner `json:"owner"` + Value uint `json:"value"` + Condition int `json:"condition"` + Colours []string `json:"colours"` +} + +// SetConditionNew set the condition of the object to mark as new +func (ba *BasicObject) SetConditionNew() { + ba.Condition = 0 +} + +// SetConditionUsed set the condition of the object to mark as used +func (ba *BasicObject) SetConditionUsed() { + ba.Condition = 1 +} diff --git a/v2/internal/functionaltests/contracts/complexcontract/complexcontract.go b/v2/internal/functionaltests/contracts/complexcontract/complexcontract.go new file mode 100644 index 0000000..0230da7 --- /dev/null +++ b/v2/internal/functionaltests/contracts/complexcontract/complexcontract.go @@ -0,0 +1,147 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package complexcontract + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/functionaltests/contracts/utils" +) + +// ComplexContract contract for handling the business logic of a basic object +type ComplexContract struct { + contractapi.Contract +} + +// NewObject adds a new basic object to the world state using id as key +func (c *ComplexContract) NewObject(ctx utils.CustomTransactionContextInterface, id string, owner BasicOwner, value uint, colours []string) error { + existing := ctx.GetCallData() + + if existing != nil { + return fmt.Errorf("cannot create new object in world state as key %s already exists", id) + } + + ba := BasicObject{} + ba.ID = id + ba.Owner = owner + ba.Value = value + ba.Colours = colours + ba.SetConditionNew() + + baBytes, _ := json.Marshal(ba) + + err := ctx.GetStub().PutState(id, []byte(baBytes)) + + if err != nil { + return errors.New("unable to interact with world state") + } + + return nil +} + +// UpdateOwner changes the ownership of a basic object and mark it as used +func (c *ComplexContract) UpdateOwner(ctx utils.CustomTransactionContextInterface, id string, newOwner BasicOwner) error { + existing := ctx.GetCallData() + + if existing == nil { + return fmt.Errorf("cannot update object in world state as key %s does not exist", id) + } + + ba := BasicObject{} + + err := json.Unmarshal(existing, &ba) + + if err != nil { + return fmt.Errorf("data retrieved from world state for key %s was not of type BasicObject", id) + } + + ba.Owner = newOwner + ba.SetConditionUsed() + + baBytes, _ := json.Marshal(ba) + + err = ctx.GetStub().PutState(id, []byte(baBytes)) + + if err != nil { + return errors.New("unable to interact with world state") + } + + return nil +} + +// UpdateValue changes the value of a basic object to add the value passed +func (c *ComplexContract) UpdateValue(ctx utils.CustomTransactionContextInterface, id string, valueAdd int) error { + existing := ctx.GetCallData() + + if existing == nil { + return fmt.Errorf("cannot update object in world state as key %s does not exist", id) + } + + ba := BasicObject{} + + err := json.Unmarshal(existing, &ba) + + if err != nil { + return fmt.Errorf("data retrieved from world state for key %s was not of type BasicObject", id) + } + + newValue := int(ba.Value) + valueAdd + + if newValue < 0 { + newValue = 0 + } + + ba.Value = uint(newValue) + + baBytes, _ := json.Marshal(ba) + + err = ctx.GetStub().PutState(id, []byte(baBytes)) + + if err != nil { + return errors.New("unable to interact with world state") + } + + return nil +} + +// GetObject returns the object with id given from the world state +func (c *ComplexContract) GetObject(ctx utils.CustomTransactionContextInterface, id string) (*BasicObject, error) { + existing := ctx.GetCallData() + + if existing == nil { + return nil, fmt.Errorf("cannot read world state pair with key %s. Does not exist", id) + } + + ba := new(BasicObject) + + err := json.Unmarshal(existing, ba) + + if err != nil { + return nil, fmt.Errorf("data retrieved from world state for key %s was not of type BasicObject", id) + } + + return ba, nil +} + +// GetValue returns the value from the object with id given from the world state +func (c *ComplexContract) GetValue(ctx utils.CustomTransactionContextInterface, id string) (uint, error) { + existing := ctx.GetCallData() + + if existing == nil { + return 0, fmt.Errorf("cannot read world state pair with key %s. Does not exist", id) + } + + ba := new(BasicObject) + + err := json.Unmarshal(existing, ba) + + if err != nil { + return 0, fmt.Errorf("data retrieved from world state for key %s was not of type BasicObject", id) + } + + return ba.Value, nil +} diff --git a/v2/internal/functionaltests/contracts/complexcontract/contract-metadata/metadata.json b/v2/internal/functionaltests/contracts/complexcontract/contract-metadata/metadata.json new file mode 100644 index 0000000..b5a9e06 --- /dev/null +++ b/v2/internal/functionaltests/contracts/complexcontract/contract-metadata/metadata.json @@ -0,0 +1,156 @@ +{ + "contracts": { + "ComplexContract": { + "info": { + "title": "ComplexContract", + "version": "latest" + }, + "name": "ComplexContract", + "transactions": [ + { + "parameters": [ + { + "name": "param0", + "schema": { + "type": "string" + } + } + ], + "returns": { + "$ref": "#/components/schemas/BasicObject" + }, + "tag": [ + "evaluate", + "EVALUATE" + ], + "name": "GetObject" + }, + { + "parameters": [ + { + "name": "param0", + "schema": { + "type": "string" + } + } + ], + "returns": { + "type": "number", + "format": "double", + "maximum": 10, + "minimum": 0, + "multipleOf": 1 + }, + "tag": [ + "evaluate", + "EVALUATE" + ], + "name": "GetValue" + }, + { + "parameters": [ + { + "name": "param0", + "schema": { + "type": "string", + "pattern": "^OBJECT_\\d$" + } + }, + { + "name": "param1", + "schema": { + "$ref": "#/components/schemas/BasicOwner" + } + }, + { + "name": "param2", + "schema": { + "type": "number", + "format": "double", + "maximum": 18446744073709552000, + "minimum": 0, + "multipleOf": 1 + } + }, + { + "name": "param3", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "tag": [ + "submit", + "SUBMIT" + ], + "name": "NewObject" + }, + { + "parameters": [ + { + "name": "param0", + "schema": { + "type": "string" + } + }, + { + "name": "param1", + "schema": { + "$ref": "#/components/schemas/BasicOwner" + } + } + ], + "tag": [ + "submit", + "SUBMIT" + ], + "name": "UpdateOwner" + }, + { + "parameters": [ + { + "name": "param0", + "schema": { + "type": "string" + } + }, + { + "name": "param1", + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "tag": [ + "submit", + "SUBMIT" + ], + "name": "UpdateValue" + } + ] + }, + "org.hyperledger.fabric": { + "info": { + "title": "org.hyperledger.fabric", + "version": "latest" + }, + "name": "org.hyperledger.fabric", + "transactions": [ + { + "returns": { + "type": "string" + }, + "tag": [ + "evaluate", + "EVALUATE" + ], + "name": "GetMetadata" + } + ] + } + } +} \ No newline at end of file diff --git a/v2/internal/functionaltests/contracts/extendedsimplecontract/extendedsimplecontract.go b/v2/internal/functionaltests/contracts/extendedsimplecontract/extendedsimplecontract.go new file mode 100644 index 0000000..6d53b60 --- /dev/null +++ b/v2/internal/functionaltests/contracts/extendedsimplecontract/extendedsimplecontract.go @@ -0,0 +1,62 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package extendedsimplecontract + +import ( + "errors" + "fmt" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/functionaltests/contracts/utils" +) + +// ExtendedSimpleContract contract for handling writing and reading from the world state +type ExtendedSimpleContract struct { + contractapi.Contract +} + +// Create adds a new key with value to the world state +func (esc *ExtendedSimpleContract) Create(ctx utils.CustomTransactionContextInterface, key string) error { + existing := ctx.GetCallData() + + if existing != nil { + return fmt.Errorf("cannot create world state pair with key %s. Already exists", key) + } + + err := ctx.GetStub().PutState(key, []byte("Initialised")) + + if err != nil { + return errors.New("unable to interact with world state") + } + + return nil +} + +// Update changes the value with key in the world state +func (esc *ExtendedSimpleContract) Update(ctx utils.CustomTransactionContextInterface, key string, value string) error { + existing := ctx.GetCallData() + + if existing == nil { + return fmt.Errorf("cannot update world state pair with key %s. Does not exist", key) + } + + err := ctx.GetStub().PutState(key, []byte(value)) + + if err != nil { + return errors.New("unable to interact with world state") + } + + return nil +} + +// Read returns the value at key in the world state +func (esc *ExtendedSimpleContract) Read(ctx utils.CustomTransactionContextInterface, key string) (string, error) { + existing := ctx.GetCallData() + + if existing == nil { + return "", fmt.Errorf("cannot read world state pair with key %s. Does not exist", key) + } + + return string(existing), nil +} diff --git a/v2/internal/functionaltests/contracts/simplecontract/simplecontract.go b/v2/internal/functionaltests/contracts/simplecontract/simplecontract.go new file mode 100644 index 0000000..f702241 --- /dev/null +++ b/v2/internal/functionaltests/contracts/simplecontract/simplecontract.go @@ -0,0 +1,73 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package simplecontract + +import ( + "errors" + "fmt" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" +) + +// SimpleContract with biz logic +type SimpleContract struct { + contractapi.Contract +} + +// Create - Initialises a key value pair with the given ID in the world state +func (sc *SimpleContract) Create(ctx contractapi.TransactionContextInterface, key string) error { + existing, err := ctx.GetStub().GetState(key) + + if err != nil { + return errors.New("unable to interact with world state") + } + + if existing != nil { + return fmt.Errorf("cannot create key. Key with id %s already exists", key) + } + + err = ctx.GetStub().PutState(key, []byte("Initialised")) + + if err != nil { + return errors.New("unable to interact with world state") + } + + return nil +} + +// Update - Updates a key with given ID in the world state +func (sc *SimpleContract) Update(ctx contractapi.TransactionContextInterface, key string, value string) error { + existing, err := ctx.GetStub().GetState(key) + + if err != nil { + return errors.New("unable to interact with world state") + } + + if existing == nil { + return fmt.Errorf("cannot update key. Key with id %s does not exist", key) + } + + err = ctx.GetStub().PutState(key, []byte(value)) + + if err != nil { + return errors.New("unable to interact with world state") + } + + return nil +} + +// Read - Returns value of a key with given ID from world state as string +func (sc *SimpleContract) Read(ctx contractapi.TransactionContextInterface, key string) (string, error) { + existing, err := ctx.GetStub().GetState(key) + + if err != nil { + return "", errors.New("unable to interact with world state") + } + + if existing == nil { + return "", fmt.Errorf("cannot read key. Key with id %s does not exist", key) + } + + return string(existing), nil +} diff --git a/v2/internal/functionaltests/contracts/utils/utils.go b/v2/internal/functionaltests/contracts/utils/utils.go new file mode 100644 index 0000000..e1fe2e0 --- /dev/null +++ b/v2/internal/functionaltests/contracts/utils/utils.go @@ -0,0 +1,65 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "errors" + "fmt" + "strings" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" +) + +// CustomTransactionContextInterface adds extra methods to basic context +// interface to give access to callData +type CustomTransactionContextInterface interface { + contractapi.TransactionContextInterface + + SetCallData([]byte) + GetCallData() []byte +} + +// CustomTransactionContext adds extra field to contractapi.TransactionContext +// so that data can be between calls +type CustomTransactionContext struct { + contractapi.TransactionContext + callData []byte +} + +// SetCallData sets the call data property +func (ctx *CustomTransactionContext) SetCallData(bytes []byte) { + ctx.callData = bytes +} + +// GetCallData gets the call data property +func (ctx *CustomTransactionContext) GetCallData() []byte { + return ctx.callData +} + +// GetWorldState takes a key and sets what is found in the world state for that +// key in the transaction context +func GetWorldState(ctx CustomTransactionContextInterface) error { + _, params := ctx.GetStub().GetFunctionAndParameters() + + if len(params) < 1 { + return errors.New("missing key for world state") + } + + existing, err := ctx.GetStub().GetState(params[0]) + + if err != nil { + return errors.New("unable to interact with world state") + } + + ctx.SetCallData(existing) + + return nil +} + +// UnknownTransactionHandler logs details of a bad transaction request +// and returns a shim error +func UnknownTransactionHandler(ctx CustomTransactionContextInterface) error { + fcn, args := ctx.GetStub().GetFunctionAndParameters() + return fmt.Errorf("invalid function %s passed with args [%s]", fcn, strings.Join(args, ", ")) +} diff --git a/v2/internal/functionaltests/features/badpaths/errors.feature b/v2/internal/functionaltests/features/badpaths/errors.feature new file mode 100644 index 0000000..7e1ffac --- /dev/null +++ b/v2/internal/functionaltests/features/badpaths/errors.feature @@ -0,0 +1,70 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +@errors +@badpaths +Feature: Error paths + + Check how errors are handled by api + + Scenario: User calls unknown function when contract uses unknown transaction handler + Given I have created chaincode from "SimpleContract" + And I have initialised the chaincode + When I submit the "FakeFunction" transaction + | Some | Args | + Then I should receive an unsuccessful response "Function FakeFunction not found in contract SimpleContract" + + Scenario: User calls unknown function when contract has set an unknown transaction handler + Given I have created chaincode from "ExtendedSimpleContract" + And I should be able to initialise the chaincode + When I submit the "FakeFunction" transaction + | Some | Args | + Then I should receive an unsuccessful response "invalid function FakeFunction passed with args [Some, Args]" + + Scenario: Contract function returns error + Given I have created chaincode from "SimpleContract" + And I should be able to initialise the chaincode + When I submit the "Read" transaction + | MISSING_KEY | + Then I should receive an unsuccessful response "cannot read key. Key with id MISSING_KEY does not exist" + + Scenario: User sends bad basic data type + Given I have created chaincode from "ComplexContract" + And I should be able to initialise the chaincode + When I submit the "NewObject" transaction + | OBJECT_1 | {"name": "Andy", "contact": "Leave well alone"} | -10 | ["red", "white", "blue"] | + Then I should receive an unsuccessful response "error managing parameter param2. conversion error. cannot convert passed value -10 to uint" + + Scenario: Users sends bad object data type + Given I have created chaincode from "ComplexContract" + And I should be able to initialise the chaincode + When I submit the "NewObject" transaction + | OBJECT_1 | {"firstname": "Andy", "contact": "Leave well alone"} | 1000 | ["red", "white", "blue"] | + Then I should receive an unsuccessful response "error managing parameter param1. value did not match schema:\n1. param1: Additional property firstname is not allowed\n2. param1: name is required" + + Scenario: User sends data that does not match custom metadata + Given I am using metadata file "contracts/complexcontract/contract-metadata/metadata.json" + And I have created chaincode from "ComplexContract" + When I submit the "NewObject" transaction + | OBJECT_A | {"name": "Andy", "contact": "Leave well alone"} | 1000 | ["red", "white", "blue"] | + Then I should receive an unsuccessful response "error managing parameter param0. value did not match schema:\n1. param0: Does not match pattern '^OBJECT_\d$'" + + Scenario: Contract returns data that does not match custom metadata + Given I am using metadata file "contracts/complexcontract/contract-metadata/metadata.json" + And I have created chaincode from "ComplexContract" + And I submit the "NewObject" transaction + | OBJECT_1 | {"name": "Andy", "contact": "Leave well alone"} | 1000 | ["red", "white", "blue"] | + And I receive a successful response + When I submit the "GetValue" transaction + | OBJECT_1 | + Then I should receive an unsuccessful response "error handling success response. value did not match schema:\n1. return: Must be less than or equal to 10" + + Scenario: User configures bad metadata file + Given I am using metadata file "utils/bad_metadata.json" + Then I fail to create chaincode from "SimpleContract" + + Scenario: User sends invalid namespace to multi contract + Given I have created chaincode from multiple contracts + | SimpleContract | ComplexContract | + When I submit the "FakeContract:NewObject" transaction + | SomeValue | + Then I should receive an unsuccessful response "Contract not found with name FakeContract" \ No newline at end of file diff --git a/v2/internal/functionaltests/features/goldenpaths/complexchaincode.feature b/v2/internal/functionaltests/features/goldenpaths/complexchaincode.feature new file mode 100644 index 0000000..24b732f --- /dev/null +++ b/v2/internal/functionaltests/features/goldenpaths/complexchaincode.feature @@ -0,0 +1,35 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +@goldenpaths +@complex +Feature: Complex Chaincode Golden Path + + Golden path of a chaincode which handles putting and getting an object + + Background: Initialise + Given I have created chaincode from "ComplexContract" + Then I should be able to initialise the chaincode + When I submit the "NewObject" transaction + | OBJECT_1 | {"name": "Andy", "contact": "Leave well alone"} | 1000 | ["red", "white", "blue"] | + Then I should receive a successful response + + Scenario: Read new complex object + When I submit the "GetObject" transaction + | OBJECT_1 | + Then I should receive a successful response '{"id":"OBJECT_1","owner":{"name":"Andy","contact":"Leave well alone"},"value":1000,"condition":0,"colours":["red","white","blue"]}' + + Scenario: Update complex object owner + When I submit the "UpdateOwner" transaction + | OBJECT_1 | {"name": "Liam", "contact": "Bug whenever"} | + Then I should receive a successful response + When I submit the "GetObject" transaction + | OBJECT_1 | + Then I should receive a successful response '{"id":"OBJECT_1","owner":{"name":"Liam","contact":"Bug whenever"},"value":1000,"condition":1,"colours":["red","white","blue"]}' + + Scenario: Update complex object value + When I submit the "UpdateValue" transaction + | OBJECT_1 | -50 | + Then I should receive a successful response + When I submit the "GetObject" transaction + | OBJECT_1 | + Then I should receive a successful response '{"id":"OBJECT_1","owner":{"name":"Andy","contact":"Leave well alone"},"value":950,"condition":0,"colours":["red","white","blue"]}' diff --git a/v2/internal/functionaltests/features/goldenpaths/extendedsimplechaincode.feature b/v2/internal/functionaltests/features/goldenpaths/extendedsimplechaincode.feature new file mode 100644 index 0000000..f5c2d58 --- /dev/null +++ b/v2/internal/functionaltests/features/goldenpaths/extendedsimplechaincode.feature @@ -0,0 +1,34 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +@goldenpaths +@extended +Feature: Extended Simple Chancode Golden Path + + Golden path of a very basic put and get chaincode which uses a before transaction + + Background: Initialise + Given I have created chaincode from "ExtendedSimpleContract" + Then I should be able to initialise the chaincode + + Scenario: Create key value pair + When I submit the "Create" transaction + | KEY_1 | + Then I should receive a successful response + + Scenario: Read key value pair + Given I submit the "Create" transaction + | KEY_1 | + When I submit the "Read" transaction + | KEY_1 | + Then I should receive a successful response "Initialised" + + Scenario: Update key value pair + Given I submit the "Create" transaction + | KEY_1 | + When I submit the "Update" transaction + | KEY_1 | Updated | + Then I should receive a successful response + When I submit the "Read" transaction + | KEY_1 | + Then I should receive a successful response "Updated" + diff --git a/v2/internal/functionaltests/features/goldenpaths/multicontractchaincode.feature b/v2/internal/functionaltests/features/goldenpaths/multicontractchaincode.feature new file mode 100644 index 0000000..89384be --- /dev/null +++ b/v2/internal/functionaltests/features/goldenpaths/multicontractchaincode.feature @@ -0,0 +1,26 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +@goldenpaths +@multi +Feature: Multiple Contract Chaincode Golden Path + + Golden path of chaincode made up of multiple contracts + + Background: Initialise + Given I have created chaincode from multiple contracts + | SimpleContract | ComplexContract | + Then I should be able to initialise the chaincode + + Scenario: Talk to simple contract + When I submit the "Create" transaction + | KEY_1 | + And I submit the "Read" transaction + | KEY_1 | + Then I should receive a successful response "Initialised" + + Scenario: Talk to complex contract + When I submit the "ComplexContract:NewObject" transaction + | OBJECT_1 | {"name": "Andy", "contact": "Leave well alone"} | 1000 | ["red", "white", "blue"] | + And I submit the "ComplexContract:GetObject" transaction + | OBJECT_1 | + Then I should receive a successful response '{"id":"OBJECT_1","owner":{"name":"Andy","contact":"Leave well alone"},"value":1000,"condition":0,"colours":["red","white","blue"]}' \ No newline at end of file diff --git a/v2/internal/functionaltests/features/goldenpaths/simplechaincode.feature b/v2/internal/functionaltests/features/goldenpaths/simplechaincode.feature new file mode 100644 index 0000000..3b609fb --- /dev/null +++ b/v2/internal/functionaltests/features/goldenpaths/simplechaincode.feature @@ -0,0 +1,33 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +@goldenpaths +@simple +Feature: Simple Chancode Path + + Golden path of a very basic put and get chaincode + + Background: Initialise + Given I have created chaincode from "SimpleContract" + Then I should be able to initialise the chaincode + + Scenario: Create key value pair + When I submit the "Create" transaction + | KEY_1 | + Then I should receive a successful response + + Scenario: Read key value pair + Given I submit the "Create" transaction + | KEY_1 | + When I submit the "Read" transaction + | KEY_1 | + Then I should receive a successful response "Initialised" + + Scenario: Update key value pair + Given I submit the "Create" transaction + | KEY_1 | + When I submit the "Update" transaction + | KEY_1 | Updated | + Then I should receive a successful response + When I submit the "Read" transaction + | KEY_1 | + Then I should receive a successful response "Updated" \ No newline at end of file diff --git a/v2/internal/functionaltests/mockstub_test.go b/v2/internal/functionaltests/mockstub_test.go new file mode 100644 index 0000000..47a34c5 --- /dev/null +++ b/v2/internal/functionaltests/mockstub_test.go @@ -0,0 +1,354 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package fvtests + +import ( + "errors" + + "github.com/hyperledger/fabric-chaincode-go/v2/shim" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + minUnicodeRuneValue = 0 //U+0000 +) + +// MockStub is an implementation of ChaincodeStubInterface for unit testing chaincode. +// Use this instead of ChaincodeStub in your chaincode's unit test calls to Init or Invoke. +type MockStub struct { + // arguments the stub was called with + args [][]byte + + // A pointer back to the chaincode that will invoke this, set by constructor. + // If a peer calls this stub, the chaincode will be invoked from here. + cc shim.Chaincode + + // State keeps name value pairs + State map[string][]byte + + // stores a transaction uuid while being Invoked / Deployed + // TODO if a chaincode uses recursion this may need to be a stack of TxIDs or possibly a reference counting map + TxID string + + TxTimestamp *timestamppb.Timestamp + + // mocked signedProposal + signedProposal *peer.SignedProposal + + // stores a channel ID of the proposal + ChannelID string +} + +// GetTxID ... +func (stub *MockStub) GetTxID() string { + return stub.TxID +} + +// GetChannelID ... +func (stub *MockStub) GetChannelID() string { + return stub.ChannelID +} + +// GetArgs ... +func (stub *MockStub) GetArgs() [][]byte { + return stub.args +} + +// GetStringArgs ... +func (stub *MockStub) GetStringArgs() []string { + args := stub.GetArgs() + strargs := make([]string, 0, len(args)) + for _, barg := range args { + strargs = append(strargs, string(barg)) + } + return strargs +} + +// GetFunctionAndParameters ... +func (stub *MockStub) GetFunctionAndParameters() (function string, params []string) { + allargs := stub.GetStringArgs() + function = "" + params = []string{} + if len(allargs) >= 1 { + function = allargs[0] + params = allargs[1:] + } + return +} + +// MockTransactionStart Used to indicate to a chaincode that it is part of a transaction. +// This is important when chaincodes invoke each other. +// MockStub doesn't support concurrent transactions at present. +func (stub *MockStub) MockTransactionStart(txid string) { + stub.TxID = txid + stub.setSignedProposal(&peer.SignedProposal{}) + stub.TxTimestamp = timestamppb.Now() +} + +// MockTransactionEnd End a mocked transaction, clearing the UUID. +func (stub *MockStub) MockTransactionEnd(uuid string) { + stub.signedProposal = nil + stub.TxID = "" +} + +// MockInit Initialise this chaincode, also starts and ends a transaction. +func (stub *MockStub) MockInit(txID string, args [][]byte) *peer.Response { + stub.args = args + stub.MockTransactionStart(txID) + res := stub.cc.Init(stub) + stub.MockTransactionEnd(txID) + return res +} + +// MockInvoke Invoke this chaincode, also starts and ends a transaction. +func (stub *MockStub) MockInvoke(txID string, args [][]byte) *peer.Response { + stub.args = args + stub.MockTransactionStart(txID) + res := stub.cc.Invoke(stub) + stub.MockTransactionEnd(txID) + return res +} + +// GetDecorations ... +func (stub *MockStub) GetDecorations() map[string][]byte { + return nil +} + +// MockInvokeWithSignedProposal Invoke this chaincode, also starts and ends a transaction. +func (stub *MockStub) MockInvokeWithSignedProposal(txID string, args [][]byte, sp *peer.SignedProposal) *peer.Response { + stub.args = args + stub.MockTransactionStart(txID) + stub.signedProposal = sp + res := stub.cc.Invoke(stub) + stub.MockTransactionEnd(txID) + return res +} + +// GetPrivateData ... +func (stub *MockStub) GetPrivateData(collection string, key string) ([]byte, error) { + return nil, errors.New("not implemented") +} + +// GetPrivateDataHash ... +func (stub *MockStub) GetPrivateDataHash(collection, key string) ([]byte, error) { + return nil, errors.New("not implemented") +} + +// PutPrivateData ... +func (stub *MockStub) PutPrivateData(collection string, key string, value []byte) error { + return errors.New("not implemented") +} + +// DelPrivateData ... +func (stub *MockStub) DelPrivateData(collection string, key string) error { + return errors.New("not implemented") +} + +// PurgePrivateData ... +func (stub *MockStub) PurgePrivateData(collection string, key string) error { + return errors.New("not implemented") +} + +// GetPrivateDataByRange ... +func (stub *MockStub) GetPrivateDataByRange(collection, startKey, endKey string) (shim.StateQueryIteratorInterface, error) { + return nil, errors.New("not implemented") +} + +// GetPrivateDataByPartialCompositeKey ... +func (stub *MockStub) GetPrivateDataByPartialCompositeKey(collection, objectType string, attributes []string) (shim.StateQueryIteratorInterface, error) { + return nil, errors.New("not implemented") +} + +// GetPrivateDataQueryResult ... +func (stub *MockStub) GetPrivateDataQueryResult(collection, query string) (shim.StateQueryIteratorInterface, error) { + return nil, errors.New("not implemented") +} + +// GetState retrieves the value for a given key from the ledger +func (stub *MockStub) GetState(key string) ([]byte, error) { + value := stub.State[key] + return value, nil +} + +// PutState writes the specified `value` and `key` into the ledger. +func (stub *MockStub) PutState(key string, value []byte) error { + if stub.TxID == "" { + err := errors.New("cannot PutState without a transactions - call stub.MockTransactionStart()?") + return err + } + + // If the value is nil or empty, delete the key + if len(value) == 0 { + return stub.DelState(key) + } + + stub.State[key] = value + + return nil +} + +// DelState removes the specified `key` and its value from the ledger. +func (stub *MockStub) DelState(key string) error { + delete(stub.State, key) + return nil +} + +// GetStateByRange ... +func (stub *MockStub) GetStateByRange(startKey, endKey string) (shim.StateQueryIteratorInterface, error) { + return nil, errors.New("not implemented") +} + +// GetQueryResult function can be invoked by a chaincode to perform a +// rich query against state database. Only supported by state database implementations +// that support rich query. The query string is in the syntax of the underlying +// state database. An iterator is returned which can be used to iterate (next) over +// the query result set +func (stub *MockStub) GetQueryResult(query string) (shim.StateQueryIteratorInterface, error) { + // Not implemented since the mock engine does not have a query engine. + // However, a very simple query engine that supports string matching + // could be implemented to test that the framework supports queries + return nil, errors.New("not implemented") +} + +// GetHistoryForKey function can be invoked by a chaincode to return a history of +// key values across time. GetHistoryForKey is intended to be used for read-only queries. +func (stub *MockStub) GetHistoryForKey(key string) (shim.HistoryQueryIteratorInterface, error) { + return nil, errors.New("not implemented") +} + +// GetStateByPartialCompositeKey function can be invoked by a chaincode to query the +// state based on a given partial composite key. This function returns an +// iterator which can be used to iterate over all composite keys whose prefix +// matches the given partial composite key. This function should be used only for +// a partial composite key. For a full composite key, an iter with empty response +// would be returned. +func (stub *MockStub) GetStateByPartialCompositeKey(objectType string, attributes []string) (shim.StateQueryIteratorInterface, error) { + return nil, errors.New("not implemented") +} + +// CreateCompositeKey combines the list of attributes +// to form a composite key. +func (stub *MockStub) CreateCompositeKey(objectType string, attributes []string) (string, error) { + return shim.CreateCompositeKey(objectType, attributes) +} + +// SplitCompositeKey splits the composite key into attributes +// on which the composite key was formed. +func (stub *MockStub) SplitCompositeKey(compositeKey string) (string, []string, error) { + return splitCompositeKey(compositeKey) +} + +func splitCompositeKey(compositeKey string) (string, []string, error) { + componentIndex := 1 + components := []string{} + for i := 1; i < len(compositeKey); i++ { + if compositeKey[i] == minUnicodeRuneValue { + components = append(components, compositeKey[componentIndex:i]) + componentIndex = i + 1 + } + } + return components[0], components[1:], nil +} + +// GetStateByRangeWithPagination ... +func (stub *MockStub) GetStateByRangeWithPagination(startKey, endKey string, pageSize int32, + bookmark string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { + return nil, nil, errors.New("not implemented") +} + +// GetStateByPartialCompositeKeyWithPagination ... +func (stub *MockStub) GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, + pageSize int32, bookmark string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { + return nil, nil, errors.New("not implemented") +} + +// GetQueryResultWithPagination ... +func (stub *MockStub) GetQueryResultWithPagination(query string, pageSize int32, + bookmark string) (shim.StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { + return nil, nil, nil +} + +// InvokeChaincode locally calls the specified chaincode `Invoke`. +// E.g. stub1.InvokeChaincode("othercc", funcArgs, channel) +// Before calling this make sure to create another MockStub stub2, call shim.NewMockStub("othercc", Chaincode) +// and register it with stub1 by calling stub1.MockPeerChaincode("othercc", stub2, channel) +func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) *peer.Response { + return nil +} + +// GetCreator ... +func (stub *MockStub) GetCreator() ([]byte, error) { + return nil, errors.New("not implemented") +} + +// SetTransient set TransientMap to mockStub +func (stub *MockStub) SetTransient(tMap map[string][]byte) error { + return errors.New("not implemented") +} + +// GetTransient ... +func (stub *MockStub) GetTransient() (map[string][]byte, error) { + return nil, errors.New("not implemented") +} + +// GetBinding Not implemented ... +func (stub *MockStub) GetBinding() ([]byte, error) { + return nil, errors.New("not implemented") +} + +// GetSignedProposal Not implemented ... +func (stub *MockStub) GetSignedProposal() (*peer.SignedProposal, error) { + return stub.signedProposal, nil +} + +func (stub *MockStub) setSignedProposal(sp *peer.SignedProposal) { + stub.signedProposal = sp +} + +// GetArgsSlice Not implemented ... +func (stub *MockStub) GetArgsSlice() ([]byte, error) { + return nil, errors.New("not implemented") +} + +// GetTxTimestamp ... +func (stub *MockStub) GetTxTimestamp() (*timestamppb.Timestamp, error) { + if stub.TxTimestamp == nil { + return nil, errors.New("timestamp not set") + } + return stub.TxTimestamp, nil +} + +// SetEvent ... +func (stub *MockStub) SetEvent(name string, payload []byte) error { + return errors.New("not implemented") +} + +// SetStateValidationParameter ... +func (stub *MockStub) SetStateValidationParameter(key string, ep []byte) error { + return errors.New("not implemented") +} + +// GetStateValidationParameter ... +func (stub *MockStub) GetStateValidationParameter(key string) ([]byte, error) { + return nil, errors.New("not implemented") +} + +// SetPrivateDataValidationParameter ... +func (stub *MockStub) SetPrivateDataValidationParameter(collection, key string, ep []byte) error { + return errors.New("not implemented") +} + +// GetPrivateDataValidationParameter ... +func (stub *MockStub) GetPrivateDataValidationParameter(collection, key string) ([]byte, error) { + return nil, errors.New("not implemented") +} + +// NewMockStub Constructor to initialise the internal State map +func NewMockStub(cc shim.Chaincode) *MockStub { + return &MockStub{ + cc: cc, + State: make(map[string][]byte), + } +} diff --git a/v2/internal/functionaltests/step_definitions_test.go b/v2/internal/functionaltests/step_definitions_test.go new file mode 100644 index 0000000..7609573 --- /dev/null +++ b/v2/internal/functionaltests/step_definitions_test.go @@ -0,0 +1,268 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package fvtests + +import ( + "context" + "errors" + "fmt" + "math/rand" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/cucumber/godog" + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/functionaltests/contracts/complexcontract" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/functionaltests/contracts/extendedsimplecontract" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/functionaltests/contracts/simplecontract" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/functionaltests/contracts/utils" + "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" +) + +var contractsMap map[string]contractapi.ContractInterface = map[string]contractapi.ContractInterface{ + "SimpleContract": new(simplecontract.SimpleContract), + "ExtendedSimpleContract": NewExtendedContract(), + "ComplexContract": NewComplexContract(), +} + +func NewExtendedContract() *extendedsimplecontract.ExtendedSimpleContract { + esc := new(extendedsimplecontract.ExtendedSimpleContract) + + esc.TransactionContextHandler = new(utils.CustomTransactionContext) + esc.BeforeTransaction = utils.GetWorldState + esc.UnknownTransaction = utils.UnknownTransactionHandler + + return esc +} + +func NewComplexContract() *complexcontract.ComplexContract { + cc := new(complexcontract.ComplexContract) + + cc.TransactionContextHandler = new(utils.CustomTransactionContext) + cc.BeforeTransaction = utils.GetWorldState + cc.UnknownTransaction = utils.UnknownTransactionHandler + + return cc +} + +type suiteContext struct { + lastResponse *peer.Response + stub *MockStub + chaincode *contractapi.ContractChaincode + metadataFolder string +} + +type suiteContextKey struct{} + +func cleanup(ctx context.Context) { + if sc, ok := ctx.Value(suiteContextKey{}).(suiteContext); ok && sc.metadataFolder != "" { + os.RemoveAll(sc.metadataFolder) + } +} + +func iHaveCreatedChaincodeFrom(ctx context.Context, name string) (context.Context, error) { + defer cleanup(ctx) + + if _, ok := contractsMap[name]; !ok { + return ctx, fmt.Errorf("Invalid contract name %s", name) + } + + chaincode, err := contractapi.NewChaincode(contractsMap[name]) + if err != nil { + return ctx, fmt.Errorf("expected to get nil for error on create chaincode but got " + err.Error()) + } + + sc := suiteContext{} + sc.chaincode = chaincode + sc.stub = NewMockStub(sc.chaincode) + + return context.WithValue(ctx, suiteContextKey{}, sc), nil +} + +func iHaveCreatedChaincodeFromMultipleContracts(ctx context.Context, contractsTbl *godog.Table) (context.Context, error) { + defer cleanup(ctx) + if len(contractsTbl.Rows) > 1 { + return ctx, fmt.Errorf("expected table with one row of contracts") + } + + contracts := []contractapi.ContractInterface{} + + for _, row := range contractsTbl.Rows { + for _, cell := range row.Cells { + contract, ok := contractsMap[cell.Value] + + if !ok { + return ctx, fmt.Errorf("Invalid contract name %s", cell.Value) + } + + contracts = append(contracts, contract) + } + } + + chaincode, err := contractapi.NewChaincode(contracts...) + + if err != nil { + return ctx, fmt.Errorf("expected to get nil for error on create chaincode but got " + err.Error()) + } + sc := suiteContext{} + sc.chaincode = chaincode + sc.stub = NewMockStub(sc.chaincode) + return context.WithValue(ctx, suiteContextKey{}, sc), nil +} + +func iShouldBeAbleToInitialiseTheChaincode(ctx context.Context) (context.Context, error) { + sc, ok := ctx.Value(suiteContextKey{}).(suiteContext) + if !ok { + return ctx, errors.New("there are no contracts available") + } + + txID := strconv.Itoa(rand.Int()) //nolint:gosec + + sc.stub.MockTransactionStart(txID) + response := sc.stub.MockInit(txID, [][]byte{}) + sc.stub.MockTransactionEnd(txID) + + if response.GetStatus() != int32(200) { + return ctx, fmt.Errorf("expected to get status 200 on init but got " + strconv.Itoa(int(response.GetStatus()))) + } + + return context.WithValue(ctx, suiteContextKey{}, sc), nil +} + +func iShouldReceiveASuccessfulResponse(ctx context.Context, result string) (context.Context, error) { + sc, ok := ctx.Value(suiteContextKey{}).(suiteContext) + if !ok { + return ctx, errors.New("there are no contracts available") + } + + payload := string(sc.lastResponse.GetPayload()) + if result != "" && payload != result { + return ctx, fmt.Errorf("expected to get payload :" + result + ": but got :" + payload + ":") + } + + return ctx, nil + +} + +func iSubmitTheTransaction(ctx context.Context, function string, argsTbl *godog.Table) (context.Context, error) { + sc, ok := ctx.Value(suiteContextKey{}).(suiteContext) + if !ok { + return ctx, errors.New("there are no contracts available") + } + + txID := strconv.Itoa(rand.Int()) //nolint:gosec + + argBytes := [][]byte{} + argBytes = append(argBytes, []byte(function)) + + if len(argsTbl.Rows) > 1 { + return ctx, fmt.Errorf("expected zero or one table of args") + } + + for _, row := range argsTbl.Rows { + for _, cell := range row.Cells { + argBytes = append(argBytes, []byte(cell.Value)) + } + } + + sc.stub.MockTransactionStart(txID) + response := sc.stub.MockInvoke(txID, argBytes) + sc.stub.MockTransactionEnd(txID) + + sc.lastResponse = response + + return context.WithValue(ctx, suiteContextKey{}, sc), nil + +} + +func iAmUsingMetadataFile(ctx context.Context, file string) (context.Context, error) { + ex, execErr := os.Executable() + if execErr != nil { + return ctx, fmt.Errorf("failed to read metadata from file. Could not find location of executable. %s", execErr.Error()) + } + exPath := filepath.Dir(ex) + metadataPath := filepath.Join(exPath, file) + + metadataBytes, err := os.ReadFile(metadataPath) + if err != nil { + return ctx, fmt.Errorf("failed to read metadata from file. Could not read file %s. %s", metadataPath, err) + } + + metadataFolder := filepath.Join(exPath, metadata.MetadataFolder) + + if err := os.MkdirAll(metadataFolder, os.ModePerm); err != nil { + return ctx, err + } + + if err := os.WriteFile(filepath.Join(metadataFolder, metadata.MetadataFile), metadataBytes, os.ModePerm); err != nil { //nolint:gosec + return ctx, err + } + + sc := suiteContext{} + sc.metadataFolder = metadataFolder + + return context.WithValue(ctx, suiteContextKey{}, sc), nil + +} + +func iFailToCreateChaincodeFrom(ctx context.Context, name string) (context.Context, error) { + _, err := iHaveCreatedChaincodeFrom(ctx, name) + + if err == nil { + return ctx, fmt.Errorf("Expected to get an error") + } + + return ctx, nil +} + +func iShouldReceiveAnUnsuccessfulResponse(ctx context.Context, result string) (context.Context, error) { + + sc, ok := ctx.Value(suiteContextKey{}).(suiteContext) + if !ok { + return ctx, errors.New("there are no contracts available") + } + + if sc.lastResponse.GetStatus() == int32(200) { + return ctx, fmt.Errorf("expected to not get status 200 on invoke") + } + + result = strings.Join(strings.Split(result, "\\n"), "\n") + + message := sc.lastResponse.GetMessage() + if result != "" && message != result { + return ctx, fmt.Errorf("expected to get message " + result + " but got " + message) + } + return ctx, nil +} + +func TestFeatures(t *testing.T) { + suite := godog.TestSuite{ + ScenarioInitializer: InitializeScenario, + Options: &godog.Options{ + Format: "pretty", + Paths: []string{"features"}, + TestingT: t, // Testing instance that will run subtests. + }, + } + + if suite.Run() != 0 { + t.Fatal("non-zero status returned, failed to run feature tests") + } +} + +func InitializeScenario(ctx *godog.ScenarioContext) { + ctx.Step(`^I have created chaincode from "([^"]*)"$`, iHaveCreatedChaincodeFrom) + ctx.Step(`^I have created chaincode from multiple contracts$`, iHaveCreatedChaincodeFromMultipleContracts) + ctx.Step(`^I should be able to initialise the chaincode$`, iShouldBeAbleToInitialiseTheChaincode) + ctx.Step(`^I have initialised the chaincode$`, iShouldBeAbleToInitialiseTheChaincode) + ctx.Step(`^I (?:should\s)?receive a successful response\s?(?:(?:["'](.*?)["'])?)$`, iShouldReceiveASuccessfulResponse) + ctx.Step(`^I submit the "([^"]*)" transaction$`, iSubmitTheTransaction) + ctx.Step(`^I am using metadata file "([^"]*)"$`, iAmUsingMetadataFile) + ctx.Step(`^I fail to create chaincode from "([^"]*)"$`, iFailToCreateChaincodeFrom) + ctx.Step(`^I should receive an unsuccessful response "([^"]*)"$`, iShouldReceiveAnUnsuccessfulResponse) +} diff --git a/v2/internal/functionaltests/utils/bad_metadata.json b/v2/internal/functionaltests/utils/bad_metadata.json new file mode 100644 index 0000000..cd2b991 --- /dev/null +++ b/v2/internal/functionaltests/utils/bad_metadata.json @@ -0,0 +1,5 @@ +{ + "contracts": { + "duck": "goose" + } +} \ No newline at end of file diff --git a/v2/internal/internal_test.go b/v2/internal/internal_test.go new file mode 100644 index 0000000..779ff12 --- /dev/null +++ b/v2/internal/internal_test.go @@ -0,0 +1,25 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "fmt" + "os" + "testing" +) + +func TestMain(m *testing.M) { + rc := m.Run() + + if rc == 0 && testing.CoverMode() != "" { + c := testing.Coverage() + + if c < 1 { + fmt.Println("Tests passed but coverage failed at", c) + rc = -1 + } + } + + os.Exit(rc) +} diff --git a/v2/internal/transaction_handler.go b/v2/internal/transaction_handler.go new file mode 100644 index 0000000..528e20a --- /dev/null +++ b/v2/internal/transaction_handler.go @@ -0,0 +1,90 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "errors" + "fmt" + "reflect" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi/utils" + "github.com/hyperledger/fabric-contract-api-go/v2/serializer" +) + +// TransactionHandlerType enum for type of transaction handled +type TransactionHandlerType int + +const ( + // TransactionHandlerTypeBefore before transaction type + TransactionHandlerTypeBefore TransactionHandlerType = iota + 1 + // TransactionHandlerTypeUnknown before transaction type + TransactionHandlerTypeUnknown + // TransactionHandlerTypeAfter before transaction type + TransactionHandlerTypeAfter +) + +func (tht TransactionHandlerType) String() (string, error) { + switch tht { + case TransactionHandlerTypeBefore: + return "Before", nil + case TransactionHandlerTypeAfter: + return "After", nil + case TransactionHandlerTypeUnknown: + return "Unknown", nil + default: + return "", errors.New("invalid transaction handler type") + } +} + +// TransactionHandler extension of contract function that manages function which handles calls +// to before, after and unknown transaction functions +type TransactionHandler struct { + ContractFunction + handlesType TransactionHandlerType +} + +// Call calls tranaction function using string args and handles formatting the response into useful types +func (th TransactionHandler) Call(ctx reflect.Value, data interface{}, serializer serializer.TransactionSerializer) (string, interface{}, error) { + values := []reflect.Value{} + + if th.params.context != nil { + values = append(values, ctx) + } + + if th.handlesType == TransactionHandlerTypeAfter && len(th.params.fields) == 1 { + if data == nil { + values = append(values, reflect.Zero(reflect.TypeOf(new(utils.UndefinedInterface)))) + } else { + values = append(values, reflect.ValueOf(data)) + } + } + + someResp := th.function.Call(values) + + return th.handleResponse(someResp, nil, nil, serializer) +} + +// NewTransactionHandler create a new transaction handler from a given function +func NewTransactionHandler(fn interface{}, contextHandlerType reflect.Type, handlesType TransactionHandlerType) (*TransactionHandler, error) { + cf, err := NewContractFunctionFromFunc(fn, 0, contextHandlerType) + + if err != nil { + str, _ := handlesType.String() + return nil, fmt.Errorf("error creating %s. %s", str, err.Error()) + } else if handlesType != TransactionHandlerTypeAfter && len(cf.params.fields) > 0 { + str, _ := handlesType.String() + return nil, fmt.Errorf("%s transactions may not take any params other than the transaction context", str) + } else if handlesType == TransactionHandlerTypeAfter && len(cf.params.fields) > 1 { + return nil, fmt.Errorf("after transactions must take at most one non-context param") + } else if handlesType == TransactionHandlerTypeAfter && len(cf.params.fields) == 1 && cf.params.fields[0].Kind() != reflect.Interface { + return nil, fmt.Errorf("after transaction must take type interface{} as their only non-context param") + } + + th := TransactionHandler{ + *cf, + handlesType, + } + + return &th, nil +} diff --git a/v2/internal/transaction_handler_test.go b/v2/internal/transaction_handler_test.go new file mode 100644 index 0000000..c00a5b2 --- /dev/null +++ b/v2/internal/transaction_handler_test.go @@ -0,0 +1,176 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "errors" + "fmt" + "reflect" + "testing" + + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi/utils" + "github.com/hyperledger/fabric-contract-api-go/v2/serializer" + "github.com/stretchr/testify/assert" +) + +// ================================ +// HELPERS +// ================================ + +type transactionHandlerStruct struct{} + +func (ms *transactionHandlerStruct) BasicFunction(str string) string { + return str +} + +func (ms *transactionHandlerStruct) AdvancedFunction(str string, str2 string) string { + return str + str2 +} + +func (ms *transactionHandlerStruct) GoodBeforeUnknownAfterFunction() string { + return "CALLED GoodBeforeUnknownAfterFunction" +} + +func (ms *transactionHandlerStruct) GoodBeforeUnknownAfterFunctionWithContext(ctx *TransactionContext) string { + return "CALLED GoodBeforeUnknownAfterFunctionWithContext WITH " + ctx.Str +} + +func (ms *transactionHandlerStruct) GoodAfterFunction(iface interface{}) string { + return iface.(string) +} + +func (ms *transactionHandlerStruct) GoodAfterFunctionForUndefinedInterface(iface interface{}) bool { + _, ok := iface.(*utils.UndefinedInterface) + return ok +} + +func (ms *transactionHandlerStruct) BadFunction(param1 complex64) complex64 { + return param1 +} + +type TransactionContext struct { + Str string +} + +var basicContextPtrType = reflect.TypeOf(new(TransactionContext)) + +// ================================ +// TEST +// ================================ + +func TestString(t *testing.T) { + var err error + var str string + + str, err = TransactionHandlerTypeBefore.String() + assert.Nil(t, err, "should output no error when before type") + assert.Equal(t, "Before", str, "should output Before for before type") + + str, err = TransactionHandlerTypeAfter.String() + assert.Nil(t, err, "should output no error when after type") + assert.Equal(t, "After", str, "should output After for after type") + + str, err = TransactionHandlerTypeUnknown.String() + assert.Nil(t, err, "should output no error when unknown type") + assert.Equal(t, "Unknown", str, "should output Unknown for unknown type") + + str, err = TransactionHandlerType(TransactionHandlerTypeAfter + 1).String() + assert.Error(t, err, errors.New("invalid transaction handler type"), "should error when not one of enum") + assert.Equal(t, "", str, "should return blank string for error") +} + +func TestNewTransactionHandler(t *testing.T) { + var th *TransactionHandler + var err error + var cf *ContractFunction + + ms := transactionHandlerStruct{} + + _, err = NewTransactionHandler(ms.BasicFunction, basicContextPtrType, TransactionHandlerTypeBefore) + assert.EqualError(t, err, "Before transactions may not take any params other than the transaction context", "should error when before function takes args but not just the context") + + _, err = NewTransactionHandler(ms.BasicFunction, basicContextPtrType, TransactionHandlerTypeUnknown) + assert.EqualError(t, err, "Unknown transactions may not take any params other than the transaction context", "should error when unknown function takes args but not just the context") + + _, err = NewTransactionHandler(ms.AdvancedFunction, basicContextPtrType, TransactionHandlerTypeAfter) + assert.EqualError(t, err, "after transactions must take at most one non-context param", "should error when after function takes more than one non-context arg") + + _, err = NewTransactionHandler(ms.BasicFunction, basicContextPtrType, TransactionHandlerTypeAfter) + assert.EqualError(t, err, "after transaction must take type interface{} as their only non-context param", "should error when after function takes correct number of non-context args but not interface type") + + _, expectedErr := NewContractFunctionFromFunc(ms.BadFunction, 0, basicContextPtrType) + _, err = NewTransactionHandler(ms.BadFunction, basicContextPtrType, TransactionHandlerTypeAfter) + assert.EqualError(t, err, fmt.Sprintf("error creating After. %s", expectedErr.Error()), "should error when new contract function errors") + + th, err = NewTransactionHandler(ms.GoodBeforeUnknownAfterFunction, basicContextPtrType, TransactionHandlerTypeBefore) + cf, _ = NewContractFunctionFromFunc(ms.GoodBeforeUnknownAfterFunction, 0, basicContextPtrType) + assert.Nil(t, err, "should not error for valid tx handler (before)") + assert.Equal(t, TransactionHandlerTypeBefore, th.handlesType, "should create a txn handler for a before txn") + assert.Equal(t, th.params, cf.params, "should create a txn handler for a before txn that has matching contract function") + assert.Equal(t, th.returns, cf.returns, "should create a txn handler for a before txn that has matching contract function") + + th, err = NewTransactionHandler(ms.GoodBeforeUnknownAfterFunction, basicContextPtrType, TransactionHandlerTypeUnknown) + cf, _ = NewContractFunctionFromFunc(ms.GoodBeforeUnknownAfterFunction, 0, basicContextPtrType) + assert.Nil(t, err, "should not error for valid tx handler (unknown)") + assert.Equal(t, TransactionHandlerTypeUnknown, th.handlesType, "should create a txn handler for an unknown txn") + assert.Equal(t, th.params, cf.params, "should create a txn handler for an unknown txn that has matching contract function") + assert.Equal(t, th.returns, cf.returns, "should create a txn handler for an unknown txn that has matching contract function") + + th, err = NewTransactionHandler(ms.GoodBeforeUnknownAfterFunction, basicContextPtrType, TransactionHandlerTypeAfter) + cf, _ = NewContractFunctionFromFunc(ms.GoodBeforeUnknownAfterFunction, 0, basicContextPtrType) + assert.Nil(t, err, "should not error for valid tx handler (after)") + assert.Equal(t, TransactionHandlerTypeAfter, th.handlesType, "should create a txn handler for an after txn") + assert.Equal(t, th.params, cf.params, "should create a txn handler for an after txn that has matching contract function") + assert.Equal(t, th.returns, cf.returns, "should create a txn handler for an after txn that has matching contract function") + + th, err = NewTransactionHandler(ms.GoodAfterFunction, basicContextPtrType, TransactionHandlerTypeAfter) + cf, _ = NewContractFunctionFromFunc(ms.GoodAfterFunction, 0, basicContextPtrType) + assert.Nil(t, err, "should not error for valid tx handler (afetr with param)") + assert.Equal(t, TransactionHandlerTypeAfter, th.handlesType, "should create a txn handler for an after txn with arg") + assert.Equal(t, th.params, cf.params, "should create a txn handler for an after txn with arg that has matching contract function") + assert.Equal(t, th.returns, cf.returns, "should create a txn handler for an after txn with arg that has matching contract function") +} + +func TestTHCall(t *testing.T) { + var th *TransactionHandler + var ctx = &TransactionContext{Str: "HELLO WORLD"} + var expectedStr string + var expectedIFace interface{} + var expectedErr error + var actualStr string + var actualIFace interface{} + var actualErr error + + serializer := new(serializer.JSONSerializer) + ms := transactionHandlerStruct{} + + th, _ = NewTransactionHandler(ms.GoodBeforeUnknownAfterFunction, basicContextPtrType, TransactionHandlerTypeBefore) + expectedStr, expectedIFace, expectedErr = th.handleResponse([]reflect.Value{reflect.ValueOf(ms.GoodBeforeUnknownAfterFunction())}, nil, nil, serializer) + actualStr, actualIFace, actualErr = th.Call(reflect.ValueOf(ctx), nil, serializer) + assert.Equal(t, expectedStr, actualStr, "should produce same string as handle response on real function") + assert.Equal(t, expectedIFace.(string), actualIFace.(string), "should produce same interface as handle response on real function") + assert.Equal(t, expectedErr, actualErr, "should produce same error as handle response on real function") + + th, _ = NewTransactionHandler(ms.GoodBeforeUnknownAfterFunctionWithContext, basicContextPtrType, TransactionHandlerTypeBefore) + expectedStr, expectedIFace, expectedErr = th.handleResponse([]reflect.Value{reflect.ValueOf(ms.GoodBeforeUnknownAfterFunctionWithContext(ctx))}, nil, nil, serializer) + actualStr, actualIFace, actualErr = th.Call(reflect.ValueOf(ctx), nil, serializer) + assert.Equal(t, expectedStr, actualStr, "should produce same string as handle response on real function with context") + assert.Equal(t, expectedIFace.(string), actualIFace.(string), "should produce same interface as handle response on real function with context") + assert.Equal(t, expectedErr, actualErr, "should produce same error as handle response on real function with context") + + th, _ = NewTransactionHandler(ms.GoodAfterFunction, basicContextPtrType, TransactionHandlerTypeAfter) + expectedStr, expectedIFace, expectedErr = th.handleResponse([]reflect.Value{reflect.ValueOf(ms.GoodAfterFunction("some str"))}, nil, nil, serializer) + actualStr, actualIFace, actualErr = th.Call(reflect.ValueOf(ctx), "some str", serializer) + assert.Equal(t, expectedStr, actualStr, "should produce same string as handle response on real function for after with param") + assert.Equal(t, expectedIFace.(string), actualIFace.(string), "should produce same interface as handle response on real function for after with param") + assert.Equal(t, expectedErr, actualErr, "should produce same error as handle response on real function for after with param") + + var ui *utils.UndefinedInterface + th, _ = NewTransactionHandler(ms.GoodAfterFunctionForUndefinedInterface, basicContextPtrType, TransactionHandlerTypeAfter) + expectedStr, expectedIFace, expectedErr = th.handleResponse([]reflect.Value{reflect.ValueOf(ms.GoodAfterFunctionForUndefinedInterface(ui))}, nil, nil, serializer) + actualStr, actualIFace, actualErr = th.Call(reflect.ValueOf(ctx), nil, serializer) + assert.Equal(t, expectedStr, actualStr, "should produce same string as handle response on real function for after with undefined interface") + assert.Equal(t, expectedIFace.(bool), actualIFace.(bool), "should produce same interface as handle response on real function for after with undefined interface") + assert.Equal(t, expectedErr, actualErr, "should produce same error as handle response on real function for after with undefined interface") +} diff --git a/v2/internal/types/types.go b/v2/internal/types/types.go new file mode 100644 index 0000000..df90e5d --- /dev/null +++ b/v2/internal/types/types.go @@ -0,0 +1,362 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "math" + "reflect" + "strconv" + "time" + + "github.com/go-openapi/spec" +) + +type basicType interface { + Convert(string) (reflect.Value, error) + GetSchema() *spec.Schema +} + +type stringType struct{} + +func (st *stringType) Convert(value string) (reflect.Value, error) { + return reflect.ValueOf(value), nil +} + +func (st *stringType) GetSchema() *spec.Schema { + return spec.StringProperty() +} + +type boolType struct{} + +func (bt *boolType) Convert(value string) (reflect.Value, error) { + var boolVal bool + var err error + if value != "" { + boolVal, err = strconv.ParseBool(value) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to bool", value) + } + } + + return reflect.ValueOf(boolVal), nil +} + +func (bt *boolType) GetSchema() *spec.Schema { + return spec.BooleanProperty() +} + +type intType struct{} + +func (it *intType) Convert(value string) (reflect.Value, error) { + var intVal int + var err error + if value != "" { + intVal, err = strconv.Atoi(value) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to int", value) + } + } + + return reflect.ValueOf(intVal), nil +} + +func (it *intType) GetSchema() *spec.Schema { + return spec.Int64Property() +} + +type int8Type struct{} + +func (it *int8Type) Convert(value string) (reflect.Value, error) { + var intVal int8 + if value != "" { + int64val, err := strconv.ParseInt(value, 10, 8) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to int8", value) + } + + intVal = int8(int64val) + } + + return reflect.ValueOf(intVal), nil +} + +func (it *int8Type) GetSchema() *spec.Schema { + return spec.Int8Property() +} + +type int16Type struct{} + +func (it *int16Type) Convert(value string) (reflect.Value, error) { + var intVal int16 + if value != "" { + int64val, err := strconv.ParseInt(value, 10, 16) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to int16", value) + } + + intVal = int16(int64val) + } + + return reflect.ValueOf(intVal), nil +} + +func (it *int16Type) GetSchema() *spec.Schema { + return spec.Int16Property() +} + +type int32Type struct{} + +func (it *int32Type) Convert(value string) (reflect.Value, error) { + var intVal int32 + if value != "" { + int64val, err := strconv.ParseInt(value, 10, 32) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to int32", value) + } + + intVal = int32(int64val) + } + + return reflect.ValueOf(intVal), nil +} + +func (it *int32Type) GetSchema() *spec.Schema { + return spec.Int32Property() +} + +type int64Type struct{} + +func (it *int64Type) Convert(value string) (reflect.Value, error) { + var intVal int64 + var err error + if value != "" { + intVal, err = strconv.ParseInt(value, 10, 64) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to int64", value) + } + } + + return reflect.ValueOf(intVal), nil +} + +func (it *int64Type) GetSchema() *spec.Schema { + return spec.Int64Property() +} + +type uintType struct{} + +func (ut *uintType) Convert(value string) (reflect.Value, error) { + var uintVal uint + if value != "" { + uint64Val, err := strconv.ParseUint(value, 10, 64) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to uint", value) + } + + uintVal = uint(uint64Val) + } + + return reflect.ValueOf(uintVal), nil +} + +func (ut *uintType) GetSchema() *spec.Schema { + schema := spec.Float64Property() + + multOf := float64(1) + schema.MultipleOf = &multOf + minimum := float64(0) + schema.Minimum = &minimum + maximum := float64(math.MaxUint64) + schema.Maximum = &maximum + return schema +} + +type uint8Type struct{} + +func (ut *uint8Type) Convert(value string) (reflect.Value, error) { + var uintVal uint8 + if value != "" { + uint64Val, err := strconv.ParseUint(value, 10, 8) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to uint8", value) + } + + uintVal = uint8(uint64Val) + } + + return reflect.ValueOf(uintVal), nil +} + +func (ut *uint8Type) GetSchema() *spec.Schema { + schema := spec.Int32Property() + minimum := float64(0) + schema.Minimum = &minimum + maximum := float64(math.MaxUint8) + schema.Maximum = &maximum + return schema +} + +type uint16Type struct{} + +func (ut *uint16Type) Convert(value string) (reflect.Value, error) { + var uintVal uint16 + if value != "" { + uint64Val, err := strconv.ParseUint(value, 10, 16) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to uint16", value) + } + + uintVal = uint16(uint64Val) + } + + return reflect.ValueOf(uintVal), nil +} + +func (ut *uint16Type) GetSchema() *spec.Schema { + schema := spec.Int64Property() + minimum := float64(0) + schema.Minimum = &minimum + maximum := float64(math.MaxUint16) + schema.Maximum = &maximum + return schema +} + +type uint32Type struct{} + +func (ut *uint32Type) Convert(value string) (reflect.Value, error) { + var uintVal uint32 + if value != "" { + uint64Val, err := strconv.ParseUint(value, 10, 32) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to uint32", value) + } + + uintVal = uint32(uint64Val) + } + + return reflect.ValueOf(uintVal), nil +} + +func (ut *uint32Type) GetSchema() *spec.Schema { + schema := spec.Int64Property() + minimum := float64(0) + schema.Minimum = &minimum + maximum := float64(4294967295) + schema.Maximum = &maximum + return schema +} + +type uint64Type struct{} + +func (ut *uint64Type) Convert(value string) (reflect.Value, error) { + var uintVal uint64 + var err error + if value != "" { + uintVal, err = strconv.ParseUint(value, 10, 64) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to uint64", value) + } + } + + return reflect.ValueOf(uintVal), nil +} + +func (ut *uint64Type) GetSchema() *spec.Schema { + schema := spec.Float64Property() + multOf := float64(1) + schema.MultipleOf = &multOf + minimum := float64(0) + schema.Minimum = &minimum + maximum := float64(18446744073709551615) + schema.Maximum = &maximum + return schema +} + +type float32Type struct{} + +func (ft *float32Type) Convert(value string) (reflect.Value, error) { + var floatVal float32 + if value != "" { + float64Val, err := strconv.ParseFloat(value, 32) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to float32", value) + } + + floatVal = float32(float64Val) + } + + return reflect.ValueOf(floatVal), nil +} + +func (ft *float32Type) GetSchema() *spec.Schema { + return spec.Float32Property() +} + +type float64Type struct{} + +func (ft *float64Type) Convert(value string) (reflect.Value, error) { + var floatVal float64 + var err error + if value != "" { + floatVal, err = strconv.ParseFloat(value, 64) + + if err != nil { + return reflect.Value{}, fmt.Errorf("cannot convert passed value %s to float64", value) + } + } + + return reflect.ValueOf(floatVal), nil +} + +func (ft *float64Type) GetSchema() *spec.Schema { + return spec.Float64Property() +} + +type interfaceType struct{} + +func (st *interfaceType) Convert(value string) (reflect.Value, error) { + return reflect.ValueOf(value), nil +} + +func (st *interfaceType) GetSchema() *spec.Schema { + return new(spec.Schema) +} + +// BasicTypes the base types usable in the contract api +var BasicTypes = map[reflect.Kind]basicType{ + reflect.Bool: new(boolType), + reflect.Float32: new(float32Type), + reflect.Float64: new(float64Type), + reflect.Int: new(intType), + reflect.Int8: new(int8Type), + reflect.Int16: new(int16Type), + reflect.Int32: new(int32Type), + reflect.Int64: new(int64Type), + reflect.String: new(stringType), + reflect.Uint: new(uintType), + reflect.Uint8: new(uint8Type), + reflect.Uint16: new(uint16Type), + reflect.Uint32: new(uint32Type), + reflect.Uint64: new(uint64Type), + reflect.Interface: new(interfaceType), +} + +// ErrorType reflect type for errors +var ErrorType = reflect.TypeOf((*error)(nil)).Elem() + +// TimeType reflect type for time +var TimeType = reflect.TypeOf(time.Time{}) diff --git a/v2/internal/types/types_test.go b/v2/internal/types/types_test.go new file mode 100644 index 0000000..34ebbd2 --- /dev/null +++ b/v2/internal/types/types_test.go @@ -0,0 +1,473 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "math" + "os" + "reflect" + "strconv" + "testing" + + "github.com/go-openapi/spec" + "github.com/stretchr/testify/assert" +) + +// ================================ +// HELPERS +// ================================ +const convertError = "cannot convert passed value %s to int" + +// ================================ +// TESTS +// ================================ + +func TestMain(m *testing.M) { + rc := m.Run() + + if rc == 0 && testing.CoverMode() != "" { + c := testing.Coverage() + + if c < 1 { + fmt.Println("Tests passed but coverage failed at", c) + rc = -1 + } + } + + os.Exit(rc) +} + +func TestStringType(t *testing.T) { + var stringTypeVar = new(stringType) + + // Test GetSchema + assert.Equal(t, spec.StringProperty(), stringTypeVar.GetSchema(), "should return open api string spec") + + // Test Convert + val, err := stringTypeVar.Convert("some string") + assert.Nil(t, err, "should not return error for valid string value") + assert.Equal(t, "some string", val.Interface().(string), "should have returned the same string") +} + +func TestBoolType(t *testing.T) { + var boolTypeVar = new(boolType) + + // Test GetSchema + assert.Equal(t, spec.BooleanProperty(), boolTypeVar.GetSchema(), "should return open api bool spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = boolTypeVar.Convert("true") + assert.NoError(t, err, "should not return error for valid bool (true) value") + assert.True(t, val.Interface().(bool), "should have returned the boolean true") + + val, err = boolTypeVar.Convert("false") + assert.NoError(t, err, "should not return error for valid bool (false) value") + assert.False(t, val.Interface().(bool), "should have returned the boolean false") + + val, err = boolTypeVar.Convert("") + assert.NoError(t, err, "should not return error for valid bool (blank) value") + assert.False(t, val.Interface().(bool), "should have returned the boolean false for blank value") + + // val, err = boolTypeVar.Convert("non bool") + // assert.EqualError(t, err, fmt.Sprintf(convertError, "non bool"), "should return error for invalid bool value") + // assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for non bool") +} + +func TestIntType(t *testing.T) { + var intTypeVar = new(intType) + + // Test GetSchema + assert.Equal(t, spec.Int64Property(), intTypeVar.GetSchema(), "should return open api int64 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = intTypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid int (123) value") + assert.Equal(t, 123, val.Interface().(int), "should have returned the int value 123") + + val, err = intTypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid int (blank) value") + assert.Equal(t, 0, val.Interface().(int), "should have returned the default int value") + + val, err = intTypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid int value") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int") +} + +func TestInt8Type(t *testing.T) { + var int8TypeVar = new(int8Type) + + // Test GetSchema + assert.Equal(t, spec.Int8Property(), int8TypeVar.GetSchema(), "should return open api int8 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = int8TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid int8 (123) value") + assert.Equal(t, int8(123), val.Interface().(int8), "should have returned the int8 value 123") + + val, err = int8TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid int8 (blank) value") + assert.Equal(t, int8(0), val.Interface().(int8), "should have returned the default int8 value") + + val, err = int8TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid int8 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int8 (NaN)") + + tooBig := strconv.Itoa(math.MaxInt8 + 1) + val, err = int8TypeVar.Convert(tooBig) + assert.Error(t, err, fmt.Errorf(convertError, tooBig), "should return error for invalid int8 value (too large)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int8 (too large)") +} + +func TestInt16Type(t *testing.T) { + var int16TypeVar = new(int16Type) + + // Test GetSchema + assert.Equal(t, spec.Int16Property(), int16TypeVar.GetSchema(), "should return open api int16 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = int16TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid int16 (123) value") + assert.Equal(t, int16(123), val.Interface().(int16), "should have returned the int16 value 123") + + val, err = int16TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid int16 (blank) value") + assert.Equal(t, int16(0), val.Interface().(int16), "should have returned the default int16 value") + + val, err = int16TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid int16 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int16 (NaN)") + + tooBig := strconv.Itoa(math.MaxInt16 + 1) + val, err = int16TypeVar.Convert(tooBig) + assert.Error(t, err, fmt.Errorf(convertError, tooBig), "should return error for invalid int16 value (too large)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int16 (too large)") +} + +func TestInt32Type(t *testing.T) { + var int32TypeVar = new(int32Type) + + // Test GetSchema + assert.Equal(t, spec.Int32Property(), int32TypeVar.GetSchema(), "should return open api int32 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = int32TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid int32 (123) value") + assert.Equal(t, int32(123), val.Interface().(int32), "should have returned the int32 value 123") + + val, err = int32TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid int32 (blank) value") + assert.Equal(t, int32(0), val.Interface().(int32), "should have returned the default int32 value") + + val, err = int32TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid int32 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int32 (NaN)") + + tooBig := strconv.Itoa(math.MaxInt32 + 1) + val, err = int32TypeVar.Convert(tooBig) + assert.Error(t, err, fmt.Errorf(convertError, tooBig), "should return error for invalid int32 value (too large)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int32 (too large)") +} + +func TestInt64Type(t *testing.T) { + var int64TypeVar = new(int64Type) + + // Test GetSchema + assert.Equal(t, spec.Int64Property(), int64TypeVar.GetSchema(), "should return open api int64 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = int64TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid int64 (123) value") + assert.Equal(t, int64(123), val.Interface().(int64), "should have returned the int64 value 123") + + val, err = int64TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid int64 (blank) value") + assert.Equal(t, int64(0), val.Interface().(int64), "should have returned the default int64 value") + + val, err = int64TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid int64 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid int64 (NaN)") +} + +func TestUintType(t *testing.T) { + var uintTypeVar = new(uintType) + + // Test GetSchema + expectedSchema := spec.Float64Property() + multOf := float64(1) + expectedSchema.MultipleOf = &multOf + minimum := float64(0) + expectedSchema.Minimum = &minimum + maximum := float64(math.MaxUint64) + expectedSchema.Maximum = &maximum + actualSchema := uintTypeVar.GetSchema() + + assert.Equal(t, expectedSchema, actualSchema, "should return valid open api format that fits uints") + + // Test Convert + var val reflect.Value + var err error + + val, err = uintTypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid uint (123) value") + assert.Equal(t, uint(123), val.Interface().(uint), "should have returned the uint value 123") + + val, err = uintTypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid uint (blank) value") + assert.Equal(t, uint(0), val.Interface().(uint), "should have returned the default uint value") + + val, err = uintTypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid uint value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint (NaN)") + + val, err = uintTypeVar.Convert("-1") + assert.Error(t, err, fmt.Errorf(convertError, "-1"), "should return error for invalid uint value (-1)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint (-1)") +} + +func TestUint8Type(t *testing.T) { + var uint8TypeVar = new(uint8Type) + + // Test GetSchema + expectedSchema := spec.Int32Property() + minimum := float64(0) + expectedSchema.Minimum = &minimum + + maximum := float64(math.MaxUint8) + expectedSchema.Maximum = &maximum + actualSchema := uint8TypeVar.GetSchema() + + assert.Equal(t, expectedSchema, actualSchema, "should return valid open api format that fits uint8s") + + // Test Convert + var val reflect.Value + var err error + + val, err = uint8TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid uint8 (123) value") + assert.Equal(t, uint8(123), val.Interface().(uint8), "should have returned the uint8 value 123") + + val, err = uint8TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid uint8 (blank) value") + assert.Equal(t, uint8(0), val.Interface().(uint8), "should have returned the default uint8 value") + + val, err = uint8TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid uint8 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint8 (NaN)") + + val, err = uint8TypeVar.Convert("-1") + assert.Error(t, err, fmt.Errorf(convertError, "-1"), "should return error for invalid uint8 value (-1)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint8 (-1)") + + tooBig := fmt.Sprint(math.MaxUint8 + 1) + val, err = uint8TypeVar.Convert(tooBig) + assert.Error(t, err, fmt.Errorf(convertError, tooBig), "should return error for invalid uint8 value (too large)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint8 (too large)") +} + +func TestUint16Type(t *testing.T) { + var uint16TypeVar = new(uint16Type) + + // Test GetSchema + expectedSchema := spec.Int64Property() + minimum := float64(0) + expectedSchema.Minimum = &minimum + maximum := float64(math.MaxUint16) + expectedSchema.Maximum = &maximum + actualSchema := uint16TypeVar.GetSchema() + + assert.Equal(t, expectedSchema, actualSchema, "should return valid open api format that fits uint16s") + + // Test Convert + var val reflect.Value + var err error + + val, err = uint16TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid uint16 (123) value") + assert.Equal(t, uint16(123), val.Interface().(uint16), "should have returned the uint16 value 123") + + val, err = uint16TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid uint16 (blank) value") + assert.Equal(t, uint16(0), val.Interface().(uint16), "should have returned the default uint16 value") + + val, err = uint16TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid uint16 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint16 (NaN)") + + val, err = uint16TypeVar.Convert("-1") + assert.Error(t, err, fmt.Errorf(convertError, "-1"), "should return error for invalid uint16 value (-1)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint16 (-1)") + + tooBig := fmt.Sprint(math.MaxUint16 + 1) + val, err = uint16TypeVar.Convert(tooBig) + assert.Error(t, err, fmt.Errorf(convertError, tooBig), "should return error for invalid uint16 value (too large)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint16 (too large)") +} + +func TestUint32Type(t *testing.T) { + var uint32TypeVar = new(uint32Type) + + // Test GetSchema + expectedSchema := spec.Int64Property() + minimum := float64(0) + expectedSchema.Minimum = &minimum + maximum := float64(math.MaxUint32) + expectedSchema.Maximum = &maximum + actualSchema := uint32TypeVar.GetSchema() + + assert.Equal(t, expectedSchema, actualSchema, "should return valid open api format that fits uint32s") + + // Test Convert + var val reflect.Value + var err error + + val, err = uint32TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid uint32 (123) value") + assert.Equal(t, uint32(123), val.Interface().(uint32), "should have returned the uint32 value 123") + + val, err = uint32TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid uint32 (blank) value") + assert.Equal(t, uint32(0), val.Interface().(uint32), "should have returned the default uint32 value") + + val, err = uint32TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid uint32 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint32 (NaN)") + + val, err = uint32TypeVar.Convert("-1") + assert.Error(t, err, fmt.Errorf(convertError, "-1"), "should return error for invalid uint32 value (-1)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint32 (-1)") + + tooBig := fmt.Sprint(math.MaxUint32 + 1) + val, err = uint32TypeVar.Convert(tooBig) + assert.Error(t, err, fmt.Errorf(convertError, tooBig), "should return error for invalid uint32 value (too large)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint32 (too large)") +} + +func TestUint64Type(t *testing.T) { + var uint64TypeVar = new(uint64Type) + + // Test GetSchema + expectedSchema := spec.Float64Property() + multOf := float64(1) + expectedSchema.MultipleOf = &multOf + minimum := float64(0) + expectedSchema.Minimum = &minimum + maximum := float64(math.MaxUint64) + expectedSchema.Maximum = &maximum + actualSchema := uint64TypeVar.GetSchema() + + assert.Equal(t, expectedSchema, actualSchema, "should return valid open api format that fits uint64s") + + // Test Convert + var val reflect.Value + var err error + + val, err = uint64TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid uint64 (123) value") + assert.Equal(t, uint64(123), val.Interface().(uint64), "should have returned the uint64 value 123") + + val, err = uint64TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid uint64 (blank) value") + assert.Equal(t, uint64(0), val.Interface().(uint64), "should have returned the default uint64 value") + + val, err = uint64TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid uint64 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint64 (NaN)") + + val, err = uint64TypeVar.Convert("-1") + assert.Error(t, err, fmt.Errorf(convertError, "-1"), "should return error for invalid uint64 value (-1)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint64 (-1)") +} + +func TestFloat32Type(t *testing.T) { + var float32TypeVar = new(float32Type) + + // Test GetSchema + assert.Equal(t, spec.Float32Property(), float32TypeVar.GetSchema(), "should return open api float32 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = float32TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid float32 (123) value") + assert.Equal(t, float32(123), val.Interface().(float32), "should have returned the float32 value 123") + + val, err = float32TypeVar.Convert("123.456") + assert.Nil(t, err, "should not return error for valid float32 (123.456) value") + assert.Equal(t, float32(123.456), val.Interface().(float32), "should have returned the float32 value 123.456") + + val, err = float32TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid float32 (blank) value") + assert.Equal(t, float32(0), val.Interface().(float32), "should have returned the default float32 value") + + val, err = float32TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid float32 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid float32 (NaN)") + + tooBig := fmt.Sprint(math.MaxFloat64) + val, err = float32TypeVar.Convert(tooBig) + assert.Error(t, err, fmt.Errorf(convertError, tooBig), "should return error for invalid uint32 value (too large)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid uint32 (too large)") +} + +func TestFloat64Type(t *testing.T) { + var float64TypeVar = new(float64Type) + + // Test GetSchema + assert.Equal(t, spec.Float64Property(), float64TypeVar.GetSchema(), "should return open api float64 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = float64TypeVar.Convert("123") + assert.Nil(t, err, "should not return error for valid float64 (123) value") + assert.Equal(t, float64(123), val.Interface().(float64), "should have returned the float64 value 123") + + val, err = float64TypeVar.Convert("123.456") + assert.Nil(t, err, "should not return error for valid float64 (123.456) value") + assert.Equal(t, float64(123.456), val.Interface().(float64), "should have returned the float64 value 123.456") + + val, err = float64TypeVar.Convert("") + assert.Nil(t, err, "should not return error for valid float64 (blank) value") + assert.Equal(t, float64(0), val.Interface().(float64), "should have returned the default float64 value") + + val, err = float64TypeVar.Convert("not a number") + assert.Error(t, err, fmt.Errorf(convertError, "not a number"), "should return error for invalid float64 value (NaN)") + assert.Equal(t, reflect.Value{}, val, "should have returned the blank value for invalid float64 (NaN)") +} + +func TestInterfaceType(t *testing.T) { + var interfaceTypeVar = new(interfaceType) + + // Test GetSchema + assert.Equal(t, new(spec.Schema), interfaceTypeVar.GetSchema(), "should return open api float64 spec") + + // Test Convert + var val reflect.Value + var err error + + val, err = interfaceTypeVar.Convert("hello world") + assert.Nil(t, err, "should never return error for interface") + assert.Equal(t, "hello world", val.Interface().(string), "should return string that went in") +} diff --git a/v2/internal/types_handler.go b/v2/internal/types_handler.go new file mode 100644 index 0000000..dcbe3ea --- /dev/null +++ b/v2/internal/types_handler.go @@ -0,0 +1,156 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "fmt" + "reflect" + "sort" + "unicode" + + "github.com/hyperledger/fabric-contract-api-go/v2/internal/types" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/utils" +) + +func basicTypesAsSlice() []string { + typesArr := []string{} + + for el := range types.BasicTypes { + typesArr = append(typesArr, el.String()) + } + sort.Strings(typesArr) + + return typesArr +} + +func listBasicTypes() string { + return utils.SliceAsCommaSentence(basicTypesAsSlice()) +} + +func arrayOfValidType(array reflect.Value, additionalTypes []reflect.Type) error { + if array.Len() < 1 { + return fmt.Errorf("arrays must have length greater than 0") + } + + return typeIsValid(array.Index(0).Type(), additionalTypes, false) +} + +func structOfValidType(obj reflect.Type, additionalTypes []reflect.Type) error { + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + + for i := 0; i < obj.NumField(); i++ { + field := obj.Field(i) + + if runes := []rune(field.Name); len(runes) > 0 && !unicode.IsUpper(runes[0]) && field.Tag.Get("metadata") == "" { + // Skip validation for private fields, except those tagged as metadata + continue + } + + err := typeIsValid(field.Type, additionalTypes, false) + + if err != nil { + return err + } + } + + return nil +} + +func typeInSlice(a reflect.Type, list []reflect.Type) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + +func typeIsValid(t reflect.Type, additionalTypes []reflect.Type, allowError bool) error { + kind := t.Kind() + if kind == reflect.Array { + array := reflect.New(t).Elem() + return arrayOfValidType(array, additionalTypes) + } else if kind == reflect.Slice { + slice := reflect.MakeSlice(t, 1, 1) + return typeIsValid(slice.Index(0).Type(), additionalTypes, false) + } else if kind == reflect.Map { + if t.Key().Kind() != reflect.String { + return fmt.Errorf("map key type %s is not valid. Expected string", t.Key().String()) + } + + return typeIsValid(t.Elem(), additionalTypes, false) + } else if !typeInSlice(t, additionalTypes) { + if kind == reflect.Struct { + additionalTypes = append(additionalTypes, t) + additionalTypes = append(additionalTypes, reflect.PointerTo(t)) + // add self for cyclic + return structOfValidType(t, additionalTypes) + } else if kind == reflect.Ptr && t.Elem().Kind() == reflect.Struct { + additionalTypes = append(additionalTypes, t) + additionalTypes = append(additionalTypes, t.Elem()) + // add self for cyclic + return structOfValidType(t, additionalTypes) + } else if _, ok := types.BasicTypes[t.Kind()]; !ok || (!allowError && t == types.ErrorType) || (t.Kind() == reflect.Interface && t.String() != "interface {}" && t.String() != "error") { + errStr := "" + + if allowError { + errStr = " error," + } + + return fmt.Errorf("type %s is not valid. Expected a struct or one of the basic types%s %s or an array/slice of these", t.String(), errStr, listBasicTypes()) + } + } + + return nil +} + +func typeMatchesInterface(toMatch reflect.Type, iface reflect.Type) error { + if iface.Kind() != reflect.Interface { + return fmt.Errorf("type passed for interface is not an interface") + } + + for i := 0; i < iface.NumMethod(); i++ { + ifaceMethod := iface.Method(i) + matchMethod, exists := toMatch.MethodByName(ifaceMethod.Name) + + if !exists { + return fmt.Errorf("missing function %s", ifaceMethod.Name) + } + + ifaceNumIn := ifaceMethod.Type.NumIn() + matchNumIn := matchMethod.Type.NumIn() - 1 // skip over which the function is acting on + + if ifaceNumIn != matchNumIn { + return fmt.Errorf("parameter mismatch in method %s. Expected %d, got %d", ifaceMethod.Name, ifaceNumIn, matchNumIn) + } + + for j := 0; j < ifaceNumIn; j++ { + ifaceIn := ifaceMethod.Type.In(j) + matchIn := matchMethod.Type.In(j + 1) + + if ifaceIn.Kind() != matchIn.Kind() { + return fmt.Errorf("parameter mismatch in method %s at parameter %d. Expected %s, got %s", ifaceMethod.Name, j, ifaceIn.Name(), matchIn.Name()) + } + } + + ifaceNumOut := ifaceMethod.Type.NumOut() + matchNumOut := matchMethod.Type.NumOut() + if ifaceNumOut != matchNumOut { + return fmt.Errorf("return mismatch in method %s. Expected %d, got %d", ifaceMethod.Name, ifaceNumOut, matchNumOut) + } + + for j := 0; j < ifaceNumOut; j++ { + ifaceOut := ifaceMethod.Type.Out(j) + matchOut := matchMethod.Type.Out(j) + + if ifaceOut.Kind() != matchOut.Kind() { + return fmt.Errorf("return mismatch in method %s at return %d. Expected %s, got %s", ifaceMethod.Name, j, ifaceOut.Name(), matchOut.Name()) + } + } + } + + return nil +} diff --git a/v2/internal/types_handler_test.go b/v2/internal/types_handler_test.go new file mode 100644 index 0000000..0c757f3 --- /dev/null +++ b/v2/internal/types_handler_test.go @@ -0,0 +1,369 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package internal + +import ( + "errors" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/hyperledger/fabric-contract-api-go/v2/internal/types" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/utils" + "github.com/stretchr/testify/assert" +) + +// ================================ +// HELPERS +// ================================ +const basicErr = "type %s is not valid. Expected a struct or one of the basic types %s or an array/slice of these" + +type goodStruct struct { + Prop1 string + Prop2 int `json:"prop2"` +} + +type BadStruct struct { + Prop1 string `json:"Prop1"` + Prop2 complex64 `json:"prop2"` +} + +type goodStructWithBadPrivateFields struct { + //lint:ignore U1000 unused + unexported complex64 + Valid int `json:"Valid"` +} + +type badStructWithMetadataPrivateFields struct { + Prop string `json:"prop"` + //lint:ignore U1000 unused + class complex64 `metadata:"class"` +} + +type UsefulInterface interface{} + +var badType = reflect.TypeOf(complex64(1)) +var badArrayType = reflect.TypeOf([1]complex64{}) +var badSliceType = reflect.TypeOf([]complex64{}) +var badMapItemType = reflect.TypeOf(map[string]complex64{}) +var badMapKeyType = reflect.TypeOf(map[complex64]string{}) + +var boolRefType = reflect.TypeOf(true) +var stringRefType = reflect.TypeOf("") +var intRefType = reflect.TypeOf(1) +var int8RefType = reflect.TypeOf(int8(1)) +var int16RefType = reflect.TypeOf(int16(1)) +var int32RefType = reflect.TypeOf(int32(1)) +var int64RefType = reflect.TypeOf(int64(1)) +var uintRefType = reflect.TypeOf(uint(1)) +var uint8RefType = reflect.TypeOf(uint8(1)) +var uint16RefType = reflect.TypeOf(uint16(1)) +var uint32RefType = reflect.TypeOf(uint32(1)) +var uint64RefType = reflect.TypeOf(uint64(1)) +var float32RefType = reflect.TypeOf(float32(1.0)) +var float64RefType = reflect.TypeOf(1.0) + +type myInterface interface { + SomeFunction(string, int) (string, error) +} + +type structFailsParamLength struct{} + +func (s *structFailsParamLength) SomeFunction(param1 string) (string, error) { + return "", nil +} + +type structFailsParamType struct{} + +func (s *structFailsParamType) SomeFunction(param1 string, param2 float32) (string, error) { + return "", nil +} + +type structFailsReturnLength struct{} + +func (s *structFailsReturnLength) SomeFunction(param1 string, param2 int) string { + return "" +} + +type structFailsReturnType struct{} + +func (s *structFailsReturnType) SomeFunction(param1 string, param2 int) (string, int) { + return "", 0 +} + +type structMeetsInterface struct{} + +func (s *structMeetsInterface) SomeFunction(param1 string, param2 int) (string, error) { + return "", nil +} + +// ================================ +// TESTS +// ================================ + +func TestListBasicTypes(t *testing.T) { + types := []string{"bool", "float32", "float64", "int", "int16", "int32", "int64", "int8", "interface", "string", "uint", "uint16", "uint32", "uint64", "uint8"} + + assert.Equal(t, utils.SliceAsCommaSentence(types), listBasicTypes(), "should return basic types as a human readable list") +} + +func TestArrayOfValidType(t *testing.T) { + // Further tested by typeIsValid array tests + + var err error + + zeroArr := [0]int{} + err = arrayOfValidType(reflect.ValueOf(zeroArr), []reflect.Type{}) + assert.Equal(t, errors.New("arrays must have length greater than 0"), err, "should throw error when 0 length array passed") + + badArr := [1]complex128{} + err = arrayOfValidType(reflect.ValueOf(badArr), []reflect.Type{}) + assert.EqualError(t, err, typeIsValid(reflect.TypeOf(complex128(1)), []reflect.Type{}, false).Error(), "should throw error when invalid type passed") +} + +func TestStructOfValidType(t *testing.T) { + assert.Nil(t, structOfValidType(reflect.TypeOf(new(goodStruct)), []reflect.Type{}), "should not return an error for a pointer struct") + + assert.Nil(t, structOfValidType(reflect.TypeOf(goodStruct{}), []reflect.Type{}), "should not return an error for a valid struct") + + assert.EqualError(t, structOfValidType(reflect.TypeOf(BadStruct{}), []reflect.Type{}), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should return an error for invalid struct") + + assert.Nil(t, structOfValidType(reflect.TypeOf(goodStructWithBadPrivateFields{}), []reflect.Type{}), "should not return an error for unexported fields") + + assert.EqualError(t, structOfValidType(reflect.TypeOf(badStructWithMetadataPrivateFields{}), []reflect.Type{}), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should return an error for invalid metadata private fields") +} + +func TestTypeIsValid(t *testing.T) { + // HELPERS + badArr := reflect.New(badArrayType).Elem() + + type goodStruct2 struct { + Prop1 goodStruct + } + + type goodStruct3 struct { + Prop1 *goodStruct + } + + type goodStruct4 struct { + Prop1 interface{} + } + + type goodStruct5 struct { + Prop1 *goodStruct5 + } + + type BadStruct2 struct { + Prop1 BadStruct + } + + type BadStruct3 struct { + Prop1 UsefulInterface + } + + // TESTS + assert.Nil(t, typeIsValid(boolRefType, []reflect.Type{}, false), "should not return an error for a bool type") + assert.Nil(t, typeIsValid(stringRefType, []reflect.Type{}, false), "should not return an error for a string type") + assert.Nil(t, typeIsValid(intRefType, []reflect.Type{}, false), "should not return an error for int type") + assert.Nil(t, typeIsValid(int8RefType, []reflect.Type{}, false), "should not return an error for int8 type") + assert.Nil(t, typeIsValid(int16RefType, []reflect.Type{}, false), "should not return an error for int16 type") + assert.Nil(t, typeIsValid(int32RefType, []reflect.Type{}, false), "should not return an error for int32 type") + assert.Nil(t, typeIsValid(int64RefType, []reflect.Type{}, false), "should not return an error for int64 type") + assert.Nil(t, typeIsValid(uintRefType, []reflect.Type{}, false), "should not return an error for uint type") + assert.Nil(t, typeIsValid(uint8RefType, []reflect.Type{}, false), "should not return an error for uint8 type") + assert.Nil(t, typeIsValid(uint16RefType, []reflect.Type{}, false), "should not return an error for uint16 type") + assert.Nil(t, typeIsValid(uint32RefType, []reflect.Type{}, false), "should not return an error for uint32 type") + assert.Nil(t, typeIsValid(uint64RefType, []reflect.Type{}, false), "should not return an error for uint64 type") + assert.Nil(t, typeIsValid(float32RefType, []reflect.Type{}, false), "should not return an error for float32 type") + assert.Nil(t, typeIsValid(float64RefType, []reflect.Type{}, false), "should not return an error for float64 type") + assert.Nil(t, typeIsValid(float64RefType, []reflect.Type{}, false), "should not return an error for float64 type") + assert.Nil(t, typeIsValid(reflect.TypeOf(goodStruct4{}).Field(0).Type, []reflect.Type{}, false), "should not return error for interface{} type") + + assert.Nil(t, typeIsValid(types.ErrorType, []reflect.Type{}, true), "should not return an error for error type on allow error") + assert.Nil(t, typeIsValid(types.TimeType, []reflect.Type{}, false), "should not return an error for time type on allow error") + + assert.Nil(t, typeIsValid(reflect.TypeOf([1]string{}), []reflect.Type{}, false), "should not return an error for a string array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]bool{}), []reflect.Type{}, false), "should not return an error for a bool array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]int{}), []reflect.Type{}, false), "should not return an error for an int array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]int8{}), []reflect.Type{}, false), "should not return an error for an int8 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]int16{}), []reflect.Type{}, false), "should not return an error for an int16 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]int32{}), []reflect.Type{}, false), "should not return an error for an int32 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]int64{}), []reflect.Type{}, false), "should not return an error for an int64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]uint{}), []reflect.Type{}, false), "should not return an error for a uint array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]uint8{}), []reflect.Type{}, false), "should not return an error for a uint8 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]uint16{}), []reflect.Type{}, false), "should not return an error for a uint16 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]uint32{}), []reflect.Type{}, false), "should not return an error for a uint32 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]uint64{}), []reflect.Type{}, false), "should not return an error for a uint64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]float32{}), []reflect.Type{}, false), "should not return an error for a float32 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]float64{}), []reflect.Type{}, false), "should not return an error for a float64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]byte{}), []reflect.Type{}, false), "should not return an error for a float64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1]rune{}), []reflect.Type{}, false), "should not return an error for a float64 array type") + + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]string{}), []reflect.Type{}, false), "should not return an error for a multidimensional string array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]bool{}), []reflect.Type{}, false), "should not return an error for a multidimensional bool array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]int{}), []reflect.Type{}, false), "should not return an error for an multidimensional int array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]int8{}), []reflect.Type{}, false), "should not return an error for an multidimensional int8 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]int16{}), []reflect.Type{}, false), "should not return an error for an multidimensional int16 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]int32{}), []reflect.Type{}, false), "should not return an error for an multidimensional int32 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]int64{}), []reflect.Type{}, false), "should not return an error for an multidimensional int64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]uint{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]uint8{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint8 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]uint16{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint16 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]uint32{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint32 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]uint64{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]float32{}), []reflect.Type{}, false), "should not return an error for a multidimensional float32 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]float64{}), []reflect.Type{}, false), "should not return an error for a multidimensional float64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]byte{}), []reflect.Type{}, false), "should not return an error for a multidimensional float64 array type") + assert.Nil(t, typeIsValid(reflect.TypeOf([1][1]rune{}), []reflect.Type{}, false), "should not return an error for a multidimensional float64 array type") + + assert.Nil(t, typeIsValid(reflect.TypeOf([1][2][3][4][5][6][7][8]string{}), []reflect.Type{}, false), "should not return an error for a very multidimensional string array type") + + assert.Nil(t, typeIsValid(reflect.TypeOf([]string{}), []reflect.Type{}, false), "should not return an error for a string slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]bool{}), []reflect.Type{}, false), "should not return an error for a bool slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]int{}), []reflect.Type{}, false), "should not return an error for a int slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]int8{}), []reflect.Type{}, false), "should not return an error for a int8 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]int16{}), []reflect.Type{}, false), "should not return an error for a int16 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]int32{}), []reflect.Type{}, false), "should not return an error for a int32 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]int64{}), []reflect.Type{}, false), "should not return an error for a int64 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]uint{}), []reflect.Type{}, false), "should not return an error for a uint slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]uint8{}), []reflect.Type{}, false), "should not return an error for a uint8 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]uint16{}), []reflect.Type{}, false), "should not return an error for a uint16 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]uint32{}), []reflect.Type{}, false), "should not return an error for a uint32 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]uint64{}), []reflect.Type{}, false), "should not return an error for a uint64 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]float32{}), []reflect.Type{}, false), "should not return an error for a float32 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]float64{}), []reflect.Type{}, false), "should not return an error for a float64 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]byte{}), []reflect.Type{}, false), "should not return an error for a byte slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([]rune{}), []reflect.Type{}, false), "should not return an error for a rune slice type") + + assert.Nil(t, typeIsValid(reflect.TypeOf([][]string{}), []reflect.Type{}, false), "should not return an error for a multidimensional string slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]bool{}), []reflect.Type{}, false), "should not return an error for a multidimensional bool slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]int{}), []reflect.Type{}, false), "should not return an error for a multidimensional int slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]int8{}), []reflect.Type{}, false), "should not return an error for a multidimensional int8 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]int16{}), []reflect.Type{}, false), "should not return an error for a multidimensional int16 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]int32{}), []reflect.Type{}, false), "should not return an error for a multidimensional int32 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]int64{}), []reflect.Type{}, false), "should not return an error for a multidimensional int64 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]uint{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]uint8{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint8 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]uint16{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint16 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]uint32{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint32 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]uint64{}), []reflect.Type{}, false), "should not return an error for a multidimensional uint64 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]float32{}), []reflect.Type{}, false), "should not return an error for a multidimensional float32 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]float64{}), []reflect.Type{}, false), "should not return an error for a multidimensional float64 slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]byte{}), []reflect.Type{}, false), "should not return an error for a multidimensional byte slice type") + assert.Nil(t, typeIsValid(reflect.TypeOf([][]rune{}), []reflect.Type{}, false), "should not return an error for a multidimensional rune slice type") + + assert.Nil(t, typeIsValid(reflect.TypeOf([][][][][][][][]string{}), []reflect.Type{}, false), "should not return an error for a very multidimensional string slice type") + + assert.Nil(t, typeIsValid(reflect.TypeOf([2][]string{}), []reflect.Type{}, false), "should not return an error for a string slice of array type") + + assert.Nil(t, typeIsValid(reflect.TypeOf(goodStruct{}), []reflect.Type{}, false), "should not return an error for a valid struct") + + assert.Nil(t, typeIsValid(reflect.TypeOf([1]goodStruct{}), []reflect.Type{}, false), "should not return an error for an array of valid struct") + + assert.Nil(t, typeIsValid(reflect.TypeOf([]goodStruct{}), []reflect.Type{}, false), "should not return an error for a slice of valid struct") + + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]string{}), []reflect.Type{}, false), "should not return an error for a map string item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]bool{}), []reflect.Type{}, false), "should not return an error for a map bool item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]int{}), []reflect.Type{}, false), "should not return an error for a map int item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]int8{}), []reflect.Type{}, false), "should not return an error for a map int8 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]int16{}), []reflect.Type{}, false), "should not return an error for a map int16 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]int32{}), []reflect.Type{}, false), "should not return an error for a map int32 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]int64{}), []reflect.Type{}, false), "should not return an error for a map int64 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]uint{}), []reflect.Type{}, false), "should not return an error for a map uint item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]uint8{}), []reflect.Type{}, false), "should not return an error for a map uint8 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]uint16{}), []reflect.Type{}, false), "should not return an error for a map uint16 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]uint32{}), []reflect.Type{}, false), "should not return an error for a map uint32 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]uint64{}), []reflect.Type{}, false), "should not return an error for a map uint64 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]float32{}), []reflect.Type{}, false), "should not return an error for a map float32 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]float64{}), []reflect.Type{}, false), "should not return an error for a map float64 item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]byte{}), []reflect.Type{}, false), "should not return an error for a map byte item type") + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]rune{}), []reflect.Type{}, false), "should not return an error for a map rune item type") + + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]map[string]string{}), []reflect.Type{}, false), "should not return an error for a map of map") + + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string]goodStruct{}), []reflect.Type{}, false), "should not return an error for a map with struct item type") + + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string][1]string{}), []reflect.Type{}, false), "should not return an error for a map with string array item type") + + assert.Nil(t, typeIsValid(reflect.TypeOf(map[string][]string{}), []reflect.Type{}, false), "should not return an error for a map with string slice item type") + + assert.Nil(t, typeIsValid(reflect.TypeOf(goodStruct2{}), []reflect.Type{}, false), "should not return an error for a valid struct with struct property") + + assert.Nil(t, typeIsValid(reflect.TypeOf(goodStruct3{}), []reflect.Type{}, false), "should not return an error for a valid struct with struct ptr property") + + assert.Nil(t, typeIsValid(reflect.TypeOf(goodStruct5{}), []reflect.Type{}, false), "should not return an error for a valid struct with cyclic dependency") + + assert.Nil(t, typeIsValid(badType, []reflect.Type{badType}, false), "should not error when type not in basic types but is in additional types") + assert.Nil(t, typeIsValid(reflect.TypeOf(BadStruct{}), []reflect.Type{reflect.TypeOf(BadStruct{})}, false), "should not error when bad struct is in additional types") + assert.Nil(t, typeIsValid(reflect.TypeOf(BadStruct2{}), []reflect.Type{reflect.TypeOf(BadStruct{})}, false), "should not error when bad struct is in additional types and passed type has that as property") + + assert.EqualError(t, typeIsValid(badType, []reflect.Type{}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should have returned error for invalid basic type") + + assert.EqualError(t, typeIsValid(badArrayType, []reflect.Type{}, false), arrayOfValidType(badArr, []reflect.Type{}).Error(), "should have returned error for invalid array type") + + assert.EqualError(t, typeIsValid(badSliceType, []reflect.Type{}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should have returned error for invalid slice type") + + assert.EqualError(t, typeIsValid(badMapItemType, []reflect.Type{}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should have returned error for invalid map item type") + + assert.EqualError(t, typeIsValid(badMapKeyType, []reflect.Type{}, false), "map key type complex64 is not valid. Expected string", "should have returned error for invalid map key type") + + zeroMultiArr := [1][0]int{} + err := typeIsValid(reflect.TypeOf(zeroMultiArr), []reflect.Type{}, false) + assert.Equal(t, errors.New("arrays must have length greater than 0"), err, "should throw error when 0 length array passed in multi level array") + + err = typeIsValid(types.ErrorType, []reflect.Type{}, false) + assert.EqualError(t, err, fmt.Sprintf(basicErr, types.ErrorType.String(), listBasicTypes()), "should throw error when error passed and allowError false") + + badMultiArr := [1][1]complex128{} + err = typeIsValid(reflect.TypeOf(badMultiArr), []reflect.Type{}, false) + assert.Equal(t, fmt.Errorf(basicErr, "complex128", listBasicTypes()), err, "should throw error when bad multidimensional array passed") + + badMultiSlice := [][]complex128{} + err = typeIsValid(reflect.TypeOf(badMultiSlice), []reflect.Type{}, false) + assert.Equal(t, fmt.Errorf(basicErr, "complex128", listBasicTypes()), err, "should throw error when 0 length array passed") + + assert.EqualError(t, typeIsValid(reflect.TypeOf([]BadStruct{}), []reflect.Type{}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should return an error for array of invalid struct") + + assert.EqualError(t, typeIsValid(reflect.TypeOf([]BadStruct{}), []reflect.Type{}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should return an error for slice of invalid struct") + + assert.EqualError(t, typeIsValid(reflect.TypeOf(BadStruct2{}), []reflect.Type{}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should return an error for struct with invalid property of a struct") + + assert.EqualError(t, typeIsValid(reflect.TypeOf(BadStruct2{}), []reflect.Type{}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should return an error for struct with invalid property of a pointer to struct") + + assert.EqualError(t, typeIsValid(reflect.TypeOf(BadStruct3{}), []reflect.Type{}, false), fmt.Sprintf(basicErr, "internal.UsefulInterface", listBasicTypes()), "should return an error for struct with invalid property of an interface not (interface{})") + + assert.EqualError(t, typeIsValid(badArrayType, []reflect.Type{badArrayType}, false), arrayOfValidType(badArr, []reflect.Type{badArrayType}).Error(), "should have returned error for invalid array type") + + assert.EqualError(t, typeIsValid(badSliceType, []reflect.Type{badSliceType}, false), fmt.Sprintf(basicErr, badType.String(), listBasicTypes()), "should have returned error for invalid slice type") + + assert.EqualError(t, typeIsValid(badType, []reflect.Type{}, true), fmt.Sprintf(strings.Replace(basicErr, "types", "types%s", 1), badType.String(), " error,", listBasicTypes()), "should have returned include error in list of valid types") +} + +func TestTypeMatchesInterface(t *testing.T) { + var err error + + interfaceType := reflect.TypeOf((*myInterface)(nil)).Elem() + + err = typeMatchesInterface(reflect.TypeOf(new(BadStruct)), reflect.TypeOf("")) + assert.EqualError(t, err, "type passed for interface is not an interface", "should error when type passed is not an interface") + + err = typeMatchesInterface(reflect.TypeOf(new(BadStruct)), interfaceType) + assert.EqualError(t, err, "missing function SomeFunction", "should error when type passed is missing required method in interface") + + err = typeMatchesInterface(reflect.TypeOf(new(structFailsParamLength)), interfaceType) + assert.EqualError(t, err, "parameter mismatch in method SomeFunction. Expected 2, got 1", "should error when type passed has method but different number of parameters") + + err = typeMatchesInterface(reflect.TypeOf(new(structFailsParamType)), interfaceType) + assert.EqualError(t, err, "parameter mismatch in method SomeFunction at parameter 1. Expected int, got float32", "should error when type passed has method but different parameter types") + + err = typeMatchesInterface(reflect.TypeOf(new(structFailsReturnLength)), interfaceType) + assert.EqualError(t, err, "return mismatch in method SomeFunction. Expected 2, got 1", "should error when type passed has method but different number of returns") + + err = typeMatchesInterface(reflect.TypeOf(new(structFailsReturnType)), interfaceType) + assert.EqualError(t, err, "return mismatch in method SomeFunction at return 1. Expected error, got int", "should error when type passed has method but different return types") + + err = typeMatchesInterface(reflect.TypeOf(new(structMeetsInterface)), interfaceType) + assert.Nil(t, err, "should not error when struct meets interface") +} diff --git a/v2/internal/utils/utils.go b/v2/internal/utils/utils.go new file mode 100644 index 0000000..44e8c08 --- /dev/null +++ b/v2/internal/utils/utils.go @@ -0,0 +1,42 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "sort" + "strconv" + "strings" + + "github.com/xeipuuv/gojsonschema" +) + +// ValidateErrorsToString converts errors from JSON schema output into readable string +func ValidateErrorsToString(resErrors []gojsonschema.ResultError) string { + toReturn := "" + + sort.Slice(resErrors[:], func(i, j int) bool { + return resErrors[i].String() < resErrors[j].String() + }) + + for i, v := range resErrors { + toReturn += strconv.Itoa(i+1) + ". " + v.String() + "\n" + } + + return strings.Trim(toReturn, "\n") +} + +// StringInSlice returns whether string exists in string slice +func StringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + +// SliceAsCommaSentence returns string slice as comma separated sentence +func SliceAsCommaSentence(slice []string) string { + return strings.Replace(strings.Join(slice, " and "), " and ", ", ", len(slice)-2) +} diff --git a/v2/internal/utils/utils_test.go b/v2/internal/utils/utils_test.go new file mode 100644 index 0000000..7a9abee --- /dev/null +++ b/v2/internal/utils/utils_test.go @@ -0,0 +1,76 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/xeipuuv/gojsonschema" +) + +// ================================ +// HELPERS +// ================================ + +type MyResultError struct { + gojsonschema.ResultError + message string +} + +func (re MyResultError) String() string { + return re.message +} + +// ================================ +// TESTS +// ================================ + +func TestMain(m *testing.M) { + rc := m.Run() + + if rc == 0 && testing.CoverMode() != "" { + c := testing.Coverage() + + if c < 1 { + fmt.Println("Tests passed but coverage failed at", c) + rc = -1 + } + } + + os.Exit(rc) +} + +func TestValidateErrorsToString(t *testing.T) { + // should join errors with a new line + error1 := MyResultError{ + message: "some error message", + } + error2 := MyResultError{ + message: "yet another error message", + } + + assert.Equal(t, "1. some error message", ValidateErrorsToString([]gojsonschema.ResultError{error1}), "should return nicely formatted single error") + assert.Equal(t, "1. some error message\n2. yet another error message", ValidateErrorsToString([]gojsonschema.ResultError{error1, error2}), "should return nicely formatted multiple error") +} + +func TestStringInSlice(t *testing.T) { + slice := []string{"word", "another word"} + + // Should return true when string present in slice + assert.True(t, StringInSlice("word", slice), "should have returned true when string in slice") + + // Should return false when string not present in slice + assert.False(t, StringInSlice("bad word", slice), "should have returned true when string in slice") +} + +func TestSliceAsCommaSentence(t *testing.T) { + slice := []string{"one", "two", "three"} + + assert.Equal(t, "one, two and three", SliceAsCommaSentence(slice), "should have put commas between slice elements and join last element with and") + + assert.Equal(t, "one", SliceAsCommaSentence([]string{"one"}), "should handle single item") +} diff --git a/v2/metadata/a_metadata-packr.go b/v2/metadata/a_metadata-packr.go new file mode 100644 index 0000000..300a1d6 --- /dev/null +++ b/v2/metadata/a_metadata-packr.go @@ -0,0 +1,13 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +// Code generated by github.com/gobuffalo/packr. DO NOT EDIT. + +package metadata + +import "github.com/gobuffalo/packr" + +// You can use the "packr clean" command to clean up this, +// and any other packr generated files. +func init() { + packr.PackJSONBytes("./schema", "schema.json", "\"ewogICAgIiRzY2hlbWEiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNC9zY2hlbWEjIiwKICAgICJ0eXBlIjogIm9iamVjdCIsCiAgICAidGl0bGUiOiAiSHlwZXJsZWRnZXIgRmFicmljIENvbnRyYWN0IERlZmluaXRpb24gSlNPTiBTY2hlbWEiLAogICAgInJlcXVpcmVkIjogWwogICAgICAgICJpbmZvIiwKICAgICAgICAiY29udHJhY3RzIgogICAgXSwKICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICJpbmZvIjogewogICAgICAgICAgICAiJHJlZiI6ICIjL2RlZmluaXRpb25zL2luZm8iCiAgICAgICAgfSwKICAgICAgICAiY29udHJhY3RzIjogewogICAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgICAicGF0dGVyblByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICAgICAiXi4qJCI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICIjL2RlZmluaXRpb25zL2NvbnRyYWN0IgogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICAiY29tcG9uZW50cyI6IHsKICAgICAgICAgICAgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9jb21wb25lbnRzIgogICAgICAgIH0KICAgIH0sCiAgICAiZGVmaW5pdGlvbnMiOiB7CiAgICAgICAgImluZm8iOiB7CiAgICAgICAgICAgICJ0eXBlIjogIm9iamVjdCIsCiAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJHZW5lcmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBBUEkuIiwKICAgICAgICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICAgICAgICAgInZlcnNpb24iLAogICAgICAgICAgICAgICAgInRpdGxlIgogICAgICAgICAgICBdLAogICAgICAgICAgICAicHJvcGVydGllcyI6IHsKICAgICAgICAgICAgICAgICJ0aXRsZSI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJBIHVuaXF1ZSBhbmQgcHJlY2lzZSB0aXRsZSBvZiB0aGUgQVBJLiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAidmVyc2lvbiI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJBIHNlbWFudGljIHZlcnNpb24gbnVtYmVyIG9mIHRoZSBBUEkuIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJBIGxvbmdlciBkZXNjcmlwdGlvbiBvZiB0aGUgQVBJLiBTaG91bGQgYmUgZGlmZmVyZW50IGZyb20gdGhlIHRpdGxlLiAgR2l0SHViIEZsYXZvcmVkIE1hcmtkb3duIGlzIGFsbG93ZWQuIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJ0ZXJtc09mU2VydmljZSI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJUaGUgdGVybXMgb2Ygc2VydmljZSBmb3IgdGhlIEFQSS4iCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgImNvbnRhY3QiOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9jb250YWN0IgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJsaWNlbnNlIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvbGljZW5zZSIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgImNvbnRhY3QiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIm9iamVjdCIsCiAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJDb250YWN0IGluZm9ybWF0aW9uIGZvciB0aGUgb3duZXJzIG9mIHRoZSBBUEkuIiwKICAgICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICAgICAibmFtZSI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJUaGUgaWRlbnRpZnlpbmcgbmFtZSBvZiB0aGUgY29udGFjdCBwZXJzb24vb3JnYW5pemF0aW9uLiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAidXJsIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIlRoZSBVUkwgcG9pbnRpbmcgdG8gdGhlIGNvbnRhY3QgaW5mb3JtYXRpb24uIiwKICAgICAgICAgICAgICAgICAgICAiZm9ybWF0IjogInVyaSIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZW1haWwiOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiVGhlIGVtYWlsIGFkZHJlc3Mgb2YgdGhlIGNvbnRhY3QgcGVyc29uL29yZ2FuaXphdGlvbi4iLAogICAgICAgICAgICAgICAgICAgICJmb3JtYXQiOiAiZW1haWwiCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgICJsaWNlbnNlIjogewogICAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgICAicmVxdWlyZWQiOiBbCiAgICAgICAgICAgICAgICAibmFtZSIKICAgICAgICAgICAgXSwKICAgICAgICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogZmFsc2UsCiAgICAgICAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiVGhlIG5hbWUgb2YgdGhlIGxpY2Vuc2UgdHlwZS4gSXQncyBlbmNvdXJhZ2VkIHRvIHVzZSBhbiBPU0kgY29tcGF0aWJsZSBsaWNlbnNlLiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAidXJsIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIlRoZSBVUkwgcG9pbnRpbmcgdG8gdGhlIGxpY2Vuc2UuIiwKICAgICAgICAgICAgICAgICAgICAiZm9ybWF0IjogInVyaSIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgImNvbnRyYWN0IjogewogICAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiIiwKICAgICAgICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICAgICAgICAgIm5hbWUiLAogICAgICAgICAgICAgICAgInRyYW5zYWN0aW9ucyIKICAgICAgICAgICAgXSwKICAgICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICAgICAiZGVmYXVsdCI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJib29sZWFuIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJpbmZvIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvaW5mbyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibmFtZSI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJBIHVuaXF1ZSBhbmQgcHJlY2lzZSB0aXRsZSBvZiB0aGUgQVBJLiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAidHJhbnNhY3Rpb25zIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogImFycmF5IiwKICAgICAgICAgICAgICAgICAgICAiaXRlbXMiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvdHJhbnNhY3Rpb24iCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICAib2JqZWN0dHlwZSI6IHsKICAgICAgICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIkEgY29tcGxleCB0eXBlIHVzZWQgaW4gYSBkb21haW4iLAogICAgICAgICAgICAicmVxdWlyZWQiOiBbCiAgICAgICAgICAgICAgICAiJGlkIiwKICAgICAgICAgICAgICAgICJwcm9wZXJ0aWVzIgogICAgICAgICAgICBdLAogICAgICAgICAgICAicHJvcGVydGllcyI6IHsKICAgICAgICAgICAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgICAgICJeLiokIjogewogICAgICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICIjL2RlZmluaXRpb25zL3NjaGVtYSIKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgInJlcXVpcmVkIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIy9kZWZpbml0aW9ucy9zdHJpbmdBcnJheSIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgInBhcmFtZXRlcnNMaXN0IjogewogICAgICAgICAgICAidHlwZSI6ICJhcnJheSIsCiAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJUaGUgcGFyYW1ldGVycyBuZWVkZWQgdG8gc2VuZCBhIHZhbGlkIEFQSSBjYWxsLiIsCiAgICAgICAgICAgICJhZGRpdGlvbmFsSXRlbXMiOiBmYWxzZSwKICAgICAgICAgICAgIml0ZW1zIjogewogICAgICAgICAgICAgICAgIm9uZU9mIjogWwogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9wYXJhbWV0ZXIiCiAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvanNvblJlZmVyZW5jZSIKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBdCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJ1bmlxdWVJdGVtcyI6IHRydWUKICAgICAgICB9LAogICAgICAgICJ0cmFuc2FjdGlvbiI6IHsKICAgICAgICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogInNpbmdsZSB0cmFuc2FjdGlvbiBzcGVjaWZpY2F0aW9uIiwKICAgICAgICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICAgICAgICAgIm5hbWUiCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgIm5hbWUiOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAibmFtZSBvZiB0aGUgdHJhbnNhY3Rpb24gIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJ0YWciOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiYXJyYXkiLAogICAgICAgICAgICAgICAgICAgICJpdGVtcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogImZyZWUgZm9ybWF0IHRhZ3MiCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJwYXJhbWV0ZXJzIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvcGFyYW1ldGVyc0xpc3QiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgInJldHVybnMiOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9zY2hlbWEiCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgICJwYXJhbWV0ZXIiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIm9iamVjdCIsCiAgICAgICAgICAgICJyZXF1aXJlZCI6IFsKICAgICAgICAgICAgICAgICJuYW1lIiwKICAgICAgICAgICAgICAgICJzY2hlbWEiCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogInN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIkEgYnJpZWYgZGVzY3JpcHRpb24gb2YgdGhlIHBhcmFtZXRlci4gVGhpcyBjb3VsZCBjb250YWluIGV4YW1wbGVzIG9mIHVzZS4gIEdpdEh1YiBGbGF2b3JlZCBNYXJrZG93biBpcyBhbGxvd2VkLiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibmFtZSI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6ICJUaGUgbmFtZSBvZiB0aGUgcGFyYW1ldGVyLiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAicmVxdWlyZWQiOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAiYm9vbGVhbiIsCiAgICAgICAgICAgICAgICAgICAgImRlc2NyaXB0aW9uIjogIkRldGVybWluZXMgd2hldGhlciBvciBub3QgdGhpcyBwYXJhbWV0ZXIgaXMgcmVxdWlyZWQgb3Igb3B0aW9uYWwuIiwKICAgICAgICAgICAgICAgICAgICAiZGVmYXVsdCI6IGZhbHNlCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgInNjaGVtYSI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICIjL2RlZmluaXRpb25zL3NjaGVtYSIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogZmFsc2UKICAgICAgICB9LAogICAgICAgICJqc29uUmVmZXJlbmNlIjogewogICAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgICAicmVxdWlyZWQiOiBbCiAgICAgICAgICAgICAgICAiJHJlZiIKICAgICAgICAgICAgXSwKICAgICAgICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogZmFsc2UsCiAgICAgICAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgIiRyZWYiOiB7CiAgICAgICAgICAgICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgfSwKICAgICAgICAic2NoZW1hIjogewogICAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgICAiZGVzY3JpcHRpb24iOiAiQSBkZXRlcm1pbmlzdGljIHZlcnNpb24gb2YgYSBKU09OIFNjaGVtYSBvYmplY3QuIiwKICAgICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICAgICAiJHJlZiI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgImZvcm1hdCI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgInRpdGxlIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIy9wcm9wZXJ0aWVzL3RpdGxlIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMvcHJvcGVydGllcy9kZXNjcmlwdGlvbiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZGVmYXVsdCI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMvcHJvcGVydGllcy9kZWZhdWx0IgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJtdWx0aXBsZU9mIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIy9wcm9wZXJ0aWVzL211bHRpcGxlT2YiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgIm1heGltdW0iOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNC9zY2hlbWEjL3Byb3BlcnRpZXMvbWF4aW11bSIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZXhjbHVzaXZlTWF4aW11bSI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMvcHJvcGVydGllcy9leGNsdXNpdmVNYXhpbXVtIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJtaW5pbXVtIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIy9wcm9wZXJ0aWVzL21pbmltdW0iCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgImV4Y2x1c2l2ZU1pbmltdW0iOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNC9zY2hlbWEjL3Byb3BlcnRpZXMvZXhjbHVzaXZlTWluaW11bSIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibWF4TGVuZ3RoIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIy9kZWZpbml0aW9ucy9wb3NpdGl2ZUludGVnZXIiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgIm1pbkxlbmd0aCI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMvZGVmaW5pdGlvbnMvcG9zaXRpdmVJbnRlZ2VyRGVmYXVsdDAiCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgInBhdHRlcm4iOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNC9zY2hlbWEjL3Byb3BlcnRpZXMvcGF0dGVybiIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibWF4SXRlbXMiOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNC9zY2hlbWEjL2RlZmluaXRpb25zL3Bvc2l0aXZlSW50ZWdlciIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibWluSXRlbXMiOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNC9zY2hlbWEjL2RlZmluaXRpb25zL3Bvc2l0aXZlSW50ZWdlckRlZmF1bHQwIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJ1bmlxdWVJdGVtcyI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMvcHJvcGVydGllcy91bmlxdWVJdGVtcyIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAibWF4UHJvcGVydGllcyI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMvZGVmaW5pdGlvbnMvcG9zaXRpdmVJbnRlZ2VyIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJtaW5Qcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIy9kZWZpbml0aW9ucy9wb3NpdGl2ZUludGVnZXJEZWZhdWx0MCIKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAicmVxdWlyZWQiOiB7CiAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNC9zY2hlbWEjL2RlZmluaXRpb25zL3N0cmluZ0FycmF5IgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJlbnVtIjogewogICAgICAgICAgICAgICAgICAgICIkcmVmIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIy9wcm9wZXJ0aWVzL2VudW0iCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgICAgICJhbnlPZiI6IFsKICAgICAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9zY2hlbWEiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIjogImJvb2xlYW4iCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgICAgICJkZWZhdWx0Ijoge30KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAidHlwZSI6IHsKICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMvcHJvcGVydGllcy90eXBlIgogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJpdGVtcyI6IHsKICAgICAgICAgICAgICAgICAgICAiYW55T2YiOiBbCiAgICAgICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvc2NoZW1hIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJhcnJheSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWluSXRlbXMiOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIml0ZW1zIjogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvc2NoZW1hIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgXSwKICAgICAgICAgICAgICAgICAgICAiZGVmYXVsdCI6IHt9CiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgImFsbE9mIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogImFycmF5IiwKICAgICAgICAgICAgICAgICAgICAibWluSXRlbXMiOiAxLAogICAgICAgICAgICAgICAgICAgICJpdGVtcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9zY2hlbWEiCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogIm9iamVjdCIsCiAgICAgICAgICAgICAgICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICIjL2RlZmluaXRpb25zL3NjaGVtYSIKICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICJkZWZhdWx0Ijoge30KICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZGlzY3JpbWluYXRvciI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgInJlYWRPbmx5IjogewogICAgICAgICAgICAgICAgICAgICJ0eXBlIjogImJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICJkZWZhdWx0IjogZmFsc2UKICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAiZXhhbXBsZSI6IHt9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJhZGRpdGlvbmFsUHJvcGVydGllcyI6IGZhbHNlCiAgICAgICAgfSwKICAgICAgICAiY29tcG9uZW50cyI6IHsKICAgICAgICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgICAgICAgICAic2NoZW1hcyI6IHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJvYmplY3QiLAogICAgICAgICAgICAgICAgICAgICJwYXR0ZXJuUHJvcGVydGllcyI6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIl4uKiQiOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJHJlZiI6ICIjL2RlZmluaXRpb25zL29iamVjdHR5cGUiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9Cn0K\"") +} diff --git a/v2/metadata/ioutils.go b/v2/metadata/ioutils.go new file mode 100644 index 0000000..1ed87eb --- /dev/null +++ b/v2/metadata/ioutils.go @@ -0,0 +1,33 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + os "os" + "path" + "runtime" +) + +// For testing! +type ioutilInterface interface { + ReadFile(string) ([]byte, error) +} + +type ioutilFront struct{} + +func (i ioutilFront) ReadFile(filename string) ([]byte, error) { + return os.ReadFile(filename) +} + +var ioutilAbs ioutilInterface = ioutilFront{} + +func readLocalFile(localPath string) ([]byte, error) { + _, filename, _, _ := runtime.Caller(1) + + schemaPath := path.Join(path.Dir(filename), localPath) + + file, err := ioutilAbs.ReadFile(schemaPath) + + return file, err +} diff --git a/v2/metadata/ioutils_test.go b/v2/metadata/ioutils_test.go new file mode 100644 index 0000000..5e23e2f --- /dev/null +++ b/v2/metadata/ioutils_test.go @@ -0,0 +1,25 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + "os" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadLocalFile(t *testing.T) { + + file, err := readLocalFile("i don't exist") + _, expectedErr := os.ReadFile("i don't exist") + assert.Nil(t, file, "should not return file on error") + assert.Contains(t, err.Error(), strings.Split(expectedErr.Error(), ":")[1], "should return same error as ioutils read file") + + file, err = readLocalFile("schema/schema.json") + expectedFile, _ := os.ReadFile("./schema/schema.json") + assert.Equal(t, expectedFile, file, "should return same file") + assert.Nil(t, err, "should return same err") +} diff --git a/v2/metadata/metadata.go b/v2/metadata/metadata.go new file mode 100644 index 0000000..cd3b1f6 --- /dev/null +++ b/v2/metadata/metadata.go @@ -0,0 +1,301 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + "encoding/json" + "errors" + "fmt" + os "os" + "path/filepath" + "reflect" + + "github.com/go-openapi/spec" + "github.com/gobuffalo/packr" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/utils" + "github.com/xeipuuv/gojsonschema" +) + +// MetadataFolder name of the main folder metadata should be placed in +const MetadataFolder = "META-INF" + +// MetadataFolderSecondary name of the secondary folder metadata should be placed in +const MetadataFolderSecondary = "contract-metadata" + +// MetadataFile name of file metadata should be written in +const MetadataFile = "metadata.json" + +// Helpers for testing +type osInterface interface { + Executable() (string, error) + Stat(string) (os.FileInfo, error) + IsNotExist(error) bool +} + +type osFront struct{} + +func (o osFront) Executable() (string, error) { + return os.Executable() +} + +func (o osFront) Stat(name string) (os.FileInfo, error) { + return os.Stat(name) +} + +func (o osFront) IsNotExist(err error) bool { + return os.IsNotExist(err) +} + +var osAbs osInterface = osFront{} + +// GetJSONSchema returns the JSON schema used for metadata +func GetJSONSchema() ([]byte, error) { + box := packr.NewBox("./schema") + + schema, err := box.Find("schema.json") + + return schema, err +} + +// ParameterMetadata details about a parameter used for a transaction. +type ParameterMetadata struct { + Description string `json:"description,omitempty"` + Name string `json:"name"` + Schema *spec.Schema `json:"schema"` + CompiledSchema *gojsonschema.Schema `json:"-"` +} + +// ReturnMetadata details about the return type for a transaction +type ReturnMetadata struct { + Schema *spec.Schema + CompiledSchema *gojsonschema.Schema +} + +// TransactionMetadata contains information on what makes up a transaction +// When JSON serialized the Returns object is flattened to contain the schema +type TransactionMetadata struct { + Parameters []ParameterMetadata `json:"parameters,omitempty"` + Returns ReturnMetadata `json:"-"` + Tag []string `json:"tag,omitempty"` + Name string `json:"name"` +} + +type tmAlias TransactionMetadata +type jsonTransactionMetadata struct { + *tmAlias + ReturnsSchema *spec.Schema `json:"returns,omitempty"` +} + +// UnmarshalJSON handles converting JSON to TransactionMetadata since returns is flattened +// in swagger +func (tm *TransactionMetadata) UnmarshalJSON(data []byte) error { + jtm := jsonTransactionMetadata{tmAlias: (*tmAlias)(tm)} + + err := json.Unmarshal(data, &jtm) + + if err != nil { + return err + } + + tm.Returns = ReturnMetadata{} + tm.Returns.Schema = jtm.ReturnsSchema + + return nil +} + +// MarshalJSON handles converting TransactionMetadata to JSON since returns is flattened +// in swagger +func (tm *TransactionMetadata) MarshalJSON() ([]byte, error) { + jtm := jsonTransactionMetadata{tmAlias: (*tmAlias)(tm), ReturnsSchema: tm.Returns.Schema} + + return json.Marshal(&jtm) +} + +// ContactMetadata contains contact details about an author of a contract/chaincode +type ContactMetadata struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Email string `json:"email,omitempty"` +} + +// LicenseMetadata contains licensing information for contract/chaincode +type LicenseMetadata struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` +} + +// InfoMetadata contains additional information to clarify use of contract/chaincode +type InfoMetadata struct { + Description string `json:"description,omitempty"` + Title string `json:"title,omitempty"` + Contact *ContactMetadata `json:"contact,omitempty"` + License *LicenseMetadata `json:"license,omitempty"` + Version string `json:"version,omitempty"` +} + +// ContractMetadata contains information about what makes up a contract +type ContractMetadata struct { + Info *InfoMetadata `json:"info,omitempty"` + Name string `json:"name"` + Transactions []TransactionMetadata `json:"transactions"` + Default bool `json:"default"` +} + +// ObjectMetadata description of a component +type ObjectMetadata struct { + ID string `json:"$id"` + Properties map[string]spec.Schema `json:"properties"` + Required []string `json:"required"` + AdditionalProperties bool `json:"additionalProperties"` +} + +// ComponentMetadata stores map of schemas of all components +type ComponentMetadata struct { + Schemas map[string]ObjectMetadata `json:"schemas,omitempty"` +} + +// ContractChaincodeMetadata describes a chaincode made using the contract api +type ContractChaincodeMetadata struct { + Info *InfoMetadata `json:"info,omitempty"` + Contracts map[string]ContractMetadata `json:"contracts"` + Components ComponentMetadata `json:"components"` +} + +// Append merge two sets of metadata. Source value will override the original +// values only in fields that are not yet set i.e. when info nil, contracts nil or +// zero length array, components empty. +func (ccm *ContractChaincodeMetadata) Append(source ContractChaincodeMetadata) { + if ccm.Info == nil { + ccm.Info = source.Info + } + + if len(ccm.Contracts) == 0 { + if ccm.Contracts == nil { + ccm.Contracts = make(map[string]ContractMetadata) + } + + for key, value := range source.Contracts { + ccm.Contracts[key] = value + } + } + + if reflect.DeepEqual(ccm.Components, ComponentMetadata{}) { + ccm.Components = source.Components + } +} + +// CompileSchemas compile parameter and return schemas for use by gojsonschema. +// When validating against the compiled schema you will need to make the +// comparison json have a key of the parameter name for parameters or +// return for return values e.g {"param1": "value"}. Compilation process +// resolves references to components +func (ccm *ContractChaincodeMetadata) CompileSchemas() error { + compileSchema := func(propName string, schema *spec.Schema, components ComponentMetadata) (*gojsonschema.Schema, error) { + combined := make(map[string]interface{}) + combined["components"] = components + combined["properties"] = make(map[string]interface{}) + combined["properties"].(map[string]interface{})[propName] = schema + + combinedLoader := gojsonschema.NewGoLoader(combined) + + return gojsonschema.NewSchema(combinedLoader) + } + + for contractName, contract := range ccm.Contracts { + for txIdx, tx := range contract.Transactions { + for paramIdx, param := range tx.Parameters { + gjsSchema, err := compileSchema(param.Name, param.Schema, ccm.Components) + + if err != nil { + return fmt.Errorf("error compiling schema for %s [%s]. %s schema invalid. %s", contractName, tx.Name, param.Name, err.Error()) + } + + param.CompiledSchema = gjsSchema + tx.Parameters[paramIdx] = param + } + + if tx.Returns.Schema != nil { + gjsSchema, err := compileSchema("return", tx.Returns.Schema, ccm.Components) + + if err != nil { + return fmt.Errorf("error compiling schema for %s [%s]. Return schema invalid. %s", contractName, tx.Name, err.Error()) + } + + tx.Returns.CompiledSchema = gjsSchema + } + + contract.Transactions[txIdx] = tx + } + ccm.Contracts[contractName] = contract + } + + return nil +} + +// ReadMetadataFile return the contents of metadata file as ContractChaincodeMetadata +func ReadMetadataFile() (ContractChaincodeMetadata, error) { + + fileMetadata := ContractChaincodeMetadata{} + + ex, execErr := osAbs.Executable() + if execErr != nil { + return ContractChaincodeMetadata{}, fmt.Errorf("failed to read metadata from file. Could not find location of executable. %s", execErr.Error()) + } + exPath := filepath.Dir(ex) + metadataPath := filepath.Join(exPath, MetadataFolder, MetadataFile) + + _, err := osAbs.Stat(metadataPath) + + if osAbs.IsNotExist(err) { + metadataPath = filepath.Join(exPath, MetadataFolderSecondary, MetadataFile) + _, err = osAbs.Stat(metadataPath) + if osAbs.IsNotExist(err) { + return ContractChaincodeMetadata{}, errors.New("failed to read metadata from file. Metadata file does not exist") + } + } + + fileMetadata.Contracts = make(map[string]ContractMetadata) + + metadataBytes, err := ioutilAbs.ReadFile(metadataPath) + + if err != nil { + return ContractChaincodeMetadata{}, fmt.Errorf("failed to read metadata from file. Could not read file %s. %s", metadataPath, err) + } + + err = json.Unmarshal(metadataBytes, &fileMetadata) + if err != nil { + return ContractChaincodeMetadata{}, err + } + + return fileMetadata, nil +} + +// ValidateAgainstSchema takes a ContractChaincodeMetadata and runs it against the +// schema that defines valid metadata structure. If it does not meet the schema it +// returns an error detailing why +func ValidateAgainstSchema(metadata ContractChaincodeMetadata) error { + jsonSchema, err := GetJSONSchema() + + if err != nil { + return fmt.Errorf("failed to read JSON schema. %s", err.Error()) + } + + metadataBytes, _ := json.Marshal(metadata) + + schemaLoader := gojsonschema.NewBytesLoader(jsonSchema) + metadataLoader := gojsonschema.NewBytesLoader(metadataBytes) + + schema, _ := gojsonschema.NewSchema(schemaLoader) + + result, err := schema.Validate(metadataLoader) + if err != nil { + return err + } + + if !result.Valid() { + return fmt.Errorf("cannot use metadata. Metadata did not match schema:\n%s", utils.ValidateErrorsToString(result.Errors())) + } + + return nil +} diff --git a/v2/metadata/metadata_main_test.go b/v2/metadata/metadata_main_test.go new file mode 100644 index 0000000..306d216 --- /dev/null +++ b/v2/metadata/metadata_main_test.go @@ -0,0 +1,25 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + "fmt" + "os" + "testing" +) + +func TestMain(m *testing.M) { + rc := m.Run() + + if rc == 0 && testing.CoverMode() != "" { + c := testing.Coverage() + + if c < 0.95 { + fmt.Println("Tests passed but coverage failed at", c) + rc = -1 + } + } + + os.Exit(rc) +} diff --git a/v2/metadata/metadata_test.go b/v2/metadata/metadata_test.go new file mode 100755 index 0000000..0fe6901 --- /dev/null +++ b/v2/metadata/metadata_test.go @@ -0,0 +1,413 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "strings" + "testing" + + "github.com/go-openapi/spec" + "github.com/stretchr/testify/assert" + "github.com/xeipuuv/gojsonschema" +) + +// ================================ +// Helpers +// ================================ + +var ContractMetaNumberOfCalls int + +type ioUtilReadFileTestStr struct{} + +func (io ioUtilReadFileTestStr) ReadFile(filename string) ([]byte, error) { + return nil, errors.New("some error") +} + +type ioUtilWorkTestStr struct{} + +func (io ioUtilWorkTestStr) ReadFile(filename string) ([]byte, error) { + if strings.Contains(filename, "schema.json") { + return os.ReadFile(filename) + } + + return []byte("{\"info\":{\"title\":\"my contract\",\"version\":\"0.0.1\"},\"contracts\":{},\"components\":{}}"), nil +} + +type osExcTestStr struct{} + +func (o osExcTestStr) Executable() (string, error) { + return "", errors.New("some error") +} + +func (o osExcTestStr) Stat(name string) (os.FileInfo, error) { + return nil, nil +} + +func (o osExcTestStr) IsNotExist(err error) bool { + return false +} + +type osStatTestStr struct{} + +func (o osStatTestStr) Executable() (string, error) { + return "", nil +} + +func (o osStatTestStr) Stat(name string) (os.FileInfo, error) { + return os.Stat("some bad file") +} + +func (o osStatTestStr) IsNotExist(err error) bool { + return os.IsNotExist(err) +} + +type osStatTestStrContractMeta struct{} + +func (o osStatTestStrContractMeta) Executable() (string, error) { + return "", nil +} + +func (o osStatTestStrContractMeta) Stat(name string) (os.FileInfo, error) { + ContractMetaNumberOfCalls++ + if ContractMetaNumberOfCalls == 1 { + ContractMetaNumberOfCalls++ + return os.Stat("some bad file") + } + return os.Stat("some good file") +} + +func (o osStatTestStrContractMeta) IsNotExist(err error) bool { + return false +} + +type osWorkTestStrContractMeta struct{} + +func (o osWorkTestStrContractMeta) Executable() (string, error) { + return "", nil +} + +func (o osWorkTestStrContractMeta) Stat(name string) (os.FileInfo, error) { + ContractMetaNumberOfCalls++ + if ContractMetaNumberOfCalls == 1 { + ContractMetaNumberOfCalls++ + return os.Stat("some bad file") + } + return os.Stat("some good file") +} + +func (o osWorkTestStrContractMeta) IsNotExist(err error) bool { + return false +} + +type osWorkTestStr struct{} + +func (o osWorkTestStr) Executable() (string, error) { + return "", nil +} + +func (o osWorkTestStr) Stat(name string) (os.FileInfo, error) { + return os.Stat("some good file") +} + +func (o osWorkTestStr) IsNotExist(err error) bool { + return false +} + +// ================================ +// Tests +// ================================ + +func TestGetJSONSchema(t *testing.T) { + var schema []byte + var err error + + expectedSchema, expectedErr := readLocalFile("schema/schema.json") + schema, err = GetJSONSchema() + + if expectedErr != nil { + panic("TEST FAILED. Reading schema should not return error") + } + + assert.Nil(t, err, "should not error when getting schema") + assert.Equal(t, expectedSchema, schema, "should return same schema as in file. Have you updated schema without running packr?") +} + +func TestUnmarshalJSON(t *testing.T) { + ttm := new(TransactionMetadata) + + err := json.Unmarshal([]byte("{\"name\": 1}"), ttm) + assert.EqualError(t, err, "json: cannot unmarshal number into Go struct field jsonTransactionMetadata.name of type string", "should error on bad JSON") + + err = json.Unmarshal([]byte("{\"name\":\"Transaction1\",\"returns\":{\"type\":\"string\"}}"), ttm) + assert.Nil(t, err, "should not error on valid json") + assert.Equal(t, &TransactionMetadata{Name: "Transaction1", Returns: ReturnMetadata{Schema: spec.StringProperty()}}, ttm, "should setup TransactionMetadata from json bytes") + +} + +func TestMarshalJSON(t *testing.T) { + ttm := TransactionMetadata{Name: "Transaction1", Returns: ReturnMetadata{Schema: spec.StringProperty()}} + bytes, err := json.Marshal(&ttm) + + assert.Nil(t, err, "should not error on marshall") + assert.Equal(t, "{\"name\":\"Transaction1\",\"returns\":{\"type\":\"string\"}}", string(bytes), "should return JSON with returns as schema not object") +} + +func TestAppend(t *testing.T) { + var ccm ContractChaincodeMetadata + + source := ContractChaincodeMetadata{} + source.Info = new(InfoMetadata) + source.Info.Title = "A title" + source.Info.Version = "Some version" + + someContract := ContractMetadata{} + someContract.Name = "some contract" + + source.Contracts = make(map[string]ContractMetadata) + source.Contracts["some contract"] = someContract + + someComponent := ObjectMetadata{} + + source.Components = ComponentMetadata{} + source.Components.Schemas = make(map[string]ObjectMetadata) + source.Components.Schemas["some component"] = someComponent + + // should use the source info when info is blank + ccm = ContractChaincodeMetadata{} + ccm.Append(source) + + assert.Equal(t, source.Info, ccm.Info, "should have used source info when info blank") + + // should use own info when info set + ccm = ContractChaincodeMetadata{} + ccm.Info = new(InfoMetadata) + ccm.Info.Title = "An existing title" + ccm.Info.Version = "Some existing version" + + someInfo := ccm.Info + + ccm.Append(source) + + assert.Equal(t, someInfo, ccm.Info, "should have used own info when info existing") + assert.NotEqual(t, source.Info, ccm.Info, "should not use source info when info exists") + + // should use the source contract when contract is 0 length and nil + ccm = ContractChaincodeMetadata{} + ccm.Append(source) + + assert.Equal(t, source.Contracts, ccm.Contracts, "should have used source info when contract 0 length map") + + // should use the source contract when contract is 0 length and not nil + ccm = ContractChaincodeMetadata{} + ccm.Contracts = make(map[string]ContractMetadata) + ccm.Append(source) + + assert.Equal(t, source.Contracts, ccm.Contracts, "should have used source info when contract 0 length map") + + // should use own contract when contract greater than 1 + anotherContract := ContractMetadata{} + anotherContract.Name = "some contract" + + ccm = ContractChaincodeMetadata{} + ccm.Contracts = make(map[string]ContractMetadata) + ccm.Contracts["another contract"] = anotherContract + + contractMap := ccm.Contracts + + assert.Equal(t, contractMap, ccm.Contracts, "should have used own contracts when contracts existing") + assert.NotEqual(t, source.Contracts, ccm.Contracts, "should not have used source contracts when existing contracts") + + // should use source components when components is empty + ccm = ContractChaincodeMetadata{} + ccm.Append(source) + + assert.Equal(t, ccm.Components, source.Components, "should use sources components") + + // should use own components when components is empty + anotherComponent := ObjectMetadata{} + + ccm = ContractChaincodeMetadata{} + ccm.Components = ComponentMetadata{} + ccm.Components.Schemas = make(map[string]ObjectMetadata) + ccm.Components.Schemas["another component"] = anotherComponent + + ccmComponent := ccm.Components + + ccm.Append(source) + + assert.Equal(t, ccmComponent, ccm.Components, "should have used own components") + assert.NotEqual(t, source.Components, ccm.Components, "should not be same as source components") +} + +func TestCompileSchemas(t *testing.T) { + var err error + + badReturn := ReturnMetadata{ + Schema: spec.RefProperty("non-existent"), + } + + badParameter := ParameterMetadata{ + Name: "badParam", + Schema: spec.RefProperty("non-existent"), + } + + goodReturn := ReturnMetadata{ + Schema: spec.Int64Property(), + } + + nilReturn := ReturnMetadata{ + Schema: nil, + } + + goodParameter1 := ParameterMetadata{ + Name: "goodParam1", + Schema: spec.RefProperty("#/components/schemas/someComponent"), + } + + goodParameter2 := ParameterMetadata{ + Name: "goodParam2", + Schema: spec.StringProperty(), + } + + someComponent := ObjectMetadata{ + Properties: make(map[string]spec.Schema), + Required: []string{}, + } + someTransaction := TransactionMetadata{ + Name: "someTransaction", + } + someContract := ContractMetadata{ + Transactions: []TransactionMetadata{someTransaction}, + } + + ccm := ContractChaincodeMetadata{} + ccm.Components = ComponentMetadata{} + ccm.Components.Schemas = make(map[string]ObjectMetadata) + ccm.Components.Schemas["someComponent"] = someComponent + ccm.Contracts = make(map[string]ContractMetadata) + ccm.Contracts["someContract"] = someContract + + someTransaction.Returns = badReturn + someContract.Transactions[0] = someTransaction + ccm.Contracts["someContract"] = someContract + err = ccm.CompileSchemas() + assert.Contains(t, err.Error(), "error compiling schema for someContract [someTransaction]. Return schema invalid.", "should error on bad schema for return value") + + someTransaction.Parameters = []ParameterMetadata{badParameter} + someContract.Transactions[0] = someTransaction + ccm.Contracts["someContract"] = someContract + err = ccm.CompileSchemas() + assert.Contains(t, err.Error(), "error compiling schema for someContract [someTransaction]. badParam schema invalid.", "should error on bad schema for param value") + + someTransaction.Returns = goodReturn + someTransaction.Parameters = []ParameterMetadata{goodParameter1, goodParameter2} + someContract.Transactions[0] = someTransaction + ccm.Contracts["someContract"] = someContract + err = ccm.CompileSchemas() + assert.Nil(t, err, "should not error on good metadata") + validateCompiledSchema(t, "goodParam1", make(map[string]interface{}), ccm.Contracts["someContract"].Transactions[0].Parameters[0].CompiledSchema) + validateCompiledSchema(t, "goodParam2", "abc", ccm.Contracts["someContract"].Transactions[0].Parameters[1].CompiledSchema) + validateCompiledSchema(t, "return", 1, ccm.Contracts["someContract"].Transactions[0].Returns.CompiledSchema) + + someTransaction.Returns = nilReturn + someTransaction.Parameters = []ParameterMetadata{goodParameter1, goodParameter2} + someContract.Transactions[0] = someTransaction + ccm.Contracts["someContract"] = someContract + err = ccm.CompileSchemas() + assert.Nil(t, err, "should not error on good metadata when return is nil") + validateCompiledSchema(t, "goodParam1", make(map[string]interface{}), ccm.Contracts["someContract"].Transactions[0].Parameters[0].CompiledSchema) + validateCompiledSchema(t, "goodParam2", "abc", ccm.Contracts["someContract"].Transactions[0].Parameters[1].CompiledSchema) + assert.Nil(t, ccm.Contracts["someContract"].Transactions[0].Returns.CompiledSchema, "should set compiled schema nil on no return") +} + +func validateCompiledSchema(t *testing.T, propName string, propValue interface{}, compiledSchema *gojsonschema.Schema) { + t.Helper() + + returnValidator := make(map[string]interface{}) + returnValidator["return"] = propValue + + toValidateLoader := gojsonschema.NewGoLoader(returnValidator) + + result, _ := compiledSchema.Validate(toValidateLoader) + + assert.True(t, result.Valid(), fmt.Sprintf("should validate for %s compiled schema", propName)) +} + +func TestReadMetadataFile(t *testing.T) { + ContractMetaNumberOfCalls = 0 + var metadata ContractChaincodeMetadata + var err error + + oldOsHelper := osAbs + + osAbs = osExcTestStr{} + metadata, err = ReadMetadataFile() + assert.EqualError(t, err, "failed to read metadata from file. Could not find location of executable. some error", "should error when cannot read file due to exec error") + assert.Equal(t, ContractChaincodeMetadata{}, metadata, "should return blank metadata when cannot read file due to exec error") + + osAbs = osStatTestStr{} + metadata, err = ReadMetadataFile() + assert.EqualError(t, err, "failed to read metadata from file. Metadata file does not exist", "should error when cannot read file due to stat error") + assert.Equal(t, ContractChaincodeMetadata{}, metadata, "should return blank metadata when cannot read file due to stat error") + + osAbs = osStatTestStrContractMeta{} + metadata, err = ReadMetadataFile() + assert.Equal(t, ContractMetaNumberOfCalls, 2, "Should check contract-metadata directory if META-INF doesn't contain metadata.json file") + assert.Contains(t, err.Error(), "failed to read metadata from file. Could not read file", "should error when cannot read file due to read error") + assert.Equal(t, ContractChaincodeMetadata{}, metadata, "should return blank metadata when cannot read file due to read error") + ContractMetaNumberOfCalls = 0 + + oldIoUtilHelper := ioutilAbs + osAbs = osWorkTestStr{} + + ioutilAbs = ioUtilReadFileTestStr{} + metadata, err = ReadMetadataFile() + assert.Contains(t, err.Error(), "failed to read metadata from file. Could not read file", "should error when cannot read file due to read error") + assert.Equal(t, ContractChaincodeMetadata{}, metadata, "should return blank metadata when cannot read file due to read error") + + ioutilAbs = ioUtilWorkTestStr{} + metadata, err = ReadMetadataFile() + assert.NoError(t, err, "should not return error when can read file") + metadataBytes := []byte("{\"info\":{\"title\":\"my contract\",\"version\":\"0.0.1\"},\"contracts\":{},\"components\":{}}") + expectedContractChaincodeMetadata := ContractChaincodeMetadata{} + err = json.Unmarshal(metadataBytes, &expectedContractChaincodeMetadata) + assert.NoError(t, err, "json unmarshal") + assert.Equal(t, expectedContractChaincodeMetadata, metadata, "should return contract metadata that was in the file") + + osAbs = osWorkTestStrContractMeta{} + metadata, err = ReadMetadataFile() + assert.Equal(t, ContractMetaNumberOfCalls, 2, "Should check contract-metadata directory if META-INF doesn't contain metadata.json file") + assert.Nil(t, err, "should not return error when can read file") + assert.Equal(t, expectedContractChaincodeMetadata, metadata, "should return contract metadata that was in the file") + ContractMetaNumberOfCalls = 0 + + ioutilAbs = oldIoUtilHelper + osAbs = oldOsHelper +} + +func TestValidateAgainstSchema(t *testing.T) { + var err error + + oldIoUtilHelper := ioutilAbs + oldOsHelper := osAbs + osAbs = osWorkTestStr{} + + metadata := ContractChaincodeMetadata{} + + ioutilAbs = ioUtilWorkTestStr{} + + err = ValidateAgainstSchema(metadata) + assert.EqualError(t, err, "cannot use metadata. Metadata did not match schema:\n1. (root): info is required\n2. contracts: Invalid type. Expected: object, given: null", "should error when metadata given does not match schema") + + metadata, _ = ReadMetadataFile() + err = ValidateAgainstSchema(metadata) + assert.Nil(t, err, "should not error for valid metadata") + + ioutilAbs = oldIoUtilHelper + osAbs = oldOsHelper +} diff --git a/v2/metadata/schema.go b/v2/metadata/schema.go new file mode 100644 index 0000000..9ae75a2 --- /dev/null +++ b/v2/metadata/schema.go @@ -0,0 +1,209 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + "fmt" + "reflect" + "strings" + "unicode" + + "github.com/go-openapi/spec" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/types" +) + +// GetSchema returns the open api spec schema for a given type. For struct types the property +// name used in the generated schema will be the name of the property unless a metadata or json +// tag exists for the property. Metadata tags take precedence over json tags. Private properties +// without a metadata tag will be ignored. Json tags are not used for private properties. Components +// will be added to component metadata if the field is a struct type. The schema will then reference +// this component +func GetSchema(field reflect.Type, components *ComponentMetadata) (*spec.Schema, error) { + return getSchema(field, components, false) +} + +func getSchema(field reflect.Type, components *ComponentMetadata, nested bool) (*spec.Schema, error) { + var schema *spec.Schema + var err error + + if bt, ok := types.BasicTypes[field.Kind()]; !ok { + if field == types.TimeType { + schema = spec.DateTimeProperty() + } else if field.Kind() == reflect.Array { + schema, err = buildArraySchema(reflect.New(field).Elem(), components, nested) + } else if field.Kind() == reflect.Slice { + schema, err = buildSliceSchema(reflect.MakeSlice(field, 1, 1), components, nested) + } else if field.Kind() == reflect.Map { + schema, err = buildMapSchema(reflect.MakeMap(field), components, nested) + } else if field.Kind() == reflect.Struct || (field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct) { + schema, err = buildStructSchema(field, components, nested) + } else { + return nil, fmt.Errorf("%s was not a valid type", field.String()) + } + } else { + return bt.GetSchema(), nil + } + + if err != nil { + return nil, err + } + + return schema, nil +} + +func buildArraySchema(array reflect.Value, components *ComponentMetadata, nested bool) (*spec.Schema, error) { + if array.Len() < 1 { + return nil, fmt.Errorf("arrays must have length greater than 0") + } + + lowerSchema, err := getSchema(array.Index(0).Type(), components, nested) + + if err != nil { + return nil, err + } + + return spec.ArrayProperty(lowerSchema), nil +} + +func buildSliceSchema(slice reflect.Value, components *ComponentMetadata, nested bool) (*spec.Schema, error) { + if slice.Len() < 1 { + slice = reflect.MakeSlice(slice.Type(), 1, 10) + } + + lowerSchema, err := getSchema(slice.Index(0).Type(), components, nested) + + if err != nil { + return nil, err + } + + return spec.ArrayProperty(lowerSchema), nil +} + +func buildMapSchema(rmap reflect.Value, components *ComponentMetadata, nested bool) (*spec.Schema, error) { + lowerSchema, err := getSchema(rmap.Type().Elem(), components, nested) + + if err != nil { + return nil, err + } + + return spec.MapProperty(lowerSchema), nil +} + +func addComponentIfNotExists(obj reflect.Type, components *ComponentMetadata) error { + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + + if _, ok := components.Schemas[obj.Name()]; ok { + return nil + } + + schema := ObjectMetadata{} + schema.ID = obj.Name() + schema.Required = []string{} + schema.Properties = make(map[string]spec.Schema) + schema.AdditionalProperties = false + + if components.Schemas == nil { + components.Schemas = make(map[string]ObjectMetadata) + } + + components.Schemas[obj.Name()] = schema // lock up slot for cyclic + + for i := 0; i < obj.NumField(); i++ { + err := getField(obj.Field(i), &schema, components) + + if err != nil { + delete(components.Schemas, obj.Name()) + return err + } + } + + components.Schemas[obj.Name()] = schema // include changes + + return nil +} + +func getField(field reflect.StructField, schema *ObjectMetadata, components *ComponentMetadata) error { + if field.Anonymous { + if field.Type.Kind() == reflect.Struct { + for i := 0; i < field.Type.NumField(); i++ { + err := getField(field.Type.Field(i), schema, components) + + if err != nil { + return err + } + } + } + + return nil + } + + name := field.Tag.Get("metadata") + required := true + + if strings.Contains(name, ",") { + spl := strings.Split(name, ",") + + name = spl[0] + + for _, val := range spl[1:] { + if strings.TrimSpace(val) == "optional" { + required = false + } + } + } + + if (unicode.IsLower([]rune(field.Name)[0]) && name == "") || name == "-" { + return nil + } else if name == "" { + name = field.Tag.Get("json") + } + + if strings.Contains(name, ",") { + spl := strings.Split(name, ",") + + name = spl[0] + } + + if name == "" || name == "-" { + name = field.Name + } + + var err error + + propSchema, err := getSchema(field.Type, components, true) + + if err != nil { + return err + } + + if required { + schema.Required = append(schema.Required, name) + } + + schema.Properties[name] = *propSchema + + return nil +} + +func buildStructSchema(obj reflect.Type, components *ComponentMetadata, nested bool) (*spec.Schema, error) { + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + + err := addComponentIfNotExists(obj, components) + + if err != nil { + return nil, err + } + + refPath := "#/components/schemas/" + + if nested { + refPath = "" + } + + return spec.RefSchema(refPath + obj.Name()), nil +} diff --git a/v2/metadata/schema/schema.json b/v2/metadata/schema/schema.json new file mode 100644 index 0000000..87b1026 --- /dev/null +++ b/v2/metadata/schema/schema.json @@ -0,0 +1,352 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "title": "Hyperledger Fabric Contract Definition JSON Schema", + "required": [ + "info", + "contracts" + ], + "properties": { + "info": { + "$ref": "#/definitions/info" + }, + "contracts": { + "type": "object", + "patternProperties": { + "^.*$": { + "$ref": "#/definitions/contract" + } + } + }, + "components": { + "$ref": "#/definitions/components" + } + }, + "definitions": { + "info": { + "type": "object", + "description": "General information about the API.", + "required": [ + "version", + "title" + ], + "properties": { + "title": { + "type": "string", + "description": "A unique and precise title of the API." + }, + "version": { + "type": "string", + "description": "A semantic version number of the API." + }, + "description": { + "type": "string", + "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed." + }, + "termsOfService": { + "type": "string", + "description": "The terms of service for the API." + }, + "contact": { + "$ref": "#/definitions/contact" + }, + "license": { + "$ref": "#/definitions/license" + } + } + }, + "contact": { + "type": "object", + "description": "Contact information for the owners of the API.", + "properties": { + "name": { + "type": "string", + "description": "The identifying name of the contact person/organization." + }, + "url": { + "type": "string", + "description": "The URL pointing to the contact information.", + "format": "uri" + }, + "email": { + "type": "string", + "description": "The email address of the contact person/organization.", + "format": "email" + } + } + }, + "license": { + "type": "object", + "required": [ + "name" + ], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the license type. It's encouraged to use an OSI compatible license." + }, + "url": { + "type": "string", + "description": "The URL pointing to the license.", + "format": "uri" + } + } + }, + "contract": { + "type": "object", + "description": "", + "required": [ + "name", + "transactions" + ], + "properties": { + "default": { + "type": "boolean" + }, + "info": { + "$ref": "#/definitions/info" + }, + "name": { + "type": "string", + "description": "A unique and precise title of the API." + }, + "transactions": { + "type": "array", + "items": { + "$ref": "#/definitions/transaction" + } + } + } + }, + "objecttype": { + "type": "object", + "description": "A complex type used in a domain", + "required": [ + "$id", + "properties" + ], + "properties": { + "properties": { + "^.*$": { + "$ref": "#/definitions/schema" + } + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + } + } + }, + "parametersList": { + "type": "array", + "description": "The parameters needed to send a valid API call.", + "additionalItems": false, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/parameter" + }, + { + "$ref": "#/definitions/jsonReference" + } + ] + }, + "uniqueItems": true + }, + "transaction": { + "type": "object", + "description": "single transaction specification", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "name of the transaction " + }, + "tag": { + "type": "array", + "items": { + "type": "string", + "description": "free format tags" + } + }, + "parameters": { + "$ref": "#/definitions/parametersList" + }, + "returns": { + "$ref": "#/definitions/schema" + } + } + }, + "parameter": { + "type": "object", + "required": [ + "name", + "schema" + ], + "properties": { + "description": { + "type": "string", + "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." + }, + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "required": { + "type": "boolean", + "description": "Determines whether or not this parameter is required or optional.", + "default": false + }, + "schema": { + "$ref": "#/definitions/schema" + } + }, + "additionalProperties": false + }, + "jsonReference": { + "type": "object", + "required": [ + "$ref" + ], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string" + } + } + }, + "schema": { + "type": "object", + "description": "A deterministic version of a JSON Schema object.", + "properties": { + "$ref": { + "type": "string" + }, + "format": { + "type": "string" + }, + "title": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + }, + "description": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + }, + "default": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + }, + "multipleOf": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + }, + "maximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + }, + "exclusiveMaximum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + }, + "minimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + }, + "exclusiveMinimum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + }, + "maxLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minLength": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "pattern": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + }, + "maxItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minItems": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "uniqueItems": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + }, + "maxProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + }, + "minProperties": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + }, + "required": { + "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + }, + "enum": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + }, + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "boolean" + } + ], + "default": {} + }, + "type": { + "$ref": "http://json-schema.org/draft-04/schema#/properties/type" + }, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/schema" + }, + { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + } + ], + "default": {} + }, + "allOf": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/schema" + } + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/schema" + }, + "default": {} + }, + "discriminator": { + "type": "string" + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "example": {} + }, + "additionalProperties": false + }, + "components": { + "type": "object", + "properties": { + "schemas": { + "type": "object", + "patternProperties": { + "^.*$": { + "$ref": "#/definitions/objecttype" + } + } + } + } + } + } +} diff --git a/v2/metadata/schema_test.go b/v2/metadata/schema_test.go new file mode 100644 index 0000000..094bb0c --- /dev/null +++ b/v2/metadata/schema_test.go @@ -0,0 +1,519 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + "errors" + "fmt" + "reflect" + "testing" + + "github.com/go-openapi/spec" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/types" + + "github.com/stretchr/testify/assert" +) + +// ================================ +// HELPERS +// ================================ + +type EmbededType struct { + Prop0 string +} + +type simpleStruct struct { + Prop1 string + //lint:ignore U1000 unused + prop2 string + //lint:ignore U1000 unused + prop3 string `metadata:"propname"` + Prop4 string `json:"jsonname" metadata:",optional"` + Prop5 string `json:"-"` + Prop6 string `metadata:"-"` + Prop7 string `metadata:",optional"` + Prop8 string `metadata:"prop8, optional"` + Prop9 string `json:"jsonname2,omitempty"` +} + +var simpleStructPropertiesMap = map[string]spec.Schema{ + "Prop1": *spec.StringProperty(), + "propname": *spec.StringProperty(), + "jsonname": *spec.StringProperty(), + "Prop5": *spec.StringProperty(), + "Prop7": *spec.StringProperty(), + "prop8": *spec.StringProperty(), + "jsonname2": *spec.StringProperty(), +} + +var simpleStructMetadata = ObjectMetadata{ + ID: "simpleStruct", + Properties: simpleStructPropertiesMap, + Required: []string{"Prop1", "propname", "Prop5", "jsonname2"}, + AdditionalProperties: false, +} + +type complexStruct struct { + EmbededType + Prop1 string + Prop2 simpleStruct + Prop3 *complexStruct `metadata:",optional"` +} + +var complexStructPropertiesMap = map[string]spec.Schema{ + "Prop0": *spec.StringProperty(), + "Prop1": *spec.StringProperty(), + "Prop2": *spec.RefSchema("simpleStruct"), + "Prop3": *spec.RefSchema("complexStruct"), +} + +var complexStructMetadata = ObjectMetadata{ + ID: "complexStruct", + Properties: complexStructPropertiesMap, + Required: []string{"Prop0", "Prop1", "Prop2"}, + AdditionalProperties: false, +} + +type superComplexStruct struct { + complexStruct + Prop4 []complexStruct + Prop5 [2]simpleStruct + Prop6 map[string]*complexStruct + Prop7 map[string][]*simpleStruct +} + +var superComplexStructPropertiesMap = map[string]spec.Schema{ + "Prop0": *spec.StringProperty(), + "Prop1": *spec.StringProperty(), + "Prop2": *spec.RefSchema("simpleStruct"), + "Prop3": *spec.RefSchema("complexStruct"), + "Prop4": *spec.ArrayProperty(spec.RefSchema("complexStruct")), + "Prop5": *spec.ArrayProperty(spec.RefSchema("simpleStruct")), + "Prop6": *spec.MapProperty(spec.RefSchema("complexStruct")), + "Prop7": *spec.MapProperty(spec.ArrayProperty(spec.RefSchema("simpleStruct"))), +} + +var superComplexStructMetadata = ObjectMetadata{ + ID: "superComplexStruct", + Properties: superComplexStructPropertiesMap, + Required: append(complexStructMetadata.Required, "Prop4", "Prop5", "Prop6", "Prop7"), + AdditionalProperties: false, +} + +type badStruct struct { + Prop1 complex64 +} + +var badType = reflect.TypeOf(complex64(1)) +var badArrayType = reflect.TypeOf([1]complex64{}) +var badSliceType = reflect.TypeOf([]complex64{}) +var badMapItemType = reflect.TypeOf(map[string]complex64{}) + +var boolRefType = reflect.TypeOf(true) +var stringRefType = reflect.TypeOf("") +var intRefType = reflect.TypeOf(1) +var int8RefType = reflect.TypeOf(int8(1)) +var int16RefType = reflect.TypeOf(int16(1)) +var int32RefType = reflect.TypeOf(int32(1)) +var int64RefType = reflect.TypeOf(int64(1)) +var uintRefType = reflect.TypeOf(uint(1)) +var uint8RefType = reflect.TypeOf(uint8(1)) +var uint16RefType = reflect.TypeOf(uint16(1)) +var uint32RefType = reflect.TypeOf(uint32(1)) +var uint64RefType = reflect.TypeOf(uint64(1)) +var float32RefType = reflect.TypeOf(float32(1.0)) +var float64RefType = reflect.TypeOf(1.0) + +func testGetSchema(t *testing.T, typ reflect.Type, expectedSchema *spec.Schema) { + var schema *spec.Schema + var err error + + t.Helper() + + schema, err = GetSchema(typ, nil) + + assert.Nil(t, err, fmt.Sprintf("err should be nil for type (%s)", typ.Name())) + assert.Equal(t, expectedSchema, schema, fmt.Sprintf("should return expected schema for type (%s)", typ.Name())) +} + +// ================================ +// TESTS +// ================================ + +func TestBuildArraySchema(t *testing.T) { + var schema *spec.Schema + var err error + + zeroArr := [0]int{} + schema, err = buildArraySchema(reflect.ValueOf(zeroArr), nil, false) + assert.Equal(t, errors.New("arrays must have length greater than 0"), err, "should throw error when 0 length array passed") + assert.Nil(t, schema, "should not have returned a schema for zero array") + + schema, err = buildArraySchema(reflect.ValueOf([1]complex128{}), nil, false) + _, expectedErr := getSchema(reflect.TypeOf(complex128(1)), nil, false) + assert.Nil(t, schema, "spec should be nil when GetSchema fails for array") + assert.Equal(t, expectedErr, err, "should have same error as GetSchema for array") + + schema, err = buildArraySchema(reflect.ValueOf([1]string{}), nil, false) + expectedLowerSchema, _ := getSchema(reflect.TypeOf(""), nil, false) + assert.Nil(t, err, "should not error for valid array") + assert.Equal(t, spec.ArrayProperty(expectedLowerSchema), schema, "should return array of lower schema") +} + +func TestBuildSliceSchema(t *testing.T) { + var schema *spec.Schema + var err error + + schema, err = buildSliceSchema(reflect.ValueOf([]complex128{}), nil, false) + _, expectedErr := GetSchema(reflect.TypeOf(complex128(1)), nil) + assert.Nil(t, schema, "spec should be nil when GetSchema errors for slice") + assert.Equal(t, expectedErr, err, "should have same error as Getschema for slice") + + schema, err = buildSliceSchema(reflect.ValueOf([]string{}), nil, false) + expectedLowerSchema, _ := getSchema(reflect.TypeOf(""), nil, false) + assert.Nil(t, err, "should not error for valid slice") + assert.Equal(t, spec.ArrayProperty(expectedLowerSchema), schema, "should return spec array of lower schema for slice") +} + +func TestBuildMapSchema(t *testing.T) { + var schema *spec.Schema + var err error + + schema, err = buildMapSchema(reflect.ValueOf(make(map[string]complex128)), nil, false) + _, expectedErr := getSchema(reflect.TypeOf(complex128(1)), nil, false) + assert.Nil(t, schema, "spec should be nil when GetSchema errors for map") + assert.Equal(t, expectedErr, err, "should have same error as Getschema for map") + + schema, err = buildMapSchema(reflect.ValueOf(make(map[string]string)), nil, false) + expectedLowerSchema, _ := getSchema(reflect.TypeOf(""), nil, false) + assert.Nil(t, err, "should not error for valid map") + assert.Equal(t, spec.MapProperty(expectedLowerSchema), schema, "should return spec map of lower schema") +} + +func TestAddComponentIfNotExists(t *testing.T) { + var err error + var components *ComponentMetadata + var ok bool + + someObject := ObjectMetadata{} + someObject.Properties = make(map[string]spec.Schema) + someObject.Properties["some property"] = spec.Schema{} + + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + components.Schemas["simpleStruct"] = someObject + + err = addComponentIfNotExists(reflect.TypeOf(simpleStruct{}), components) + assert.Nil(t, err, "should return nil for error when component of name already exists") + assert.Equal(t, len(components.Schemas), 1, "should not have added a new component when one already exists") + _, ok = components.Schemas["simpleStruct"].Properties["some property"] + assert.True(t, ok, "should not overwrite existing component") + + err = addComponentIfNotExists(reflect.TypeOf(new(simpleStruct)), components) + assert.Nil(t, err, "should return nil for error when component of name already exists for pointer") + assert.Equal(t, len(components.Schemas), 1, "should not have added a new component when one already exists for pointer") + _, ok = components.Schemas["simpleStruct"].Properties["some property"] + assert.True(t, ok, "should not overwrite existing component when already exists and pointer passed") + + err = addComponentIfNotExists(reflect.TypeOf(badStruct{}), components) + _, expectedError := GetSchema(reflect.TypeOf(complex64(1)), components) + assert.EqualError(t, err, expectedError.Error(), "should use the same error as GetSchema when GetSchema errors") + + components.Schemas = nil + err = addComponentIfNotExists(reflect.TypeOf(simpleStruct{}), components) + assert.Nil(t, err, "should not error when adding new component when schemas not initialised") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should set correct metadata for new component when schemas not initialised") + + delete(components.Schemas, "simpleStruct") + components.Schemas["otherStruct"] = someObject + err = addComponentIfNotExists(reflect.TypeOf(simpleStruct{}), components) + assert.Nil(t, err, "should not error when adding new component") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should set correct metadata for new component") + assert.Equal(t, components.Schemas["otherStruct"], someObject, "should not affect existing components") +} + +func TestBuildStructSchema(t *testing.T) { + var schema *spec.Schema + var err error + + components := new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = buildStructSchema(reflect.TypeOf(badStruct{}), components, false) + expectedErr := addComponentIfNotExists(reflect.TypeOf(badStruct{}), components) + assert.Nil(t, schema, "spec should be nil when buildStructSchema fails from addComponentIfNotExists") + assert.NotNil(t, err, "error should not be nil") + assert.Equal(t, expectedErr, err, "should have same error as addComponentIfNotExists") + + schema, err = buildStructSchema(reflect.TypeOf(simpleStruct{}), components, false) + assert.Nil(t, err, "should not return error when struct is good") + assert.Equal(t, schema, spec.RefSchema("#/components/schemas/simpleStruct"), "should make schema ref to component") + _, ok := components.Schemas["simpleStruct"] + assert.True(t, ok, "should have added component") + + schema, err = buildStructSchema(reflect.TypeOf(simpleStruct{}), components, true) + assert.Nil(t, err, "should not return error when struct is good") + assert.Equal(t, schema, spec.RefSchema("simpleStruct"), "should make schema ref to component for nested ref") + _, ok = components.Schemas["simpleStruct"] + assert.True(t, ok, "should have added component for nested ref") + + schema, err = buildStructSchema(reflect.TypeOf(new(simpleStruct)), components, false) + assert.Nil(t, err, "should not return error when pointer to struct is good") + assert.Equal(t, schema, spec.RefSchema("#/components/schemas/simpleStruct"), "should make schema ref to component") + + _, ok = components.Schemas["simpleStruct"] + assert.True(t, ok, "should have use already added component") +} + +func TestGetSchema(t *testing.T) { + var schema *spec.Schema + var err error + var expectedErr error + + components := new(ComponentMetadata) + + schema, err = GetSchema(badType, components) + assert.EqualError(t, err, "complex64 was not a valid type", "should return error for invalid type") + assert.Nil(t, schema, "should return no schema for bad type") + + schema, err = GetSchema(badArrayType, components) + _, expectedErr = buildArraySchema(reflect.New(badArrayType).Elem(), components, false) + assert.EqualError(t, err, expectedErr.Error(), "should return error when build array errors") + assert.Nil(t, schema, "should return no schema when build array errors") + + schema, err = GetSchema(badSliceType, components) + _, expectedErr = buildSliceSchema(reflect.MakeSlice(badSliceType, 1, 1), components, false) + assert.EqualError(t, err, expectedErr.Error(), "should return error when build slice errors") + assert.Nil(t, schema, "should return no schema when build slice errors") + + schema, err = GetSchema(badMapItemType, components) + _, expectedErr = buildMapSchema(reflect.MakeMap(badMapItemType), components, false) + assert.EqualError(t, err, expectedErr.Error(), "should return error when build map errors") + assert.Nil(t, schema, "should return no schema when build map errors") + + schema, err = GetSchema(reflect.TypeOf(badStruct{}), components) + _, expectedErr = buildStructSchema(reflect.TypeOf(badStruct{}), components, false) + assert.EqualError(t, err, expectedErr.Error(), "should return error when build struct errors") + assert.Nil(t, schema, "should return no schema when build struct errors") + + // Test basic types + testGetSchema(t, stringRefType, types.BasicTypes[reflect.String].GetSchema()) + testGetSchema(t, boolRefType, types.BasicTypes[reflect.Bool].GetSchema()) + testGetSchema(t, intRefType, types.BasicTypes[reflect.Int].GetSchema()) + testGetSchema(t, int8RefType, types.BasicTypes[reflect.Int8].GetSchema()) + testGetSchema(t, int16RefType, types.BasicTypes[reflect.Int16].GetSchema()) + testGetSchema(t, int32RefType, types.BasicTypes[reflect.Int32].GetSchema()) + testGetSchema(t, int64RefType, types.BasicTypes[reflect.Int64].GetSchema()) + testGetSchema(t, uintRefType, types.BasicTypes[reflect.Uint].GetSchema()) + testGetSchema(t, uint8RefType, types.BasicTypes[reflect.Uint8].GetSchema()) + testGetSchema(t, uint16RefType, types.BasicTypes[reflect.Uint16].GetSchema()) + testGetSchema(t, uint32RefType, types.BasicTypes[reflect.Uint32].GetSchema()) + testGetSchema(t, uint64RefType, types.BasicTypes[reflect.Uint64].GetSchema()) + testGetSchema(t, float32RefType, types.BasicTypes[reflect.Float32].GetSchema()) + testGetSchema(t, float64RefType, types.BasicTypes[reflect.Float64].GetSchema()) + + // Test advanced types + testGetSchema(t, types.TimeType, spec.DateTimeProperty()) + + // Should return schema for arrays made of each of the valid types + stringArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.String].GetSchema()) + boolArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Bool].GetSchema()) + intArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Int].GetSchema()) + int8ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Int8].GetSchema()) + int16ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Int16].GetSchema()) + int32ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Int32].GetSchema()) + int64ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Int64].GetSchema()) + uintArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Uint].GetSchema()) + uint8ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Uint8].GetSchema()) + uint16ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Uint16].GetSchema()) + uint32ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Uint32].GetSchema()) + uint64ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Uint64].GetSchema()) + float32ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Float32].GetSchema()) + float64ArraySchema := spec.ArrayProperty(types.BasicTypes[reflect.Float64].GetSchema()) + + testGetSchema(t, reflect.TypeOf([1]string{}), stringArraySchema) + testGetSchema(t, reflect.TypeOf([1]bool{}), boolArraySchema) + testGetSchema(t, reflect.TypeOf([1]int{}), intArraySchema) + testGetSchema(t, reflect.TypeOf([1]int8{}), int8ArraySchema) + testGetSchema(t, reflect.TypeOf([1]int16{}), int16ArraySchema) + testGetSchema(t, reflect.TypeOf([1]int32{}), int32ArraySchema) + testGetSchema(t, reflect.TypeOf([1]int64{}), int64ArraySchema) + testGetSchema(t, reflect.TypeOf([1]uint{}), uintArraySchema) + testGetSchema(t, reflect.TypeOf([1]uint8{}), uint8ArraySchema) + testGetSchema(t, reflect.TypeOf([1]uint16{}), uint16ArraySchema) + testGetSchema(t, reflect.TypeOf([1]uint32{}), uint32ArraySchema) + testGetSchema(t, reflect.TypeOf([1]uint64{}), uint64ArraySchema) + testGetSchema(t, reflect.TypeOf([1]float32{}), float32ArraySchema) + testGetSchema(t, reflect.TypeOf([1]float64{}), float64ArraySchema) + testGetSchema(t, reflect.TypeOf([1]byte{}), uint8ArraySchema) + testGetSchema(t, reflect.TypeOf([1]rune{}), int32ArraySchema) + + // Should return schema for multidimensional arrays made of each of the basic types + testGetSchema(t, reflect.TypeOf([1][1]string{}), spec.ArrayProperty(stringArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]bool{}), spec.ArrayProperty(boolArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]int{}), spec.ArrayProperty(intArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]int8{}), spec.ArrayProperty(int8ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]int16{}), spec.ArrayProperty(int16ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]int32{}), spec.ArrayProperty(int32ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]int64{}), spec.ArrayProperty(int64ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]uint{}), spec.ArrayProperty(uintArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]uint8{}), spec.ArrayProperty(uint8ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]uint16{}), spec.ArrayProperty(uint16ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]uint32{}), spec.ArrayProperty(uint32ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]uint64{}), spec.ArrayProperty(uint64ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]float32{}), spec.ArrayProperty(float32ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]float64{}), spec.ArrayProperty(float64ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]byte{}), spec.ArrayProperty(uint8ArraySchema)) + testGetSchema(t, reflect.TypeOf([1][1]rune{}), spec.ArrayProperty(int32ArraySchema)) + + // Should build schema for a big multidimensional array + testGetSchema(t, reflect.TypeOf([1][2][3][4][5][6][7][8]string{}), spec.ArrayProperty(spec.ArrayProperty(spec.ArrayProperty(spec.ArrayProperty(spec.ArrayProperty(spec.ArrayProperty(spec.ArrayProperty(stringArraySchema)))))))) + + // Should return error when array is not one of the valid types + badSlice := []complex128{} + schema, err = GetSchema(reflect.TypeOf(badSlice), nil) + + assert.EqualError(t, err, "complex128 was not a valid type", "should throw error when invalid type passed") + assert.Nil(t, schema, "should not have returned a schema for an array of bad type") + + // Should return an error when array is passed with sub array with a length of zero + zeroSubArrInSlice := [][0]int{} + schema, err = GetSchema(reflect.TypeOf(zeroSubArrInSlice), nil) + + assert.Equal(t, errors.New("arrays must have length greater than 0"), err, "should throw error when 0 length array passed") + assert.Nil(t, schema, "should not have returned a schema for zero array") + + // should build schema for slices of original types + testGetSchema(t, reflect.TypeOf([]string{""}), stringArraySchema) + testGetSchema(t, reflect.TypeOf([]bool{true}), boolArraySchema) + testGetSchema(t, reflect.TypeOf([]int{1}), intArraySchema) + testGetSchema(t, reflect.TypeOf([]int8{1}), int8ArraySchema) + testGetSchema(t, reflect.TypeOf([]int16{1}), int16ArraySchema) + testGetSchema(t, reflect.TypeOf([]int32{1}), int32ArraySchema) + testGetSchema(t, reflect.TypeOf([]int64{1}), int64ArraySchema) + testGetSchema(t, reflect.TypeOf([]uint{1}), uintArraySchema) + testGetSchema(t, reflect.TypeOf([]uint8{1}), uint8ArraySchema) + testGetSchema(t, reflect.TypeOf([]uint16{1}), uint16ArraySchema) + testGetSchema(t, reflect.TypeOf([]uint32{1}), uint32ArraySchema) + testGetSchema(t, reflect.TypeOf([]uint64{1}), uint64ArraySchema) + testGetSchema(t, reflect.TypeOf([]float32{1}), float32ArraySchema) + testGetSchema(t, reflect.TypeOf([]float64{1}), float64ArraySchema) + testGetSchema(t, reflect.TypeOf([]byte{1}), uint8ArraySchema) + testGetSchema(t, reflect.TypeOf([]rune{1}), int32ArraySchema) + + // Should return schema for multidimensional slices made of each of the basic types + testGetSchema(t, reflect.TypeOf([][]bool{{}}), spec.ArrayProperty(boolArraySchema)) + testGetSchema(t, reflect.TypeOf([][]int{{}}), spec.ArrayProperty(intArraySchema)) + testGetSchema(t, reflect.TypeOf([][]int8{{}}), spec.ArrayProperty(int8ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]int16{{}}), spec.ArrayProperty(int16ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]int32{{}}), spec.ArrayProperty(int32ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]int64{{}}), spec.ArrayProperty(int64ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]uint{{}}), spec.ArrayProperty(uintArraySchema)) + testGetSchema(t, reflect.TypeOf([][]uint8{{}}), spec.ArrayProperty(uint8ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]uint16{{}}), spec.ArrayProperty(uint16ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]uint32{{}}), spec.ArrayProperty(uint32ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]uint64{{}}), spec.ArrayProperty(uint64ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]float32{{}}), spec.ArrayProperty(float32ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]float64{{}}), spec.ArrayProperty(float64ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]byte{{}}), spec.ArrayProperty(uint8ArraySchema)) + testGetSchema(t, reflect.TypeOf([][]rune{{}}), spec.ArrayProperty(int32ArraySchema)) + + // Should handle an array of slice + testGetSchema(t, reflect.TypeOf([1][]string{}), spec.ArrayProperty(stringArraySchema)) + + // Should handle a slice of array + testGetSchema(t, reflect.TypeOf([][1]string{{}}), spec.ArrayProperty(stringArraySchema)) + + // Should handle a map + testGetSchema(t, reflect.TypeOf(map[string]int{}), spec.MapProperty(types.BasicTypes[reflect.Int].GetSchema())) + + // Should handle a of map map + testGetSchema(t, reflect.TypeOf(map[string]map[string]int{}), spec.MapProperty(spec.MapProperty(types.BasicTypes[reflect.Int].GetSchema()))) + + // Should return error when multidimensional array/slice/array is bad + badMixedArr := [1][][0]string{} + schema, err = GetSchema(reflect.TypeOf(badMixedArr), nil) + + assert.EqualError(t, err, "arrays must have length greater than 0", "should throw error when 0 length array passed") + assert.Nil(t, schema, "schema should be nil when sub array bad type") + + // Should handle a valid struct and add to components + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = GetSchema(reflect.TypeOf(simpleStruct{}), components) + + assert.Nil(t, err, "should return nil when valid object") + assert.Equal(t, len(components.Schemas), 1, "should have added a new component") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should have added correct metadata to components") + assert.Equal(t, schema, spec.RefSchema("#/components/schemas/simpleStruct")) + + // should handle pointer to struct + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = GetSchema(reflect.TypeOf(new(simpleStruct)), components) + + assert.Nil(t, err, "should return nil when valid object") + assert.Equal(t, len(components.Schemas), 1, "should have added a new component") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should have added correct metadata to components") + assert.Equal(t, schema, spec.RefSchema("#/components/schemas/simpleStruct")) + + // Should handle an array of structs + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = GetSchema(reflect.TypeOf([1]simpleStruct{}), components) + + assert.Nil(t, err, "should return nil when valid object") + assert.Equal(t, len(components.Schemas), 1, "should have added a new component") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should have added correct metadata to components") + assert.Equal(t, schema, spec.ArrayProperty(spec.RefSchema("#/components/schemas/simpleStruct"))) + + // Should handle a slice of structs + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = GetSchema(reflect.TypeOf([]simpleStruct{}), components) + + assert.Nil(t, err, "should return nil when valid object") + assert.Equal(t, len(components.Schemas), 1, "should have added a new component") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should have added correct metadata to components") + assert.Equal(t, schema, spec.ArrayProperty(spec.RefSchema("#/components/schemas/simpleStruct"))) + + // Should handle a valid struct with struct property and add to components + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = GetSchema(reflect.TypeOf(new(complexStruct)), components) + + assert.Nil(t, err, "should return nil when valid object") + assert.Equal(t, len(components.Schemas), 2, "should have added two new components") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should have added correct metadata to components for sub struct") + assert.Equal(t, components.Schemas["complexStruct"], complexStructMetadata, "should have added correct metadata to components for main struct") + assert.Equal(t, schema, spec.RefSchema("#/components/schemas/complexStruct")) + + // Should handle a valid struct with struct properties of array, slice and map types + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = GetSchema(reflect.TypeOf(new(superComplexStruct)), components) + + assert.Nil(t, err, "should return nil when valid object") + assert.Equal(t, len(components.Schemas), 3, "should have added two new components") + assert.Equal(t, components.Schemas["simpleStruct"], simpleStructMetadata, "should have added correct metadata to components for sub struct") + assert.Equal(t, components.Schemas["complexStruct"], complexStructMetadata, "should have added correct metadata to components for sub struct") + assert.Equal(t, components.Schemas["superComplexStruct"], superComplexStructMetadata, "should have added correct metadata to components for main struct") + assert.Equal(t, schema, spec.RefSchema("#/components/schemas/superComplexStruct")) + + // Should return an error for a bad struct + components = new(ComponentMetadata) + components.Schemas = make(map[string]ObjectMetadata) + + schema, err = GetSchema(reflect.TypeOf(new(badStruct)), components) + + assert.Nil(t, schema, "should not give back a schema when struct is bad") + assert.EqualError(t, err, "complex64 was not a valid type", "should return err when invalid object") + assert.Equal(t, len(components.Schemas), 0, "should not have added new component") +} diff --git a/v2/serializer/json_transaction_serializer.go b/v2/serializer/json_transaction_serializer.go new file mode 100644 index 0000000..2fe9a62 --- /dev/null +++ b/v2/serializer/json_transaction_serializer.go @@ -0,0 +1,151 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package serializer + +import ( + "encoding/json" + "fmt" + "reflect" + "time" + + "github.com/hyperledger/fabric-contract-api-go/v2/internal/types" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/utils" + "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + + "github.com/xeipuuv/gojsonschema" +) + +// JSONSerializer an implementation of TransactionSerializer for handling conversion of to and from +// JSON string formats into usable values for chaincode. It is the default serializer for chaincode +// created by NewChaincode +type JSONSerializer struct{} + +// FromString takes a parameter and converts it to a reflect value representing the goal data type. If +// a parameter metadata is passed it will validate that the converted value meets the rules specified by that +// parameter's compiled schema. For complex data structures e.g. structs, arrays etc. the string value passed +// should be in JSON format. The default Go JSON unmarshaller is used for converting complex types and as such +// it does not respect private properties or properties using the api metadata tag by default. If +// you use either of these in your struct and expect data passed in to use these then you should write +// your own unmarshall function to handle this for your struct. +// Docs on how the Go JSON Unmarshaller works: https://golang.org/pkg/encoding/json/ +// For date-time types strings should be passed in RFC3339 format. +func (js *JSONSerializer) FromString(param string, fieldType reflect.Type, paramMetadata *metadata.ParameterMetadata, components *metadata.ComponentMetadata) (reflect.Value, error) { + converted, err := convertArg(fieldType, param) + + if err != nil { + return reflect.Value{}, err + } + + if paramMetadata != nil { + err := validateAgainstSchema(paramMetadata.Name, fieldType, param, converted.Interface(), paramMetadata.CompiledSchema) + + if err != nil { + return reflect.Value{}, err + } + } + + return converted, nil +} + +// ToString takes a reflect value, the type of what the value originally was. If a non nil return metadata is supplied +// it will compare the value against the compiled schema and validate it matches the rules set out. Since it uses the compiled +// schema components is not used. It returns a string representation of the original value, complex types such as structs, arrays +// etc are returned in a JSON format. Structs, Arrays, Slices and Maps use the default JSON marshaller for creating the string. +// For structs this will therefore not include private properties (even if tagged with metadata) in the string or use the metadata +// tag value for the property name in the produced string by default. To include these within the string whilst using this serializer +// you should write a custom Marshall function on your struct +// Docs on how the Go JSON Marshaller works: https://golang.org/pkg/encoding/json/ +// For date-time types the resulting string will meet the RFC3339 format +func (js *JSONSerializer) ToString(result reflect.Value, resultType reflect.Type, returns *metadata.ReturnMetadata, components *metadata.ComponentMetadata) (string, error) { + var str string + + if !isNillableType(result.Kind()) || !result.IsNil() { + if resultType == types.TimeType { + str = result.Interface().(time.Time).Format(time.RFC3339) + } else if isMarshallingType(resultType) || resultType.Kind() == reflect.Interface && isMarshallingType(result.Type()) { + bytes, _ := json.Marshal(result.Interface()) + str = string(bytes) + } else { + str = fmt.Sprint(result.Interface()) + } + + if returns != nil { + err := validateAgainstSchema("return", resultType, str, result.Interface(), returns.CompiledSchema) + + if err != nil { + return "", err + } + } + } + + return str, nil +} + +func createArraySliceMapOrStruct(param string, objType reflect.Type) (reflect.Value, error) { + obj := reflect.New(objType) + + err := json.Unmarshal([]byte(param), obj.Interface()) + + if err != nil { + return reflect.Value{}, fmt.Errorf("value %s was not passed in expected format %s", param, objType.String()) + } + + return obj.Elem(), nil +} + +func convertArg(fieldType reflect.Type, paramValue string) (reflect.Value, error) { + var converted reflect.Value + + var err error + if fieldType == types.TimeType { + var t time.Time + t, err = time.Parse(time.RFC3339, paramValue) + converted = reflect.ValueOf(t) + } else if fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice || fieldType.Kind() == reflect.Map || fieldType.Kind() == reflect.Struct || (fieldType.Kind() == reflect.Ptr && fieldType.Elem().Kind() == reflect.Struct) { + converted, err = createArraySliceMapOrStruct(paramValue, fieldType) + } else { + converted, err = types.BasicTypes[fieldType.Kind()].Convert(paramValue) + } + + if err != nil { + return reflect.Value{}, fmt.Errorf("conversion error. %s", err.Error()) + } + + return converted, nil +} + +func validateAgainstSchema(propName string, typ reflect.Type, stringValue string, obj interface{}, schema *gojsonschema.Schema) error { + toValidate := make(map[string]interface{}) + + if typ == reflect.TypeOf(time.Time{}) { + toValidate[propName] = stringValue + } else if typ.Kind() == reflect.Struct || (typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct) { + // use a map for structs as schema seems to like that + structMap := make(map[string]interface{}) + if err := json.Unmarshal([]byte(stringValue), &structMap); err != nil { + return err + } + toValidate[propName] = structMap + } else { + toValidate[propName] = obj + } + + toValidateLoader := gojsonschema.NewGoLoader(toValidate) + + result, _ := schema.Validate(toValidateLoader) + + if !result.Valid() { + return fmt.Errorf("value did not match schema:\n%s", utils.ValidateErrorsToString(result.Errors())) + } + + return nil +} + +func isNillableType(kind reflect.Kind) bool { + return kind == reflect.Ptr || kind == reflect.Interface || kind == reflect.Map || kind == reflect.Slice || kind == reflect.Chan || kind == reflect.Func +} + +func isMarshallingType(typ reflect.Type) bool { + return typ.Kind() == reflect.Array || typ.Kind() == reflect.Slice || typ.Kind() == reflect.Map || typ.Kind() == reflect.Struct || (typ.Kind() == reflect.Ptr && isMarshallingType(typ.Elem())) +} diff --git a/v2/serializer/json_transaction_serializer_test.go b/v2/serializer/json_transaction_serializer_test.go new file mode 100644 index 0000000..06a6347 --- /dev/null +++ b/v2/serializer/json_transaction_serializer_test.go @@ -0,0 +1,319 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package serializer + +import ( + "encoding/json" + "fmt" + "reflect" + "testing" + "time" + + "github.com/go-openapi/spec" + "github.com/hyperledger/fabric-contract-api-go/v2/internal/types" + "github.com/hyperledger/fabric-contract-api-go/v2/metadata" + "github.com/stretchr/testify/assert" + "github.com/xeipuuv/gojsonschema" +) + +// ================================ +// HELPERS +// ================================ + +type simpleStruct struct { + Prop1 string `json:"prop1"` + prop2 string +} + +type UsefulInterface interface{} + +//lint:ignore U1000 unused +type usefulStruct struct { + ptr *string + iface UsefulInterface + mp map[string]string + slice []string + channel chan string + basic string + array [1]string + strct simpleStruct + strctPtr *simpleStruct +} + +func (us usefulStruct) DoNothing() string { + return "nothing" +} + +func createGoJSONSchemaSchema(propName string, schema *spec.Schema, components *metadata.ComponentMetadata) *gojsonschema.Schema { + combined := make(map[string]interface{}) + combined["components"] = components + combined["properties"] = make(map[string]interface{}) + combined["properties"].(map[string]interface{})[propName] = schema + + combinedLoader := gojsonschema.NewGoLoader(combined) + + gjs, _ := gojsonschema.NewSchema(combinedLoader) + + return gjs +} + +func testConvertArgsBasicType(t *testing.T, expected interface{}, str string) { + t.Helper() + + var expectedValue reflect.Value + var actualValue reflect.Value + var actualErr error + + typ := reflect.TypeOf(expected) + + expectedValue, _ = types.BasicTypes[typ.Kind()].Convert(str) + actualValue, actualErr = convertArg(typ, str) + assert.Nil(t, actualErr, fmt.Sprintf("should not return an error on good convert (%s)", typ.Name())) + assert.Equal(t, expectedValue.Interface(), actualValue.Interface(), fmt.Sprintf("should return same value as convert for good convert (%s)", typ.Name())) +} + +func testConvertArgsComplexType(t *testing.T, expected interface{}, str string) { + t.Helper() + + var expectedValue reflect.Value + var actualValue reflect.Value + var actualErr error + + typ := reflect.TypeOf(expected) + + expectedValue, _ = createArraySliceMapOrStruct(str, typ) + actualValue, actualErr = convertArg(typ, str) + assert.Nil(t, actualErr, fmt.Sprintf("should not return an error on good complex convert (%s)", typ.Name())) + assert.Equal(t, expectedValue.Interface(), actualValue.Interface(), fmt.Sprintf("should return same value as convert for good complex convert (%s)", typ.Name())) +} + +// ================================ +// TESTS +// ================================ + +func TestIsNillableType(t *testing.T) { + usefulStruct := usefulStruct{} + usefulStructType := reflect.TypeOf(usefulStruct) + + assert.True(t, isNillableType(usefulStructType.Field(0).Type.Kind()), "should return true for pointers") + + assert.True(t, isNillableType(usefulStructType.Field(1).Type.Kind()), "should return true for interfaces") + + assert.True(t, isNillableType(usefulStructType.Field(2).Type.Kind()), "should return true for maps") + + assert.True(t, isNillableType(usefulStructType.Field(3).Type.Kind()), "should return true for slices") + + assert.True(t, isNillableType(usefulStructType.Field(4).Type.Kind()), "should return true for channels") + + assert.True(t, isNillableType(usefulStructType.Method(0).Type.Kind()), "should return true for func") + + assert.False(t, isNillableType(usefulStructType.Field(5).Type.Kind()), "should return false for something that isnt the above") +} + +func TestIsMarshallingType(t *testing.T) { + usefulStruct := usefulStruct{} + usefulStructType := reflect.TypeOf(usefulStruct) + + assert.True(t, isMarshallingType(usefulStructType.Field(6).Type), "should return true for arrays") + + assert.True(t, isMarshallingType(usefulStructType.Field(3).Type), "should return true for slices") + + assert.True(t, isMarshallingType(usefulStructType.Field(2).Type), "should return true for maps") + + assert.True(t, isMarshallingType(usefulStructType.Field(7).Type), "should return true for structs") + + assert.True(t, isMarshallingType(usefulStructType.Field(8).Type), "should return true for pointer of marshalling type") + + assert.False(t, isMarshallingType(usefulStructType.Field(5).Type), "should return false for something that isnt the above") + + assert.False(t, isMarshallingType(usefulStructType.Field(0).Type), "should return false for pointer to non marshalling type") +} + +func TestCreateArraySliceMapOrStruct(t *testing.T) { + var val reflect.Value + var err error + + arrType := reflect.TypeOf([1]string{}) + val, err = createArraySliceMapOrStruct("bad json", arrType) + assert.EqualError(t, err, fmt.Sprintf("value %s was not passed in expected format %s", "bad json", arrType.String()), "should error when JSON marshall fails") + assert.Equal(t, reflect.Value{}, val, "should return an empty value when error found") + + val, err = createArraySliceMapOrStruct("[\"array\"]", arrType) + assert.Nil(t, err, "should not error for valid array JSON") + assert.Equal(t, [1]string{"array"}, val.Interface().([1]string), "should produce populated array") + + val, err = createArraySliceMapOrStruct("[\"slice\", \"slice\", \"baby\"]", reflect.TypeOf([]string{})) + assert.Nil(t, err, "should not error for valid slice JSON") + assert.Equal(t, []string{"slice", "slice", "baby"}, val.Interface().([]string), "should produce populated slice") + + val, err = createArraySliceMapOrStruct("{\"Prop1\": \"value\"}", reflect.TypeOf(simpleStruct{})) + assert.Nil(t, err, "should not error for valid struct json") + assert.Equal(t, simpleStruct{"value", ""}, val.Interface().(simpleStruct), "should produce populated struct") + + val, err = createArraySliceMapOrStruct("{\"key\": 1}", reflect.TypeOf(make(map[string]int))) + assert.Nil(t, err, "should not error for valid map JSON") + assert.Equal(t, map[string]int{"key": 1}, val.Interface().(map[string]int), "should produce populated map") +} + +func TestConvertArg(t *testing.T) { + var expectedErr error + var actualValue reflect.Value + var actualErr error + + _, expectedErr = types.BasicTypes[reflect.Int].Convert("NaN") + actualValue, actualErr = convertArg(reflect.TypeOf(1), "NaN") + assert.Equal(t, reflect.Value{}, actualValue, "should not return a value when basic type conversion fails") + assert.EqualError(t, actualErr, fmt.Sprintf("conversion error. %s", expectedErr.Error()), "should error on basic type conversion error using message") + + _, expectedErr = createArraySliceMapOrStruct("Not an array", reflect.TypeOf([1]string{})) + actualValue, actualErr = convertArg(reflect.TypeOf([1]string{}), "Not an array") + assert.Equal(t, reflect.Value{}, actualValue, "should not return a value when complex type conversion fails") + assert.EqualError(t, actualErr, fmt.Sprintf("conversion error. %s", expectedErr.Error()), "should error on complex type conversion error using message") + + // should handle basic types + testConvertArgsBasicType(t, "some string", "some string") + testConvertArgsBasicType(t, 1, "1") + testConvertArgsBasicType(t, int8(1), "1") + testConvertArgsBasicType(t, int16(1), "1") + testConvertArgsBasicType(t, int32(1), "1") + testConvertArgsBasicType(t, int64(1), "1") + testConvertArgsBasicType(t, uint(1), "1") + testConvertArgsBasicType(t, uint8(1), "1") + testConvertArgsBasicType(t, uint16(1), "1") + testConvertArgsBasicType(t, uint32(1), "1") + testConvertArgsBasicType(t, uint64(1), "1") + testConvertArgsBasicType(t, true, "true") + + // should handle array, slice, map and struct + testConvertArgsComplexType(t, [1]int{}, "[1,2,3]") + testConvertArgsComplexType(t, []string{}, "[\"a\",\"string\",\"array\"]") + testConvertArgsComplexType(t, make(map[string]bool), "{\"a\": true, \"b\": false}") + testConvertArgsComplexType(t, simpleStruct{}, "{\"Prop1\": \"hello\"}") + testConvertArgsComplexType(t, &simpleStruct{}, "{\"Prop1\": \"hello\"}") + + // should handle time + actualValue, actualErr = convertArg(types.TimeType, "2002-10-02T15:00:00Z") + assert.Nil(t, actualErr, "should not return error for RFC3339 type") + expectedTime, _ := time.Parse(time.RFC3339, "2002-10-02T15:00:00Z") + assert.Equal(t, expectedTime, actualValue.Interface(), "should create error using string for error type") +} + +func TestValidateAgainstSchema(t *testing.T) { + var comparisonSchema *gojsonschema.Schema + var err error + + components := new(metadata.ComponentMetadata) + + comparisonSchema = createGoJSONSchemaSchema("prop", types.BasicTypes[reflect.Uint].GetSchema(), components) + err = validateAgainstSchema("prop", reflect.TypeOf(-1), "-1", -1, comparisonSchema) + assert.Contains(t, err.Error(), "value did not match schema", "should error when data doesnt match schema") + + comparisonSchema = createGoJSONSchemaSchema("prop", spec.DateTimeProperty(), nil) + timeObj, _ := time.Parse(time.RFC3339, "2002-10-02T15:00:00Z") + err = validateAgainstSchema("prop", types.TimeType, "2002/10/02 15:00:00", timeObj, comparisonSchema) + assert.Contains(t, err.Error(), "value did not match schema", "should error for invalid time") + + comparisonSchema = createGoJSONSchemaSchema("prop", types.BasicTypes[reflect.Uint].GetSchema(), components) + err = validateAgainstSchema("prop", reflect.TypeOf(10), "10", 10, comparisonSchema) + assert.Nil(t, err, "should not error when matches schema") + + expectedStruct := new(simpleStruct) + expectedStruct.Prop1 = "hello" + bytes, _ := json.Marshal(expectedStruct) + schema, _ := metadata.GetSchema(reflect.TypeOf(expectedStruct), components) + comparisonSchema = createGoJSONSchemaSchema("prop", schema, components) + err = validateAgainstSchema("prop", reflect.TypeOf(expectedStruct), string(bytes), expectedStruct, comparisonSchema) + assert.Nil(t, err, "should handle struct") +} + +func TestFromString(t *testing.T) { + var err error + var value reflect.Value + var expectedErr error + var schema *spec.Schema + var compiledSchema *gojsonschema.Schema + var paramMetadata metadata.ParameterMetadata + + serializer := new(JSONSerializer) + + value, err = serializer.FromString("some string", reflect.TypeOf(1), nil, nil) + _, expectedErr = convertArg(reflect.TypeOf(1), "some string") + assert.EqualError(t, err, expectedErr.Error(), "should error when convertArg errors") + assert.Equal(t, reflect.Value{}, value, "should return an empty reflect value when it errors due to convertArg") + + float := float64(2) + schema = spec.Int64Property() + schema.Minimum = &float + compiledSchema = createGoJSONSchemaSchema("param1", schema, nil) + paramMetadata = metadata.ParameterMetadata{Name: "param1", Schema: schema, CompiledSchema: compiledSchema} + value, err = serializer.FromString("1", reflect.TypeOf(1), ¶mMetadata, nil) + expectedErr = validateAgainstSchema("param1", reflect.TypeOf(1), "1", 1, compiledSchema) + assert.EqualError(t, err, expectedErr.Error(), "should error when validateAgainstSchema errors") + assert.Equal(t, reflect.Value{}, value, "should return an empty reflect value when it errors due to validateAgainstSchema") + + value, err = serializer.FromString("1234", reflect.TypeOf(1), nil, nil) + assert.Nil(t, err, "should not error when convert args passes and no schema") + assert.Equal(t, reflect.ValueOf(1234).Interface(), value.Interface(), "should reflect value for converted arg") + + expectedStruct := new(simpleStruct) + expectedStruct.Prop1 = "hello" + components := new(metadata.ComponentMetadata) + schema, _ = metadata.GetSchema(reflect.TypeOf(expectedStruct), components) + compiledSchema = createGoJSONSchemaSchema("param1", schema, components) + paramMetadata = metadata.ParameterMetadata{Name: "param1", Schema: schema, CompiledSchema: compiledSchema} + value, err = serializer.FromString("{\"prop1\":\"hello\"}", reflect.TypeOf(expectedStruct), ¶mMetadata, components) + assert.Nil(t, err, "should not error when convert args passes and schema passes") + assert.Equal(t, reflect.ValueOf(expectedStruct).Interface(), value.Interface(), "should reflect value for converted arg when arg and schema passes") +} + +func TestToString(t *testing.T) { + var err error + var value string + var expectedErr error + var schema *spec.Schema + var compiledSchema *gojsonschema.Schema + var returnMetadata metadata.ReturnMetadata + + serializer := new(JSONSerializer) + + var nilResult *simpleStruct + value, err = serializer.ToString(reflect.ValueOf(nilResult), reflect.TypeOf(new(simpleStruct)), nil, nil) + assert.Nil(t, err, "should not error when receives nil") + assert.Equal(t, "", value, "should return blank string for nil value") + + result := new(simpleStruct) + result.Prop1 = "property 1" + value, err = serializer.ToString(reflect.ValueOf(result), reflect.TypeOf(result), nil, nil) + assert.Nil(t, err, "should not error when receives non nil nillable type") + assert.Equal(t, "{\"prop1\":\"property 1\"}", value, "should return JSON formatted value for marshallable type") + + value, err = serializer.ToString(reflect.ValueOf(1), reflect.TypeOf(1), nil, nil) + assert.Nil(t, err, "should not error when receives non nillable and marshalling type") + assert.Equal(t, "1", value, "should return sprint version of value when not marshalling type") + + testTime, _ := time.Parse(time.RFC3339, "2002-10-02T15:00:00Z") + value, err = serializer.ToString(reflect.ValueOf(testTime), types.TimeType, nil, nil) + assert.Nil(t, err, "should not error for time") + assert.Equal(t, "2002-10-02T15:00:00Z", value, "should return string version of time in RFC3339 format") + + float := float64(2) + schema = spec.Int64Property() + schema.Minimum = &float + compiledSchema = createGoJSONSchemaSchema("return", schema, nil) + returnMetadata = metadata.ReturnMetadata{Schema: schema, CompiledSchema: compiledSchema} + value, err = serializer.ToString(reflect.ValueOf(1), reflect.TypeOf(1), &returnMetadata, nil) + expectedErr = validateAgainstSchema("return", reflect.TypeOf(1), "1", 1, compiledSchema) + assert.EqualError(t, err, expectedErr.Error(), "should error when validateAgainstSchema errors") + assert.Equal(t, "", value, "should return an empty string value when it errors due to validateAgainstSchema") + + expectedStruct := new(simpleStruct) + expectedStruct.Prop1 = "hello" + components := new(metadata.ComponentMetadata) + schema, _ = metadata.GetSchema(reflect.TypeOf(expectedStruct), components) + compiledSchema = createGoJSONSchemaSchema("return", schema, components) + returnMetadata = metadata.ReturnMetadata{Schema: schema, CompiledSchema: compiledSchema} + value, err = serializer.ToString(reflect.ValueOf(expectedStruct), reflect.TypeOf(expectedStruct), &returnMetadata, components) + assert.Nil(t, err, "should not error when making a string passes and schema passes") + assert.Equal(t, "{\"prop1\":\"hello\"}", value, "should return string value when schema passes") +} diff --git a/v2/serializer/serializer_test.go b/v2/serializer/serializer_test.go new file mode 100644 index 0000000..c0752cb --- /dev/null +++ b/v2/serializer/serializer_test.go @@ -0,0 +1,25 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package serializer + +import ( + "fmt" + "os" + "testing" +) + +func TestMain(m *testing.M) { + rc := m.Run() + + if rc == 0 && testing.CoverMode() != "" { + c := testing.Coverage() + + if c < 1 { + fmt.Println("Tests passed but coverage failed at", c) + rc = -1 + } + } + + os.Exit(rc) +} diff --git a/v2/serializer/transaction_serializer.go b/v2/serializer/transaction_serializer.go new file mode 100644 index 0000000..e365924 --- /dev/null +++ b/v2/serializer/transaction_serializer.go @@ -0,0 +1,29 @@ +// Copyright the Hyperledger Fabric contributors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package serializer + +import ( + "reflect" + + "github.com/hyperledger/fabric-contract-api-go/v2/metadata" +) + +// TransactionSerializer defines the functions a valid transaction serializer +// should have. Serializers to be used by a chaincode must implement this interface. Serializers +// are called on calls to chaincode with FromString used to format arguments going in to a call +// and ToString to format the success response from a call +type TransactionSerializer interface { + // FromString receives the value in its original string form, the reflected type that the + // new value should be of, the schema defining the rules that the converted value should + // adhere to and components which the schema may point to as a reference. The schema and + // component metadata may be nil. The function should produce a reflect value which matches + // the goal type. + FromString(string, reflect.Type, *metadata.ParameterMetadata, *metadata.ComponentMetadata) (reflect.Value, error) + + // ToString receives a reflected value of a value, the reflected type of that that value was + // originally, the schema defining the rules of what that value should meet and components + // which the schema may point to as a reference. The schema and component metadata may be nil + // The function should produce a string which represents the original value + ToString(reflect.Value, reflect.Type, *metadata.ReturnMetadata, *metadata.ComponentMetadata) (string, error) +}